commit 4dad9e5d5bcfe4d89f94396569b85a01b98109a4 Author: jongjae Date: Thu May 21 23:37:34 2026 +0900 feat: SongCreator 씬 완성 — Beat Sage URL 지원, info.dat 메타데이터 자동 추출 - BeatSageUploader: audio_url 지원(UploadFromUrl), PollAndDownload 공통화, ZIP 500 오류 3회 재시도 - BeatSageConverter: info.dat 파싱(SongMetadata), BPM 자동 감지 → 노트 타이밍 변환에 적용 - SongCreatorManager: title/BPM 필수 입력 제거, 난이도 4개 자동 선택, GenerateFlowFromUrl 버그 수정 - NasPublisher: audioPath null 허용(URL 흐름에서 로컬 파일 없는 경우 스킵) - .gitignore/.gitattributes 초기 설정 Co-Authored-By: Claude Sonnet 4.6 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..34a4ec1 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# Unity recommended line endings +* text=auto eol=lf + +# Force binary for Unity binary assets +*.png binary +*.jpg binary +*.wav binary +*.mp3 binary +*.ogg binary +*.fbx binary +*.asset binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6528df0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# Unity +/Library/ +/Temp/ +/Obj/ +/Build/ +/Builds/ +/Logs/ +/UserSettings/ + +# IDE / Generated +*.csproj +*.slnx +*.sln +.vscode/ +.vs/ + +# Claude Code local settings +.claude/ + +# Credentials — never commit +.env +/Assets/StreamingAssets/nas_config.json +/Assets/StreamingAssets/nas_config.json.meta diff --git a/Assets/DefaultVolumeProfile.asset b/Assets/DefaultVolumeProfile.asset new file mode 100644 index 0000000..5393bfe --- /dev/null +++ b/Assets/DefaultVolumeProfile.asset @@ -0,0 +1,15 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d7fd9488000d3734a9e00ee676215985, type: 3} + m_Name: DefaultVolumeProfile + m_EditorClassIdentifier: Unity.RenderPipelines.Core.Runtime::UnityEngine.Rendering.VolumeProfile + components: [] diff --git a/Assets/DefaultVolumeProfile.asset.meta b/Assets/DefaultVolumeProfile.asset.meta new file mode 100644 index 0000000..4512f8c --- /dev/null +++ b/Assets/DefaultVolumeProfile.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0767462997e881e4980faede0fe3cc8a +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Editor.meta b/Assets/Editor.meta new file mode 100644 index 0000000..224ca02 --- /dev/null +++ b/Assets/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 967f9dfcbece854419d004baa2dd052d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Editor/VRBeatSaberSceneBuilder.cs b/Assets/Editor/VRBeatSaberSceneBuilder.cs new file mode 100644 index 0000000..cf80095 --- /dev/null +++ b/Assets/Editor/VRBeatSaberSceneBuilder.cs @@ -0,0 +1,589 @@ +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.UI; +using TMPro; + +public static class VRBeatSaberSceneBuilder +{ + private const string MenuScene = "Assets/VRBeatsKit/Scenes/Menu.unity"; + private const string SongCreatorDest = "Assets/Scenes/SongCreator.unity"; + + // ───────────────────────────────────────────── + // ⓪ Fix — Set Graphics API to D3D11 (Oculus requirement) + // ───────────────────────────────────────────── + [MenuItem("Tools/VRBeatSaber/⓪ Fix — Set Graphics API to D3D11")] + public static void FixGraphicsAPI() + { + var d3d11 = new[] { GraphicsDeviceType.Direct3D11 }; + + PlayerSettings.SetUseDefaultGraphicsAPIs(BuildTarget.StandaloneWindows, false); + PlayerSettings.SetUseDefaultGraphicsAPIs(BuildTarget.StandaloneWindows64, false); + PlayerSettings.SetGraphicsAPIs(BuildTarget.StandaloneWindows, d3d11); + PlayerSettings.SetGraphicsAPIs(BuildTarget.StandaloneWindows64, d3d11); + + Debug.Log("[SceneBuilder] ✓ Graphics API set to Direct3D11 for Windows."); + } + + [MenuItem("Tools/VRBeatSaber/⓪ Fix — Allow HTTP connections")] + public static void FixAllowHttp() + { + PlayerSettings.insecureHttpOption = InsecureHttpOption.AlwaysAllowed; + Debug.Log("[SceneBuilder] ✓ Insecure HTTP connections allowed."); + } + + // ───────────────────────────────────────────── + // Fix — Remove missing script components from open scene + // ───────────────────────────────────────────── + [MenuItem("Tools/VRBeatSaber/Fix — Remove Missing Scripts (open scene)")] + public static void RemoveMissingScripts() + { + int removed = 0; + foreach (var go in Object.FindObjectsByType(FindObjectsSortMode.None)) + { + var so = new SerializedObject(go); + var components = so.FindProperty("m_Component"); + for (int i = components.arraySize - 1; i >= 0; i--) + { + var comp = components.GetArrayElementAtIndex(i) + .FindPropertyRelative("component") + .objectReferenceValue; + if (comp == null) + { + components.DeleteArrayElementAtIndex(i); + removed++; + } + } + so.ApplyModifiedPropertiesWithoutUndo(); + } + + var activeScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene(); + EditorSceneManager.MarkSceneDirty(activeScene); + Debug.Log($"[SceneBuilder] ✓ Removed {removed} missing script(s) from '{activeScene.name}'."); + } + + // ───────────────────────────────────────────── + // ① Menu — Add Song Creator Button + // ───────────────────────────────────────────── + [MenuItem("Tools/VRBeatSaber/① Menu — Add Song Creator Button")] + public static void PatchMenuAddSongCreatorButton() + { + var scene = EditorSceneManager.OpenScene(MenuScene, OpenSceneMode.Single); + + var settings = GameObject.Find("Settings"); + if (settings == null) { Debug.LogError("[SceneBuilder] 'Settings' not found."); return; } + + if (settings.transform.Find("BG/Song Creator") != null) + { Debug.LogWarning("[SceneBuilder] Button already exists."); return; } + + var bg = settings.transform.Find("BG"); + if (bg == null) { Debug.LogError("[SceneBuilder] 'Settings/BG' not found."); return; } + + var btnGO = CreateStyledButton(bg, "Song Creator", new Vector2(0f, -20f), new Vector2(80f, 14f), 8f); + var loader = btnGO.AddComponent(); + InjectPrivate(loader, "button", btnGO.GetComponent