├── .gitignore ├── LightmapRepacker ├── Assets │ ├── Editor.meta │ ├── Editor │ │ ├── Common.meta │ │ ├── Common │ │ │ ├── Debug.cs │ │ │ ├── Debug.cs.meta │ │ │ ├── EditorUtils.cs │ │ │ ├── EditorUtils.cs.meta │ │ │ ├── FileUtils.cs │ │ │ ├── FileUtils.cs.meta │ │ │ ├── JSON.meta │ │ │ ├── JSON │ │ │ │ ├── JSONObject.cs │ │ │ │ ├── JSONObject.cs.meta │ │ │ │ ├── JSONTemplates.cs │ │ │ │ ├── JSONTemplates.cs.meta │ │ │ │ ├── SimpleJSON.cs │ │ │ │ ├── SimpleJSON.cs.meta │ │ │ │ ├── VectorTemplates.cs │ │ │ │ └── VectorTemplates.cs.meta │ │ │ ├── StringUtils.cs │ │ │ ├── StringUtils.cs.meta │ │ │ ├── UnityLogFile.cs │ │ │ ├── UnityLogFile.cs.meta │ │ │ ├── Utils.cs │ │ │ └── Utils.cs.meta │ │ ├── LightmapRepacker.cs │ │ ├── LightmapRepacker.cs.meta │ │ ├── LightmapViewer.cs │ │ ├── LightmapViewer.cs.meta │ │ ├── NativeAPI.cs │ │ ├── NativeAPI.cs.meta │ │ ├── UVSheetViewer.cs │ │ └── UVSheetViewer.cs.meta │ ├── Plugins.meta │ ├── Plugins │ │ ├── x86.meta │ │ ├── x86 │ │ │ ├── FGDKit_NativeCore.dll │ │ │ └── FGDKit_NativeCore.dll.meta │ │ ├── x86_64.meta │ │ └── x86_64 │ │ │ ├── FGDKit_NativeCore.dll │ │ │ └── FGDKit_NativeCore.dll.meta │ ├── Scene.meta │ ├── Scene │ │ ├── mat.mat │ │ ├── mat.mat.meta │ │ ├── static_41417.FBX │ │ ├── static_41417.FBX.meta │ │ ├── temp.meta │ │ ├── test.meta │ │ ├── test.unity │ │ ├── test.unity.meta │ │ └── test │ │ │ ├── Building.fbx │ │ │ ├── Building.fbx.meta │ │ │ ├── LightingData.asset │ │ │ ├── LightingData.asset.meta │ │ │ ├── Lightmap-0_comp_light.exr │ │ │ ├── Lightmap-0_comp_light.exr.meta │ │ │ ├── ReflectionProbe-0.exr │ │ │ └── ReflectionProbe-0.exr.meta │ ├── gmcs.rsp │ └── gmcs.rsp.meta ├── LightmapRepacker.sln ├── Packages │ └── manifest.json ├── ProjectSettings │ ├── AudioManager.asset │ ├── ClusterInputManager.asset │ ├── DynamicsManager.asset │ ├── EditorBuildSettings.asset │ ├── EditorSettings.asset │ ├── GraphicsSettings.asset │ ├── InputManager.asset │ ├── NavMeshAreas.asset │ ├── NetworkManager.asset │ ├── Physics2DSettings.asset │ ├── PresetManager.asset │ ├── ProjectSettings.asset │ ├── ProjectVersion.txt │ ├── QualitySettings.asset │ ├── TagManager.asset │ ├── TimeManager.asset │ └── UnityConnectSettings.asset ├── obj │ └── Debug │ │ ├── Assembly-CSharp-Editor.csproj.CoreCompileInputs.cache │ │ ├── Assembly-CSharp-Editor.csproj.FileListAbsolute.txt │ │ ├── Assembly-CSharp-Editor.csprojAssemblyReference.cache │ │ ├── Assembly-CSharp-Editor.csprojResolveAssemblyReference.cache │ │ └── DesignTimeResolveAssemblyReferencesInput.cache └── pictures │ ├── 1.png │ ├── 10.png │ ├── 100.png │ ├── 11.png │ ├── 111.png │ ├── 3.png │ ├── 4.png │ ├── after_fix_scaleOffset.png │ ├── after_fix_scaleOffset_2.png │ ├── final0.png │ ├── final1.png │ └── perfect_rect.png └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.suo 2 | .vs 3 | *.csproj 4 | Temp 5 | Library -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0e9c0f6fd3d752444b264e693d6cb3c9 3 | folderAsset: yes 4 | timeCreated: 1517970613 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 39786c042a70ef24299d579f9a2e7118 3 | folderAsset: yes 4 | timeCreated: 1517971882 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/Debug.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Diagnostics; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading; 8 | 9 | namespace Common { 10 | 11 | public static class UDebug { 12 | 13 | delegate void LogDelegate( String msg ); 14 | delegate void LogExceptionDelegate( Exception e ); 15 | static LogDelegate _Log = NoAction; 16 | static LogDelegate _LogError = NoAction; 17 | static LogDelegate _LogWarning = NoAction; 18 | static LogExceptionDelegate _LogException = NoActionException; 19 | static int _LogCount = 0; 20 | static int _ErrorCount = 0; 21 | static int _WarningCount = 0; 22 | static int _ExceptionCount = 0; 23 | static int _AssertFailCount = 0; 24 | static bool _enabled = true; 25 | 26 | public static bool showCallStackEnabled = true; 27 | public static bool enabled { 28 | get { 29 | return _enabled; 30 | } 31 | set { 32 | if ( _enabled != value ) { 33 | _enabled = value; 34 | if ( _enabled ) { 35 | _Log = UnityEngine.Debug.Log; 36 | _LogError = UnityEngine.Debug.LogError; 37 | _LogException = UnityEngine.Debug.LogException; 38 | _LogWarning = UnityEngine.Debug.LogWarning; 39 | } else { 40 | _Log = NoAction; 41 | _LogError = NoAction; 42 | _LogWarning = NoAction; 43 | _LogException = NoActionException; 44 | } 45 | } 46 | } 47 | } 48 | 49 | public static int errorCount { 50 | get { 51 | return _ErrorCount; 52 | } 53 | } 54 | 55 | public static int warningCount { 56 | get { 57 | return _WarningCount; 58 | } 59 | } 60 | 61 | public static int assertFailCount { 62 | get { 63 | return _AssertFailCount; 64 | } 65 | } 66 | 67 | public static int exceptionCount { 68 | get { 69 | return _ExceptionCount; 70 | } 71 | } 72 | 73 | public static bool hasWarning { 74 | get { 75 | return ( _ExceptionCount + _ErrorCount + _AssertFailCount + _WarningCount ) > 0; 76 | } 77 | } 78 | 79 | public static bool hasError { 80 | get { 81 | return ( _ExceptionCount + _ErrorCount + _AssertFailCount ) > 0; 82 | } 83 | } 84 | 85 | public static void ClearLogCounter() { 86 | Interlocked.Exchange( ref _LogCount, 0 ); 87 | Interlocked.Exchange( ref _ErrorCount, 0 ); 88 | Interlocked.Exchange( ref _WarningCount, 0 ); 89 | Interlocked.Exchange( ref _ExceptionCount, 0 ); 90 | Interlocked.Exchange( ref _AssertFailCount, 0 ); 91 | } 92 | 93 | public delegate bool AssertTest(); 94 | 95 | public static void MessageBox( String s ) { 96 | #if UNITY_EDITOR && UNITY_EDITOR_WIN 97 | if ( showCallStackEnabled ) { 98 | using ( var prcShell = new System.Diagnostics.Process() ) { 99 | prcShell.StartInfo.FileName = UnityEngine.Application.dataPath + "/../../Tool/Bin/MsgBoxer.exe"; 100 | Common.UDebug.Log( prcShell.StartInfo.FileName ); 101 | prcShell.StartInfo.Arguments = "\"" + s + "\""; 102 | prcShell.Start(); 103 | } 104 | } 105 | #endif 106 | } 107 | 108 | public static void ShowCallStack() { 109 | MessageBox( GetCallStack( 2 ) ); 110 | } 111 | 112 | public static String GetCallStack( int skipFrame = 1 ) { 113 | StackTrace stackTrace = new StackTrace( skipFrame, true ); // get call stack 114 | StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) 115 | // write call stack method names 116 | var sb = new StringBuilder( 200 ); 117 | for ( int i = 0; i < stackFrames.Length; ++i ) { 118 | var frame = stackFrames[i]; 119 | sb.Append( frame.GetMethod() ); 120 | sb.Append( "(at " ); 121 | sb.Append( frame.GetFileName() ); 122 | sb.Append( ": " ); 123 | sb.Append( frame.GetFileLineNumber() ); 124 | sb.Append( ")\n" ); 125 | } 126 | return sb.ToString(); 127 | } 128 | 129 | public static int GetCallStackSize() { 130 | StackTrace stackTrace = new StackTrace( 1, false ); // get call stack 131 | return stackTrace.FrameCount; 132 | } 133 | 134 | public static String SafeFormat( String format, T arg ) { 135 | if ( format != null ) { 136 | try { 137 | return String.Format( format, arg ); 138 | } catch ( Exception e ) { 139 | Common.UDebug.LogException( e ); 140 | } 141 | } 142 | return String.Empty; 143 | } 144 | 145 | public static String SafeFormat( String format, T1 arg1, T2 arg2 ) { 146 | if ( format != null ) { 147 | try { 148 | return String.Format( format, arg1, arg2 ); 149 | } catch ( Exception e ) { 150 | Common.UDebug.LogException( e ); 151 | } 152 | } 153 | return String.Empty; 154 | } 155 | 156 | public static String SafeFormat( String format, T1 arg1, T2 arg2, T3 arg3 ) { 157 | if ( format != null ) { 158 | try { 159 | return String.Format( format, arg1, arg2, arg3 ); 160 | } catch ( Exception e ) { 161 | Common.UDebug.LogException( e ); 162 | } 163 | } 164 | return String.Empty; 165 | } 166 | 167 | public static String SafeFormat( String format, T1 arg1, T2 arg2, T3 arg3, T4 arg4 ) { 168 | if ( format != null ) { 169 | try { 170 | return String.Format( format, arg1, arg2, arg3, arg4 ); 171 | } catch ( Exception e ) { 172 | Common.UDebug.LogException( e ); 173 | } 174 | } 175 | return String.Empty; 176 | } 177 | 178 | public static String SafeFormat( String format, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5 ) { 179 | if ( format != null ) { 180 | try { 181 | return String.Format( format, arg1, arg2, arg3, arg4, arg5 ); 182 | } catch ( Exception e ) { 183 | Common.UDebug.LogException( e ); 184 | } 185 | } 186 | return String.Empty; 187 | } 188 | 189 | public static String SafeFormat( String format, params object[] args ) { 190 | if ( format != null && args != null ) { 191 | try { 192 | return String.Format( format, args ); 193 | } catch ( Exception e ) { 194 | Common.UDebug.LogException( e ); 195 | } 196 | } 197 | return String.Empty; 198 | } 199 | 200 | #if UNITY_EDITOR 201 | static int _IgnoreCount = 0; 202 | #endif 203 | 204 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 205 | static public void Assert( bool condition, string assertString = null ) { 206 | if ( !condition ) { 207 | Interlocked.Increment( ref _AssertFailCount ); 208 | #if UNITY_EDITOR 209 | try { 210 | StackTrace myTrace = new StackTrace( true ); 211 | StackFrame myFrame = myTrace.GetFrame( 1 ); 212 | assertString = assertString ?? String.Empty; 213 | string assertInformation = "Filename: " + myFrame.GetFileName() + "\nMethod: " + myFrame.GetMethod() + "\nLine: " + myFrame.GetFileLineNumber(); 214 | ULogFile.sharedInstance.LogError( assertInformation ); 215 | var s = assertString + "\n" + assertInformation; 216 | LogError( s ); 217 | if ( _IgnoreCount < 5 ) { 218 | UnityEngine.Debug.Break(); 219 | if ( UnityEditor.EditorUtility.DisplayDialog( "Assert!", s, "Debug", "Ignore" ) ) { 220 | UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal( myFrame.GetFileName(), myFrame.GetFileLineNumber() ); 221 | } else { 222 | ++_IgnoreCount; 223 | } 224 | } 225 | } catch ( Exception e ) { 226 | UnityEngine.Debug.LogError( e.ToString() ); 227 | } 228 | #else 229 | System.Diagnostics.Debug.Assert( condition, assertString ); 230 | #endif 231 | } 232 | } 233 | 234 | #region base logger 235 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 236 | public static void Log( String arg ) { 237 | Interlocked.Increment( ref _LogCount ); 238 | _Log( arg ); 239 | } 240 | 241 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 242 | public static void Log( T arg ) { 243 | Interlocked.Increment( ref _LogCount ); 244 | _Log( arg.ToString() ); 245 | } 246 | 247 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 248 | public static void LogError( String arg ) { 249 | Interlocked.Increment( ref _ErrorCount ); 250 | _LogError( arg ); 251 | } 252 | 253 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 254 | public static void LogError( T arg ) { 255 | Interlocked.Increment( ref _ErrorCount ); 256 | _LogError( arg.ToString() ); 257 | } 258 | 259 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 260 | public static void LogWarning( String arg ) { 261 | Interlocked.Increment( ref _WarningCount ); 262 | _LogWarning( arg ); 263 | } 264 | 265 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 266 | public static void LogWarning( T arg ) { 267 | Interlocked.Increment( ref _WarningCount ); 268 | _LogWarning( arg.ToString() ); 269 | } 270 | 271 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 272 | public static void LogException( Exception e ) { 273 | Interlocked.Increment( ref _ExceptionCount ); 274 | _LogException( e ); 275 | } 276 | #endregion 277 | 278 | #region LogEx 279 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 280 | public static void Log( String format, T arg ) { 281 | Log( SafeFormat( format, arg ) ); 282 | } 283 | 284 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 285 | public static void Log( String format, T1 arg1, T2 arg2 ) { 286 | Log( SafeFormat( format, arg1, arg2 ) ); 287 | } 288 | 289 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 290 | public static void Log( String format, T1 arg1, T2 arg2, T3 arg3 ) { 291 | Log( SafeFormat( format, arg1, arg2, arg3 ) ); 292 | } 293 | 294 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 295 | public static void Log( String format, T1 arg1, T2 arg2, T3 arg3, T4 arg4 ) { 296 | Log( SafeFormat( format, arg1, arg2, arg3, arg4 ) ); 297 | } 298 | 299 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 300 | public static void Log( String format, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5 ) { 301 | Log( SafeFormat( format, arg1, arg2, arg3, arg4, arg5 ) ); 302 | } 303 | 304 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 305 | public static void Log( String format, params object[] args ) { 306 | Log( SafeFormat( format, args ) ); 307 | } 308 | #endregion 309 | 310 | #region LogErrorEx 311 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 312 | public static void LogError( String format, T arg ) { 313 | LogError( SafeFormat( format, arg ) ); 314 | } 315 | 316 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 317 | public static void LogError( String format, T1 arg1, T2 arg2 ) { 318 | LogError( SafeFormat( format, arg1, arg2 ) ); 319 | } 320 | 321 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 322 | public static void LogError( String format, T1 arg1, T2 arg2, T3 arg3 ) { 323 | LogError( SafeFormat( format, arg1, arg2, arg3 ) ); 324 | } 325 | 326 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 327 | public static void LogError( String format, T1 arg1, T2 arg2, T3 arg3, T4 arg4 ) { 328 | LogError( SafeFormat( format, arg1, arg2, arg3, arg4 ) ); 329 | } 330 | 331 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 332 | public static void LogError( String format, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5 ) { 333 | LogError( SafeFormat( format, arg1, arg2, arg3, arg4, arg5 ) ); 334 | } 335 | 336 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 337 | public static void LogError( String format, params object[] args ) { 338 | LogError( SafeFormat( format, args ) ); 339 | } 340 | #endregion 341 | 342 | #region LogWarningEx 343 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 344 | public static void LogWarning( String format, T args ) { 345 | LogWarning( SafeFormat( format, args ) ); 346 | } 347 | 348 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 349 | public static void LogWarning( String format, T1 arg1, T2 arg2 ) { 350 | LogWarning( SafeFormat( format, arg1, arg2 ) ); 351 | } 352 | 353 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 354 | public static void LogWarning( String format, T1 arg1, T2 arg2, T3 arg3 ) { 355 | LogWarning( SafeFormat( format, arg1, arg2, arg3 ) ); 356 | } 357 | 358 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 359 | public static void LogWarning( String format, T1 arg1, T2 arg2, T3 arg3, T4 arg4 ) { 360 | LogWarning( SafeFormat( format, arg1, arg2, arg3, arg4 ) ); 361 | } 362 | 363 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 364 | public static void LogWarning( String format, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5 ) { 365 | LogWarning( SafeFormat( format, arg1, arg2, arg3, arg4, arg5 ) ); 366 | } 367 | 368 | 369 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 370 | public static void LogWarning( String format, params object[] args ) { 371 | LogWarning( SafeFormat( format, args ) ); 372 | } 373 | #endregion 374 | 375 | [Conditional( "DEBUG" ), Conditional( "UNITY_EDITOR" ), Conditional( "ENABLE_ASSERTION" )] 376 | public static void BreakPoint() { 377 | #if UNITY_EDITOR 378 | UnityEngine.Debug.DebugBreak(); 379 | #else 380 | System.Diagnostics.Debugger.Break(); 381 | #endif 382 | } 383 | 384 | private static void NoAction( String msg ) { 385 | #if !UNITY_EDITOR 386 | Console.WriteLine( msg ); 387 | #endif 388 | } 389 | 390 | private static void NoActionException( Exception e ) { 391 | #if !UNITY_EDITOR 392 | Console.WriteLine( e.ToString() ); 393 | #endif 394 | } 395 | 396 | public static void Print( String arg ) { 397 | #if UNITY_4 || UNITY_5 || UNITY_EDITOR || DEBUG 398 | UnityEngine.Debug.Log( arg ); 399 | #else 400 | Console.WriteLine( arg ); 401 | #endif 402 | } 403 | 404 | public static void Print( T arg ) { 405 | #if UNITY_4 || UNITY_5 || UNITY_EDITOR || DEBUG 406 | UnityEngine.Debug.Log( arg.ToString() ); 407 | #else 408 | Console.WriteLine( arg.ToString() ); 409 | #endif 410 | } 411 | 412 | public static void Print( String format, T arg ) { 413 | #if UNITY_4 || UNITY_5 || UNITY_EDITOR || DEBUG 414 | UnityEngine.Debug.Log( SafeFormat( format, arg ) ); 415 | #else 416 | Console.WriteLine( SafeFormat( format, arg ) ); 417 | #endif 418 | } 419 | 420 | public static void Print( String format, T1 arg1, T2 arg2 ) { 421 | #if UNITY_4 || UNITY_5 || UNITY_EDITOR || DEBUG 422 | UnityEngine.Debug.Log( SafeFormat( format, arg1, arg2 ) ); 423 | #else 424 | Console.WriteLine( SafeFormat( format, arg1, arg2 ) ); 425 | #endif 426 | } 427 | 428 | public static void Print( String format, T1 arg1, T2 arg2, T3 arg3 ) { 429 | #if UNITY_4 || UNITY_5 || UNITY_EDITOR || DEBUG 430 | UnityEngine.Debug.Log( SafeFormat( format, arg1, arg2, arg3 ) ); 431 | #else 432 | Console.WriteLine( SafeFormat( format, arg1, arg2, arg3 ) ); 433 | #endif 434 | } 435 | 436 | public static void Print( String format, T1 arg1, T2 arg2, T3 arg3, T4 arg4 ) { 437 | #if UNITY_4 || UNITY_5 || UNITY_EDITOR || DEBUG 438 | UnityEngine.Debug.Log( SafeFormat( format, arg1, arg2, arg3, arg4 ) ); 439 | #else 440 | Console.WriteLine( SafeFormat( format, arg1, arg2, arg3, arg4 ) ); 441 | #endif 442 | } 443 | 444 | public static void Print( String format, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5 ) { 445 | #if UNITY_4 || UNITY_5 || UNITY_EDITOR || DEBUG 446 | UnityEngine.Debug.Log( SafeFormat( format, arg1, arg2, arg3, arg4, arg5 ) ); 447 | #else 448 | Console.WriteLine( SafeFormat( format, arg1, arg2, arg3, arg4, arg5 ) ); 449 | #endif 450 | } 451 | 452 | public static void Print( String format, params object[] args ) { 453 | #if UNITY_4 || UNITY_5 || UNITY_EDITOR || DEBUG 454 | UnityEngine.Debug.Log( SafeFormat( format, args ) ); 455 | #else 456 | Console.WriteLine( format, args ); 457 | #endif 458 | } 459 | 460 | static UDebug() { 461 | bool editor = false; 462 | #if UNITY_EDITOR 463 | editor = true; 464 | if ( UnityEditor.EditorApplication.isPlaying == false ) { 465 | enabled = true; 466 | } 467 | #endif 468 | if ( UnityEngine.Debug.isDebugBuild || editor ) { 469 | if ( enabled ) { 470 | _Log = UnityEngine.Debug.Log; 471 | _LogError = UnityEngine.Debug.LogError; 472 | _LogException = UnityEngine.Debug.LogException; 473 | _LogWarning = UnityEngine.Debug.LogWarning; 474 | } 475 | } 476 | } 477 | } 478 | } 479 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/Debug.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 04b9e82a440954749bfd4651794fa70a 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/EditorUtils.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_EDITOR 2 | using System; 3 | using System.Text; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Security.Cryptography; 7 | using UnityEngine; 8 | using UnityEditor; 9 | using System.IO; 10 | using System.Linq; 11 | using System.Reflection; 12 | using Common; 13 | 14 | namespace AresEditor { 15 | 16 | public static class EditorUtils { 17 | 18 | public static readonly String ProjectDirectory = String.Empty; 19 | 20 | static EditorUtils() { 21 | ProjectDirectory = EditorUtils.GetProjectUnityRootPath(); 22 | } 23 | 24 | public static String GetProjectRootPath() { 25 | string rootPath = Application.dataPath + "/../.."; 26 | if ( Directory.Exists( rootPath ) ) { 27 | rootPath = Path.GetFullPath( rootPath ); 28 | return rootPath.Replace( '\\', '/' ); 29 | } else { 30 | return rootPath; 31 | } 32 | } 33 | 34 | public static String GetProjectUnityRootPath() { 35 | string rootPath = Application.dataPath + "/.."; 36 | if ( Directory.Exists( rootPath ) ) { 37 | rootPath = Path.GetFullPath( rootPath ); 38 | return rootPath.Replace( '\\', '/' ); 39 | } else { 40 | return rootPath; 41 | } 42 | } 43 | 44 | public static String GetProjectUnityRootName() { 45 | var path = GetProjectUnityRootPath().TrimEnd( '/' ); 46 | return path.Substring( path.LastIndexOf( '/' ) + 1 ); 47 | } 48 | 49 | public static void BrowseFolderWin( String path, bool selectFile = false ) { 50 | if ( Application.platform == RuntimePlatform.WindowsEditor ) { 51 | System.Diagnostics.Process prcShell = new System.Diagnostics.Process(); 52 | prcShell.StartInfo.FileName = "explorer.exe"; 53 | path = path.Replace( '/', '\\' ); 54 | if ( selectFile && File.Exists( path ) ) { 55 | prcShell.StartInfo.Arguments = "/select, " + path; 56 | } else { 57 | prcShell.StartInfo.Arguments = path; 58 | } 59 | prcShell.Start(); 60 | } else { 61 | UnityEngine.Debug.Log( "Log dir: " + path ); 62 | } 63 | } 64 | 65 | public static void BrowseFolder( String path ) { 66 | if ( Application.isEditor ) { 67 | System.Diagnostics.Process prcShell = new System.Diagnostics.Process(); 68 | if ( Application.platform == RuntimePlatform.OSXEditor ) { 69 | prcShell.StartInfo.FileName = "open"; 70 | path = path.Replace( '\\', '/' ); 71 | } else if ( Application.platform == RuntimePlatform.WindowsEditor ) { 72 | prcShell.StartInfo.FileName = "explorer.exe"; 73 | path = path.Replace( '/', '\\' ); 74 | } else { 75 | return; 76 | } 77 | prcShell.StartInfo.Arguments = path; 78 | prcShell.Start(); 79 | } else { 80 | UnityEngine.Debug.Log( "Log dir: " + path ); 81 | } 82 | } 83 | 84 | public static void ClearLog() { 85 | var assembly = Assembly.GetAssembly( typeof( SceneView ) ); 86 | var type = assembly.GetType( "UnityEditorInternal.LogEntries" ); 87 | var method = type.GetMethod( "Clear" ); 88 | method.Invoke( null, null ); 89 | } 90 | 91 | public static void CheckCompileError() { 92 | Assembly assembly = Assembly.GetAssembly( typeof( SceneView ) ); 93 | Type logEntries = assembly.GetType( "UnityEditorInternal.LogEntries" ); 94 | logEntries.GetMethod( "Clear" ).Invoke( null, null ); 95 | int count = ( int )logEntries.GetMethod( "GetCount" ).Invoke( null, null ); 96 | if ( count > 0 ) { 97 | Common.UDebug.LogError( "Cannot build because you have compile errors!" ); 98 | } 99 | } 100 | 101 | public static Func FileExtFilter( String ext ) { 102 | if ( !ext.StartsWith( "." ) ) { 103 | ext = "." + ext; 104 | } 105 | return fileName => fileName.EndsWith( ext, StringComparison.CurrentCultureIgnoreCase ); 106 | } 107 | 108 | public static bool FileFilter_prefab( String fileName ) { 109 | return fileName.EndsWith( ".prefab", StringComparison.CurrentCultureIgnoreCase ); 110 | } 111 | 112 | public static String RelateToAssetsPath( String path ) { 113 | if ( path.StartsWith( Application.dataPath ) ) { 114 | return "Assets" + path.Substring( Application.dataPath.Length ); 115 | } 116 | return path; 117 | } 118 | 119 | public static String GetSelectedPath() { 120 | var path = String.Empty; 121 | var sel = Selection.GetFiltered( typeof( UnityEngine.Object ), SelectionMode.Assets ); 122 | foreach ( var obj in sel ) { 123 | path = AssetDatabase.GetAssetPath( obj ); 124 | if ( !string.IsNullOrEmpty( path ) && Directory.Exists( path ) ) { 125 | break; 126 | } 127 | } 128 | return path; 129 | } 130 | 131 | public static List GetAllSelectedPath() { 132 | var paths = new List(); 133 | var sel = Selection.GetFiltered( typeof( UnityEngine.Object ), SelectionMode.Assets ); 134 | foreach ( var obj in sel ) { 135 | var path = AssetDatabase.GetAssetPath( obj ); 136 | if ( !string.IsNullOrEmpty( path ) && Directory.Exists( path ) ) { 137 | paths.Add( path ); 138 | } 139 | } 140 | return paths; 141 | } 142 | 143 | public static String GetFullPath( String path ) { 144 | path = Path.GetFullPath( ( new Uri( path ) ).LocalPath ); 145 | path = path.Replace( '\\', '/' ); 146 | if ( FileUtils.CreateDirectory( path ) ) { 147 | return path; 148 | } else { 149 | return String.Empty; 150 | } 151 | } 152 | 153 | public static FileStream FileOpenRead( String filePath ) { 154 | if ( !System.IO.Path.IsPathRooted( filePath ) ) { 155 | filePath = ProjectDirectory + "/" + filePath; 156 | filePath = filePath.Replace( System.IO.Path.AltDirectorySeparatorChar, System.IO.Path.DirectorySeparatorChar ); 157 | } 158 | return File.OpenRead( filePath ); 159 | } 160 | 161 | static String _Md5Asset( String filePath, 162 | MD5CryptoServiceProvider md5Service, 163 | byte[] buffer, StringBuilder sb ) { 164 | try { 165 | int bytesRead = 0; 166 | using ( var file = FileOpenRead( filePath ) ) { 167 | while ( ( bytesRead = file.Read( buffer, 0, buffer.Length ) ) > 0 ) { 168 | md5Service.TransformBlock( buffer, 0, bytesRead, buffer, 0 ); 169 | } 170 | } 171 | var meta = filePath + ".meta"; 172 | if ( File.Exists( meta ) ) { 173 | bytesRead = 0; 174 | using ( var file = FileOpenRead( meta ) ) { 175 | while ( ( bytesRead = file.Read( buffer, 0, buffer.Length ) ) > 0 ) { 176 | md5Service.TransformBlock( buffer, 0, bytesRead, buffer, 0 ); 177 | } 178 | } 179 | } 180 | md5Service.TransformFinalBlock( buffer, 0, bytesRead ); 181 | var hashBytes = md5Service.Hash; 182 | for ( int i = 0; i < hashBytes.Length; i++ ) { 183 | sb.Append( hashBytes[ i ].ToString( "x2" ) ); 184 | } 185 | return sb.ToString(); 186 | } catch ( Exception e ) { 187 | ULogFile.sharedInstance.LogError( "_Md5Asset: {0}\ncd: {1}", filePath, ProjectDirectory ); 188 | ULogFile.sharedInstance.LogException( e ); 189 | } 190 | return String.Empty; 191 | } 192 | 193 | public static String Md5Asset( String filePath ) { 194 | if ( !File.Exists( filePath ) ) { 195 | return String.Empty; 196 | } 197 | const int chunkSize = 10240; 198 | var _MD5Service = new MD5CryptoServiceProvider(); 199 | var buffer = new byte[ chunkSize ]; 200 | return _Md5Asset( filePath, _MD5Service, buffer, new StringBuilder() ); 201 | } 202 | 203 | public static String Md5File( String filePath ) { 204 | try { 205 | using ( var stream = new BufferedStream( FileOpenRead( filePath ), 4096 ) ) { 206 | var _MD5Service = new MD5CryptoServiceProvider(); 207 | var hashBytes = _MD5Service.ComputeHash( stream ); 208 | var sb = new StringBuilder(); 209 | for ( int i = 0; i < hashBytes.Length; i++ ) { 210 | sb.Append( hashBytes[ i ].ToString( "x2" ) ); 211 | } 212 | return sb.ToString(); 213 | } 214 | } catch ( Exception e ) { 215 | ULogFile.sharedInstance.LogException( e ); 216 | } 217 | return String.Empty; 218 | } 219 | 220 | public static String Md5String( String str ) { 221 | var bytes = UTF8Encoding.Default.GetBytes( str ); 222 | var md5 = new MD5CryptoServiceProvider(); 223 | var hashBytes = md5.ComputeHash( bytes ); 224 | var sb = new StringBuilder(); 225 | for ( int i = 0; i < hashBytes.Length; i++ ) { 226 | sb.Append( hashBytes[ i ].ToString( "x2" ) ); 227 | } 228 | return sb.ToString(); 229 | } 230 | } 231 | } 232 | #endif 233 | //EOF 234 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/EditorUtils.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8b36715da8ce0b243bde55b2b01854d3 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/FileUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.IO; 5 | 6 | namespace Common { 7 | 8 | public static class FileUtils { 9 | 10 | public static FileStream OpenFile( String fullPath ) { 11 | FileStream fs = null; 12 | try { 13 | fs = File.Open( fullPath, FileMode.Open, FileAccess.Read, FileShare.Read ); 14 | } catch ( Exception e ) { 15 | Common.ULogFile.sharedInstance.LogException( e ); 16 | } 17 | return fs; 18 | } 19 | 20 | // create a directory 21 | // each sub directories will be created if any of them don't exist. 22 | public static bool CreateDirectory( String dirName ) { 23 | try { 24 | // first remove file name and extension; 25 | var ext = Path.GetExtension( dirName ); 26 | String fileNameAndExt = Path.GetFileName( dirName ); 27 | if ( !String.IsNullOrEmpty( fileNameAndExt ) && !string.IsNullOrEmpty( ext ) ) { 28 | dirName = dirName.Substring( 0, dirName.Length - fileNameAndExt.Length ); 29 | } 30 | var sb = new StringBuilder(); 31 | var dirs = dirName.Split( '/', '\\' ); 32 | if ( dirs.Length > 0 ) { 33 | if ( dirName[ 0 ] == '/' ) { 34 | // abs path tag on Linux OS 35 | dirs[ 0 ] = "/" + dirs[ 0 ]; 36 | } 37 | } 38 | for ( int i = 0; i < dirs.Length; ++i ) { 39 | if ( dirs[ i ].Length == 0 ) { 40 | continue; 41 | } 42 | if ( sb.Length != 0 ) { 43 | sb.Append( '/' ); 44 | } 45 | sb.Append( dirs[ i ] ); 46 | var cur = sb.ToString(); 47 | if ( String.IsNullOrEmpty( cur ) ) { 48 | continue; 49 | } 50 | if ( !Directory.Exists( cur ) ) { 51 | var info = Directory.CreateDirectory( cur ); 52 | if ( null == info ) { 53 | return false; 54 | } 55 | } 56 | } 57 | return true; 58 | } catch ( Exception e ) { 59 | ULogFile.sharedInstance.LogException( e ); 60 | } 61 | return false; 62 | } 63 | 64 | public static bool CopyDirectory( String sourceDirName, String destDirName, bool copySubDirs ) { 65 | // Get the subdirectories for the specified directory. 66 | DirectoryInfo dir = new DirectoryInfo( sourceDirName ); 67 | if ( !dir.Exists ) { 68 | return false; 69 | } 70 | DirectoryInfo[] dirs = dir.GetDirectories(); 71 | // If the destination directory doesn't exist, create it. 72 | if ( !Directory.Exists( destDirName ) ) { 73 | Directory.CreateDirectory( destDirName ); 74 | } 75 | // Get the files in the directory and copy them to the new location. 76 | FileInfo[] files = dir.GetFiles(); 77 | foreach ( FileInfo file in files ) { 78 | string temppath = Path.Combine( destDirName, file.Name ); 79 | file.CopyTo( temppath, false ); 80 | } 81 | var ret = true; 82 | // If copying subdirectories, copy them and their contents to new location. 83 | if ( copySubDirs ) { 84 | foreach ( DirectoryInfo subdir in dirs ) { 85 | string temppath = Path.Combine( destDirName, subdir.Name ); 86 | ret &= CopyDirectory( subdir.FullName, temppath, copySubDirs ); 87 | } 88 | } 89 | return ret; 90 | } 91 | 92 | public enum TreeWalkerCmd { 93 | Continue, 94 | Skip, 95 | Exit, 96 | } 97 | 98 | public interface ITreeWalker { 99 | bool IsRecursive(); 100 | // will be called for each file while WalkTree is running 101 | TreeWalkerCmd DoFile( String name ); 102 | // will be called for each directory while WalkTree is running 103 | TreeWalkerCmd DoDirectory( String name ); 104 | // wildmatch pattern 105 | String FileSearchPattern(); 106 | String DirectorySearchPattern(); 107 | } 108 | 109 | public class BaseTreeWalker : ITreeWalker { 110 | public virtual bool IsRecursive() { return true; } 111 | public virtual TreeWalkerCmd DoFile( String name ) { return TreeWalkerCmd.Continue; } 112 | public virtual TreeWalkerCmd DoDirectory( String name ) { return TreeWalkerCmd.Continue; } 113 | public virtual String FileSearchPattern() { return "*"; } 114 | public virtual String DirectorySearchPattern() { return "*"; } 115 | } 116 | 117 | class TreeDeleter : BaseTreeWalker, IDisposable { 118 | public List fileList = new List(); 119 | public List dirList = new List(); 120 | public override bool IsRecursive() { return true; } 121 | public override TreeWalkerCmd DoFile( String name ) { 122 | fileList.Add( name ); 123 | return TreeWalkerCmd.Continue; 124 | } 125 | public override TreeWalkerCmd DoDirectory( String name ) { 126 | dirList.Add( name ); 127 | return TreeWalkerCmd.Continue; 128 | } 129 | public void Dispose() { 130 | try { 131 | for ( int i = 0; i < fileList.Count; ++i ) { 132 | File.Delete( fileList[i] ); 133 | } 134 | for ( int i = dirList.Count - 1; i >= 0; --i ) { 135 | Directory.Delete( dirList[i] ); 136 | } 137 | } catch ( Exception e ) { 138 | UDebug.LogException( e ); 139 | } 140 | } 141 | } 142 | 143 | public static bool RemoveTree( String dirName, bool delSelf = false ) { 144 | int count = 0; 145 | using ( var td = new TreeDeleter() ) { 146 | WalkTree( dirName, td ); 147 | count = td.dirList.Count + td.fileList.Count; 148 | } 149 | if ( delSelf ) { 150 | try { 151 | if ( Directory.Exists( dirName ) ) { 152 | Directory.Delete( dirName ); 153 | } 154 | } catch ( Exception e ) { 155 | UDebug.LogException( e ); 156 | } 157 | } 158 | return count != 0; 159 | } 160 | 161 | public static void WalkTree( String dirName, ITreeWalker walker ) { 162 | var dirCount = 0; 163 | dirName = Common.StringUtils.StandardisePath( dirName ); 164 | Stack dirStack = new Stack(); 165 | dirStack.Push( dirName ); 166 | while ( dirStack.Count > 0 ) { 167 | String lastPath = dirStack.Pop(); 168 | DirectoryInfo di = new DirectoryInfo( lastPath ); 169 | if ( !di.Exists || ( ( di.Attributes & FileAttributes.Hidden ) != 0 && dirCount > 0 ) ) { 170 | continue; 171 | } 172 | ++dirCount; 173 | foreach ( FileInfo fileInfo in di.GetFiles( walker.FileSearchPattern() ) ) { 174 | // compose full file name from dirName 175 | String f = lastPath; 176 | if ( f[f.Length - 1] == '/' ) { 177 | f += fileInfo.Name; 178 | } else { 179 | f = f + "/" + fileInfo.Name; 180 | } 181 | var cmd = walker.DoFile( f ); 182 | switch ( cmd ) { 183 | case TreeWalkerCmd.Skip: 184 | continue; 185 | case TreeWalkerCmd.Exit: 186 | goto EXIT; 187 | } 188 | } 189 | if ( walker.IsRecursive() ) { 190 | foreach ( DirectoryInfo dirInfo in di.GetDirectories( walker.DirectorySearchPattern() ) ) { 191 | // compose full path name from dirName 192 | String p = lastPath; 193 | if ( p[p.Length - 1] == '/' ) { 194 | p += dirInfo.Name; 195 | } else { 196 | p = p + "/" + dirInfo.Name; 197 | } 198 | FileAttributes fa = File.GetAttributes( p ); 199 | if ( ( fa & FileAttributes.Hidden ) == 0 ) { 200 | var cmd = walker.DoDirectory( p ); 201 | switch ( cmd ) { 202 | case TreeWalkerCmd.Skip: 203 | continue; 204 | case TreeWalkerCmd.Exit: 205 | goto EXIT; 206 | } 207 | dirStack.Push( p ); 208 | } 209 | } 210 | } 211 | } 212 | EXIT: 213 | ; 214 | } 215 | 216 | class DirectoryScanner : BaseTreeWalker { 217 | List m_allDirs; 218 | Func m_filter; 219 | bool m_recursive; 220 | public DirectoryScanner( List fs, Func filter, bool recursive ) { 221 | m_allDirs = fs; 222 | m_filter = filter; 223 | m_recursive = recursive; 224 | } 225 | public override bool IsRecursive() { 226 | return m_recursive; 227 | } 228 | public override TreeWalkerCmd DoDirectory( String name ) { 229 | if ( m_filter == null || !m_filter( name ) ) { 230 | m_allDirs.Add( name ); 231 | } 232 | return TreeWalkerCmd.Continue; 233 | } 234 | } 235 | 236 | class FileScanner : BaseTreeWalker { 237 | List m_allFiles; 238 | Func m_filter; 239 | bool m_recursive; 240 | public FileScanner( List fs, Func filter, bool recursive ) { 241 | m_allFiles = fs; 242 | m_filter = filter; 243 | m_recursive = recursive; 244 | } 245 | public override bool IsRecursive() { 246 | return m_recursive; 247 | } 248 | public override TreeWalkerCmd DoFile( String name ) { 249 | if ( m_filter == null || m_filter( name ) ) { 250 | m_allFiles.Add( name ); 251 | } 252 | return TreeWalkerCmd.Continue; 253 | } 254 | } 255 | 256 | public static List GetFileList( String path, Func filter, bool recursive = true ) { 257 | var ret = new List(); 258 | if ( !String.IsNullOrEmpty( path ) ) { 259 | var fs = new FileScanner( ret, filter, recursive ); 260 | WalkTree( path, fs ); 261 | } 262 | return ret; 263 | } 264 | 265 | public static List GetDirectoryList( String path, Func filter, bool recursive = true ) { 266 | var ret = new List(); 267 | if ( !String.IsNullOrEmpty( path ) ) { 268 | var fs = new DirectoryScanner( ret, filter, recursive ); 269 | WalkTree( path, fs ); 270 | } 271 | return ret; 272 | } 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/FileUtils.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fcb8ecb4d2d7f5c4a92b17a910b5594f 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/JSON.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d31b71188f802eb47b373ff1966e9a4e 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/JSON/JSONObject.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c56cca2bfdc6da948bf23995bb3ae069 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/JSON/JSONTemplates.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | 5 | /* 6 | * http://www.opensource.org/licenses/lgpl-2.1.php 7 | * JSONTemplates class 8 | * for use with Unity 9 | * Copyright Matt Schoen 2010 10 | */ 11 | 12 | public static partial class JSONTemplates { 13 | static readonly HashSet touched = new HashSet(); 14 | 15 | public static JSONObject TOJSON(object obj) { //For a generic guess 16 | if(touched.Add(obj)) { 17 | JSONObject result = JSONObject.obj; 18 | //Fields 19 | FieldInfo[] fieldinfo = obj.GetType().GetFields(); 20 | foreach(FieldInfo fi in fieldinfo) { 21 | JSONObject val = JSONObject.nullJO; 22 | if(!fi.GetValue(obj).Equals(null)) { 23 | MethodInfo info = typeof(JSONTemplates).GetMethod("From" + fi.FieldType.Name); 24 | if(info != null) { 25 | object[] parms = new object[1]; 26 | parms[0] = fi.GetValue(obj); 27 | val = (JSONObject)info.Invoke(null, parms); 28 | } else if(fi.FieldType == typeof(string)) 29 | val = JSONObject.CreateStringObject(fi.GetValue(obj).ToString()); 30 | else 31 | val = JSONObject.Create(fi.GetValue(obj).ToString()); 32 | } 33 | if(val) { 34 | if(val.type != JSONObject.Type.NULL) 35 | result.AddField(fi.Name, val); 36 | else Debug.LogWarning("Null for this non-null object, property " + fi.Name + " of class " + obj.GetType().Name + ". Object type is " + fi.FieldType.Name); 37 | } 38 | } 39 | //Properties 40 | PropertyInfo[] propertyInfo = obj.GetType().GetProperties(); 41 | foreach(PropertyInfo pi in propertyInfo) { 42 | //This section should mirror part of AssetFactory.AddScripts() 43 | JSONObject val = JSONObject.nullJO; 44 | if(!pi.GetValue(obj, null).Equals(null)) { 45 | MethodInfo info = typeof(JSONTemplates).GetMethod("From" + pi.PropertyType.Name); 46 | if(info != null) { 47 | object[] parms = new object[1]; 48 | parms[0] = pi.GetValue(obj, null); 49 | val = (JSONObject)info.Invoke(null, parms); 50 | } else if(pi.PropertyType == typeof(string)) 51 | val = JSONObject.CreateStringObject(pi.GetValue(obj, null).ToString()); 52 | else 53 | val = JSONObject.Create(pi.GetValue(obj, null).ToString()); 54 | } 55 | if(val) { 56 | if(val.type != JSONObject.Type.NULL) 57 | result.AddField(pi.Name, val); 58 | else Debug.LogWarning("Null for this non-null object, property " + pi.Name + " of class " + obj.GetType().Name + ". Object type is " + pi.PropertyType.Name); 59 | } 60 | } 61 | return result; 62 | } 63 | Debug.LogWarning("trying to save the same data twice"); 64 | return JSONObject.nullJO; 65 | } 66 | } 67 | 68 | /* 69 | * Some helpful code templates for the JSON class 70 | * 71 | * LOOP THROUGH OBJECT 72 | for(int i = 0; i < obj.Count; i++){ 73 | if(obj.keys[i] != null){ 74 | switch((string)obj.keys[i]){ 75 | case "key1": 76 | do stuff with (JSONObject)obj.list[i]; 77 | break; 78 | case "key2": 79 | do stuff with (JSONObject)obj.list[i]; 80 | break; 81 | } 82 | } 83 | } 84 | * 85 | * LOOP THROUGH ARRAY 86 | foreach(JSONObject ob in obj.list) 87 | do stuff with ob; 88 | */ 89 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/JSON/JSONTemplates.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0a17052a3def56b4ebea997f85304acf 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/JSON/SimpleJSON.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6b1508e4877d15b42b9a7dc0e17bc067 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/JSON/VectorTemplates.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public static partial class JSONTemplates { 4 | 5 | /* 6 | * Vector2 7 | */ 8 | public static Vector2 ToVector2(JSONObject obj) { 9 | float x = obj["x"] ? obj["x"].f : 0; 10 | float y = obj["y"] ? obj["y"].f : 0; 11 | return new Vector2(x, y); 12 | } 13 | public static JSONObject FromVector2(Vector2 v) { 14 | JSONObject vdata = JSONObject.obj; 15 | if(v.x != 0) vdata.AddField("x", v.x); 16 | if(v.y != 0) vdata.AddField("y", v.y); 17 | return vdata; 18 | } 19 | /* 20 | * Vector3 21 | */ 22 | public static JSONObject FromVector3(Vector3 v) { 23 | JSONObject vdata = JSONObject.obj; 24 | if(v.x != 0) vdata.AddField("x", v.x); 25 | if(v.y != 0) vdata.AddField("y", v.y); 26 | if(v.z != 0) vdata.AddField("z", v.z); 27 | return vdata; 28 | } 29 | public static Vector3 ToVector3(JSONObject obj) { 30 | float x = obj["x"] ? obj["x"].f : 0; 31 | float y = obj["y"] ? obj["y"].f : 0; 32 | float z = obj["z"] ? obj["z"].f : 0; 33 | return new Vector3(x, y, z); 34 | } 35 | /* 36 | * Vector4 37 | */ 38 | public static JSONObject FromVector4(Vector4 v) { 39 | JSONObject vdata = JSONObject.obj; 40 | if(v.x != 0) vdata.AddField("x", v.x); 41 | if(v.y != 0) vdata.AddField("y", v.y); 42 | if(v.z != 0) vdata.AddField("z", v.z); 43 | if(v.w != 0) vdata.AddField("w", v.w); 44 | return vdata; 45 | } 46 | public static Vector4 ToVector4(JSONObject obj) { 47 | float x = obj["x"] ? obj["x"].f : 0; 48 | float y = obj["y"] ? obj["y"].f : 0; 49 | float z = obj["z"] ? obj["z"].f : 0; 50 | float w = obj["w"] ? obj["w"].f : 0; 51 | return new Vector4(x, y, z, w); 52 | } 53 | /* 54 | * Matrix4x4 55 | */ 56 | public static JSONObject FromMatrix4x4(Matrix4x4 m) { 57 | JSONObject mdata = JSONObject.obj; 58 | if(m.m00 != 0) mdata.AddField("m00", m.m00); 59 | if(m.m01 != 0) mdata.AddField("m01", m.m01); 60 | if(m.m02 != 0) mdata.AddField("m02", m.m02); 61 | if(m.m03 != 0) mdata.AddField("m03", m.m03); 62 | if(m.m10 != 0) mdata.AddField("m10", m.m10); 63 | if(m.m11 != 0) mdata.AddField("m11", m.m11); 64 | if(m.m12 != 0) mdata.AddField("m12", m.m12); 65 | if(m.m13 != 0) mdata.AddField("m13", m.m13); 66 | if(m.m20 != 0) mdata.AddField("m20", m.m20); 67 | if(m.m21 != 0) mdata.AddField("m21", m.m21); 68 | if(m.m22 != 0) mdata.AddField("m22", m.m22); 69 | if(m.m23 != 0) mdata.AddField("m23", m.m23); 70 | if(m.m30 != 0) mdata.AddField("m30", m.m30); 71 | if(m.m31 != 0) mdata.AddField("m31", m.m31); 72 | if(m.m32 != 0) mdata.AddField("m32", m.m32); 73 | if(m.m33 != 0) mdata.AddField("m33", m.m33); 74 | return mdata; 75 | } 76 | public static Matrix4x4 ToMatrix4x4(JSONObject obj) { 77 | Matrix4x4 result = new Matrix4x4(); 78 | if(obj["m00"]) result.m00 = obj["m00"].f; 79 | if(obj["m01"]) result.m01 = obj["m01"].f; 80 | if(obj["m02"]) result.m02 = obj["m02"].f; 81 | if(obj["m03"]) result.m03 = obj["m03"].f; 82 | if(obj["m10"]) result.m10 = obj["m10"].f; 83 | if(obj["m11"]) result.m11 = obj["m11"].f; 84 | if(obj["m12"]) result.m12 = obj["m12"].f; 85 | if(obj["m13"]) result.m13 = obj["m13"].f; 86 | if(obj["m20"]) result.m20 = obj["m20"].f; 87 | if(obj["m21"]) result.m21 = obj["m21"].f; 88 | if(obj["m22"]) result.m22 = obj["m22"].f; 89 | if(obj["m23"]) result.m23 = obj["m23"].f; 90 | if(obj["m30"]) result.m30 = obj["m30"].f; 91 | if(obj["m31"]) result.m31 = obj["m31"].f; 92 | if(obj["m32"]) result.m32 = obj["m32"].f; 93 | if(obj["m33"]) result.m33 = obj["m33"].f; 94 | return result; 95 | } 96 | /* 97 | * Quaternion 98 | */ 99 | public static JSONObject FromQuaternion(Quaternion q) { 100 | JSONObject qdata = JSONObject.obj; 101 | if(q.w != 0) qdata.AddField("w", q.w); 102 | if(q.x != 0) qdata.AddField("x", q.x); 103 | if(q.y != 0) qdata.AddField("y", q.y); 104 | if(q.z != 0) qdata.AddField("z", q.z); 105 | return qdata; 106 | } 107 | public static Quaternion ToQuaternion(JSONObject obj) { 108 | float x = obj["x"] ? obj["x"].f : 0; 109 | float y = obj["y"] ? obj["y"].f : 0; 110 | float z = obj["z"] ? obj["z"].f : 0; 111 | float w = obj["w"] ? obj["w"].f : 0; 112 | return new Quaternion(x, y, z, w); 113 | } 114 | /* 115 | * Color 116 | */ 117 | public static JSONObject FromColor(Color c) { 118 | JSONObject cdata = JSONObject.obj; 119 | if(c.r != 0) cdata.AddField("r", c.r); 120 | if(c.g != 0) cdata.AddField("g", c.g); 121 | if(c.b != 0) cdata.AddField("b", c.b); 122 | if(c.a != 0) cdata.AddField("a", c.a); 123 | return cdata; 124 | } 125 | public static Color ToColor(JSONObject obj) { 126 | Color c = new Color(); 127 | for(int i = 0; i < obj.Count; i++) { 128 | switch(obj.keys[i]) { 129 | case "r": c.r = obj[i].f; break; 130 | case "g": c.g = obj[i].f; break; 131 | case "b": c.b = obj[i].f; break; 132 | case "a": c.a = obj[i].f; break; 133 | } 134 | } 135 | return c; 136 | } 137 | /* 138 | * Layer Mask 139 | */ 140 | public static JSONObject FromLayerMask(LayerMask l) { 141 | JSONObject result = JSONObject.obj; 142 | result.AddField("value", l.value); 143 | return result; 144 | } 145 | public static LayerMask ToLayerMask(JSONObject obj) { 146 | LayerMask l = new LayerMask {value = (int)obj["value"].n}; 147 | return l; 148 | } 149 | public static JSONObject FromRect(Rect r) { 150 | JSONObject result = JSONObject.obj; 151 | if(r.x != 0) result.AddField("x", r.x); 152 | if(r.y != 0) result.AddField("y", r.y); 153 | if(r.height != 0) result.AddField("height", r.height); 154 | if(r.width != 0) result.AddField("width", r.width); 155 | return result; 156 | } 157 | public static Rect ToRect(JSONObject obj) { 158 | Rect r = new Rect(); 159 | for(int i = 0; i < obj.Count; i++) { 160 | switch(obj.keys[i]) { 161 | case "x": r.x = obj[i].f; break; 162 | case "y": r.y = obj[i].f; break; 163 | case "height": r.height = obj[i].f; break; 164 | case "width": r.width = obj[i].f; break; 165 | } 166 | } 167 | return r; 168 | } 169 | public static JSONObject FromRectOffset(RectOffset r) { 170 | JSONObject result = JSONObject.obj; 171 | if(r.bottom != 0) result.AddField("bottom", r.bottom); 172 | if(r.left != 0) result.AddField("left", r.left); 173 | if(r.right != 0) result.AddField("right", r.right); 174 | if(r.top != 0) result.AddField("top", r.top); 175 | return result; 176 | } 177 | public static RectOffset ToRectOffset(JSONObject obj) { 178 | RectOffset r = new RectOffset(); 179 | for(int i = 0; i < obj.Count; i++) { 180 | switch(obj.keys[i]) { 181 | case "bottom": r.bottom = (int)obj[i].n; break; 182 | case "left": r.left = (int)obj[i].n; break; 183 | case "right": r.right = (int)obj[i].n; break; 184 | case "top": r.top = (int)obj[i].n; break; 185 | } 186 | } 187 | return r; 188 | } 189 | 190 | public static AnimationCurve ToAnimationCurve(JSONObject obj){ 191 | AnimationCurve a = new AnimationCurve(); 192 | if(obj.HasField("keys")){ 193 | JSONObject keys = obj.GetField("keys"); 194 | for(int i =0; i < keys.list.Count;i++){ 195 | a.AddKey(ToKeyframe(keys[i])); 196 | } 197 | } 198 | if(obj.HasField("preWrapMode")) 199 | a.preWrapMode = (WrapMode)((int)obj.GetField("preWrapMode").n); 200 | if(obj.HasField("postWrapMode")) 201 | a.postWrapMode = (WrapMode)((int)obj.GetField("postWrapMode").n); 202 | return a; 203 | } 204 | 205 | public static JSONObject FromAnimationCurve(AnimationCurve a){ 206 | JSONObject result = JSONObject.obj; 207 | result.AddField("preWrapMode", a.preWrapMode.ToString()); 208 | result.AddField("postWrapMode", a.postWrapMode.ToString()); 209 | if(a.keys.Length > 0){ 210 | JSONObject keysJSON = JSONObject.Create(); 211 | for(int i =0; i < a.keys.Length;i++){ 212 | keysJSON.Add(FromKeyframe(a.keys[i])); 213 | } 214 | result.AddField("keys", keysJSON); 215 | } 216 | return result; 217 | } 218 | 219 | public static Keyframe ToKeyframe(JSONObject obj){ 220 | Keyframe k = new Keyframe(obj.HasField("time")? obj.GetField("time").n : 0, obj.HasField("value")? obj.GetField("value").n : 0); 221 | if(obj.HasField("inTangent")) k.inTangent = obj.GetField("inTangent").n; 222 | if(obj.HasField("outTangent")) k.outTangent = obj.GetField("outTangent").n; 223 | if(obj.HasField("tangentMode")) k.tangentMode = (int)obj.GetField("tangentMode").n; 224 | 225 | return k; 226 | } 227 | public static JSONObject FromKeyframe(Keyframe k){ 228 | JSONObject result = JSONObject.obj; 229 | if(k.inTangent != 0) result.AddField("inTangent", k.inTangent); 230 | if(k.outTangent != 0) result.AddField("outTangent", k.outTangent); 231 | if(k.tangentMode != 0) result.AddField("tangentMode", k.tangentMode); 232 | if(k.time != 0) result.AddField("time", k.time); 233 | if(k.value != 0) result.AddField("value", k.value); 234 | return result; 235 | } 236 | 237 | } 238 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/JSON/VectorTemplates.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d4d4e3ac673696248a8bd64cca9e305b 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/StringUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Security.Cryptography; 6 | using UnityEngine; 7 | 8 | namespace Common { 9 | 10 | public static class StringUtils { 11 | 12 | const int GlobalStringBuilderPoolSize = 10; 13 | const int MaxGlobalStringBuilderPoolSize = GlobalStringBuilderPoolSize * 4; 14 | const int StringBuilderInitSize = 128; 15 | const int StringBuilderMaxSize = 256; 16 | 17 | static Queue[] _StringBuilderPoolBuf = null; // allocating & allocated pools 18 | static int _StringBuilderPoolIndex = 0; // allocator pool index in buf 19 | 20 | static StringUtils() { 21 | _StringBuilderPoolBuf = new Queue[ 2 ] { 22 | new Queue(), 23 | new Queue() 24 | }; 25 | for ( int i = 0; i < GlobalStringBuilderPoolSize; ++i ) { 26 | _StringBuilderPoolBuf[ _StringBuilderPoolIndex ].Enqueue( new StringBuilder( StringBuilderInitSize ) ); 27 | } 28 | } 29 | 30 | public static StringBuilder newStringBuilder { 31 | get { 32 | var curPool = _StringBuilderPoolBuf[ _StringBuilderPoolIndex ]; 33 | if ( curPool.Count == 0 ) { 34 | // swap allocator pool 35 | _StringBuilderPoolIndex = ( _StringBuilderPoolIndex + 1 ) & 1; 36 | curPool = _StringBuilderPoolBuf[ _StringBuilderPoolIndex ]; 37 | } 38 | var dstIndex = ( _StringBuilderPoolIndex + 1 ) & 1; 39 | var dstPool = _StringBuilderPoolBuf[ dstIndex ]; 40 | if ( curPool.Count > 0 ) { 41 | var sb = curPool.Dequeue(); 42 | dstPool.Enqueue( sb ); 43 | sb.Length = 0; 44 | return sb; 45 | } else { 46 | var sb = new StringBuilder( StringBuilderInitSize ); 47 | dstPool.Enqueue( sb ); 48 | return sb; 49 | } 50 | } 51 | } 52 | 53 | public static StringBuilder AllocateStringBuilder() { 54 | var curPool = _StringBuilderPoolBuf[ _StringBuilderPoolIndex ]; 55 | if ( curPool.Count == 0 ) { 56 | // swap allocator pool 57 | _StringBuilderPoolIndex = ( _StringBuilderPoolIndex + 1 ) & 1; 58 | curPool = _StringBuilderPoolBuf[ _StringBuilderPoolIndex ]; 59 | } 60 | if ( curPool.Count > 0 ) { 61 | var sb = curPool.Dequeue(); 62 | sb.Length = 0; 63 | return sb; 64 | } else { 65 | return new StringBuilder( StringBuilderInitSize ); 66 | } 67 | } 68 | 69 | public static void FreeStringBuilder( StringBuilder sb ) { 70 | var curPool = _StringBuilderPoolBuf[ _StringBuilderPoolIndex ]; 71 | if ( sb != null && curPool.Count < MaxGlobalStringBuilderPoolSize ) { 72 | sb.Length = 0; 73 | if ( sb.Capacity > StringBuilderMaxSize ) { 74 | sb.Capacity = StringBuilderInitSize; 75 | } 76 | curPool.Enqueue( sb ); 77 | } 78 | } 79 | 80 | public static void SplitFilename( String qualifiedName, out String outBasename, out String outPath ) { 81 | String path = qualifiedName.Replace( '\\', '/' ); 82 | int i = path.LastIndexOf( '/' ); 83 | if ( i == -1 ) { 84 | outPath = String.Empty; 85 | outBasename = qualifiedName; 86 | } else { 87 | outBasename = path.Substring( i + 1, path.Length - i - 1 ); 88 | outPath = path.Substring( 0, i + 1 ); 89 | } 90 | } 91 | 92 | public static String StandardisePath( String init ) { 93 | String path = init.Replace( '\\', '/' ); 94 | if ( path.Length > 0 && path[ path.Length - 1 ] != '/' ) { 95 | path += '/'; 96 | } 97 | return path; 98 | } 99 | 100 | public static String StandardisePathWithoutSlash( String init ) { 101 | String path = init.Replace( '\\', '/' ); 102 | while ( path.Length > 0 && path[ path.Length - 1 ] == '/' ) { 103 | path = path.Remove( path.Length - 1 ); 104 | } 105 | return path; 106 | } 107 | 108 | public static String RemoveExtension( String fullName ) { 109 | int dot = fullName.LastIndexOf( '.' ); 110 | if ( dot != -1 ) { 111 | return fullName.Substring( 0, dot ); 112 | } else { 113 | return fullName; 114 | } 115 | } 116 | 117 | public static void SplitBaseFilename( String fullName, out String outBasename, out String outExtention ) { 118 | int i = fullName.LastIndexOf( '.' ); 119 | if ( i == -1 ) { 120 | outExtention = String.Empty; 121 | outBasename = fullName; 122 | } else { 123 | outExtention = fullName.Substring( i + 1 ); 124 | outBasename = fullName.Substring( 0, i ); 125 | } 126 | } 127 | 128 | public static String SafeFormat( String format, T arg ) { 129 | if ( format != null ) { 130 | try { 131 | return String.Format( format, arg ); 132 | } catch ( Exception e ) { 133 | UDebug.LogException( e ); 134 | } 135 | } 136 | return String.Empty; 137 | } 138 | 139 | public static String SafeFormat( String format, T1 arg1, T2 arg2 ) { 140 | if ( format != null ) { 141 | try { 142 | return String.Format( format, arg1, arg2 ); 143 | } catch ( Exception e ) { 144 | UDebug.LogException( e ); 145 | } 146 | } 147 | return String.Empty; 148 | } 149 | 150 | public static String SafeFormat( String format, T1 arg1, T2 arg2, T3 arg3 ) { 151 | if ( format != null ) { 152 | try { 153 | return String.Format( format, arg1, arg2, arg3 ); 154 | } catch ( Exception e ) { 155 | UDebug.LogException( e ); 156 | } 157 | } 158 | return String.Empty; 159 | } 160 | 161 | public static String SafeFormat( String format, params object[] args ) { 162 | if ( format != null && args != null ) { 163 | try { 164 | return String.Format( format, args ); 165 | } catch ( Exception e ) { 166 | Common.UDebug.LogException( e ); 167 | } 168 | } 169 | return String.Empty; 170 | } 171 | 172 | public static void SplitFullFilename( String qualifiedName, out String outBasename, out String outExtention, out String outPath ) { 173 | String fullName = String.Empty; 174 | SplitFilename( qualifiedName, out fullName, out outPath ); 175 | SplitBaseFilename( fullName, out outBasename, out outExtention ); 176 | } 177 | 178 | public static String MakeRelativePath( String workingDirectory, String fullPath ) { 179 | String result = String.Empty; 180 | int offset; 181 | // this is the easy case. The file is inside of the working directory. 182 | if ( fullPath.StartsWith( workingDirectory ) ) { 183 | return fullPath.Substring( workingDirectory.Length + 1 ); 184 | } 185 | // the hard case has to back out of the working directory 186 | String[] baseDirs = workingDirectory.Split( ':', '\\', '/' ); 187 | String[] fileDirs = fullPath.Split( ':', '\\', '/' ); 188 | 189 | // if we failed to split (empty strings?) or the drive letter does not match 190 | if ( baseDirs.Length <= 0 || fileDirs.Length <= 0 || baseDirs[ 0 ] != fileDirs[ 0 ] ) { 191 | // can't create a relative path between separate harddrives/partitions. 192 | return fullPath; 193 | } 194 | // skip all leading directories that match 195 | for ( offset = 1; offset < baseDirs.Length; offset++ ) { 196 | if ( baseDirs[ offset ] != fileDirs[ offset ] ) 197 | break; 198 | } 199 | // back out of the working directory 200 | for ( int i = 0; i < ( baseDirs.Length - offset ); i++ ) { 201 | result += "..\\"; 202 | } 203 | // step into the file path 204 | for ( int i = offset; i < fileDirs.Length - 1; i++ ) { 205 | result += fileDirs[ i ] + "\\"; 206 | } 207 | // append the file 208 | result += fileDirs[ fileDirs.Length - 1 ]; 209 | return result; 210 | } 211 | 212 | public static String FormatMemorySize( int size ) { 213 | if ( size < 1024 ) { 214 | return String.Format( "{0} B", size ); 215 | } else if ( size < 1024 * 1024 ) { 216 | return String.Format( "{0:f2} KB", size >> 10 ); 217 | } else if ( size < 1024 * 1024 * 1024 ) { 218 | return String.Format( "{0:f2} MB", size >> 20 ); 219 | } else { 220 | return String.Format( "{0:f2} GB", size >> 30 ); 221 | } 222 | } 223 | 224 | public static String FormatMemorySize( long size ) { 225 | if ( size < 1024 ) { 226 | return String.Format( "{0} B", size ); 227 | } else if ( size < 1024 * 1024 ) { 228 | return String.Format( "{0:f2} KB", size >> 10 ); 229 | } else if ( size < 1024 * 1024 * 1024 ) { 230 | return String.Format( "{0:f2} MB", size >> 20 ); 231 | } else if ( size < 1024L * 1024 * 1024 * 1024 ) { 232 | return String.Format( "{0:f2} GB", size >> 30 ); 233 | } else { 234 | return String.Format( "{0:f2} TB", size >> 40 ); 235 | } 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/StringUtils.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 750a97cb4e0927f4481fc517fbfb80d1 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/UnityLogFile.cs: -------------------------------------------------------------------------------- 1 | #define UNITY 2 | 3 | using System; 4 | using System.IO; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Diagnostics; 10 | using System.Text.RegularExpressions; 11 | #if UNITY 12 | using UnityEngine; 13 | #endif 14 | 15 | public class LogFile : IDisposable { 16 | 17 | public const String DefaultLogFileName = "default"; 18 | public const String LogFolderNamePattern = @"^\d{4}-\d{2}-\d{2} \d{2}-\d\d-\d\d \(\d{4}\)$"; 19 | public const int HowLongForStoreLogInHours = 7 * 24; 20 | 21 | public static String ROOT { 22 | get { 23 | return Application.persistentDataPath + "/Log"; 24 | } 25 | } 26 | 27 | static String OutDir = String.Empty; 28 | 29 | static LogFile() { 30 | OutDir = ROOT + "/" + GetFormatedTimeText(); 31 | UnityEngine.Debug.Log( "LogPath: " + OutDir ); 32 | if ( !System.IO.Directory.Exists( OutDir ) ) { 33 | System.IO.Directory.CreateDirectory( OutDir ); 34 | } 35 | var logCreatorInfo = OutDir + "/LogCreateInfo.txt"; 36 | try { 37 | using ( var o = new StreamWriter( logCreatorInfo, false, Encoding.UTF8 ) ) { 38 | o.Write( Common.UDebug.GetCallStack( 2 ) ); 39 | } 40 | } catch ( Exception e ) { 41 | Common.UDebug.LogError( e.ToString() ); 42 | } 43 | ClearOldLogs(); 44 | } 45 | 46 | class LogTextList : List { 47 | int totalCount = 0; 48 | public new void Add( String item ) { 49 | base.Add( Common.StringUtils.SafeFormat( "<{0}> {1}\n{2}\n", totalCount, GetFormatedTimeText(), item ) ); 50 | ++totalCount; 51 | } 52 | } 53 | 54 | class Group : IComparable, IDisposable { 55 | 56 | public String groupName = String.Empty; 57 | LogTextList m_content = new LogTextList(); 58 | object m_locker = new object(); 59 | 60 | String m_outFilePath = String.Empty; 61 | FileStream m_stream = null; 62 | StreamWriter m_writer = null; 63 | 64 | int m_ioErrorCount = 0; 65 | int m_streamCheck = 0; 66 | 67 | public Group( String name, String fullPath ) { 68 | groupName = name; 69 | m_outFilePath = fullPath; 70 | _CheckStream(); 71 | } 72 | 73 | ~Group() { 74 | Dispose( false ); 75 | } 76 | 77 | static bool IsFileInUse( String path ) { 78 | if ( File.Exists( path ) ) { 79 | try { 80 | using ( var stream = new FileStream( path, FileMode.Open, FileAccess.Write ) ) { } 81 | } catch ( IOException ) { 82 | return true; 83 | } 84 | } 85 | return false; 86 | } 87 | 88 | void _CheckStream() { 89 | if ( m_streamCheck == 0 ) { 90 | ++m_streamCheck; 91 | try { 92 | if ( m_stream == null ) { 93 | try { 94 | m_stream = new FileStream( m_outFilePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite ); 95 | if ( m_stream != null ) { 96 | m_writer = new StreamWriter( m_stream, Encoding.UTF8 ); 97 | } 98 | } catch ( Exception e ) { 99 | if ( m_ioErrorCount < 10 ) { 100 | ++m_ioErrorCount; 101 | UnityEngine.Debug.LogError( m_outFilePath ); 102 | UnityEngine.Debug.LogException( e ); 103 | } 104 | } 105 | } 106 | } finally { 107 | --m_streamCheck; 108 | } 109 | } 110 | } 111 | 112 | public void CloseStream() { 113 | lock ( m_locker ) { 114 | if ( m_writer != null ) { 115 | m_writer.Dispose(); 116 | m_writer = null; 117 | } 118 | if ( m_stream != null ) { 119 | m_stream.Dispose(); 120 | m_stream = null; 121 | } 122 | } 123 | } 124 | 125 | void Dispose( bool disposing ) { 126 | if ( m_stream == null ) { 127 | return; 128 | } 129 | if ( disposing ) { 130 | // Free any other managed objects here. 131 | groupName = null; 132 | m_content = null; 133 | m_locker = null; 134 | m_outFilePath = null; 135 | } 136 | // Free any unmanaged objects here. 137 | if ( m_writer != null ) { 138 | m_writer.Dispose(); 139 | m_writer = null; 140 | } 141 | m_stream.Dispose(); 142 | m_stream = null; 143 | } 144 | 145 | public void Dispose() { 146 | if ( m_stream != null ) { 147 | Dispose( true ); 148 | GC.SuppressFinalize( this ); 149 | } 150 | } 151 | 152 | public int CompareTo( Group other ) { 153 | return groupName.CompareTo( other.groupName ); 154 | } 155 | public void Add( String text ) { 156 | lock ( m_locker ) { 157 | m_content.Add( text ); 158 | } 159 | } 160 | public void ToFile() { 161 | lock ( m_locker ) { 162 | _CheckStream(); 163 | if ( m_content != null && m_writer != null ) { 164 | for ( int i = 0; i < m_content.Count; ++i ) { 165 | m_writer.WriteLine( m_content[i] ); 166 | } 167 | m_content.Clear(); 168 | } 169 | if ( m_writer != null ) { 170 | m_writer.Flush(); 171 | } 172 | } 173 | } 174 | } 175 | Thread m_workThread = null; 176 | AutoResetEvent m_event = new AutoResetEvent( false ); 177 | object m_content_locker = new object(); 178 | List m_content = new List(); 179 | List m_content_tmp = new List(); 180 | bool m_willExit = false; 181 | internal bool m_enable = true; 182 | bool m_stackTraceEnable = true; 183 | bool m_disposed = false; 184 | bool m_paused = false; 185 | 186 | public static long GetLogFileSizeInTotal( out int fileNum ) { 187 | long fileSize = 0; 188 | var files = Common.FileUtils.GetFileList( 189 | LogFile.ROOT, 190 | fn => { 191 | try { 192 | var fi = new System.IO.FileInfo( fn ); 193 | fileSize += fi.Length; 194 | } catch ( System.IO.IOException e ) { 195 | Common.UDebug.LogException( e ); 196 | } 197 | return true; 198 | } 199 | ); 200 | fileNum = files.Count; 201 | return fileSize; 202 | } 203 | 204 | public static void ClearOldLogs() { 205 | try { 206 | var today = DateTime.Now.ToString( "yyyy-MM-dd" ); 207 | var folderList = Common.FileUtils.GetDirectoryList( 208 | LogFile.ROOT, 209 | dir => { 210 | var s = dir.LastIndexOf( '/' ); 211 | if ( s != -1 ) { 212 | var folderName = dir.Substring( s + 1 ); 213 | if ( folderName.StartsWith( today ) ) { 214 | return true; 215 | } 216 | if ( !Regex.IsMatch( folderName, LogFolderNamePattern, RegexOptions.IgnoreCase ) ) { 217 | return true; 218 | } 219 | } 220 | return false; 221 | } 222 | ); 223 | var now = DateTime.Now; 224 | for ( int i = 0; i < folderList.Count; ++i ) { 225 | var dirInfo = new DirectoryInfo( folderList[i] ); 226 | var timeSpan = now - dirInfo.CreationTime; 227 | if ( timeSpan.TotalHours > HowLongForStoreLogInHours ) { 228 | try { 229 | Common.FileUtils.RemoveTree( folderList[i], true ); 230 | } catch ( Exception e ) { 231 | Common.UDebug.LogException( e ); 232 | } 233 | } 234 | } 235 | } catch ( Exception e ) { 236 | Common.UDebug.LogException( e ); 237 | } 238 | } 239 | 240 | public static String GetLogFilePath( String groupName, String suffix = null ) { 241 | return Common.StringUtils.SafeFormat( "{0}/{1}{2}.txt", OutDir, 242 | String.IsNullOrEmpty( groupName ) ? "default" : groupName, suffix ?? String.Empty ); 243 | } 244 | 245 | public static String GetFormatedTimeText() { 246 | return DateTime.Now.ToString( "yyyy-MM-dd HH-mm-ss (ffff)" ); 247 | } 248 | 249 | static public String AppendLogInfo( String source ) { 250 | return source; 251 | } 252 | 253 | public void LogException( Exception o ) { 254 | if ( m_enable ) { 255 | var text = AppendLogInfo( o.ToString() ); 256 | UnityEngine.Debug.LogException( o ); 257 | if ( m_stackTraceEnable ) { 258 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ) ); 259 | } else { 260 | AddLog( text ); 261 | } 262 | } 263 | } 264 | 265 | public void LogExceptionEx( String groupName, Exception o ) { 266 | if ( m_enable ) { 267 | var text = AppendLogInfo( o.ToString() ); 268 | Common.UDebug.LogException( o ); 269 | if ( m_stackTraceEnable ) { 270 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ), groupName ); 271 | } else { 272 | AddLog( text, groupName ); 273 | } 274 | } 275 | } 276 | 277 | public void Log( String o ) { 278 | if ( m_enable ) { 279 | var text = AppendLogInfo( o ); 280 | Common.UDebug.Log( text ); 281 | AddLog( text ); 282 | } 283 | } 284 | 285 | public void Log( T o ) { 286 | if ( m_enable ) { 287 | var text = AppendLogInfo( o.ToString() ); 288 | Common.UDebug.Log( text ); 289 | AddLog( text ); 290 | } 291 | } 292 | 293 | public void Log( String format, T arg1 ) { 294 | if ( m_enable ) { 295 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1 ) ); 296 | Common.UDebug.Log( text ); 297 | AddLog( text ); 298 | } 299 | } 300 | 301 | public void Log( String format, T1 arg1, T2 arg2 ) { 302 | if ( m_enable ) { 303 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1, arg2 ) ); 304 | Common.UDebug.Log( text ); 305 | AddLog( text ); 306 | } 307 | } 308 | 309 | public void Log( String format, T1 arg1, T2 arg2, T3 arg3 ) { 310 | if ( m_enable ) { 311 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1, arg2, arg3 ) ); 312 | Common.UDebug.Log( text ); 313 | AddLog( text ); 314 | } 315 | } 316 | 317 | public void Log( String format, params object[] args ) { 318 | if ( m_enable ) { 319 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, args ) ); 320 | Common.UDebug.Log( text ); 321 | AddLog( text ); 322 | } 323 | } 324 | 325 | public void LogWarning( String o ) { 326 | if ( m_enable ) { 327 | var text = AppendLogInfo( o ); 328 | Common.UDebug.LogWarning( text ); 329 | if ( m_stackTraceEnable ) { 330 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ) ); 331 | } else { 332 | AddLog( text ); 333 | } 334 | } 335 | } 336 | 337 | public void LogWarning( T o ) { 338 | if ( m_enable ) { 339 | var text = AppendLogInfo( o.ToString() ); 340 | Common.UDebug.LogWarning( text ); 341 | if ( m_stackTraceEnable ) { 342 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ) ); 343 | } else { 344 | AddLog( text ); 345 | } 346 | } 347 | } 348 | 349 | public void LogWarning( String format, T arg1 ) { 350 | if ( m_enable ) { 351 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1 ) ); 352 | Common.UDebug.LogWarning( text ); 353 | if ( m_stackTraceEnable ) { 354 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ) ); 355 | } else { 356 | AddLog( text ); 357 | } 358 | } 359 | } 360 | 361 | public void LogWarning( String format, T1 arg1, T2 arg2 ) { 362 | if ( m_enable ) { 363 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1, arg2 ) ); 364 | Common.UDebug.LogWarning( text ); 365 | if ( m_stackTraceEnable ) { 366 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ) ); 367 | } else { 368 | AddLog( text ); 369 | } 370 | } 371 | } 372 | 373 | public void LogWarning( String format, T1 arg1, T2 arg2, T2 arg3 ) { 374 | if ( m_enable ) { 375 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1, arg2, arg3 ) ); 376 | Common.UDebug.LogWarning( text ); 377 | if ( m_stackTraceEnable ) { 378 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ) ); 379 | } else { 380 | AddLog( text ); 381 | } 382 | } 383 | } 384 | 385 | public void LogWarning( String format, params object[] args ) { 386 | if ( m_enable ) { 387 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, args ) ); 388 | Common.UDebug.LogWarning( text ); 389 | if ( m_stackTraceEnable ) { 390 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ) ); 391 | } else { 392 | AddLog( text ); 393 | } 394 | } 395 | } 396 | 397 | public void LogError( String o ) { 398 | if ( m_enable ) { 399 | var text = AppendLogInfo( o ); 400 | UnityEngine.Debug.LogError( text ); 401 | if ( m_stackTraceEnable ) { 402 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ) ); 403 | } else { 404 | AddLog( text ); 405 | } 406 | } 407 | } 408 | 409 | public void LogError( T o ) { 410 | if ( m_enable ) { 411 | var text = AppendLogInfo( o.ToString() ); 412 | UnityEngine.Debug.LogError( text ); 413 | if ( m_stackTraceEnable ) { 414 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ) ); 415 | } else { 416 | AddLog( text ); 417 | } 418 | } 419 | } 420 | 421 | public void LogError( String format, T arg1 ) { 422 | if ( m_enable ) { 423 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1 ) ); 424 | UnityEngine.Debug.LogError( text ); 425 | if ( m_stackTraceEnable ) { 426 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ) ); 427 | } else { 428 | AddLog( text ); 429 | } 430 | } 431 | } 432 | 433 | public void LogError( String format, T1 arg1, T2 arg2 ) { 434 | if ( m_enable ) { 435 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1, arg2 ) ); 436 | UnityEngine.Debug.LogError( text ); 437 | if ( m_stackTraceEnable ) { 438 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ) ); 439 | } else { 440 | AddLog( text ); 441 | } 442 | } 443 | } 444 | 445 | public void LogError( String format, T1 arg1, T2 arg2, T3 arg3 ) { 446 | if ( m_enable ) { 447 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1, arg2, arg3 ) ); 448 | UnityEngine.Debug.LogError( text ); 449 | if ( m_stackTraceEnable ) { 450 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ) ); 451 | } else { 452 | AddLog( text ); 453 | } 454 | } 455 | } 456 | 457 | public void LogError( String format, params object[] args ) { 458 | if ( m_enable ) { 459 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, args ) ); 460 | UnityEngine.Debug.LogError( text ); 461 | if ( m_stackTraceEnable ) { 462 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ) ); 463 | } else { 464 | AddLog( text ); 465 | } 466 | } 467 | } 468 | 469 | public void GLog( String groupName, String o ) { 470 | if ( m_enable ) { 471 | var text = AppendLogInfo( o ); 472 | Common.UDebug.Log( text ); 473 | AddLog( text, groupName ); 474 | } 475 | } 476 | 477 | public void GLog( String groupName, T o ) { 478 | if ( m_enable ) { 479 | var text = AppendLogInfo( o.ToString() ); 480 | Common.UDebug.Log( text ); 481 | AddLog( text, groupName ); 482 | } 483 | } 484 | 485 | public void GLog( String groupName, String format, T arg1 ) { 486 | if ( m_enable ) { 487 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1 ) ); 488 | Common.UDebug.Log( text ); 489 | AddLog( text, groupName ); 490 | } 491 | } 492 | 493 | public void GLog( String groupName, String format, T1 arg1, T2 arg2 ) { 494 | if ( m_enable ) { 495 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1, arg2 ) ); 496 | Common.UDebug.Log( text ); 497 | AddLog( text, groupName ); 498 | } 499 | } 500 | 501 | public void GLog( String groupName, String format, T1 arg1, T2 arg2, T3 arg3 ) { 502 | if ( m_enable ) { 503 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1, arg2, arg3 ) ); 504 | Common.UDebug.Log( text ); 505 | AddLog( text, groupName ); 506 | } 507 | } 508 | 509 | public void GLog( String groupName, String format, params object[] args ) { 510 | if ( m_enable ) { 511 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, args ) ); 512 | Common.UDebug.Log( text ); 513 | AddLog( text, groupName ); 514 | } 515 | } 516 | 517 | public void GLogWarning( String groupName, String o ) { 518 | if ( m_enable ) { 519 | var text = AppendLogInfo( o ); 520 | Common.UDebug.LogWarning( text ); 521 | if ( m_stackTraceEnable ) { 522 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ), groupName ); 523 | } else { 524 | AddLog( text, groupName ); 525 | } 526 | } 527 | } 528 | 529 | public void GLogWarning( String groupName, T o ) { 530 | if ( m_enable ) { 531 | var text = AppendLogInfo( o.ToString() ); 532 | Common.UDebug.LogWarning( text ); 533 | if ( m_stackTraceEnable ) { 534 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ), groupName ); 535 | } else { 536 | AddLog( text, groupName ); 537 | } 538 | } 539 | } 540 | 541 | public void GLogWarning( String groupName, String format, T arg1 ) { 542 | if ( m_enable ) { 543 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1 ) ); 544 | Common.UDebug.LogWarning( text ); 545 | if ( m_stackTraceEnable ) { 546 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ), groupName ); 547 | } else { 548 | AddLog( text, groupName ); 549 | } 550 | } 551 | } 552 | 553 | public void GLogWarning( String groupName, String format, T1 arg1, T2 arg2 ) { 554 | if ( m_enable ) { 555 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1, arg2 ) ); 556 | Common.UDebug.LogWarning( text ); 557 | if ( m_stackTraceEnable ) { 558 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ), groupName ); 559 | } else { 560 | AddLog( text, groupName ); 561 | } 562 | } 563 | } 564 | 565 | public void GLogWarning( String groupName, String format, T1 arg1, T2 arg2, T2 arg3 ) { 566 | if ( m_enable ) { 567 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1, arg2, arg3 ) ); 568 | Common.UDebug.LogWarning( text ); 569 | if ( m_stackTraceEnable ) { 570 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ), groupName ); 571 | } else { 572 | AddLog( text, groupName ); 573 | } 574 | } 575 | } 576 | 577 | public void GLogWarning( String groupName, String format, params object[] args ) { 578 | if ( m_enable ) { 579 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, args ) ); 580 | Common.UDebug.LogWarning( text ); 581 | if ( m_stackTraceEnable ) { 582 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ), groupName ); 583 | } else { 584 | AddLog( text, groupName ); 585 | } 586 | } 587 | } 588 | 589 | public void GLogError( String groupName, String o ) { 590 | if ( m_enable ) { 591 | var text = AppendLogInfo( o ); 592 | UnityEngine.Debug.LogError( text ); 593 | if ( m_stackTraceEnable ) { 594 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ), groupName ); 595 | } else { 596 | AddLog( text, groupName ); 597 | } 598 | } 599 | } 600 | 601 | public void GLogError( String groupName, T o ) { 602 | if ( m_enable ) { 603 | var text = AppendLogInfo( o.ToString() ); 604 | UnityEngine.Debug.LogError( text ); 605 | if ( m_stackTraceEnable ) { 606 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ), groupName ); 607 | } else { 608 | AddLog( text, groupName ); 609 | } 610 | } 611 | } 612 | 613 | public void GLogError( String groupName, String format, T arg1 ) { 614 | if ( m_enable ) { 615 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1 ) ); 616 | UnityEngine.Debug.LogError( text ); 617 | if ( m_stackTraceEnable ) { 618 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ), groupName ); 619 | } else { 620 | AddLog( text, groupName ); 621 | } 622 | } 623 | } 624 | 625 | public void GLogError( String groupName, String format, T1 arg1, T2 arg2 ) { 626 | if ( m_enable ) { 627 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1, arg2 ) ); 628 | UnityEngine.Debug.LogError( text ); 629 | if ( m_stackTraceEnable ) { 630 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ), groupName ); 631 | } else { 632 | AddLog( text, groupName ); 633 | } 634 | } 635 | } 636 | 637 | public void GLogError( String groupName, String format, T1 arg1, T2 arg2, T3 arg3 ) { 638 | if ( m_enable ) { 639 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, arg1, arg2, arg3 ) ); 640 | UnityEngine.Debug.LogError( text ); 641 | if ( m_stackTraceEnable ) { 642 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ), groupName ); 643 | } else { 644 | AddLog( text, groupName ); 645 | } 646 | } 647 | } 648 | 649 | public void GLogError( String groupName, String format, params object[] args ) { 650 | if ( m_enable ) { 651 | var text = AppendLogInfo( Common.StringUtils.SafeFormat( format, args ) ); 652 | UnityEngine.Debug.LogError( text ); 653 | if ( m_stackTraceEnable ) { 654 | AddLog( Common.StringUtils.SafeFormat( "{0}\n{1}", text, Common.UDebug.GetCallStack( 2 ) ), groupName ); 655 | } else { 656 | AddLog( text, groupName ); 657 | } 658 | } 659 | } 660 | 661 | public void _UnityLogFile() { 662 | m_workThread = new Thread( TaskFun ); 663 | m_workThread.Start(); 664 | if ( !System.IO.Directory.Exists( ROOT ) ) { 665 | System.IO.Directory.CreateDirectory( ROOT ); 666 | } 667 | } 668 | 669 | public void CloseStreams() { 670 | lock ( m_content_locker ) { 671 | if ( m_content_tmp != null ) { 672 | for ( int i = 0; i < m_content_tmp.Count; ++i ) { 673 | m_content_tmp[i].CloseStream(); 674 | } 675 | } 676 | if ( m_content != null ) { 677 | for ( int i = 0; i < m_content.Count; ++i ) { 678 | m_content[i].CloseStream(); 679 | } 680 | } 681 | } 682 | } 683 | 684 | public void Pause( bool pause ) { 685 | if ( m_paused != pause ) { 686 | m_paused = pause; 687 | } 688 | } 689 | 690 | public void Exit() { 691 | m_willExit = true; 692 | m_paused = false; 693 | m_event.Set(); 694 | if ( m_workThread != null ) { 695 | m_workThread.Join( 1000 ); 696 | m_workThread = null; 697 | } 698 | lock ( m_content_locker ) { 699 | if ( m_content_tmp != null ) { 700 | for ( int i = 0; i < m_content_tmp.Count; ++i ) { 701 | m_content_tmp[i].Dispose(); 702 | } 703 | } 704 | if ( m_content != null ) { 705 | for ( int i = 0; i < m_content.Count; ++i ) { 706 | m_content[i].Dispose(); 707 | } 708 | } 709 | m_content_tmp.Clear(); 710 | m_content.Clear(); 711 | } 712 | } 713 | 714 | int LowerBound( String key ) { 715 | int first = 0, middle; 716 | int half, len; 717 | len = m_content.Count; 718 | while ( len > 0 ) { 719 | half = len >> 1; 720 | middle = first + half; 721 | if ( m_content[middle].groupName.CompareTo( key ) < 0 ) { 722 | first = middle + 1; 723 | len = len - half - 1; 724 | } else { 725 | len = half; 726 | } 727 | } 728 | return first; 729 | } 730 | 731 | void AddLog( String text, String group = null ) { 732 | group = String.IsNullOrEmpty( group ) == false ? group : DefaultLogFileName; 733 | GetGroup( group ).Add( text ); 734 | m_event.Set(); 735 | } 736 | 737 | Group GetGroup( String name ) { 738 | Group ret = null; 739 | // binary search 740 | int index = LowerBound( name ); 741 | if ( index != m_content.Count && m_content[index].groupName == name ) { 742 | ret = m_content[index]; 743 | } else { 744 | ret = new Group( name, GetLogFilePath( name ) ); 745 | lock ( m_content_locker ) { 746 | m_content.Insert( index, ret ); 747 | } 748 | } 749 | return ret; 750 | } 751 | 752 | void TaskFun() { 753 | while ( !m_willExit ) { 754 | m_event.WaitOne(); 755 | if ( !m_paused ) { 756 | m_content_tmp.Clear(); 757 | lock ( m_content_locker ) { 758 | for ( int i = 0; i < m_content.Count; ++i ) { 759 | m_content_tmp.Add( m_content[i] ); 760 | } 761 | } 762 | for ( int i = 0; i < m_content_tmp.Count; ++i ) { 763 | m_content_tmp[i].ToFile(); 764 | } 765 | } 766 | } 767 | } 768 | 769 | ~LogFile() { 770 | Dispose( false ); 771 | } 772 | 773 | public void Dispose() { 774 | if ( !m_disposed ) { 775 | Dispose( true ); 776 | GC.SuppressFinalize( this ); 777 | } 778 | } 779 | 780 | protected virtual void Dispose( bool disposing ) { 781 | if ( m_disposed ) { 782 | return; 783 | } 784 | // Free any unmanaged objects here. 785 | Exit(); 786 | if ( disposing ) { 787 | // Free any other managed objects here. 788 | m_workThread = null; 789 | m_event = null; 790 | m_content_locker = null; 791 | m_content = null; 792 | m_content_tmp = null; 793 | } 794 | m_disposed = true; 795 | if ( UnityLogFile.m_logger == this ) { 796 | UnityLogFile.m_logger = null; 797 | } 798 | } 799 | 800 | public bool disposed { 801 | get { 802 | return m_disposed; 803 | } 804 | } 805 | } 806 | 807 | #if UNITY_EDITOR 808 | [ExecuteInEditMode] 809 | public class UnityLogFile : MonoBehaviour { 810 | #else 811 | public class UnityLogFile { 812 | #endif 813 | 814 | internal static LogFile m_logger = null; 815 | 816 | public LogFile logger { 817 | get { 818 | return static_logger; 819 | } 820 | } 821 | 822 | internal static LogFile static_logger { 823 | get { 824 | if ( m_logger == null || m_logger.disposed ) { 825 | GC.Collect( 0 ); 826 | GC.WaitForPendingFinalizers(); 827 | m_logger = new LogFile(); 828 | m_logger._UnityLogFile(); 829 | } 830 | return m_logger; 831 | } 832 | } 833 | 834 | void Awake() { 835 | m_logger = new LogFile(); 836 | m_logger._UnityLogFile(); 837 | } 838 | 839 | void OnEnable() { 840 | if ( m_logger == null ) { 841 | m_logger = new LogFile(); 842 | m_logger._UnityLogFile(); 843 | } 844 | m_logger.m_enable = true; 845 | } 846 | 847 | void OnDisable() { 848 | if ( m_logger != null ) { 849 | m_logger.m_enable = false; 850 | } 851 | } 852 | 853 | void OnDestroy() { 854 | if ( m_logger != null ) { 855 | m_logger.Dispose(); 856 | m_logger = null; 857 | } 858 | } 859 | } 860 | 861 | namespace Common { 862 | public static class ULogFile { 863 | public static LogFile sharedInstance { 864 | get { 865 | return UnityLogFile.static_logger; 866 | } 867 | } 868 | } 869 | } 870 | //EOF 871 | 872 | 873 | 874 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/UnityLogFile.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c3d697ef15e4528438d534393a7ca9a7 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.IO; 7 | using System.Reflection; 8 | using System.Diagnostics; 9 | using System.Runtime.InteropServices; 10 | 11 | namespace Common { 12 | 13 | public static class Utils { 14 | 15 | static DateTime _epoch = new DateTime( 1970, 1, 1, 0, 0, 0, DateTimeKind.Utc ); 16 | static long _epochTicks = _epoch.Ticks; 17 | static Stopwatch _globalTimer = new Stopwatch(); 18 | static long _startupTicksMS = NowUnixTimeMS(); 19 | 20 | static Utils() { 21 | _startupTicksMS = NowUnixTimeMS(); 22 | _globalTimer.Start(); 23 | ULogFile.sharedInstance.Log( "System Global Timer Started: {0}/ticks <=> {1}, IsHighResolution = {2}, Frequency = {3}", 24 | _startupTicksMS, 25 | UnixTimeMSToDateTime( _startupTicksMS ), 26 | Stopwatch.IsHighResolution, 27 | Stopwatch.Frequency 28 | ); 29 | } 30 | 31 | public static int GetSystemTicksMS() { 32 | // TickCount cycles between Int32.MinValue, which is a negative 33 | // number, and Int32.MaxValue once every 49.8 days. This sample 34 | // removes the sign bit to yield a nonnegative number that cycles 35 | // between zero and Int32.MaxValue once every 24.9 days. 36 | return ( int )_globalTimer.ElapsedMilliseconds; 37 | } 38 | 39 | public static long GetSystemTicksMS64() { 40 | return _globalTimer.ElapsedMilliseconds; 41 | } 42 | 43 | public static int GetSystemTicksSec() { 44 | return ( int )( _globalTimer.ElapsedMilliseconds / 1000 ); 45 | } 46 | 47 | public static long GetSystemTicksSec64() { 48 | return _globalTimer.ElapsedMilliseconds / 1000; 49 | } 50 | 51 | public static String GetFormatedLocalTime() { 52 | return System.DateTime.Now.ToString( "yyyy-MM-dd HH:mm:ss" ); 53 | } 54 | 55 | public static long UnixTimeMSToTicks( long ms ) { 56 | return ms * TimeSpan.TicksPerMillisecond; 57 | } 58 | 59 | public static DateTime UnixTimeTicksToDateTime( long ticks ) { 60 | return ( _epoch + new TimeSpan( ticks ) ).ToLocalTime(); 61 | } 62 | 63 | public static DateTime UnixTimeMSToDateTime( long ms ) { 64 | return UnixTimeTicksToDateTime( UnixTimeMSToTicks( ms ) ); 65 | } 66 | 67 | public static long NowUnixTimeMS() { 68 | return ( DateTime.UtcNow.Ticks - _epochTicks ) / TimeSpan.TicksPerMillisecond; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/Common/Utils.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 52a8fe179d967604ea96f5997bd4e378 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/LightmapRepacker.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 43faf8ac5a2342e43b9b9df82dd62100 3 | timeCreated: 1517978167 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/LightmapViewer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | using System.Linq; 7 | using UnityEditor; 8 | using UnityEngine; 9 | using System.Threading; 10 | using System.Diagnostics; 11 | 12 | namespace AresEditor.ArtistKit { 13 | 14 | public class LightmapViewer : EditorWindow { 15 | 16 | struct Vector4d { 17 | public double x, y, z, w; 18 | public Vector4d( double _x, double _y, double _z, double _w ) { 19 | x = _x; 20 | y = _y; 21 | z = _z; 22 | w = _w; 23 | } 24 | } 25 | 26 | static LightmapViewer m_window = null; 27 | static Dictionary _MeshUVsCache = null; 28 | static Dictionary _LightmapUVBoundsCache = null; 29 | 30 | List> _litmapUVs = null; 31 | static Texture2D _redTex = null; 32 | 33 | [MenuItem( "Tools/LightmapViewer" )] 34 | static void Init() { 35 | if ( m_window == null ) { 36 | m_window = ( LightmapViewer )EditorWindow.GetWindow( typeof( LightmapViewer ), false, "Lightmap Viewer" ); 37 | } 38 | m_window.minSize = new Vector2( 400, 300 ); 39 | m_window.position = new Rect( 0, 0, m_window.minSize.x, m_window.minSize.y ); 40 | m_window.Show(); 41 | } 42 | 43 | void OnEnable() { 44 | Selection.selectionChanged += OnSelectionChanged; 45 | UpdateCache(); 46 | } 47 | 48 | void OnDisable() { 49 | _litmapUVs = null; 50 | Selection.selectionChanged -= OnSelectionChanged; 51 | if ( _MeshUVsCache != null ) { 52 | _MeshUVsCache.Clear(); 53 | } 54 | if ( _LightmapUVBoundsCache != null ) { 55 | _LightmapUVBoundsCache.Clear(); 56 | } 57 | if ( _redTex != null ) { 58 | UnityEngine.Object.DestroyImmediate( _redTex ); 59 | _redTex = null; 60 | } 61 | } 62 | 63 | void OnDestroy() { 64 | m_window = null; 65 | } 66 | 67 | void UpdateCache() { 68 | _MeshUVsCache = _MeshUVsCache ?? new Dictionary(); 69 | _LightmapUVBoundsCache = _LightmapUVBoundsCache ?? new Dictionary(); 70 | var objs = Selection.gameObjects; 71 | List> litmapUVs = null; 72 | for ( int i = 0; i < objs.Length; ++i ) { 73 | var rs = objs[ i ].GetComponentsInChildren(); 74 | for ( int j = 0; j < rs.Length; ++j ) { 75 | var r = rs[ j ]; 76 | if ( r != null && r.lightmapIndex >= 0 ) { 77 | Mesh m = null; 78 | if ( r is MeshRenderer ) { 79 | var mf = r.GetComponent(); 80 | if ( mf != null ) { 81 | m = mf.sharedMesh; 82 | } 83 | } else if ( r is SkinnedMeshRenderer ) { 84 | var _r = r as SkinnedMeshRenderer; 85 | m = _r.sharedMesh; 86 | } 87 | 88 | if ( m != null ) { 89 | Vector4d uvBounds; 90 | if ( !_LightmapUVBoundsCache.TryGetValue( r, out uvBounds ) ) { 91 | var uv = GetMeshUV2( m ); 92 | if ( uv != null ) { 93 | var __uv = new Vector2[ uv.Length ]; 94 | Array.Copy( uv, __uv, uv.Length ); 95 | uv = __uv; 96 | litmapUVs = litmapUVs ?? new List>(); 97 | var minx = double.MaxValue; 98 | var miny = double.MaxValue; 99 | var maxx = double.MinValue; 100 | var maxy = double.MinValue; 101 | for ( var _j = 0; _j < uv.Length; ++_j ) { 102 | uv[ _j ].x *= r.lightmapScaleOffset.x; 103 | uv[ _j ].y *= r.lightmapScaleOffset.y; 104 | uv[ _j ].x += r.lightmapScaleOffset.z; 105 | uv[ _j ].y += r.lightmapScaleOffset.w; 106 | uv[ _j ].y = 1 - uv[ _j ].y; 107 | var _uv = uv[ _j ]; 108 | if ( _uv.x < minx ) { 109 | minx = _uv.x; 110 | } 111 | if ( _uv.y < miny ) { 112 | miny = _uv.y; 113 | } 114 | if ( _uv.x > maxx ) { 115 | maxx = _uv.x; 116 | } 117 | if ( _uv.y > maxy ) { 118 | maxy = _uv.y; 119 | } 120 | } 121 | var bounds = new Vector4d( minx, miny, maxx, maxy ); 122 | litmapUVs.Add( new KeyValuePair( r.lightmapIndex, bounds ) ); 123 | _LightmapUVBoundsCache.Add( r, bounds ); 124 | } 125 | } else { 126 | litmapUVs = litmapUVs ?? new List>(); 127 | litmapUVs.Add( new KeyValuePair( r.lightmapIndex, uvBounds ) ); 128 | } 129 | } 130 | } 131 | } 132 | } 133 | _litmapUVs = litmapUVs; 134 | } 135 | 136 | void OnSelectionChanged() { 137 | if ( m_window != null ) { 138 | m_window.Repaint(); 139 | UpdateCache(); 140 | } 141 | } 142 | 143 | static Vector2[] GetMeshUV2( Mesh mesh ) { 144 | Vector2[] ret = null; 145 | var assetPath = AssetDatabase.GetAssetPath( mesh ); 146 | var ti = AssetImporter.GetAtPath( assetPath ) as ModelImporter; 147 | var id = String.Format( "{0}, {1}", assetPath, mesh.GetInstanceID() ); 148 | if ( !String.IsNullOrEmpty( assetPath ) ) { 149 | if ( _MeshUVsCache.TryGetValue( id, out ret ) && ret != null ) { 150 | return ret; 151 | } 152 | if ( mesh.isReadable == false ) { 153 | if ( ti != null ) { 154 | if ( ti.isReadable ) { 155 | ret = mesh.uv2; 156 | if ( ret.Length == 0 ) { 157 | ret = mesh.uv; 158 | } 159 | } else { 160 | try { 161 | ti.isReadable = true; 162 | AssetDatabase.ImportAsset( assetPath ); 163 | ret = mesh.uv2; 164 | if ( ret.Length == 0 ) { 165 | ret = mesh.uv; 166 | } 167 | } finally { 168 | ti.isReadable = false; 169 | AssetDatabase.ImportAsset( assetPath ); 170 | } 171 | } 172 | } 173 | } else { 174 | ret = mesh.uv2; 175 | if ( ret.Length == 0 ) { 176 | ret = mesh.uv; 177 | } 178 | } 179 | _MeshUVsCache[ id ] = ret; 180 | } 181 | return ret; 182 | } 183 | 184 | Rect OnGUI_Lightmap() { 185 | Rect selectRect = new Rect(); 186 | var lightmaps = LightmapSettings.lightmaps; 187 | if ( lightmaps != null && lightmaps.Length > 0 ) { 188 | EditorGUILayout.BeginVertical(); 189 | EditorGUILayout.LabelField( "" ); 190 | var width = 0.0f; 191 | var height = 0.0f; 192 | if ( Event.current.type == EventType.Repaint ) { 193 | var rt = GUILayoutUtility.GetLastRect(); 194 | var x = rt.x; 195 | var y = rt.y; 196 | var gap = 4; 197 | var border = x; 198 | width = border * 2; 199 | height = y + border; 200 | var maxTexHeight = 0; 201 | for ( int i = 0; i < lightmaps.Length; ++i ) { 202 | if ( i > 0 ) { 203 | width += gap; 204 | x += gap; 205 | } 206 | var m = lightmaps[ i ]; 207 | var t = m.lightmapLight; 208 | var t_width = t.width; 209 | var t_height = t.height; 210 | if ( t_height > maxTexHeight ) { 211 | maxTexHeight = t_height; 212 | } 213 | if ( t != null ) { 214 | EditorGUI.DrawPreviewTexture( new Rect( x, y, t_width, t_height ), t ); 215 | if ( _litmapUVs != null ) { 216 | for ( int j = 0; j < _litmapUVs.Count; ++j ) { 217 | var uv = _litmapUVs[ j ]; 218 | if ( uv.Key == i ) { 219 | var bounds = uv.Value; 220 | double fMinX = bounds.x * t_width; 221 | double fMaxX = bounds.z * t_width; 222 | double fMinY = bounds.y * t_height; 223 | double fMaxY = bounds.w * t_height; 224 | // 绘制lightmap像素框的时候,分别取整到最大包围盒 225 | int pixMinX = ( int )Math.Max( Math.Floor( bounds.x * t_width ), 0 ); 226 | int pixMaxX = ( int )Math.Min( Math.Ceiling( bounds.z * t_width ), t_width ); 227 | int pixMinY = ( int )Math.Max( Math.Floor( bounds.y * t_height ), 0 ); 228 | int pixMaxY = ( int )Math.Min( Math.Ceiling( bounds.w * t_height ), t_height ); 229 | var color = Color.red; 230 | var _color = GUI.color; 231 | var rect = new Rect( pixMinX, pixMinY, pixMaxX - pixMinX, pixMaxY - pixMinY ); 232 | var uiRect = rect; 233 | uiRect.x += x; 234 | uiRect.y += y; 235 | var mouseOvered = uiRect.Contains( Event.current.mousePosition ); 236 | color = mouseOvered ? Color.green : Color.red; 237 | color.a = mouseOvered ? 0.0f : 0.5f; 238 | GUI.color = color; 239 | var drawRT = new Rect(); 240 | drawRT.xMin = pixMinX; 241 | drawRT.xMax = pixMaxX; 242 | drawRT.yMin = pixMinY; 243 | drawRT.yMax = pixMaxY; 244 | drawRT.x += x; 245 | drawRT.y += y; 246 | if ( _redTex == null ) { 247 | _redTex = new Texture2D( 1, 1, TextureFormat.RGBA32, false ); 248 | _redTex.SetPixel( 0, 0, new Color( 1, 0, 0, 0.2f ) ); 249 | _redTex.Apply(); 250 | } 251 | EditorGUI.DrawTextureTransparent( drawRT, _redTex, ScaleMode.StretchToFill ); 252 | color = mouseOvered ? Color.green : Color.red; 253 | color.a = mouseOvered ? 0.5f : 0.0f; 254 | if ( mouseOvered ) { 255 | // 修复误差,用于显示真实的uv框 256 | var errorMinX = ( float )( fMinX - pixMinX ); 257 | var errorMinY = ( float )( fMinY - pixMinY ); 258 | var errorMaxX = ( float )( fMaxX - pixMaxX ); 259 | var errorMaxY = ( float )( fMaxY - pixMaxY ); 260 | selectRect = rect; 261 | selectRect.xMin += errorMinX; 262 | selectRect.yMin += errorMinY; 263 | selectRect.xMax += errorMaxX; 264 | selectRect.yMax += errorMaxY; 265 | } 266 | GUI.color = color; 267 | var frameRT = drawRT; 268 | // 稍微扩大一点,刚好包围完整 269 | frameRT.x -= 1; 270 | frameRT.y -= 1; 271 | frameRT.width += 1; 272 | frameRT.height += 1; 273 | EditorGUI.DrawRect( new Rect( frameRT.xMin, frameRT.yMin, frameRT.xMax - frameRT.xMin, 1 ), color ); 274 | EditorGUI.DrawRect( new Rect( frameRT.xMin, frameRT.yMin, 1, frameRT.yMax - frameRT.yMin ), color ); 275 | EditorGUI.DrawRect( new Rect( frameRT.xMin, frameRT.yMax, frameRT.xMax - frameRT.xMin, 1 ), color ); 276 | EditorGUI.DrawRect( new Rect( frameRT.xMax, frameRT.yMin, 1, frameRT.yMax - frameRT.yMin ), color ); 277 | GUI.color = _color; 278 | } 279 | } 280 | } 281 | width += t_width; 282 | x += t_width; 283 | } 284 | } 285 | height += maxTexHeight; 286 | if ( m_window != null ) { 287 | var size = m_window.minSize; 288 | if ( width > size.x ) { 289 | size.x = width; 290 | } 291 | if ( height > size.y ) { 292 | size.y = height; 293 | } 294 | m_window.minSize = size; 295 | } 296 | } 297 | EditorGUILayout.LabelField( 298 | String.Format( "PixelRect: ( {0}, {1}, {2}, {3} )", 299 | selectRect.x, selectRect.y, selectRect.width, selectRect.height ) 300 | ); 301 | EditorGUILayout.EndVertical(); 302 | } 303 | return selectRect; 304 | } 305 | 306 | void OnGUI() { 307 | EditorGUILayout.BeginVertical(); 308 | EditorGUILayout.Space(); 309 | EditorGUILayout.BeginHorizontal(); 310 | EditorGUILayout.ObjectField( "Current Selected:", Selection.activeGameObject, typeof( GameObject ), true ); 311 | EditorGUILayout.EndHorizontal(); 312 | EditorGUILayout.Space(); 313 | OnGUI_Lightmap(); 314 | EditorGUILayout.Space(); 315 | EditorGUILayout.EndVertical(); 316 | } 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/LightmapViewer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8ffe1bd14c82a5846a2e8d008c070f47 3 | timeCreated: 1517971826 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/NativeAPI.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_ANDROID && !UNITY_EDITOR 2 | #define ANDROID_RUNTIME 3 | #endif 4 | 5 | #if UNITY_IPHONE && !UNITY_EDITOR 6 | #define IOS_RUNTIME 7 | #endif 8 | 9 | #if !UNITY_EDITOR && ( UNITY_IPHONE || UNITY_ANDROID ) 10 | #define MOBILE_RUNTIME 11 | #endif 12 | 13 | using System; 14 | using System.Runtime.InteropServices; 15 | using AOT; 16 | using Common; 17 | 18 | namespace AresEditor { 19 | 20 | public unsafe static class NativeAPI { 21 | 22 | #if IOS_RUNTIME 23 | public const String Name = "__Internal"; 24 | #else 25 | public const String Name = "FGDKit_NativeCore"; 26 | #endif 27 | #region crt 28 | [DllImport( Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "FGDKit_Free" )] 29 | public static extern void crt_free( IntPtr p ); 30 | 31 | [DllImport( Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "FGDKit_Malloc" )] 32 | public static extern IntPtr crt_malloc( Int32 size ); 33 | #endregion 34 | 35 | #region stb 36 | // 16 bytes, nominally 37 | [StructLayout( LayoutKind.Sequential )] 38 | public struct stbrp_rect { 39 | // reserved for your use: 40 | public Int32 id; 41 | // input: 42 | public UInt16 w, h; 43 | // output: 44 | public UInt16 x, y; 45 | public Int32 was_packed; // non-zero if valid packing 46 | } 47 | 48 | [StructLayout( LayoutKind.Sequential )] 49 | public unsafe struct stbrp_node { 50 | public UInt16 x, y; 51 | public stbrp_node* next; 52 | } 53 | 54 | [StructLayout( LayoutKind.Sequential )] 55 | public unsafe struct stbrp_context { 56 | public Int32 width; 57 | public Int32 height; 58 | public Int32 align; 59 | public Int32 init_mode; 60 | public Int32 heuristic; 61 | public Int32 num_nodes; 62 | public stbrp_node* active_head; 63 | public stbrp_node* free_head; 64 | public stbrp_node extra0; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' 65 | public stbrp_node extra1; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' 66 | } 67 | 68 | public enum STBRP_HEURISTIC { 69 | Skyline_default = 0, 70 | Skyline_BL_sortHeight = Skyline_default, 71 | Skyline_BF_sortHeight, 72 | } 73 | 74 | [DllImport( Name, CallingConvention = CallingConvention.Cdecl )] 75 | static extern IntPtr stbi_load( String filename, out int x, out int y, out int comp, int req_comp ); 76 | 77 | [DllImport( Name, CallingConvention = CallingConvention.Cdecl )] 78 | static extern IntPtr stbi_load_from_memory( IntPtr buffer, int len, out int x, out int y, out int comp, int req_comp ); 79 | 80 | [DllImport( Name, CallingConvention = CallingConvention.Cdecl )] 81 | static extern void stbi_image_free( IntPtr retval_from_stbi_load ); 82 | 83 | [DllImport( Name, CallingConvention = CallingConvention.Cdecl )] 84 | static extern IntPtr stbi_failure_reason(); 85 | 86 | // Assign packed locations to rectangles. The rectangles are of type 87 | // 'stbrp_rect' defined below, stored in the array 'rects', and there 88 | // are 'num_rects' many of them. 89 | // 90 | // Rectangles which are successfully packed have the 'was_packed' flag 91 | // set to a non-zero value and 'x' and 'y' store the minimum location 92 | // on each axis (i.e. bottom-left in cartesian coordinates, top-left 93 | // if you imagine y increasing downwards). Rectangles which do not fit 94 | // have the 'was_packed' flag set to 0. 95 | // 96 | // You should not try to access the 'rects' array from another thread 97 | // while this function is running, as the function temporarily reorders 98 | // the array while it executes. 99 | // 100 | // To pack into another rectangle, you need to call stbrp_init_target 101 | // again. To continue packing into the same rectangle, you can call 102 | // this function again. Calling this multiple times with multiple rect 103 | // arrays will probably produce worse packing results than calling it 104 | // a single time with the full rectangle array, but the option is 105 | // available. 106 | [DllImport( Name, CallingConvention = CallingConvention.Cdecl )] 107 | public static unsafe extern void stbrp_pack_rects( stbrp_context* context, stbrp_rect* rects, int num_rects ); 108 | 109 | // Initialize a rectangle packer to: 110 | // pack a rectangle that is 'width' by 'height' in dimensions 111 | // using temporary storage provided by the array 'nodes', which is 'num_nodes' long 112 | // 113 | // You must call this function every time you start packing into a new target. 114 | // 115 | // There is no "shutdown" function. The 'nodes' memory must stay valid for 116 | // the following stbrp_pack_rects() call (or calls), but can be freed after 117 | // the call (or calls) finish. 118 | // 119 | // Note: to guarantee best results, either: 120 | // 1. make sure 'num_nodes' >= 'width' 121 | // or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' 122 | // 123 | // If you don't do either of the above things, widths will be quantized to multiples 124 | // of small integers to guarantee the algorithm doesn't run out of temporary storage. 125 | // 126 | // If you do #2, then the non-quantized algorithm will be used, but the algorithm 127 | // may run out of temporary storage and be unable to pack some rectangles. 128 | [DllImport( Name, CallingConvention = CallingConvention.Cdecl )] 129 | public static unsafe extern void stbrp_init_target( stbrp_context* context, int width, int height, stbrp_node* nodes, int num_nodes ); 130 | 131 | // Optionally call this function after init but before doing any packing to 132 | // change the handling of the out-of-temp-memory scenario, described above. 133 | // If you call init again, this will be reset to the default (false). 134 | [DllImport( Name, CallingConvention = CallingConvention.Cdecl )] 135 | public static unsafe extern void stbrp_setup_allow_out_of_mem( stbrp_context* context, int allow_out_of_mem ); 136 | 137 | // Optionally select which packing heuristic the library should use. Different 138 | // heuristics will produce better/worse results for different data sets. 139 | // If you call init again, this will be reset to the default. 140 | [DllImport( Name, CallingConvention = CallingConvention.Cdecl )] 141 | public static unsafe extern void stbrp_setup_heuristic( stbrp_context* context, int heuristic ); 142 | 143 | public static Byte[] stbi_loadfromfile( String filename, out int x, out int y, out int comp, int req_comp = 0 ) { 144 | Byte[] data = null; 145 | var ptr = IntPtr.Zero; 146 | x = 0; 147 | y = 0; 148 | comp = 0; 149 | try { 150 | ptr = stbi_load( filename, out x, out y, out comp, req_comp ); 151 | if ( ptr != IntPtr.Zero ) { 152 | var size = x * y * comp; 153 | if ( size > 0 ) { 154 | data = new byte[ size ]; 155 | Marshal.Copy( ptr, data, 0, size ); 156 | } 157 | } 158 | } catch ( Exception e ) { 159 | if ( !( e is DllNotFoundException ) ) { 160 | UDebug.LogException( e ); 161 | } 162 | } finally { 163 | if ( ptr != IntPtr.Zero ) { 164 | stbi_image_free( ptr ); 165 | } else { 166 | var errorPtr = stbi_failure_reason(); 167 | for ( ; ; ) { 168 | if ( errorPtr != IntPtr.Zero ) { 169 | var error = Marshal.PtrToStringAnsi( errorPtr ); 170 | if ( !String.IsNullOrEmpty( error ) ) { 171 | UDebug.LogError( "stbi_loadfromfile failed: {0}", error ); 172 | break; 173 | } 174 | } 175 | UDebug.LogError( "stbi_loadfromfile failed: reason unknown." ); 176 | break; 177 | } 178 | } 179 | } 180 | return data; 181 | } 182 | 183 | public static bool stbi_loadfromfilex( String filename, Action callback, out int x, out int y, out int comp, int req_comp = 0 ) { 184 | var ptr = IntPtr.Zero; 185 | x = 0; 186 | y = 0; 187 | comp = 0; 188 | try { 189 | ptr = stbi_load( filename, out x, out y, out comp, req_comp ); 190 | if ( callback != null ) { 191 | callback( ptr, x, y, comp ); 192 | } 193 | } catch ( Exception e ) { 194 | if ( !( e is DllNotFoundException ) ) { 195 | UDebug.LogException( e ); 196 | } 197 | } finally { 198 | if ( ptr != IntPtr.Zero ) { 199 | stbi_image_free( ptr ); 200 | } 201 | } 202 | return ptr != IntPtr.Zero; 203 | } 204 | 205 | [DllImport( Name, CallingConvention = CallingConvention.Cdecl )] 206 | public static extern int stbi_write_png( String filename, int w, int h, int comp, IntPtr data, int stride_in_bytes ); 207 | 208 | [DllImport( Name, CallingConvention = CallingConvention.Cdecl )] 209 | public static extern int stbi_write_bmp( String filename, int w, int h, int comp, IntPtr data ); 210 | 211 | [DllImport( Name, CallingConvention = CallingConvention.Cdecl )] 212 | public static extern int stbi_write_tga( String filename, int w, int h, int comp, IntPtr data ); 213 | 214 | [DllImport( Name, CallingConvention = CallingConvention.Cdecl )] 215 | public static extern int stbi_write_hdr( String filename, int w, int h, int comp, IntPtr data ); 216 | #endregion 217 | 218 | #region tinyexr 219 | // Loads single-frame OpenEXR image. Assume EXR image contains A(single channel 220 | // alpha) or RGB(A) channels. 221 | // Application must free image data as returned by `out_rgba` 222 | // Result image format is: float x RGBA x width x hight 223 | // Returns negative value and may set error string in `err` when there's an 224 | // error 225 | [DllImport( Name, CallingConvention = CallingConvention.Cdecl )] 226 | public static extern Int32 LoadEXR( ref IntPtr out_rgba, out Int32 width, out Int32 height, String filename, ref IntPtr err ); 227 | 228 | // Saves single-frame OpenEXR image. Assume EXR image contains RGB(A) channels. 229 | // components must be 1(Grayscale), 3(RGB) or 4(RGBA). 230 | // Input image format is: `float x width x height`, or `float x RGB(A) x width x 231 | // hight` 232 | // Save image as fp16(HALF) format when `save_as_fp16` is positive non-zero 233 | // value. 234 | // Save image as fp32(FLOAT) format when `save_as_fp16` is 0. 235 | [DllImport( Name, CallingConvention = CallingConvention.Cdecl )] 236 | public static extern Int32 SaveEXR( IntPtr data, Int32 width, Int32 height, Int32 components, Int32 save_as_fp16, String filename ); 237 | #endregion 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/NativeAPI.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6e9d916f129db3d42b9afd5489a4295a 3 | timeCreated: 1512545117 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/UVSheetViewer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | using System.Linq; 7 | using UnityEditor; 8 | using UnityEngine; 9 | using System.Threading; 10 | using System.Diagnostics; 11 | using Common; 12 | 13 | namespace AresEditor.ArtistKit { 14 | 15 | public class UVSheetViewer : EditorWindow { 16 | 17 | enum UVChannel { 18 | UV0, 19 | UV1, 20 | UV2, 21 | UV3, 22 | } 23 | 24 | class UVData { 25 | public String name = String.Empty; 26 | public List subMeshes = null; 27 | public List uvs = new List(); 28 | public UVData() { 29 | for ( int i = 0; i < 4; ++i ) { 30 | uvs.Add( new Vector2[] { } ); 31 | } 32 | } 33 | } 34 | 35 | static UVChannel m_selectUVChannel = UVChannel.UV0; 36 | static UVSheetViewer m_window = null; 37 | 38 | static int m_pageOffset = 0; 39 | static Dictionary m_MeshUVCache = new Dictionary(); 40 | static List> m_selectedUVData = null; 41 | static Texture2D _grayTexture = null; 42 | static Material _drawMatT = null; 43 | static Material _drawMatC = null; 44 | 45 | [MenuItem( "Tools/UVSheetViewer" )] 46 | static void Init() { 47 | if ( m_window == null ) { 48 | m_window = ( UVSheetViewer )EditorWindow.GetWindow( typeof( UVSheetViewer ), false, "UVSheet Viewer" ); 49 | } 50 | m_window.minSize = new Vector2( 400, 300 ); 51 | m_window.position = new Rect( 0, 0, m_window.minSize.x, m_window.minSize.y ); 52 | m_window.Show(); 53 | } 54 | 55 | void OnDestroy() { 56 | m_window = null; 57 | if ( _grayTexture != null ) { 58 | UnityEngine.Object.DestroyImmediate( _grayTexture ); 59 | _grayTexture = null; 60 | } 61 | if ( _drawMatT != null ) { 62 | UnityEngine.Object.DestroyImmediate( _drawMatT ); 63 | _drawMatT = null; 64 | } 65 | if ( _drawMatC != null ) { 66 | UnityEngine.Object.DestroyImmediate( _drawMatC ); 67 | _drawMatC = null; 68 | } 69 | } 70 | 71 | void OnEnable() { 72 | Selection.selectionChanged += OnSelectionChanged; 73 | UpdateCache(); 74 | } 75 | 76 | void OnDisable() { 77 | Selection.selectionChanged -= OnSelectionChanged; 78 | if ( m_MeshUVCache != null ) { 79 | m_MeshUVCache.Clear(); 80 | } 81 | } 82 | 83 | void OnSelectionChanged() { 84 | if ( m_window != null ) { 85 | m_window.Repaint(); 86 | UpdateCache(); 87 | EditorApplication.delayCall += () => m_window.Repaint(); 88 | m_window.Repaint(); 89 | } 90 | } 91 | 92 | static String GetMeshAssetHash( String path, Mesh mesh ) { 93 | var assets = AssetDatabase.LoadAllAssetRepresentationsAtPath( path ); 94 | var index = Array.IndexOf( assets, mesh ); 95 | if ( path.EndsWith( ".asset" ) || assets.Length == 0 ) { 96 | index = 0; 97 | } else { 98 | UDebug.Assert( index >= 0 ); 99 | } 100 | if ( File.Exists( path ) ) { 101 | return String.Format( "{0}-{1}", EditorUtils.Md5Asset( path ), index ); 102 | } else { 103 | return String.Format( "{0}-{1}", path, mesh.GetInstanceID() ); 104 | } 105 | } 106 | 107 | static UVData GetMeshUVData( Mesh mesh ) { 108 | var assetPath = AssetDatabase.GetAssetPath( mesh ); 109 | UVData result = null; 110 | if ( String.IsNullOrEmpty( assetPath ) ) { 111 | result = null; 112 | } else { 113 | UVData md = null; 114 | var importer = AssetImporter.GetAtPath( assetPath ) as ModelImporter; 115 | var isReadable = true; 116 | var meshCompression = ModelImporterMeshCompression.Off; 117 | if ( importer != null ) { 118 | isReadable = importer.isReadable; 119 | meshCompression = importer.meshCompression; 120 | } 121 | Func readMeshData = _mesh => { 122 | var _md = new UVData(); 123 | _md.name = _mesh.name; 124 | _md.uvs[ 0 ] = _mesh.uv; 125 | _md.uvs[ 1 ] = _mesh.uv2; 126 | _md.uvs[ 2 ] = _mesh.uv3; 127 | _md.uvs[ 3 ] = _mesh.uv4; 128 | _md.subMeshes = new List( _mesh.subMeshCount ); 129 | for ( int s = 0; s < _mesh.subMeshCount; ++s ) { 130 | UDebug.Assert( _mesh.GetTopology( s ) == MeshTopology.Triangles ); 131 | _md.subMeshes.Add( _mesh.GetTriangles( s ) ); 132 | } 133 | return _md; 134 | }; 135 | UVData _cachedMeshData = null; 136 | var key = GetMeshAssetHash( assetPath, mesh ); 137 | if ( m_MeshUVCache.TryGetValue( key, out _cachedMeshData ) ) { 138 | md = _cachedMeshData; 139 | } else { 140 | if ( !isReadable || meshCompression != ModelImporterMeshCompression.Off ) { 141 | if ( importer != null ) { 142 | importer.isReadable = true; 143 | importer.meshCompression = ModelImporterMeshCompression.Off; 144 | AssetDatabase.ImportAsset( assetPath ); 145 | } 146 | } 147 | try { 148 | md = readMeshData( mesh ); 149 | } finally { 150 | if ( !isReadable || meshCompression != ModelImporterMeshCompression.Off ) { 151 | if ( importer != null ) { 152 | importer.isReadable = isReadable; 153 | importer.meshCompression = meshCompression; 154 | AssetDatabase.ImportAsset( assetPath ); 155 | } 156 | } 157 | if ( md != null ) { 158 | m_MeshUVCache.Add( key, md ); 159 | } 160 | } 161 | } 162 | result = md; 163 | } 164 | return result; 165 | } 166 | 167 | static void UpdateCache() { 168 | var objs = Selection.objects; 169 | var meshes = new HashSet(); 170 | var dict = new Dictionary(); 171 | for ( int i = 0; i < objs.Length; ++i ) { 172 | var go = objs[ i ] as GameObject; 173 | if ( go != null ) { 174 | var rs = go.GetComponentsInChildren(); 175 | for ( int j = 0; j < rs.Length; ++j ) { 176 | var r = rs[ j ]; 177 | if ( r != null ) { 178 | Mesh m = null; 179 | Material mat = null; 180 | if ( r is MeshRenderer ) { 181 | var mf = r.GetComponent(); 182 | if ( mf != null ) { 183 | m = mf.sharedMesh; 184 | } 185 | var mr = r.GetComponent(); 186 | if ( mr != null ) { 187 | mat = mr.sharedMaterial; 188 | } 189 | } else if ( r is SkinnedMeshRenderer ) { 190 | var _r = r as SkinnedMeshRenderer; 191 | m = _r.sharedMesh; 192 | if ( _r != null ) { 193 | mat = _r.sharedMaterial; 194 | } 195 | } 196 | if ( m != null ) { 197 | if ( mat != null && mat.HasProperty( "_MainTex" ) ) { 198 | dict[ m ] = mat.mainTexture as Texture2D; 199 | } 200 | meshes.Add( m ); 201 | } 202 | } 203 | } 204 | } else if ( objs[ i ] is Mesh ) { 205 | meshes.Add( objs[ i ] as Mesh ); 206 | } 207 | } 208 | m_selectedUVData = new List>(); 209 | try { 210 | int i = 0; 211 | foreach ( var mesh in meshes ) { 212 | var md = GetMeshUVData( mesh ); 213 | if ( EditorUtility.DisplayCancelableProgressBar( "UVSheetViewer", mesh.name, ( float )i / meshes.Count ) ) { 214 | break; 215 | } 216 | if ( md != null ) { 217 | Texture2D hintTex = null; 218 | dict.TryGetValue( mesh, out hintTex ); 219 | m_selectedUVData.Add( new KeyValuePair( md, hintTex ?? Texture2D.blackTexture ) ); 220 | } 221 | } 222 | } finally { 223 | EditorUtility.ClearProgressBar(); 224 | } 225 | } 226 | 227 | void OnGUI() { 228 | EditorGUILayout.BeginVertical(); 229 | EditorGUILayout.Space(); 230 | EditorGUILayout.ObjectField( "Current Selected:", Selection.activeGameObject, typeof( GameObject ), true ); 231 | m_selectUVChannel = ( UVChannel )EditorGUILayout.EnumPopup( "UVChannel", m_selectUVChannel ); 232 | EditorGUILayout.Space(); 233 | OnGUI_DrawUVSheets(); 234 | EditorGUILayout.Space(); 235 | EditorGUILayout.EndVertical(); 236 | } 237 | 238 | void OnGUI_DrawUVSheets() { 239 | const int MaxPage = 3; 240 | const int SheetWidth = 512; 241 | const int SheetHeight = 512; 242 | const int uvWindowOffsetX = 8; 243 | const int uvWindowOffsetY = 8; 244 | const int uvWindowWidth = SheetWidth - uvWindowOffsetX * 2; 245 | const int uvWindowHeight = SheetHeight - uvWindowOffsetY * 2; 246 | if ( _grayTexture == null ) { 247 | _grayTexture = new Texture2D( 1, 1 ); 248 | _grayTexture.SetPixel( 0, 0, Color.grey ); 249 | } 250 | var uvs = m_selectedUVData; 251 | if ( uvs != null && uvs.Count > 0 ) { 252 | EditorGUILayout.BeginVertical(); 253 | m_pageOffset = EditorGUILayout.IntSlider( "Page", m_pageOffset, 0, Math.Max( 0, uvs.Count - MaxPage ) ); 254 | EditorGUILayout.LabelField( "" ); 255 | var width = 0.0f; 256 | var height = 0.0f; 257 | if ( Event.current.type == EventType.Repaint ) { 258 | var rt = GUILayoutUtility.GetLastRect(); 259 | var x = rt.x; 260 | var y = rt.y; 261 | var gap = 4; 262 | var border = x; 263 | width = border * 2; 264 | height = y + border; 265 | var maxTexHeight = 0; 266 | for ( int i = m_pageOffset; i < uvs.Count; ++i ) { 267 | if ( i > m_pageOffset && ( i - m_pageOffset ) < MaxPage ) { 268 | width += gap; 269 | x += gap; 270 | } 271 | var m = m_selectedUVData[ i ]; 272 | if ( SheetHeight > maxTexHeight ) { 273 | maxTexHeight = SheetHeight; 274 | } 275 | EditorGUI.DrawTextureAlpha( new Rect( x, y, SheetWidth, SheetHeight ), _grayTexture ); 276 | var uv_rt = new Rect( x + uvWindowOffsetX, y + uvWindowOffsetY, uvWindowWidth, uvWindowHeight ); 277 | var inBounds = DrawSheet( uv_rt, m.Key, m_selectUVChannel == UVChannel.UV0, m.Value ); 278 | GUI.color = Color.black; 279 | EditorGUI.LabelField( new Rect( x + uvWindowOffsetX + 1, y + uvWindowOffsetY + 2, 120, 32 ), m.Key.name ); 280 | GUI.color = inBounds ? Color.green : Color.red; 281 | EditorGUI.LabelField( new Rect( x + uvWindowOffsetX, y + uvWindowOffsetY, 120, 32 ), m.Key.name ); 282 | 283 | if ( ( i - m_pageOffset ) < MaxPage ) { 284 | width += SheetWidth; 285 | } 286 | x += SheetWidth; 287 | } 288 | GUI.color = Color.white; 289 | 290 | height += maxTexHeight; 291 | if ( m_window != null ) { 292 | var size = m_window.minSize; 293 | if ( width != size.x ) { 294 | size.x = width; 295 | } 296 | if ( height != size.y ) { 297 | size.y = height; 298 | } 299 | m_window.minSize = size; 300 | m_window.maxSize = size; 301 | } 302 | } 303 | EditorGUILayout.EndVertical(); 304 | } 305 | } 306 | 307 | static float ClampRepeatUV( float value, ref bool modified ) { 308 | if ( Mathf.Abs( value ) < 1e-6f ) { 309 | value = 0; 310 | } 311 | if ( Mathf.Abs( 1 - value ) < 1e-6f ) { 312 | value = 1; 313 | } 314 | if ( value < 0 || value > 1 ) { 315 | var _value = value % 1.0f; 316 | if ( _value < 0.0 ) { 317 | _value += 1.0f; 318 | } 319 | modified = _value != value; 320 | return _value; 321 | } else { 322 | modified = false; 323 | return value; 324 | } 325 | } 326 | 327 | bool DrawSheet( Rect rect, UVData uvset, bool withBG = false, Texture2D tex = null ) { 328 | var result = true; 329 | if ( _drawMatT == null ) { 330 | var shader = Shader.Find( "Unlit/Texture" ); 331 | _drawMatT = new Material( shader ); 332 | } 333 | if ( _drawMatC == null ) { 334 | var shader = Shader.Find( "Hidden/Internal-Colored" ); 335 | _drawMatC = new Material( shader ); 336 | } 337 | if ( Event.current.type == EventType.Repaint ) { 338 | GUI.BeginClip( rect ); 339 | GL.PushMatrix(); 340 | 341 | GL.Clear( true, false, Color.black ); 342 | // background 343 | if ( withBG && tex != null && _drawMatT.HasProperty( "_MainTex" ) ) { 344 | _drawMatT.mainTexture = tex; 345 | } else { 346 | _drawMatT.mainTexture = Texture2D.blackTexture; 347 | } 348 | _drawMatT.SetPass( 0 ); 349 | GL.Begin( GL.QUADS ); 350 | GL.Color( Color.black ); 351 | GL.TexCoord2( 0, 1 ); 352 | GL.Vertex3( 0, 0, 0 ); 353 | GL.TexCoord2( 1, 1 ); 354 | GL.Vertex3( rect.width, 0, 0 ); 355 | GL.TexCoord2( 1, 0 ); 356 | GL.Vertex3( rect.width, rect.height, 0 ); 357 | GL.TexCoord2( 0, 0 ); 358 | GL.Vertex3( 0, rect.height, 0 ); 359 | GL.End(); 360 | 361 | _drawMatC.SetPass( 0 ); 362 | // draw grid 363 | GL.Begin( GL.LINES ); 364 | int count = 25; 365 | var sx = 0; 366 | var sy = 0; 367 | var dx = ( float )rect.width / count; 368 | var dy = ( float )rect.height / count; 369 | for ( int i = 0; i <= count; ++i ) { 370 | float f = ( i % 5 == 0 ) ? 0.5f : 0.2f; 371 | GL.Color( new Color( f, f, f, 1 ) ); 372 | sx = ( int )Math.Ceiling( dx * i ); 373 | sy = ( int )Math.Ceiling( dy * i ); 374 | GL.Vertex3( sx, 0, 0 ); 375 | GL.Vertex3( sx, rect.height, 0 ); 376 | GL.Vertex3( 0, sy, 0 ); 377 | GL.Vertex3( rect.width, sy, 0 ); 378 | } 379 | GL.End(); 380 | 381 | var scale = new Vector2( rect.width, rect.height ); 382 | var subMeshes = uvset.subMeshes; 383 | var uv = uvset.uvs[ ( int )m_selectUVChannel ]; 384 | if ( uv != null && uv.Length > 0 ) { 385 | GL.Begin( GL.LINES ); 386 | GL.Color( Color.green ); 387 | GL.TexCoord2( 0, 0 ); 388 | for ( int j = 0; j < subMeshes.Count; ++j ) { 389 | var tri = subMeshes[ j ]; 390 | for ( int t = 0; t < tri.Length; t += 3 ) { 391 | var pt0 = uv[ tri[ t ] ]; 392 | var pt1 = uv[ tri[ t + 1 ] ]; 393 | var pt2 = uv[ tri[ t + 2 ] ]; 394 | var b = false; 395 | if ( pt0.x < 0 || pt0.x > 1 ) { 396 | result = false; 397 | } 398 | if ( pt0.y < 0 || pt0.y > 1 ) { 399 | result = false; 400 | } 401 | if ( pt1.x < 0 || pt1.x > 1 ) { 402 | result = false; 403 | } 404 | if ( pt1.y < 0 || pt1.y > 1 ) { 405 | result = false; 406 | } 407 | if ( pt2.x < 0 || pt2.x > 1 ) { 408 | result = false; 409 | } 410 | if ( pt2.y < 0 || pt2.y > 1 ) { 411 | result = false; 412 | } 413 | pt0.x = ClampRepeatUV( pt0.x, ref b ); 414 | pt0.y = ClampRepeatUV( pt0.y, ref b ); 415 | pt1.x = ClampRepeatUV( pt1.x, ref b ); 416 | pt1.y = ClampRepeatUV( pt1.y, ref b ); 417 | pt2.x = ClampRepeatUV( pt2.x, ref b ); 418 | pt2.y = ClampRepeatUV( pt2.y, ref b ); 419 | pt0.y = 1 - pt0.y; 420 | pt1.y = 1 - pt1.y; 421 | pt2.y = 1 - pt2.y; 422 | pt0.Scale( scale ); 423 | pt1.Scale( scale ); 424 | pt2.Scale( scale ); 425 | if ( b ) { 426 | GL.Color( Color.red ); 427 | } else { 428 | GL.Color( Color.green ); 429 | } 430 | GL.Vertex3( pt0.x, pt0.y, 0 ); 431 | GL.Vertex3( pt1.x, pt1.y, 0 ); 432 | GL.Vertex3( pt1.x, pt1.y, 0 ); 433 | GL.Vertex3( pt2.x, pt2.y, 0 ); 434 | GL.Vertex3( pt2.x, pt2.y, 0 ); 435 | GL.Vertex3( pt0.x, pt0.y, 0 ); 436 | } 437 | } 438 | GL.End(); 439 | } 440 | 441 | GL.PopMatrix(); 442 | 443 | GUI.EndClip(); 444 | } 445 | return result; 446 | } 447 | } 448 | } 449 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Editor/UVSheetViewer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3ab8e31d244c0884c871075020d47783 3 | timeCreated: 1517971826 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Plugins.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 029227e1c4563ca48a6d4f02da4cc2a2 3 | folderAsset: yes 4 | timeCreated: 1517982708 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Plugins/x86.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cce5ee22e76cfcd45a9cf5b56bc196b7 3 | folderAsset: yes 4 | timeCreated: 1517982708 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Plugins/x86/FGDKit_NativeCore.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/Assets/Plugins/x86/FGDKit_NativeCore.dll -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Plugins/x86/FGDKit_NativeCore.dll.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dee2be40117765e49a2627256ee2f693 3 | timeCreated: 1512528376 4 | licenseType: Pro 5 | PluginImporter: 6 | serializedVersion: 1 7 | iconMap: {} 8 | executionOrder: {} 9 | isPreloaded: 0 10 | isOverridable: 0 11 | platformData: 12 | Android: 13 | enabled: 0 14 | settings: 15 | CPU: ARMv7 16 | Any: 17 | enabled: 0 18 | settings: 19 | Exclude Android: 1 20 | Exclude Editor: 0 21 | Exclude Linux: 0 22 | Exclude Linux64: 0 23 | Exclude LinuxUniversal: 0 24 | Exclude OSXIntel: 0 25 | Exclude OSXIntel64: 0 26 | Exclude OSXUniversal: 0 27 | Exclude Win: 0 28 | Exclude Win64: 1 29 | Exclude iOS: 1 30 | Editor: 31 | enabled: 1 32 | settings: 33 | CPU: x86 34 | DefaultValueInitialized: true 35 | OS: Windows 36 | Linux: 37 | enabled: 1 38 | settings: 39 | CPU: x86 40 | Linux64: 41 | enabled: 1 42 | settings: 43 | CPU: None 44 | LinuxUniversal: 45 | enabled: 1 46 | settings: 47 | CPU: AnyCPU 48 | OSXIntel: 49 | enabled: 1 50 | settings: 51 | CPU: AnyCPU 52 | OSXIntel64: 53 | enabled: 1 54 | settings: 55 | CPU: None 56 | OSXUniversal: 57 | enabled: 1 58 | settings: 59 | CPU: AnyCPU 60 | Win: 61 | enabled: 1 62 | settings: 63 | CPU: AnyCPU 64 | Win64: 65 | enabled: 0 66 | settings: 67 | CPU: None 68 | iOS: 69 | enabled: 0 70 | settings: 71 | CompileFlags: 72 | FrameworkDependencies: 73 | userData: 74 | assetBundleName: 75 | assetBundleVariant: 76 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Plugins/x86_64.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8ddf864267d325645b5d924c8ba6b815 3 | folderAsset: yes 4 | timeCreated: 1517982708 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Plugins/x86_64/FGDKit_NativeCore.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/Assets/Plugins/x86_64/FGDKit_NativeCore.dll -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Plugins/x86_64/FGDKit_NativeCore.dll.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 71baf02c8ac10084fa036a887dc3036b 3 | timeCreated: 1510277646 4 | licenseType: Pro 5 | PluginImporter: 6 | serializedVersion: 1 7 | iconMap: {} 8 | executionOrder: {} 9 | isPreloaded: 0 10 | isOverridable: 0 11 | platformData: 12 | Android: 13 | enabled: 0 14 | settings: 15 | CPU: ARMv7 16 | Any: 17 | enabled: 0 18 | settings: 19 | Exclude Android: 1 20 | Exclude Editor: 0 21 | Exclude Linux: 0 22 | Exclude Linux64: 0 23 | Exclude LinuxUniversal: 0 24 | Exclude OSXIntel: 0 25 | Exclude OSXIntel64: 0 26 | Exclude OSXUniversal: 0 27 | Exclude Win: 1 28 | Exclude Win64: 0 29 | Editor: 30 | enabled: 1 31 | settings: 32 | CPU: x86_64 33 | DefaultValueInitialized: true 34 | OS: Windows 35 | Linux: 36 | enabled: 1 37 | settings: 38 | CPU: None 39 | Linux64: 40 | enabled: 1 41 | settings: 42 | CPU: x86_64 43 | LinuxUniversal: 44 | enabled: 1 45 | settings: 46 | CPU: AnyCPU 47 | OSXIntel: 48 | enabled: 1 49 | settings: 50 | CPU: None 51 | OSXIntel64: 52 | enabled: 1 53 | settings: 54 | CPU: AnyCPU 55 | OSXUniversal: 56 | enabled: 1 57 | settings: 58 | CPU: AnyCPU 59 | Win: 60 | enabled: 0 61 | settings: 62 | CPU: None 63 | Win64: 64 | enabled: 1 65 | settings: 66 | CPU: AnyCPU 67 | userData: 68 | assetBundleName: 69 | assetBundleVariant: 70 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Scene.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dbbfa1738605adc42a0204e877f164c6 3 | folderAsset: yes 4 | timeCreated: 1517970786 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Scene/mat.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/Assets/Scene/mat.mat -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Scene/mat.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 014363a32b79b554aa7000295d7b84bc 3 | timeCreated: 1517970898 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Scene/static_41417.FBX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/Assets/Scene/static_41417.FBX -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Scene/static_41417.FBX.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 18e39bc760d00bc4e8d88b0a8ca6108f 3 | timeCreated: 1517976847 4 | licenseType: Pro 5 | ModelImporter: 6 | serializedVersion: 19 7 | fileIDToRecycleName: 8 | 100000: //RootNode 9 | 400000: //RootNode 10 | 2300000: //RootNode 11 | 3300000: //RootNode 12 | 4300000: static_41417 13 | 11100000: //RootNode 14 | materials: 15 | importMaterials: 0 16 | materialName: 0 17 | materialSearch: 1 18 | animations: 19 | legacyGenerateAnimations: 3 20 | bakeSimulation: 0 21 | resampleCurves: 1 22 | optimizeGameObjects: 0 23 | motionNodeName: 24 | animationImportErrors: 25 | animationImportWarnings: 26 | animationRetargetingWarnings: 27 | animationDoRetargetingWarnings: 0 28 | animationCompression: 1 29 | animationRotationError: 0.5 30 | animationPositionError: 0.5 31 | animationScaleError: 0.5 32 | animationWrapMode: 0 33 | extraExposedTransformPaths: [] 34 | clipAnimations: [] 35 | isReadable: 1 36 | meshes: 37 | lODScreenPercentages: [] 38 | globalScale: 1 39 | meshCompression: 0 40 | addColliders: 0 41 | importBlendShapes: 0 42 | swapUVChannels: 0 43 | generateSecondaryUV: 1 44 | useFileUnits: 0 45 | optimizeMeshForGPU: 1 46 | keepQuads: 0 47 | weldVertices: 1 48 | secondaryUVAngleDistortion: 8 49 | secondaryUVAreaDistortion: 15.000001 50 | secondaryUVHardAngle: 88 51 | secondaryUVPackMargin: 4 52 | useFileScale: 0 53 | tangentSpace: 54 | normalSmoothAngle: 60 55 | normalImportMode: 0 56 | tangentImportMode: 2 57 | importAnimation: 1 58 | copyAvatar: 0 59 | humanDescription: 60 | serializedVersion: 2 61 | human: [] 62 | skeleton: [] 63 | armTwist: 0.5 64 | foreArmTwist: 0.5 65 | upperLegTwist: 0.5 66 | legTwist: 0.5 67 | armStretch: 0.05 68 | legStretch: 0.05 69 | feetSpacing: 0 70 | rootMotionBoneName: 71 | rootMotionBoneRotation: {x: 0, y: 0, z: 0, w: 1} 72 | hasTranslationDoF: 0 73 | hasExtraRoot: 0 74 | skeletonHasParents: 0 75 | lastHumanDescriptionAvatarSource: {instanceID: 0} 76 | animationType: 1 77 | humanoidOversampling: 1 78 | additionalBone: 0 79 | userData: 80 | assetBundleName: 81 | assetBundleVariant: 82 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Scene/temp.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 817bcb704493f9645854b00da1d33bdd 3 | folderAsset: yes 4 | timeCreated: 1517984481 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Scene/test.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: df6ca49f8b1a8ec478203d923d544e2f 3 | folderAsset: yes 4 | timeCreated: 1517971386 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Scene/test.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/Assets/Scene/test.unity -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Scene/test.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3802e881972702f4f9d1f76c13eb7e21 3 | timeCreated: 1517970996 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Scene/test/Building.fbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/Assets/Scene/test/Building.fbx -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Scene/test/Building.fbx.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e116b5b08932fe44b9b355d8ed8f4b78 3 | ModelImporter: 4 | serializedVersion: 23 5 | fileIDToRecycleName: 6 | 100000: //RootNode 7 | 400000: //RootNode 8 | 2100000: No Name 9 | 2300000: //RootNode 10 | 3300000: //RootNode 11 | 4300000: Cube 12 | 2186277476908879412: ImportLogs 13 | externalObjects: {} 14 | materials: 15 | importMaterials: 1 16 | materialName: 0 17 | materialSearch: 1 18 | materialLocation: 1 19 | animations: 20 | legacyGenerateAnimations: 4 21 | bakeSimulation: 0 22 | resampleCurves: 1 23 | optimizeGameObjects: 0 24 | motionNodeName: 25 | rigImportErrors: 26 | rigImportWarnings: 27 | animationImportErrors: 28 | animationImportWarnings: 29 | animationRetargetingWarnings: 30 | animationDoRetargetingWarnings: 0 31 | importAnimatedCustomProperties: 0 32 | importConstraints: 0 33 | animationCompression: 1 34 | animationRotationError: 0.5 35 | animationPositionError: 0.5 36 | animationScaleError: 0.5 37 | animationWrapMode: 0 38 | extraExposedTransformPaths: [] 39 | extraUserProperties: [] 40 | clipAnimations: [] 41 | isReadable: 1 42 | meshes: 43 | lODScreenPercentages: [] 44 | globalScale: 1 45 | meshCompression: 0 46 | addColliders: 0 47 | importVisibility: 1 48 | importBlendShapes: 1 49 | importCameras: 1 50 | importLights: 1 51 | swapUVChannels: 0 52 | generateSecondaryUV: 1 53 | useFileUnits: 1 54 | optimizeMeshForGPU: 1 55 | keepQuads: 0 56 | weldVertices: 1 57 | preserveHierarchy: 0 58 | indexFormat: 0 59 | secondaryUVAngleDistortion: 8 60 | secondaryUVAreaDistortion: 15 61 | secondaryUVHardAngle: 88 62 | secondaryUVPackMargin: 4 63 | useFileScale: 1 64 | previousCalculatedGlobalScale: 1 65 | hasPreviousCalculatedGlobalScale: 0 66 | tangentSpace: 67 | normalSmoothAngle: 60 68 | normalImportMode: 0 69 | tangentImportMode: 2 70 | normalCalculationMode: 4 71 | importAnimation: 1 72 | copyAvatar: 0 73 | humanDescription: 74 | serializedVersion: 2 75 | human: [] 76 | skeleton: [] 77 | armTwist: 0.5 78 | foreArmTwist: 0.5 79 | upperLegTwist: 0.5 80 | legTwist: 0.5 81 | armStretch: 0.05 82 | legStretch: 0.05 83 | feetSpacing: 0 84 | rootMotionBoneName: 85 | rootMotionBoneRotation: {x: 0, y: 0, z: 0, w: 1} 86 | hasTranslationDoF: 0 87 | hasExtraRoot: 0 88 | skeletonHasParents: 1 89 | lastHumanDescriptionAvatarSource: {instanceID: 0} 90 | animationType: 0 91 | humanoidOversampling: 1 92 | additionalBone: 0 93 | userData: 94 | assetBundleName: 95 | assetBundleVariant: 96 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Scene/test/LightingData.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/Assets/Scene/test/LightingData.asset -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Scene/test/LightingData.asset.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fd7ee38a8366e8c4b96ff2029b7391ed 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 25800000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Scene/test/Lightmap-0_comp_light.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/Assets/Scene/test/Lightmap-0_comp_light.exr -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Scene/test/Lightmap-0_comp_light.exr.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8b7637a501b0fbb478d6f34e583aebf8 3 | TextureImporter: 4 | fileIDToRecycleName: {} 5 | externalObjects: {} 6 | serializedVersion: 7 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 1 25 | streamingMipmapsPriority: 0 26 | grayScaleToAlpha: 0 27 | generateCubemap: 6 28 | cubemapConvolution: 0 29 | seamlessCubemap: 0 30 | textureFormat: 1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | serializedVersion: 2 34 | filterMode: 1 35 | aniso: 3 36 | mipBias: 0 37 | wrapU: 1 38 | wrapV: 1 39 | wrapW: 1 40 | nPOTScale: 1 41 | lightmap: 0 42 | compressionQuality: 50 43 | spriteMode: 0 44 | spriteExtrude: 1 45 | spriteMeshType: 1 46 | alignment: 0 47 | spritePivot: {x: 0.5, y: 0.5} 48 | spritePixelsToUnits: 100 49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 50 | spriteGenerateFallbackPhysicsShape: 1 51 | alphaUsage: 0 52 | alphaIsTransparency: 0 53 | spriteTessellationDetail: -1 54 | textureType: 6 55 | textureShape: 1 56 | singleChannelComponent: 0 57 | maxTextureSizeSet: 0 58 | compressionQualitySet: 0 59 | textureFormatSet: 0 60 | platformSettings: 61 | - serializedVersion: 2 62 | buildTarget: DefaultTexturePlatform 63 | maxTextureSize: 2048 64 | resizeAlgorithm: 0 65 | textureFormat: -1 66 | textureCompression: 1 67 | compressionQuality: 50 68 | crunchedCompression: 0 69 | allowsAlphaSplitting: 0 70 | overridden: 0 71 | androidETC2FallbackOverride: 0 72 | spriteSheet: 73 | serializedVersion: 2 74 | sprites: [] 75 | outline: [] 76 | physicsShape: [] 77 | bones: [] 78 | spriteID: 79 | vertices: [] 80 | indices: 81 | edges: [] 82 | weights: [] 83 | spritePackingTag: 84 | pSDRemoveMatte: 0 85 | pSDShowRemoveMatteOption: 0 86 | userData: 87 | assetBundleName: 88 | assetBundleVariant: 89 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Scene/test/ReflectionProbe-0.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/Assets/Scene/test/ReflectionProbe-0.exr -------------------------------------------------------------------------------- /LightmapRepacker/Assets/Scene/test/ReflectionProbe-0.exr.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c91c01622e61b06428400aecad10ddcf 3 | TextureImporter: 4 | fileIDToRecycleName: 5 | 8900000: generatedCubemap 6 | externalObjects: {} 7 | serializedVersion: 7 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 1 11 | sRGBTexture: 1 12 | linearTexture: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapsPreserveCoverage: 0 16 | alphaTestReferenceValue: 0.5 17 | mipMapFadeDistanceStart: 1 18 | mipMapFadeDistanceEnd: 3 19 | bumpmap: 20 | convertToNormalMap: 0 21 | externalNormalMap: 0 22 | heightScale: 0.25 23 | normalMapFilter: 0 24 | isReadable: 0 25 | streamingMipmaps: 0 26 | streamingMipmapsPriority: 0 27 | grayScaleToAlpha: 0 28 | generateCubemap: 6 29 | cubemapConvolution: 1 30 | seamlessCubemap: 1 31 | textureFormat: 1 32 | maxTextureSize: 2048 33 | textureSettings: 34 | serializedVersion: 2 35 | filterMode: 2 36 | aniso: 0 37 | mipBias: 0 38 | wrapU: 1 39 | wrapV: 1 40 | wrapW: 1 41 | nPOTScale: 1 42 | lightmap: 0 43 | compressionQuality: 50 44 | spriteMode: 0 45 | spriteExtrude: 1 46 | spriteMeshType: 1 47 | alignment: 0 48 | spritePivot: {x: 0.5, y: 0.5} 49 | spritePixelsToUnits: 100 50 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 51 | spriteGenerateFallbackPhysicsShape: 1 52 | alphaUsage: 1 53 | alphaIsTransparency: 0 54 | spriteTessellationDetail: -1 55 | textureType: 0 56 | textureShape: 2 57 | singleChannelComponent: 0 58 | maxTextureSizeSet: 0 59 | compressionQualitySet: 0 60 | textureFormatSet: 0 61 | platformSettings: 62 | - serializedVersion: 2 63 | buildTarget: DefaultTexturePlatform 64 | maxTextureSize: 2048 65 | resizeAlgorithm: 0 66 | textureFormat: -1 67 | textureCompression: 1 68 | compressionQuality: 100 69 | crunchedCompression: 0 70 | allowsAlphaSplitting: 0 71 | overridden: 0 72 | androidETC2FallbackOverride: 0 73 | spriteSheet: 74 | serializedVersion: 2 75 | sprites: [] 76 | outline: [] 77 | physicsShape: [] 78 | bones: [] 79 | spriteID: 80 | vertices: [] 81 | indices: 82 | edges: [] 83 | weights: [] 84 | spritePackingTag: 85 | pSDRemoveMatte: 0 86 | pSDShowRemoveMatteOption: 0 87 | userData: 88 | assetBundleName: 89 | assetBundleVariant: 90 | -------------------------------------------------------------------------------- /LightmapRepacker/Assets/gmcs.rsp: -------------------------------------------------------------------------------- 1 | -unsafe 2 | -nowarn:0219,0414,0162,0618,0169 3 | -warnaserror:0472,0114,0108,0162,0109,0168 -------------------------------------------------------------------------------- /LightmapRepacker/Assets/gmcs.rsp.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8f1a0bc11d452d24bbe4b970869039f7 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /LightmapRepacker/LightmapRepacker.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assembly-CSharp-Editor", "Assembly-CSharp-Editor.csproj", "{F00282E2-0AFA-F1C5-60F7-408F8E863E1D}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {F00282E2-0AFA-F1C5-60F7-408F8E863E1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {F00282E2-0AFA-F1C5-60F7-408F8E863E1D}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {F00282E2-0AFA-F1C5-60F7-408F8E863E1D}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {F00282E2-0AFA-F1C5-60F7-408F8E863E1D}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /LightmapRepacker/Packages/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "com.unity.ads": "2.0.8", 4 | "com.unity.analytics": "2.0.16", 5 | "com.unity.package-manager-ui": "1.9.11", 6 | "com.unity.purchasing": "2.0.3", 7 | "com.unity.textmeshpro": "1.2.4", 8 | "com.unity.modules.ai": "1.0.0", 9 | "com.unity.modules.animation": "1.0.0", 10 | "com.unity.modules.assetbundle": "1.0.0", 11 | "com.unity.modules.audio": "1.0.0", 12 | "com.unity.modules.cloth": "1.0.0", 13 | "com.unity.modules.director": "1.0.0", 14 | "com.unity.modules.imageconversion": "1.0.0", 15 | "com.unity.modules.imgui": "1.0.0", 16 | "com.unity.modules.jsonserialize": "1.0.0", 17 | "com.unity.modules.particlesystem": "1.0.0", 18 | "com.unity.modules.physics": "1.0.0", 19 | "com.unity.modules.physics2d": "1.0.0", 20 | "com.unity.modules.screencapture": "1.0.0", 21 | "com.unity.modules.terrain": "1.0.0", 22 | "com.unity.modules.terrainphysics": "1.0.0", 23 | "com.unity.modules.tilemap": "1.0.0", 24 | "com.unity.modules.ui": "1.0.0", 25 | "com.unity.modules.uielements": "1.0.0", 26 | "com.unity.modules.umbra": "1.0.0", 27 | "com.unity.modules.unityanalytics": "1.0.0", 28 | "com.unity.modules.unitywebrequest": "1.0.0", 29 | "com.unity.modules.unitywebrequestassetbundle": "1.0.0", 30 | "com.unity.modules.unitywebrequestaudio": "1.0.0", 31 | "com.unity.modules.unitywebrequesttexture": "1.0.0", 32 | "com.unity.modules.unitywebrequestwww": "1.0.0", 33 | "com.unity.modules.vehicles": "1.0.0", 34 | "com.unity.modules.video": "1.0.0", 35 | "com.unity.modules.vr": "1.0.0", 36 | "com.unity.modules.wind": "1.0.0", 37 | "com.unity.modules.xr": "1.0.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LightmapRepacker/ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/ProjectSettings/AudioManager.asset -------------------------------------------------------------------------------- /LightmapRepacker/ProjectSettings/ClusterInputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/ProjectSettings/ClusterInputManager.asset -------------------------------------------------------------------------------- /LightmapRepacker/ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/ProjectSettings/DynamicsManager.asset -------------------------------------------------------------------------------- /LightmapRepacker/ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/ProjectSettings/EditorBuildSettings.asset -------------------------------------------------------------------------------- /LightmapRepacker/ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/ProjectSettings/EditorSettings.asset -------------------------------------------------------------------------------- /LightmapRepacker/ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/ProjectSettings/GraphicsSettings.asset -------------------------------------------------------------------------------- /LightmapRepacker/ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/ProjectSettings/InputManager.asset -------------------------------------------------------------------------------- /LightmapRepacker/ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/ProjectSettings/NavMeshAreas.asset -------------------------------------------------------------------------------- /LightmapRepacker/ProjectSettings/NetworkManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/ProjectSettings/NetworkManager.asset -------------------------------------------------------------------------------- /LightmapRepacker/ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/ProjectSettings/Physics2DSettings.asset -------------------------------------------------------------------------------- /LightmapRepacker/ProjectSettings/PresetManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/ProjectSettings/PresetManager.asset -------------------------------------------------------------------------------- /LightmapRepacker/ProjectSettings/ProjectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/ProjectSettings/ProjectSettings.asset -------------------------------------------------------------------------------- /LightmapRepacker/ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 2018.2.6f1 2 | -------------------------------------------------------------------------------- /LightmapRepacker/ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/ProjectSettings/QualitySettings.asset -------------------------------------------------------------------------------- /LightmapRepacker/ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/ProjectSettings/TagManager.asset -------------------------------------------------------------------------------- /LightmapRepacker/ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/ProjectSettings/TimeManager.asset -------------------------------------------------------------------------------- /LightmapRepacker/ProjectSettings/UnityConnectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/ProjectSettings/UnityConnectSettings.asset -------------------------------------------------------------------------------- /LightmapRepacker/obj/Debug/Assembly-CSharp-Editor.csproj.CoreCompileInputs.cache: -------------------------------------------------------------------------------- 1 | fd4b6fe915c023f4036ddcc06388c664ed4a3fcf 2 | -------------------------------------------------------------------------------- /LightmapRepacker/obj/Debug/Assembly-CSharp-Editor.csproj.FileListAbsolute.txt: -------------------------------------------------------------------------------- 1 | D:\mytoys\LightmapRepacker_FixedDemo\LightmapRepacker\obj\Debug\Assembly-CSharp-Editor.csprojResolveAssemblyReference.cache 2 | -------------------------------------------------------------------------------- /LightmapRepacker/obj/Debug/Assembly-CSharp-Editor.csprojAssemblyReference.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/obj/Debug/Assembly-CSharp-Editor.csprojAssemblyReference.cache -------------------------------------------------------------------------------- /LightmapRepacker/obj/Debug/Assembly-CSharp-Editor.csprojResolveAssemblyReference.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/obj/Debug/Assembly-CSharp-Editor.csprojResolveAssemblyReference.cache -------------------------------------------------------------------------------- /LightmapRepacker/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache -------------------------------------------------------------------------------- /LightmapRepacker/pictures/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/pictures/1.png -------------------------------------------------------------------------------- /LightmapRepacker/pictures/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/pictures/10.png -------------------------------------------------------------------------------- /LightmapRepacker/pictures/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/pictures/100.png -------------------------------------------------------------------------------- /LightmapRepacker/pictures/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/pictures/11.png -------------------------------------------------------------------------------- /LightmapRepacker/pictures/111.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/pictures/111.png -------------------------------------------------------------------------------- /LightmapRepacker/pictures/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/pictures/3.png -------------------------------------------------------------------------------- /LightmapRepacker/pictures/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/pictures/4.png -------------------------------------------------------------------------------- /LightmapRepacker/pictures/after_fix_scaleOffset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/pictures/after_fix_scaleOffset.png -------------------------------------------------------------------------------- /LightmapRepacker/pictures/after_fix_scaleOffset_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/pictures/after_fix_scaleOffset_2.png -------------------------------------------------------------------------------- /LightmapRepacker/pictures/final0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/pictures/final0.png -------------------------------------------------------------------------------- /LightmapRepacker/pictures/final1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/pictures/final1.png -------------------------------------------------------------------------------- /LightmapRepacker/pictures/perfect_rect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lujian101/LightmapRepacker_FixedDemo/2a0ea7bbeaadd15fe3ff09423b13c39753a8e24f/LightmapRepacker/pictures/perfect_rect.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LightmapRepacker Demo 2 | 3 | A demo of splitting & packing of a lightmap. (Unity 2018) 4 | 5 | ## Demo演示步骤 6 | 7 | 1. 打开Scene/test.unity这个测试场景 8 | 2. 选中需要执行Lightmap重组的Models节点 9 | 3. 执行Tools/LightmapRepacker/Repack For Selected GOs, lightmap重组,此场景的lightmap,和涉及模型都会重新计算uv,并更新回所有的Renderer 10 | 4. 如果要再次测试,请**不要保存结果**,直接重新载入一次场景,再次从步骤2开始执行 11 | 12 | ## 实现步骤说明 13 | 14 | 1. 遍历所有需要处理的Renderer,记录Renderer.lightmapScaleOffset 15 | 2. 读取用于Lightmap采样的那一套纹理,并且做简单计算 16 | 1. 计算LightmapUV的初始包围盒 17 | 2. 计算经过lightmapScaleOffset变换后的包围盒lightmapUVBounds,用于后面的像素提取 18 | ```CSharp 19 | // 放缩平移变化 20 | _uv.x *= lightmapScaleOffset.x; 21 | _uv.y *= lightmapScaleOffset.y; 22 | _uv.x += lightmapScaleOffset.z; 23 | _uv.y += lightmapScaleOffset.w; 24 | ``` 25 | 3. 计算像素范围,Lightmap像素提取之前,通过选取正确的浮点取整方式来确保UV涵盖到的像素被正确包含 26 | ```CSharp 27 | // 用扩大方式来取整,确保相关像素完美包含 28 | int pixMinX = ( int )Math.Max( Mathf.Floor( lightmapUVBounds.x * lightmapData.width ), 0 ); 29 | int pixMaxX = ( int )Math.Min( Mathf.Ceil( lightmapUVBounds.z * lightmapData.width ), lightmapData.width ); 30 | int pixMinY = ( int )Math.Max( Mathf.Floor( lightmapUVBounds.y * lightmapData.height ), 0 ); 31 | int pixMaxY = ( int )Math.Min( Mathf.Ceil( lightmapUVBounds.w * lightmapData.height ), lightmapData.height ); 32 | ``` 33 | 4. 使用stbrp_rect库来重新打包LightmapAtlas 34 | 35 | ```CSharp 36 | // 使用了非常出色的stb万能库之一:stb_rect来制作Atlas 37 | // 当然也可用Unity的Texture2D.PackTextures方法,但是要注意放缩问题 38 | // https://github.com/nothings/stb/blob/master/stb_rect_pack.h 39 | // simple 2D rectangle packer with decent quality 40 | 41 | // 获取lightmap之间的间隙宽度,用于填充块与块的右上部位 42 | var Padding = LightmapEditorSettings.padding; 43 | // 传入自己的TexturePacker之前,把border大小加上去 44 | var rt = default( NativeAPI.stbrp_rect ); 45 | rt.w = ( ushort )( ( pixMaxX - pixMinX ) + Padding ); 46 | rt.h = ( ushort )( ( pixMaxY - pixMinY ) + Padding ); 47 | ``` 48 | 49 | 演示一下Lightmap块与块之间空隙(32像素),注意最左下方是无缝的 50 | ![Padding](./LightmapRepacker/pictures/1.png) 51 | 52 | 5. 重新构建新LightmapAtlas,并计算新的lightScaleOffset 53 | ```CSharp 54 | // 经过pack计算后的输出,在输出贴图上的像素位置位置 55 | NativeAPI.stbrp_rect rt; 56 | // ... 57 | // 原始像素包围盒 58 | int i_minx = lightmapRect.lightmapPixelBounds.x; 59 | int i_miny = lightmapRect.lightmapPixelBounds.y; 60 | int i_maxx = lightmapRect.lightmapPixelBounds.z; 61 | int i_maxy = lightmapRect.lightmapPixelBounds.w; 62 | // lightmap像素块copy 63 | fixed ( Vector4* _atlas_pixels = atlas_pixels.pixels ) { 64 | for ( int y = i_miny; y < i_maxy; ++y ) { 65 | int dy = y - i_miny + rt.y; 66 | // 纹理坐标需要翻转一下,像素是翻转了的 67 | int _dy = atlasSize - 1 - dy; 68 | int _sy = lightmapData.height - 1 - y; 69 | int _dy_stride = _dy * atlasSize; 70 | int _sy_stride = _sy * lightmapData.width; 71 | for ( int x = i_minx; x < i_maxx; ++x ) { 72 | int dx = x - i_minx + rt.x; 73 | _atlas_pixels[ _dy_stride + dx ] = lightmapData.GetPixel( x, _sy ); 74 | } 75 | } 76 | } 77 | var uvBounds = new Vector4d(); 78 | uvBounds.x = ( rt.x / ( float )atlasSize ); 79 | uvBounds.y = ( rt.y / ( float )atlasSize ); 80 | // 计算在新的lightmap纹理下的纹理坐标包围盒,由于pack之前我们人为加了一个Padding,所以这里计算要减去 81 | uvBounds.z = ( ( ( rt.x + rt.w ) - Padding ) / ( float )atlasSize ); 82 | uvBounds.w = ( ( ( rt.y + rt.h ) - Padding ) / ( float )atlasSize ); 83 | // 重新计算新的scaleOffset 84 | renderer.lightmapScaleOffset = CalculateUVScaleOffset( lightmapRect.meshUVBounds, uvBounds ); 85 | ``` 86 | 87 | 6. 完成,来看看结果 88 | 89 | 1. 处理之前,场景原样 90 | 91 | ![original](./LightmapRepacker/pictures/10.png) 92 | 93 | 2. 处理之前,计算出来的像素区域,浮点像素精度 94 | 95 | ![original_uv](./LightmapRepacker/pictures/100.png) 96 | 97 | 3. 处理之后,场景样貌,注意lightmap出现了明显偏移放缩,而且还出现了黑边 98 | 99 | ![after](./LightmapRepacker/pictures/11.png) 100 | 101 | 4. 处理之后,计算出来的像素区域出现了浮点误差 102 | 103 | ![after_uv](./LightmapRepacker/pictures/111.png) 104 | 105 | 结果非常糟糕,重新映射后的lightmap不仅位置出现了明显的平移和放缩,而且还有黑边出现,这是显然无法容忍的,要想办法解决 106 | 107 | ## 解决问题 108 | 109 | 1. 消除平移和放缩误差 110 | 111 | 这个问题明显出在步骤3,为了去提取lightmap上的涵盖像素,所以从UV计算像素区域时,我们执行了一系列的取整操作,这就丢失了最重要的精度,所以我们要把这部分取整丢失的精度重新补偿回来。 112 | 113 | 1. 计算浮点像素精度的区域 114 | ```CSharp 115 | float fMinX = lightmapUVBounds.x * lightmapData.width; 116 | float fMaxX = lightmapUVBounds.z * lightmapData.width; 117 | float fMinY = lightmapUVBounds.y * lightmapData.height; 118 | float fMaxY = lightmapUVBounds.w * lightmapData.height; 119 | // 保存结果 120 | lightmapRect.lightmapPixelFBounds = new Vector4d( fMinX, fMinY, fMaxX, fMaxY ); 121 | ``` 122 | 123 | 2. 浮点补偿 124 | ```CSharp 125 | // 关键,把取整后丢失的精度,重新补偿回来,这样UV值相对新的像素位置的偏移就前后一致了 126 | var errorX = lightmapRect.lightmapPixelFBounds.x - lightmapRect.lightmapPixelBounds.x; 127 | var errorY = lightmapRect.lightmapPixelFBounds.y - lightmapRect.lightmapPixelBounds.y; 128 | var errorZ = lightmapRect.lightmapPixelFBounds.z - lightmapRect.lightmapPixelBounds.z; 129 | var errorW = lightmapRect.lightmapPixelFBounds.w - lightmapRect.lightmapPixelBounds.w; 130 | uvBounds.x = ( ( rt.x + errorX ) / ( float )atlasSize ); 131 | uvBounds.y = ( ( rt.y + errorY ) / ( float )atlasSize ); 132 | uvBounds.z = ( ( ( rt.x + rt.w + errorZ ) - Padding ) / ( float )atlasSize ); 133 | uvBounds.w = ( ( ( rt.y + rt.h + errorW ) - Padding ) / ( float )atlasSize ); 134 | ``` 135 | 136 | 3. 结果 137 | 138 | 处理后,基本上消除了lightmap的偏移和放缩误差,但是黑边依然存在 139 | ![after_fix_scaleOffset](./LightmapRepacker/pictures/after_fix_scaleOffset.png) 140 | 141 | 处理后,像素区域已经非常接近初始值了(除了x坐标有非常非常小的误差) 142 | ![after_fix_scaleOffset_2](./LightmapRepacker/pictures/after_fix_scaleOffset_2.png) 143 | 144 | 4. 还有误差? 145 | 146 | 其实到了这里,重新映射后的UV坐标已经非常接近初始值了,但是还是有非常小的误差,很明显,这是不是浮点数自身运算带来的误差呢? 147 | 148 | 我马上把单精度浮点数换成了双精度来计算,果然药到病除,结果非常完美了:) 149 | 150 | ![perfect_rect](./LightmapRepacker/pictures/perfect_rect.png) 151 | 152 | 2. 消除黑边 153 | 154 | 已经消除了lightmap的重组误差,就只剩下黑边问题需要消除了,我们离胜利已经不远了。 155 | 156 | 通过对比处理前后的lightmap贴图,发现处理之前的Lightmap并不是以黑色为底色的,而像是以某种方式把Lightmap图块像素的颜色扩散到背景上去了,很明显,这是为了消除纹理线性过滤采样到相邻像素而采用的方法。 157 | 158 | 因为这种像素颜色扩散算法不清楚,所以我打算直接在拷贝像素块的时候扩大一下范围,把原始lightmap上的邻接区域像素也一并拷贝到新的lightmap贴图中去。由于我们lightmap图块之间本来就存在Padding,所以正好我们可以把这个区域的像素瓜分了:) 159 | 160 | ```CSharp 161 | // 原始像素包围盒,不包含Border 162 | var i_minx = ( int )lightmapRect.lightmapPixelBounds.x; 163 | var i_miny = ( int )lightmapRect.lightmapPixelBounds.y; 164 | var i_maxx = ( int )lightmapRect.lightmapPixelBounds.z; 165 | var i_maxy = ( int )lightmapRect.lightmapPixelBounds.w; 166 | int rtOffset = Padding / 2; 167 | if ( Padding > 0 ) { 168 | // 扩大半个边框,瓜分Padding像素消除黑边 169 | i_minx -= Padding / 2; 170 | i_miny -= Padding / 2; 171 | i_maxx += Padding - Padding / 2; 172 | i_maxy += Padding - Padding / 2; 173 | } 174 | fixed ( Vector4* _atlas_pixels = atlas_pixels.pixels ) { 175 | for ( int y = i_miny; y < i_maxy; y++ ) { 176 | int dy = y - i_miny + ( rt.y - rtOffset ); 177 | if ( dy < 0 || dy >= atlasSize ) { 178 | // 防止超界 179 | continue; 180 | } 181 | // 纹理坐标需要翻转一下,像素是翻转了的 182 | int _dy = atlasSize - 1 - dy; 183 | int _sy = lightmapData.height - 1 - y; 184 | int _dy_stride = _dy * atlasSize; 185 | int _sy_stride = _sy * lightmapData.width; 186 | for ( int x = i_minx; x < i_maxx; x++ ) { 187 | int dx = x - i_minx + ( rt.x - rtOffset ); 188 | if ( dx < 0 || dx >= atlasSize ) { 189 | // 防止超界 190 | continue; 191 | } 192 | // 模拟一下Clamp方式的纹理采样,这里传入的坐标是可以超出原始纹理范围的 193 | _atlas_pixels[ _dy_stride + dx ] = lightmapData.GetPixelClamped( x, _sy ); 194 | } 195 | } 196 | } 197 | ``` 198 | 199 | Ta-da! 200 | 201 | 比较理想的消除了黑边 202 | 203 | ![perfect_rect](./LightmapRepacker/pictures/final0.png) 204 | 205 | Lightmap原始图块之间的边缘像素也被保留下来了,这是解决黑边的关键。在日常开发中,我们需要设置相对合适的LightmapEditorSettings.padding来消除黑边,考虑到mipmap的存在,这个值稍微大一点比较好,比如(4~8) 206 | ![perfect_rect](./LightmapRepacker/pictures/final1.png) 207 | 208 | ## 最后 209 | 210 | 为了展示以上介绍的问题解决过程,在LightmapRepacker.cs文件头定义了3个宏开关来重现问题解决之前的结果 211 | 212 | ```CSharp 213 | // 消除LightmapUV取整后的误差 214 | #define FIXING_LIGHTMAP_UVERROR 215 | // 消除单精度浮点带来的微小误差 216 | #define LIGHTMAPREPACKER_PRECISION_HIGH 217 | // 消除接缝黑边 218 | #define FIXING_LIGHTMAP_UVBORDER 219 | ``` 220 | 221 | 本工程是[LightmapRepacker_IssueDemo](https://github.com/lujian101/LightmapRepacker_IssueDemo)的问题修复版本。 222 | 223 | 采用Unity2018开发 224 | 225 | 使用了[stb_rect](https://github.com/nothings/stb/blob/master/stb_rect_pack.h)库来pack纹理 226 | 227 | 使用了[tinyexr](https://github.com/syoyo/tinyexr)来读写HDR格式的图像文件 228 | 229 | --------------------------------------------------------------------------------