Compare commits

2 Commits

Author SHA1 Message Date
whdwo798 16bc037093 UI 수정, 토글 기본값, Spawner 오디오 버그 수정
[SceneBuilder]
- 사용자 수동 조정 위치 반영 (AudioDropdown, RefreshBtn, FilePickerBtn, UrlInput, UrlDownloadBtn, 난이도 토글 4개, BackButton)
- MakeTMP 텍스트 정렬: MidlineLeft → Center
- MakePanel/Button/InputField/Dropdown: sprite = null 설정으로 흰색 배경 제거
- 버튼 텍스트 특수문자 → ASCII 대체 (→ > / ▼▶✕ 제거): NanumGothic SDF 미지원 문자 경고 해결
- 난이도 토글 4개 기본값 모두 true로 변경

[SongCreatorManager]
- Start()에서 토글 전체 꺼진 경우 자동으로 4개 모두 켜기 (씬 재빌드 없이 즉시 적용)

[Spawner]
- Awake(): playOnAwake = false, clip = null 설정 — Inspector 하드코딩 클립 자동 재생 차단
- InitGame(): AudioSource/GameSession null 체크, 파일 존재 확인, 경로 로그 추가
- audioSource.clip == null 후 yield break로 로드 실패 시 재생 방지

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 01:05:04 +09:00
whdwo798 c73ff7f412 노래 만들기 수정 — NAS 업로드 완성, Easy 제거 및 ExpertPlus 추가, 다운로드 버그 수정
- NasPublisher: DSM 7.2 multipart body 수동 구성으로 업로드 401 오류 해결
- NasPublisher: 비밀번호 StreamingAssets/nas_config.json 분리, .gitignore 등록
- NasPublisher: staticBaseUrl 포트 8180 → 80 수정
- BeatSageUploader: Easy 난이도 제거, ExpertPlus(.dat) 추가
- NoteData: DifficultyMap에서 easy 제거, expertplus 추가
- SongCreatorManager: toggleEasy → toggleExpertPlus 교체
- SongDetailPanel: btnEasy → btnExpertPlus 교체
- DownloadManager: DownloadHandlerFile 경로 정규화(Path.GetFullPath), mapFile 빈 값 방어 처리
- PersistentXRRig: FindObjectsOfType obsolete 경고 수정

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 23:39:27 +09:00
1556 changed files with 933 additions and 180176 deletions
+4
View File
@@ -75,3 +75,7 @@ crashlytics-build.properties
/[Aa]ssets/[Ss]treamingAssets/aa/* /[Aa]ssets/[Ss]treamingAssets/aa/*
# End of https://www.toptal.com/developers/gitignore/api/unity # End of https://www.toptal.com/developers/gitignore/api/unity
# 민감 정보 설정 파일
/[Aa]ssets/[Ss]treamingAssets/nas_config.json
/[Aa]ssets/[Ss]treamingAssets/nas_config.json.meta
+252 -36
View File
@@ -16,6 +16,188 @@ public static class VRBeatSaberSceneBuilder
{ {
private static GameObject s_cardPrefab; private static GameObject s_cardPrefab;
// ══════════════════════════════════════════════════════════
// 뒤로가기 버튼 패치 (기존 씬에 추가)
// ══════════════════════════════════════════════════════════
[MenuItem("Tools/VRBeatSaber/뒤로가기 버튼 추가 (기존 씬 패치)")]
public static void PatchBackButtons()
{
if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) return;
// SongSelect: 뒤로(Intro) + DetailPanel 닫기(X)
PatchSceneWithBackButton(
"Assets/Scenes/SongSelect.unity",
() =>
{
FindAndBindBackButton<SongSelectManager>("backButton", "< 뒤로", new Vector2(-500, 310), new Vector2(130, 50));
AddDetailPanelCloseButton();
});
// SongCreator: 뒤로(Intro)
PatchSceneWithBackButton(
"Assets/Scenes/SongCreator.unity",
() => FindAndBindBackButton<SongCreatorManager>("backButton", "< 뒤로", new Vector2(-405, 345), new Vector2(130, 50)));
// MapEditorScene: 뒤로(SongCreator)
PatchSceneWithBackButton(
"Assets/Scenes/MapEditorScene.unity",
FindAndBindBackButtonOnMapEditor);
// Game: 뒤로(SongSelect) — 우측 상단 소형 버튼
PatchSceneWithBackButton(
"Assets/Scenes/Game.unity",
AddGameSceneBackButton);
AssetDatabase.Refresh();
EditorUtility.DisplayDialog("완료", "뒤로가기 버튼 추가 완료!\n\n추가된 버튼:\n- SongSelect: < 뒤로 + 상세패널 X 닫기\n- SongCreator: < 뒤로\n- MapEditorScene: < 뒤로\n- Game: < 뒤로", "확인");
}
private static void PatchSceneWithBackButton(string scenePath, System.Action patchAction)
{
if (!File.Exists(scenePath)) { Debug.LogWarning($"[SceneBuilder] 씬 없음: {scenePath}"); return; }
var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
patchAction();
EditorSceneManager.SaveScene(scene);
Debug.Log($"[SceneBuilder] 패치 완료: {scenePath}");
}
private static void FindAndBindBackButton<T>(string fieldName, string label, Vector2 pos, Vector2 size) where T : MonoBehaviour
{
var mgr = Object.FindObjectOfType<T>();
if (mgr == null) { Debug.LogWarning($"[SceneBuilder] {typeof(T).Name} 없음"); return; }
// 이미 있으면 건너뜀
var so = new SerializedObject(mgr);
if (so.FindProperty(fieldName)?.objectReferenceValue != null) return;
// Canvas 찾기
var canvas = Object.FindObjectOfType<Canvas>();
if (canvas == null) return;
var backGO = MakeButton("BackButton", canvas.transform, label, pos, size);
backGO.GetComponent<UnityEngine.UI.Image>().color = new Color(0.18f, 0.18f, 0.18f);
Bind(so, fieldName, backGO.GetComponent<Button>());
so.ApplyModifiedProperties();
}
// SongDetailPanel 닫기(X) 버튼
private static void AddDetailPanelCloseButton()
{
var detail = Object.FindObjectOfType<SongDetailPanel>();
if (detail == null) { Debug.LogWarning("[SceneBuilder] SongDetailPanel 없음"); return; }
var so = new SerializedObject(detail);
if (so.FindProperty("closeButton")?.objectReferenceValue != null) return;
// DetailPanel의 RectTransform 우측 상단에 X 버튼 배치
var detailRT = detail.GetComponent<RectTransform>();
if (detailRT == null) return;
var closeGO = MakeButton("CloseButton", detail.transform, "✕", new Vector2(detailRT.sizeDelta.x / 2f - 30, detailRT.sizeDelta.y / 2f - 30), new Vector2(50, 50));
closeGO.GetComponent<Image>().color = new Color(0.6f, 0.15f, 0.15f);
Bind(so, "closeButton", closeGO.GetComponent<Button>());
so.ApplyModifiedProperties();
}
// Game씬 뒤로가기 버튼 (Canvas 없으면 생성)
private static void AddGameSceneBackButton()
{
var spawner = Object.FindObjectOfType<Spawner>();
if (spawner == null) { Debug.LogWarning("[SceneBuilder] Spawner 없음"); return; }
// Game씬에 Canvas가 있는지 확인, 없으면 생성
var canvas = Object.FindObjectOfType<Canvas>();
if (canvas == null)
{
var canvasGO = new GameObject("HUDCanvas");
var c = canvasGO.AddComponent<Canvas>();
c.renderMode = RenderMode.WorldSpace;
canvasGO.AddComponent<CanvasScaler>();
var trackedType = System.Type.GetType(
"UnityEngine.XR.Interaction.Toolkit.UI.TrackedDeviceGraphicRaycaster, Unity.XR.Interaction.Toolkit");
if (trackedType != null)
canvasGO.AddComponent(trackedType);
else
canvasGO.AddComponent<GraphicRaycaster>();
var rt = canvasGO.GetComponent<RectTransform>();
rt.sizeDelta = new Vector2(400, 80);
// 플레이어 앞 위쪽, 항상 보이는 위치
canvasGO.transform.position = new Vector3(0f, 2.2f, 1.5f);
canvasGO.transform.localScale = Vector3.one * 0.002f;
canvas = c;
}
// 이미 BackButton이 있으면 건너뜀
if (canvas.transform.Find("BackButton") != null) return;
var backGO = MakeButton("BackButton", canvas.transform, "< 뒤로", new Vector2(-130, 0), new Vector2(150, 60));
backGO.GetComponent<Image>().color = new Color(0.2f, 0.2f, 0.2f, 0.85f);
// Spawner에 backButton 필드가 없으므로 런타임 클릭 이벤트만 추가
// (Spawner는 Quest B버튼용 InputActionReference 사용, 버튼은 클릭 처리 필요)
// → GameBackButton 컴포넌트로 처리
var handler = backGO.AddComponent<GameBackButton>();
var sso = new SerializedObject(handler);
var targetProp = sso.FindProperty("targetScene");
if (targetProp != null) targetProp.stringValue = "SongSelect";
sso.ApplyModifiedProperties();
}
private static void FindAndBindBackButtonOnMapEditor()
{
var mgr = Object.FindObjectOfType<MapEditor>();
if (mgr == null) { Debug.LogWarning("[SceneBuilder] MapEditor 없음"); return; }
var so = new SerializedObject(mgr);
if (so.FindProperty("backButton")?.objectReferenceValue != null) return;
var canvas = Object.FindObjectOfType<Canvas>();
if (canvas == null)
{
// MapEditorScene에 Canvas가 없을 수 있으므로 직접 생성
var canvasGO = new GameObject("Canvas");
var c = canvasGO.AddComponent<Canvas>();
c.renderMode = RenderMode.WorldSpace;
canvasGO.AddComponent<CanvasScaler>();
canvasGO.AddComponent<GraphicRaycaster>();
var rt = canvasGO.GetComponent<RectTransform>();
rt.sizeDelta = new Vector2(200, 60);
canvasGO.transform.position = new Vector3(-0.8f, 1.8f, 2f);
canvasGO.transform.localScale = Vector3.one * 0.002f;
canvas = canvasGO.GetComponent<Canvas>();
}
var backGO = MakeButton("BackButton", canvas.transform, "< 뒤로", new Vector2(0, 0), new Vector2(180, 55));
Bind(so, "backButton", backGO.GetComponent<Button>());
so.ApplyModifiedProperties();
}
[MenuItem("Tools/VRBeatSaber/씬 재빌드 (SongCreator + SongSelect)")]
public static void RebuildCreatorAndSelect()
{
if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) return;
EnsureFolder("Assets/Scenes");
EnsureFolder("Assets/Prefab");
s_cardPrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Prefab/SongCard.prefab");
if (s_cardPrefab == null) s_cardPrefab = CreateSongCardPrefab();
BuildSongSelectScene();
BuildSongCreatorScene();
AssetDatabase.Refresh();
EditorUtility.DisplayDialog("완료",
"SongSelect + SongCreator 씬 재빌드 완료!\n\n" +
"Inspector에서 NasPublisher 비밀번호를 확인하세요.",
"확인");
}
[MenuItem("Tools/VRBeatSaber/전체 자동 설정 (한 번만 실행)")] [MenuItem("Tools/VRBeatSaber/전체 자동 설정 (한 번만 실행)")]
public static void SetupAll() public static void SetupAll()
{ {
@@ -140,6 +322,7 @@ public static class VRBeatSaberSceneBuilder
// 탭 // 탭
var tabPanel = MakePanel("TabPanel", canvas.transform, new Vector2(0, 310), new Vector2(1180, 60)); var tabPanel = MakePanel("TabPanel", canvas.transform, new Vector2(0, 310), new Vector2(1180, 60));
var backBtnSS = MakeButton("BackButton", tabPanel.transform, "< 뒤로", new Vector2(-500, 0), new Vector2(130, 50));
var tabAllBtn = MakeButton("TabAllBtn", tabPanel.transform, "전체", new Vector2(-290, 0), new Vector2(200, 50)); var tabAllBtn = MakeButton("TabAllBtn", tabPanel.transform, "전체", new Vector2(-290, 0), new Vector2(200, 50));
var tabOwnedBtn = MakeButton("TabOwnedBtn", tabPanel.transform, "보유중", new Vector2(-80, 0), new Vector2(200, 50)); var tabOwnedBtn = MakeButton("TabOwnedBtn", tabPanel.transform, "보유중", new Vector2(-80, 0), new Vector2(200, 50));
@@ -156,20 +339,22 @@ public static class VRBeatSaberSceneBuilder
var infoTxt = MakeTMP("InfoText", detailGO.transform, "BPM | 길이", new Vector2(0, 160), new Vector2(640, 30), 18); var infoTxt = MakeTMP("InfoText", detailGO.transform, "BPM | 길이", new Vector2(0, 160), new Vector2(640, 30), 18);
var diffPanel = MakePanel("DiffPanel", detailGO.transform, new Vector2(0, 90), new Vector2(640, 60)); var diffPanel = MakePanel("DiffPanel", detailGO.transform, new Vector2(0, 90), new Vector2(640, 60));
var btnEasy = MakeButton("EasyBtn", diffPanel.transform, "Easy", new Vector2(-240, 0), new Vector2(140, 50)); var btnNorm = MakeButton("NormalBtn", diffPanel.transform, "Normal", new Vector2(-240, 0), new Vector2(140, 50));
var btnNorm = MakeButton("NormalBtn", diffPanel.transform, "Normal", new Vector2(-80, 0), new Vector2(140, 50)); var btnHard = MakeButton("HardBtn", diffPanel.transform, "Hard", new Vector2(-80, 0), new Vector2(140, 50));
var btnHard = MakeButton("HardBtn", diffPanel.transform, "Hard", new Vector2(80, 0), new Vector2(140, 50)); var btnExp = MakeButton("ExpertBtn", diffPanel.transform, "Expert", new Vector2(80, 0), new Vector2(140, 50));
var btnExp = MakeButton("ExpertBtn", diffPanel.transform, "Expert", new Vector2(240, 0), new Vector2(140, 50)); var btnExpP = MakeButton("ExpertPlusBtn", diffPanel.transform, "Expert+", new Vector2(240, 0), new Vector2(140, 50));
var actPanel = MakePanel("ActionPanel", detailGO.transform, new Vector2(0, 15), new Vector2(640, 60)); var actPanel = MakePanel("ActionPanel", detailGO.transform, new Vector2(0, 15), new Vector2(640, 60));
var dlBtn = MakeButton("DownloadBtn", actPanel.transform, "다운로드", new Vector2(-165, 0), new Vector2(290, 50)); var dlBtn = MakeButton("DownloadBtn", actPanel.transform, "다운로드", new Vector2(-165, 0), new Vector2(290, 50));
var delBtn = MakeButton("DeleteBtn", actPanel.transform, "삭제", new Vector2(165, 0), new Vector2(290, 50)); var delBtn = MakeButton("DeleteBtn", actPanel.transform, "삭제", new Vector2(165, 0), new Vector2(290, 50));
var progGroup = MakePanel("ProgressGroup", detailGO.transform, new Vector2(0, -50), new Vector2(640, 40)); var progGroup = MakePanel("ProgressGroup", detailGO.transform, new Vector2(0, -50), new Vector2(640, 40));
var progSlider = MakeSlider("ProgressSlider", progGroup.transform, new Vector2(-70, 0), new Vector2(480, 28)); var progSlider = MakeSlider("ProgressSlider", progGroup.transform, new Vector2(-70, 0), new Vector2(480, 28));
var progText = MakeTMP("ProgressText", progGroup.transform, "0%", new Vector2(275, 0), new Vector2(100, 30), 16); var progText = MakeTMP("ProgressText", progGroup.transform, "0%", new Vector2(275, 0), new Vector2(100, 30), 16);
var playBtn = MakeButton("PlayButton", detailGO.transform, "플레이", new Vector2(0, -140), new Vector2(300, 65)); var playBtn = MakeButton("PlayButton", detailGO.transform, "플레이", new Vector2(0, -140), new Vector2(300, 65));
var closeBtn = MakeButton("CloseButton", detailGO.transform, "X", new Vector2(290, 270), new Vector2(50, 50));
closeBtn.GetComponent<Image>().color = new Color(0.6f, 0.15f, 0.15f);
var loadingOvr = MakePanel("LoadingOverlay", canvas.transform, Vector2.zero, new Vector2(1200, 700)); var loadingOvr = MakePanel("LoadingOverlay", canvas.transform, Vector2.zero, new Vector2(1200, 700));
loadingOvr.GetComponent<Image>().color = new Color(0, 0, 0, 0.75f); loadingOvr.GetComponent<Image>().color = new Color(0, 0, 0, 0.75f);
@@ -188,13 +373,14 @@ public static class VRBeatSaberSceneBuilder
Bind(dso, "titleText", titleTxt); Bind(dso, "titleText", titleTxt);
Bind(dso, "artistText", artistTxt); Bind(dso, "artistText", artistTxt);
Bind(dso, "infoText", infoTxt); Bind(dso, "infoText", infoTxt);
Bind(dso, "btnEasy", btnEasy.GetComponent<Button>());
Bind(dso, "btnNormal", btnNorm.GetComponent<Button>()); Bind(dso, "btnNormal", btnNorm.GetComponent<Button>());
Bind(dso, "btnHard", btnHard.GetComponent<Button>()); Bind(dso, "btnHard", btnHard.GetComponent<Button>());
Bind(dso, "btnExpert", btnExp.GetComponent<Button>()); Bind(dso, "btnExpert", btnExp.GetComponent<Button>());
Bind(dso, "btnExpertPlus", btnExpP.GetComponent<Button>());
Bind(dso, "downloadButton", dlBtn.GetComponent<Button>()); Bind(dso, "downloadButton", dlBtn.GetComponent<Button>());
Bind(dso, "deleteButton", delBtn.GetComponent<Button>()); Bind(dso, "deleteButton", delBtn.GetComponent<Button>());
Bind(dso, "playButton", playBtn.GetComponent<Button>()); Bind(dso, "playButton", playBtn.GetComponent<Button>());
Bind(dso, "closeButton", closeBtn.GetComponent<Button>());
Bind(dso, "progressGroup", progGroup); Bind(dso, "progressGroup", progGroup);
Bind(dso, "progressSlider", progSlider); Bind(dso, "progressSlider", progSlider);
Bind(dso, "progressText", progText); Bind(dso, "progressText", progText);
@@ -203,6 +389,7 @@ public static class VRBeatSaberSceneBuilder
// SongSelectManager 바인딩 // SongSelectManager 바인딩
var mgr = canvas.AddComponent<SongSelectManager>(); var mgr = canvas.AddComponent<SongSelectManager>();
var mso = new SerializedObject(mgr); var mso = new SerializedObject(mgr);
Bind(mso, "backButton", backBtnSS.GetComponent<Button>());
Bind(mso, "tabAllBtn", tabAllBtn.GetComponent<Button>()); Bind(mso, "tabAllBtn", tabAllBtn.GetComponent<Button>());
Bind(mso, "tabOwnedBtn", tabOwnedBtn.GetComponent<Button>()); Bind(mso, "tabOwnedBtn", tabOwnedBtn.GetComponent<Button>());
Bind(mso, "cardContainer", cardContainer); Bind(mso, "cardContainer", cardContainer);
@@ -236,48 +423,69 @@ public static class VRBeatSaberSceneBuilder
var publisher = gm.AddComponent<NasPublisher>(); var publisher = gm.AddComponent<NasPublisher>();
var scMgr = gm.AddComponent<SongCreatorManager>(); var scMgr = gm.AddComponent<SongCreatorManager>();
var canvas = MakeCanvas(new Vector2(900, 760), new Vector3(0, 1.5f, 2f)); var canvas = MakeCanvas(new Vector2(900, 800), new Vector3(0, 1.5f, 2f));
MakeTMP("TitleLabel", canvas.transform, "노래 만들기", new Vector2(0, 345), new Vector2(860, 55), 32); MakeTMP("TitleLabel", canvas.transform, "노래 만들기", new Vector2(0, 365), new Vector2(860, 55), 32);
var audioPanel = MakePanel("AudioPanel", canvas.transform, new Vector2(0, 250), new Vector2(860, 90)); // 음원 선택 (드롭다운 + 새로고침)
var audioDd = MakeDropdown("AudioDropdown", audioPanel.transform, new Vector2(-120, 0), new Vector2(540, 60)); var audioPanel = MakePanel("AudioPanel", canvas.transform, new Vector2(0, 280), new Vector2(860, 80));
var refreshBtn = MakeButton("RefreshBtn", audioPanel.transform, "새로고침", new Vector2(255, 0), new Vector2(160, 58)); var audioDd = MakeDropdown("AudioDropdown", audioPanel.transform, new Vector2(-98, 0), new Vector2(540, 55));
var pathHint = MakeTMP("PathHint", canvas.transform, "", new Vector2(0, 195), new Vector2(860, 28), 13); var refreshBtn = MakeButton("RefreshBtn", audioPanel.transform, "새로고침", new Vector2(295, 0), new Vector2(160, 52));
var pathHint = MakeTMP("PathHint", canvas.transform, "", new Vector2(0, 230), new Vector2(860, 26), 12);
pathHint.color = new Color(0.6f, 0.6f, 0.6f);
var metaPanel = MakePanel("MetaPanel", canvas.transform, new Vector2(0, 105), new Vector2(860, 110)); // 음원 추가 (파일 선택 / URL 다운로드)
var titleInput = MakeInputField("TitleInput", metaPanel.transform, "곡 제목", new Vector2(-280, 0), new Vector2(255, 55)); var addPanel = MakePanel("AddPanel", canvas.transform, new Vector2(0, 155), new Vector2(860, 100));
var artistInput = MakeInputField("ArtistInput", metaPanel.transform, "아티스트", new Vector2(0, 0), new Vector2(255, 55)); var addStatusTxt = MakeTMP("AddStatusText", addPanel.transform, "", new Vector2(90, 25), new Vector2(530, 36), 13);
var bpmInput = MakeInputField("BpmInput", metaPanel.transform, "BPM", new Vector2(280, 0), new Vector2(255, 55)); addStatusTxt.color = new Color(0.7f, 0.9f, 0.7f);
var urlField = MakeInputField("UrlInput", addPanel.transform, "MP3 직접 URL 입력", new Vector2(-100, 18), new Vector2(600, 40));
var urlDlBtn = MakeButton("UrlDownloadBtn", addPanel.transform, "URL 다운로드", new Vector2(305, 20), new Vector2(195, 40));
var filePickBtn = MakeButton("FilePickerBtn", addPanel.transform, "파일 선택", new Vector2(304, -29), new Vector2(195, 40));
var diffPanel = MakePanel("DiffPanel", canvas.transform, new Vector2(0, -15), new Vector2(860, 65)); // 메타데이터
var togEasy = MakeToggle("EasyToggle", diffPanel.transform, "Easy", new Vector2(-310, 0)); var metaPanel = MakePanel("MetaPanel", canvas.transform, new Vector2(0, 55), new Vector2(860, 80));
var togNormal = MakeToggle("NormalToggle", diffPanel.transform, "Normal", new Vector2(-155, 0)); var titleInput = MakeInputField("TitleInput", metaPanel.transform, "곡 제목", new Vector2(-280, 0), new Vector2(255, 50));
var togHard = MakeToggle("HardToggle", diffPanel.transform, "Hard", new Vector2(0, 0)); var artistInput = MakeInputField("ArtistInput", metaPanel.transform, "아티스트", new Vector2(0, 0), new Vector2(255, 50));
var togExpert = MakeToggle("ExpertToggle", diffPanel.transform, "Expert", new Vector2(155, 0)); var bpmInput = MakeInputField("BpmInput", metaPanel.transform, "BPM", new Vector2(280, 0), new Vector2(255, 50));
togHard.isOn = true;
togExpert.isOn = true;
var actPanel = MakePanel("ActionPanel", canvas.transform, new Vector2(0, -125), new Vector2(860, 80)); // 난이도 토글 (Easy 제거, ExpertPlus 추가)
var genBtn = MakeButton("GenerateButton", actPanel.transform, "AI 생성 시작", new Vector2(-175, 0), new Vector2(440, 65)); var diffPanel = MakePanel("DiffPanel", canvas.transform, new Vector2(0, -45), new Vector2(860, 60));
var manualBtn = MakeButton("ManualButton", actPanel.transform, "직접 만들기 →", new Vector2(245, 0), new Vector2(185, 42)); var togNormal = MakeToggle("NormalToggle", diffPanel.transform, "Normal", new Vector2(-206, 0));
var togHard = MakeToggle("HardToggle", diffPanel.transform, "Hard", new Vector2(-51, 0));
var togExpert = MakeToggle("ExpertToggle", diffPanel.transform, "Expert", new Vector2(104, 0));
var togExpertPlus = MakeToggle("ExpertPlusToggle", diffPanel.transform, "Expert+", new Vector2(259, 0));
togNormal.isOn = true;
togHard.isOn = true;
togExpert.isOn = true;
togExpertPlus.isOn = true;
var progGroup = MakePanel("ProgressGroup", canvas.transform, new Vector2(0, -250), new Vector2(860, 95)); var backBtnSC = MakeButton("BackButton", canvas.transform, "< 뒤로", new Vector2(346, 365), new Vector2(130, 50));
var statusTxt = MakeTMP("StatusText", progGroup.transform, "", new Vector2(0, 28), new Vector2(840, 36), 18);
var progSlider = MakeSlider("ProgressSlider", progGroup.transform, new Vector2(0, -15), new Vector2(820, 28)); var actPanel = MakePanel("ActionPanel", canvas.transform, new Vector2(0, -145), new Vector2(860, 75));
var genBtn = MakeButton("GenerateButton", actPanel.transform, "AI 생성 시작", new Vector2(-175, 0), new Vector2(440, 60));
var manualBtn = MakeButton("ManualButton", actPanel.transform, "직접 만들기 >", new Vector2(245, 0), new Vector2(185, 40));
var progGroup = MakePanel("ProgressGroup", canvas.transform, new Vector2(0, -265), new Vector2(860, 90));
var statusTxt = MakeTMP("StatusText", progGroup.transform, "", new Vector2(0, 25), new Vector2(840, 34), 17);
var progSlider = MakeSlider("ProgressSlider", progGroup.transform, new Vector2(0, -15), new Vector2(820, 26));
progGroup.SetActive(false); progGroup.SetActive(false);
var sso = new SerializedObject(scMgr); var sso = new SerializedObject(scMgr);
Bind(sso, "audioDropdown", audioDd); Bind(sso, "audioDropdown", audioDd);
Bind(sso, "refreshBtn", refreshBtn.GetComponent<Button>()); Bind(sso, "refreshBtn", refreshBtn.GetComponent<Button>());
Bind(sso, "inputPathHint", pathHint); Bind(sso, "inputPathHint", pathHint);
Bind(sso, "filePickerBtn", filePickBtn.GetComponent<Button>());
Bind(sso, "addStatusText", addStatusTxt);
Bind(sso, "urlInput", urlField);
Bind(sso, "urlDownloadBtn", urlDlBtn.GetComponent<Button>());
Bind(sso, "titleInput", titleInput); Bind(sso, "titleInput", titleInput);
Bind(sso, "artistInput", artistInput); Bind(sso, "artistInput", artistInput);
Bind(sso, "bpmInput", bpmInput); Bind(sso, "bpmInput", bpmInput);
Bind(sso, "toggleEasy", togEasy);
Bind(sso, "toggleNormal", togNormal); Bind(sso, "toggleNormal", togNormal);
Bind(sso, "toggleHard", togHard); Bind(sso, "toggleHard", togHard);
Bind(sso, "toggleExpert", togExpert); Bind(sso, "toggleExpert", togExpert);
Bind(sso, "toggleExpertPlus", togExpertPlus);
Bind(sso, "backButton", backBtnSC.GetComponent<Button>());
Bind(sso, "generateButton", genBtn.GetComponent<Button>()); Bind(sso, "generateButton", genBtn.GetComponent<Button>());
Bind(sso, "manualEditorButton", manualBtn.GetComponent<Button>()); Bind(sso, "manualEditorButton", manualBtn.GetComponent<Button>());
Bind(sso, "progressGroup", progGroup); Bind(sso, "progressGroup", progGroup);
@@ -438,7 +646,9 @@ public static class VRBeatSaberSceneBuilder
{ {
var go = new GameObject(name); var go = new GameObject(name);
go.transform.SetParent(parent, false); go.transform.SetParent(parent, false);
go.AddComponent<Image>().color = new Color(0.08f, 0.08f, 0.08f, 0.88f); var img = go.AddComponent<Image>();
img.color = new Color(0.08f, 0.08f, 0.08f, 0.88f);
img.sprite = null;
var rt = go.GetComponent<RectTransform>(); var rt = go.GetComponent<RectTransform>();
rt.anchoredPosition = pos; rt.anchoredPosition = pos;
rt.sizeDelta = size; rt.sizeDelta = size;
@@ -449,7 +659,9 @@ public static class VRBeatSaberSceneBuilder
{ {
var go = new GameObject(name); var go = new GameObject(name);
go.transform.SetParent(parent, false); go.transform.SetParent(parent, false);
go.AddComponent<Image>().color = new Color(0.22f, 0.22f, 0.22f); var img = go.AddComponent<Image>();
img.color = new Color(0.22f, 0.22f, 0.22f);
img.sprite = null;
go.AddComponent<Button>(); go.AddComponent<Button>();
var rt = go.GetComponent<RectTransform>(); var rt = go.GetComponent<RectTransform>();
rt.anchoredPosition = pos; rt.anchoredPosition = pos;
@@ -472,7 +684,7 @@ public static class VRBeatSaberSceneBuilder
var tmp = go.AddComponent<TextMeshProUGUI>(); var tmp = go.AddComponent<TextMeshProUGUI>();
tmp.text = text; tmp.text = text;
tmp.fontSize = fs; tmp.fontSize = fs;
tmp.alignment = TextAlignmentOptions.MidlineLeft; tmp.alignment = TextAlignmentOptions.Center;
var rt = go.GetComponent<RectTransform>(); var rt = go.GetComponent<RectTransform>();
rt.anchoredPosition = pos; rt.anchoredPosition = pos;
rt.sizeDelta = size; rt.sizeDelta = size;
@@ -483,7 +695,9 @@ public static class VRBeatSaberSceneBuilder
{ {
var go = new GameObject(name); var go = new GameObject(name);
go.transform.SetParent(parent, false); go.transform.SetParent(parent, false);
go.AddComponent<Image>().color = new Color(0.15f, 0.15f, 0.15f); var fImg = go.AddComponent<Image>();
fImg.color = new Color(0.15f, 0.15f, 0.15f);
fImg.sprite = null;
var field = go.AddComponent<TMP_InputField>(); var field = go.AddComponent<TMP_InputField>();
var rt = go.GetComponent<RectTransform>(); var rt = go.GetComponent<RectTransform>();
rt.anchoredPosition = pos; rt.anchoredPosition = pos;
@@ -517,7 +731,9 @@ public static class VRBeatSaberSceneBuilder
{ {
var go = new GameObject(name); var go = new GameObject(name);
go.transform.SetParent(parent, false); go.transform.SetParent(parent, false);
go.AddComponent<Image>().color = new Color(0.15f, 0.15f, 0.15f); var ddImg = go.AddComponent<Image>();
ddImg.color = new Color(0.15f, 0.15f, 0.15f);
ddImg.sprite = null;
var dd = go.AddComponent<TMP_Dropdown>(); var dd = go.AddComponent<TMP_Dropdown>();
var rt = go.GetComponent<RectTransform>(); var rt = go.GetComponent<RectTransform>();
rt.anchoredPosition = pos; rt.anchoredPosition = pos;
+37 -21
View File
@@ -20,21 +20,22 @@ public class BeatSageUploader : MonoBehaviour
private const float POLL_TIMEOUT = 300f; private const float POLL_TIMEOUT = 300f;
// Beat Sage 난이도 이름 매핑 (내부 → API) // Beat Sage 난이도 이름 매핑 (내부 → API)
// Beat Sage API가 인정하는 난이도: Normal, Hard, Expert, ExpertPlus (Easy 없음)
private static readonly Dictionary<string, string> DiffNames = new() private static readonly Dictionary<string, string> DiffNames = new()
{ {
{ "easy", "Easy" }, { "normal", "Normal" },
{ "normal", "Normal" }, { "hard", "Hard" },
{ "hard", "Hard" }, { "expert", "Expert" },
{ "expert", "Expert" }, { "expertplus", "ExpertPlus" },
}; };
// Beat Sage .zip 내 .dat 파일명 매핑 (내부 → zip 내 파일명) // Beat Sage .zip 내 .dat 파일명 매핑 (내부 → zip 내 파일명)
private static readonly Dictionary<string, string> DatFileNames = new() private static readonly Dictionary<string, string> DatFileNames = new()
{ {
{ "easy", "Easy.dat" }, { "normal", "Normal.dat" },
{ "normal", "Normal.dat" }, { "hard", "Hard.dat" },
{ "hard", "Hard.dat" }, { "expert", "Expert.dat" },
{ "expert", "Expert.dat" }, { "expertplus", "ExpertPlus.dat" },
}; };
public string CurrentStatus { get; private set; } = ""; public string CurrentStatus { get; private set; } = "";
@@ -50,18 +51,20 @@ public class BeatSageUploader : MonoBehaviour
Action<string> onError) Action<string> onError)
{ {
// 1단계: 레벨 생성 요청 // 1단계: 레벨 생성 요청
SetStatus("Beat Sage에 음원 전송 중..."); SetStatus("[1/4] 음원 업로드 중...");
Debug.Log($"[BeatSage] 업로드 시작 — 파일: {audioPath}");
string levelId = null; string levelId = null;
yield return CreateLevel(audioPath, difficulties, yield return CreateLevel(audioPath, difficulties,
id => levelId = id, id => levelId = id,
onError); onError);
Debug.Log($"[BeatSage] CreateLevel 완료 — levelId: {levelId}");
if (levelId == null) yield break; if (levelId == null) yield break;
onProgress?.Invoke(0.15f); onProgress?.Invoke(0.15f);
// 2단계: 생성 완료 폴링 // 2단계: 생성 완료 폴링
SetStatus("Beat Sage AI 맵 생성 ..."); SetStatus("[2/4] AI 맵 생성 시작...");
bool ready = false; bool ready = false;
float elapsed = 0f; float elapsed = 0f;
@@ -74,8 +77,9 @@ public class BeatSageUploader : MonoBehaviour
yield return PollHeartbeat(levelId, yield return PollHeartbeat(levelId,
status => status =>
{ {
ready = status == "generated"; ready = string.Equals(status, "generated", System.StringComparison.OrdinalIgnoreCase)
error = status == "error"; || string.Equals(status, "done", System.StringComparison.OrdinalIgnoreCase);
error = string.Equals(status, "error", System.StringComparison.OrdinalIgnoreCase);
}, },
onError); onError);
@@ -83,13 +87,13 @@ public class BeatSageUploader : MonoBehaviour
float progress = Mathf.Clamp01(elapsed / POLL_TIMEOUT); float progress = Mathf.Clamp01(elapsed / POLL_TIMEOUT);
onProgress?.Invoke(0.15f + progress * 0.6f); onProgress?.Invoke(0.15f + progress * 0.6f);
SetStatus($"Beat Sage AI 맵 생성 중... ({(int)elapsed}s)"); SetStatus($"[2/4] AI 맵 생성 중... {(int)elapsed}초 경과");
} }
if (!ready) { onError?.Invoke("Beat Sage 타임아웃 (5분 초과)"); yield break; } if (!ready) { onError?.Invoke("Beat Sage 타임아웃 (5분 초과)"); yield break; }
// 3단계: .zip 다운로드 // 3단계: .zip 다운로드
SetStatus("결과 다운로드 중..."); SetStatus("[3/4] 결과 다운로드 중...");
byte[] zipBytes = null; byte[] zipBytes = null;
yield return DownloadZip(levelId, yield return DownloadZip(levelId,
@@ -100,7 +104,7 @@ public class BeatSageUploader : MonoBehaviour
onProgress?.Invoke(0.9f); onProgress?.Invoke(0.9f);
// 4단계: .zip 해제 + BeatSageConverter 변환 // 4단계: .zip 해제 + BeatSageConverter 변환
SetStatus("맵 데이터 변환 중..."); SetStatus("[3/4] 맵 데이터 변환 중...");
Dictionary<string, List<NoteData>> maps = null; Dictionary<string, List<NoteData>> maps = null;
try try
@@ -114,7 +118,7 @@ public class BeatSageUploader : MonoBehaviour
} }
onProgress?.Invoke(1f); onProgress?.Invoke(1f);
SetStatus("변환 완료"); SetStatus("[3/4] 변환 완료");
onComplete?.Invoke(maps); onComplete?.Invoke(maps);
} }
@@ -126,15 +130,25 @@ public class BeatSageUploader : MonoBehaviour
byte[] audioBytes = File.ReadAllBytes(audioPath); byte[] audioBytes = File.ReadAllBytes(audioPath);
string fileName = Path.GetFileName(audioPath); string fileName = Path.GetFileName(audioPath);
// 난이도 문자열 변환: ["easy","hard"] → "Easy,Hard" // 난이도: 알 수 없는 값(easy 등)은 건너뜀, 쉼표 구분 단일 필드로 전송
var diffStr = string.Join(",", difficulties.ConvertAll(d => var mappedDiffs = new List<string>();
DiffNames.TryGetValue(d, out var n) ? n : d)); foreach (string d in difficulties)
if (DiffNames.TryGetValue(d, out var n)) mappedDiffs.Add(n);
if (mappedDiffs.Count == 0)
{
onError?.Invoke("Beat Sage가 지원하지 않는 난이도입니다. Normal/Hard/Expert/ExpertPlus 중 선택하세요.");
yield break;
}
string diffStr = string.Join(",", mappedDiffs);
Debug.Log($"[BeatSage] 전송 difficulties: '{diffStr}'");
var form = new List<IMultipartFormSection> var form = new List<IMultipartFormSection>
{ {
new MultipartFormFileSection("audio_file", audioBytes, fileName, "audio/mpeg"), new MultipartFormFileSection("audio_file", audioBytes, fileName, "audio/mpeg"),
new MultipartFormDataSection("audio_metadata_title", ""), new MultipartFormDataSection("audio_metadata_title", " "),
new MultipartFormDataSection("audio_metadata_artist", ""), new MultipartFormDataSection("audio_metadata_artist", " "),
new MultipartFormDataSection("difficulties", diffStr), new MultipartFormDataSection("difficulties", diffStr),
new MultipartFormDataSection("modes", "Standard"), new MultipartFormDataSection("modes", "Standard"),
new MultipartFormDataSection("events", "DotBlocks,Obstacles,Bombs"), new MultipartFormDataSection("events", "DotBlocks,Obstacles,Bombs"),
@@ -149,6 +163,8 @@ public class BeatSageUploader : MonoBehaviour
if (req.result != UnityWebRequest.Result.Success) if (req.result != UnityWebRequest.Result.Success)
{ {
string body = req.downloadHandler?.text ?? "(응답 없음)";
Debug.LogError($"[BeatSage] HTTP {req.responseCode} — {req.error}\n응답 본문: {body}");
onError?.Invoke($"레벨 생성 요청 실패: {req.error}"); onError?.Invoke($"레벨 생성 요청 실패: {req.error}");
yield break; yield break;
} }
+161
View File
@@ -0,0 +1,161 @@
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
/// <summary>
/// 에디터/PC 환경 전용 테스트 보조 스크립트.
/// RuntimeInitializeOnLoadMethod로 자동 생성되므로 씬에 직접 추가할 필요 없습니다.
/// Quest 빌드 시 자동으로 비활성화됩니다.
///
/// 기능:
/// 1. TrackedDeviceGraphicRaycaster → GraphicRaycaster 교체 (마우스 클릭 활성화)
/// 2. Canvas EventCamera 자동 갱신 (클릭 위치 정확도)
/// 3. ESC 키로 씬별 뒤로가기
/// </summary>
public class DesktopUIMode : MonoBehaviour
{
#if !UNITY_ANDROID || UNITY_EDITOR
// 어떤 씬에서 Play를 눌러도 자동 실행
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
private static void AutoCreate()
{
if (FindObjectOfType<DesktopUIMode>() != null) return; // 이미 있으면 생성 안 함
var go = new GameObject("[DesktopUIMode]");
go.AddComponent<DesktopUIMode>();
}
// ESC 뒤로가기 씬 매핑
private static readonly System.Collections.Generic.Dictionary<string, string> BackSceneMap =
new System.Collections.Generic.Dictionary<string, string>
{
{ "SongSelect", "Intro" },
{ "SongCreator", "Intro" },
{ "MapEditorScene", "SongCreator" },
{ "Game", "SongSelect" },
};
private void Awake()
{
// 중복 방지 (씬에 직접 놓은 경우 AutoCreate와 겹칠 수 있음)
if (FindObjectsOfType<DesktopUIMode>().Length > 1)
{
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
SceneManager.sceneLoaded += OnSceneLoaded;
PatchCanvases(); // 현재 씬 즉시 패치
}
private void OnDestroy()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
StartCoroutine(PatchAfterFrame());
}
private System.Collections.IEnumerator PatchAfterFrame()
{
yield return null; // Canvas.Awake 이후 실행 보장
PatchCanvases();
}
private void Update()
{
RefreshCanvasCameras(); // 매 프레임 worldCamera 갱신 (카메라 초기화 타이밍 보정)
if (Keyboard.current?.escapeKey.wasPressedThisFrame == true)
GoBack();
}
// ── Canvas 패치 ──────────────────────────────────────────
private static void PatchCanvases()
{
foreach (var canvas in FindObjectsOfType<Canvas>())
{
if (canvas.renderMode != RenderMode.WorldSpace) continue;
// TrackedDeviceGraphicRaycaster → GraphicRaycaster
var tracked = canvas.GetComponent("TrackedDeviceGraphicRaycaster");
if (tracked != null)
{
DestroyImmediate(tracked);
if (canvas.GetComponent<GraphicRaycaster>() == null)
canvas.gameObject.AddComponent<GraphicRaycaster>();
Debug.Log($"[DesktopUIMode] {canvas.name} Raycaster 교체 완료");
}
}
RemoveDuplicateAudioListeners();
RefreshCanvasCameras();
}
private static void RemoveDuplicateAudioListeners()
{
var listeners = FindObjectsOfType<AudioListener>();
if (listeners.Length <= 1) return;
// 첫 번째(DontDestroyOnLoad에 없는 것 우선)만 남기고 나머지 제거
AudioListener keep = null;
foreach (var al in listeners)
{
if (al.gameObject.scene.name != "DontDestroyOnLoad")
{ keep = al; break; }
}
if (keep == null) keep = listeners[0];
foreach (var al in listeners)
{
if (al != keep)
{
Debug.Log($"[DesktopUIMode] 중복 AudioListener 제거: {al.gameObject.name}");
DestroyImmediate(al);
}
}
}
private static void RefreshCanvasCameras()
{
Camera cam = Camera.main;
if (cam == null)
{
// Camera.main이 없으면 씬에서 직접 찾기 (DontDestroyOnLoad 제외)
foreach (var c in FindObjectsOfType<Camera>())
{
if (c.enabled && c.gameObject.scene.name != "DontDestroyOnLoad")
{
cam = c;
break;
}
}
}
if (cam == null) cam = FindObjectOfType<Camera>(); // 최후 수단
if (cam == null) return;
foreach (var canvas in FindObjectsOfType<Canvas>())
{
if (canvas.renderMode == RenderMode.WorldSpace && canvas.worldCamera != cam)
canvas.worldCamera = cam;
}
}
// ── ESC 뒤로가기 ─────────────────────────────────────────
private static void GoBack()
{
string current = SceneManager.GetActiveScene().name;
if (BackSceneMap.TryGetValue(current, out string target))
SceneManager.LoadScene(target);
}
#endif
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 1c063d20f87d41d40a6a01c6bd1a1736
+20 -5
View File
@@ -6,7 +6,7 @@ using UnityEngine.Networking;
public class DownloadManager : MonoBehaviour public class DownloadManager : MonoBehaviour
{ {
[SerializeField] private string baseUrl = "http://whdwo798.synology.me:8180/beatsaber"; [SerializeField] private string baseUrl = "http://whdwo798.synology.me/beatsaber";
private static string CacheRoot => Path.Combine(Application.temporaryCachePath, "beatsaber"); private static string CacheRoot => Path.Combine(Application.temporaryCachePath, "beatsaber");
@@ -64,8 +64,10 @@ public class DownloadManager : MonoBehaviour
public string MapPath(SongInfo song, string difficulty) public string MapPath(SongInfo song, string difficulty)
{ {
DifficultyInfo info = song.difficulties.Get(difficulty); DifficultyInfo info = song.difficulties.Get(difficulty);
if (info == null) return null; if (info == null || string.IsNullOrEmpty(info.mapFile)) return null;
return Path.Combine(SongDir(song.id), Path.GetFileName(info.mapFile)); string fileName = Path.GetFileName(info.mapFile);
if (string.IsNullOrEmpty(fileName)) return null;
return Path.Combine(SongDir(song.id), fileName);
} }
// ── 내부 구현 ───────────────────────────────────────────── // ── 내부 구현 ─────────────────────────────────────────────
@@ -73,10 +75,11 @@ public class DownloadManager : MonoBehaviour
private IEnumerator DownloadSongCoroutine(SongInfo song, string difficulty, private IEnumerator DownloadSongCoroutine(SongInfo song, string difficulty,
Action<float> onProgress, Action onComplete, Action<string> onError) Action<float> onProgress, Action onComplete, Action<string> onError)
{ {
Directory.CreateDirectory(SongDir(song.id)); string songDir = Path.GetFullPath(SongDir(song.id));
Directory.CreateDirectory(songDir);
// 1단계: 오디오 (70%) // 1단계: 오디오 (70%)
string audioPath = AudioPath(song.id); string audioPath = Path.Combine(songDir, $"{song.id}.mp3");
if (!File.Exists(audioPath)) if (!File.Exists(audioPath))
{ {
bool failed = false; bool failed = false;
@@ -95,7 +98,19 @@ public class DownloadManager : MonoBehaviour
yield break; yield break;
} }
if (string.IsNullOrEmpty(diffInfo.mapFile))
{
onError?.Invoke($"'{difficulty}' 맵 파일 정보 없음 — Creator에서 곡을 다시 생성해주세요");
yield break;
}
string mapPath = MapPath(song, difficulty); string mapPath = MapPath(song, difficulty);
if (mapPath != null) mapPath = Path.GetFullPath(mapPath);
if (mapPath == null)
{
onError?.Invoke($"'{difficulty}' 맵 경로 계산 실패");
yield break;
}
if (!File.Exists(mapPath)) if (!File.Exists(mapPath))
{ {
bool failed = false; bool failed = false;
+18
View File
@@ -0,0 +1,18 @@
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
/// <summary>
/// Game 씬의 뒤로가기 버튼에 자동으로 추가됩니다.
/// SceneBuilder가 Button에 이 컴포넌트를 달아 씬 이동을 처리합니다.
/// </summary>
[RequireComponent(typeof(Button))]
public class GameBackButton : MonoBehaviour
{
[SerializeField] private string targetScene = "SongSelect";
private void Start()
{
GetComponent<Button>().onClick.AddListener(() => SceneManager.LoadScene(targetScene));
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 72739d64e582ca840a355a30eba85d75
+97 -38
View File
@@ -10,15 +10,36 @@ using UnityEngine.Networking;
public class NasPublisher : MonoBehaviour public class NasPublisher : MonoBehaviour
{ {
[Header("NAS 접속 정보")] [Header("NAS 접속 정보")]
[SerializeField] private string nasBaseUrl = "http://192.168.55.3:5000"; // DSM 포트 (내부망) [SerializeField] private string nasBaseUrl = "http://192.168.55.3:5000";
[SerializeField] private string nasAccount = "admin"; [SerializeField] private string nasAccount = "admin";
[SerializeField] private string nasPassword = ""; // Inspector에서 입력 [SerializeField] private string nasRootPath = "/web/beatsaber";
[SerializeField] private string nasRootPath = "/web/beatsaber"; // NAS 내 절대경로
[Header("정적 서버 URL (songs.json 읽기용)")] [Header("정적 서버 URL (songs.json 읽기용)")]
[SerializeField] private string staticBaseUrl = "http://whdwo798.synology.me:8180/beatsaber"; [SerializeField] private string staticBaseUrl = "http://whdwo798.synology.me/beatsaber";
private string _sid = ""; // DSM 세션 ID private string _sid = "";
private string _synoToken = ""; // DSM 7 CSRF 토큰 (enable_syno_token=yes 로 획득)
private string _password = ""; // StreamingAssets/nas_config.json 에서 로드
private void Awake()
{
LoadConfig();
}
private void LoadConfig()
{
string path = Path.Combine(Application.streamingAssetsPath, "nas_config.json");
if (!File.Exists(path))
{
Debug.LogWarning("[NasPublisher] nas_config.json 없음: " + path);
return;
}
var cfg = JsonUtility.FromJson<NasConfig>(File.ReadAllText(path));
_password = cfg?.password ?? "";
}
[Serializable]
private class NasConfig { public string password; }
// ── Public API ─────────────────────────────────────────── // ── Public API ───────────────────────────────────────────
@@ -30,17 +51,17 @@ public class NasPublisher : MonoBehaviour
Action onComplete, Action onComplete,
Action<string> onError) Action<string> onError)
{ {
bool failed = false;
void OnErr(string e) { onError?.Invoke(e); failed = true; }
// 1단계: DSM 로그인 // 1단계: DSM 로그인
yield return Login(onError); yield return Login(OnErr);
if (string.IsNullOrEmpty(_sid)) yield break; if (string.IsNullOrEmpty(_sid)) yield break;
onProgress?.Invoke(0.1f); onProgress?.Invoke(0.1f);
// 2단계: 오디오 업로드 // 2단계: 오디오 업로드
yield return UploadFile( yield return UploadFile(audioPath, $"{nasRootPath}/music", $"{song.id}.mp3", OnErr);
audioPath, if (failed) { yield return Logout(); yield break; }
$"{nasRootPath}/music",
$"{song.id}.mp3",
onError);
onProgress?.Invoke(0.4f); onProgress?.Invoke(0.4f);
// 3단계: 각 난이도 맵 JSON 업로드 // 3단계: 각 난이도 맵 JSON 업로드
@@ -53,20 +74,18 @@ public class NasPublisher : MonoBehaviour
string json = BeatSageConverter.ToMapJson(kv.Value); string json = BeatSageConverter.ToMapJson(kv.Value);
byte[] bytes = Encoding.UTF8.GetBytes(json); byte[] bytes = Encoding.UTF8.GetBytes(json);
// 파일명에 맞춰 DifficultyInfo 업데이트
AssignMapFile(song, kv.Key, fileName); AssignMapFile(song, kv.Key, fileName);
yield return UploadBytes( yield return UploadBytes(bytes, fileName, $"{nasRootPath}/maps", OnErr);
bytes, fileName, if (failed) { yield return Logout(); yield break; }
$"{nasRootPath}/maps",
onError);
done++; done++;
onProgress?.Invoke(0.4f + (float)done / total * 0.3f); onProgress?.Invoke(0.4f + (float)done / total * 0.3f);
} }
// 4단계: songs.json 다운로드 → 항목 추가 → 재업로드 // 4단계: songs.json 다운로드 → 항목 추가 → 재업로드
yield return PatchSongsJson(song, onError); yield return PatchSongsJson(song, OnErr);
if (failed) { yield return Logout(); yield break; }
onProgress?.Invoke(0.95f); onProgress?.Invoke(0.95f);
// 5단계: 로그아웃 // 5단계: 로그아웃
@@ -81,11 +100,12 @@ public class NasPublisher : MonoBehaviour
private IEnumerator Login(Action<string> onError) private IEnumerator Login(Action<string> onError)
{ {
Debug.Log($"[NasPublisher] 로그인 시도 — nasBaseUrl: '{nasBaseUrl}'");
string url = $"{nasBaseUrl}/webapi/auth.cgi" + string url = $"{nasBaseUrl}/webapi/auth.cgi" +
$"?api=SYNO.API.Auth&version=3&method=login" + $"?api=SYNO.API.Auth&version=6&method=login" +
$"&account={UnityWebRequest.EscapeURL(nasAccount)}" + $"&account={UnityWebRequest.EscapeURL(nasAccount)}" +
$"&passwd={UnityWebRequest.EscapeURL(nasPassword)}" + $"&passwd={UnityWebRequest.EscapeURL(_password)}" +
$"&session=FileStation&format=sid"; $"&session=FileStation&format=sid&enable_syno_token=yes";
using var req = UnityWebRequest.Get(url); using var req = UnityWebRequest.Get(url);
yield return req.SendWebRequest(); yield return req.SendWebRequest();
@@ -96,7 +116,11 @@ public class NasPublisher : MonoBehaviour
yield break; yield break;
} }
_sid = ParseSid(req.downloadHandler.text); Debug.Log($"[NasPublisher] 로그인 응답: {req.downloadHandler.text}");
string resp = req.downloadHandler.text;
_sid = ParseJsonString(resp, "sid");
_synoToken = ParseJsonString(resp, "synotoken");
Debug.Log($"[NasPublisher] sid={_sid}, synotoken={_synoToken}");
if (string.IsNullOrEmpty(_sid)) if (string.IsNullOrEmpty(_sid))
onError?.Invoke("DSM sid 파싱 실패 — 계정 정보를 확인하세요"); onError?.Invoke("DSM sid 파싱 실패 — 계정 정보를 확인하세요");
} }
@@ -122,26 +146,61 @@ public class NasPublisher : MonoBehaviour
private IEnumerator UploadBytes(byte[] bytes, string fileName, private IEnumerator UploadBytes(byte[] bytes, string fileName,
string nasFolder, Action<string> onError) string nasFolder, Action<string> onError)
{ {
string url = $"{nasBaseUrl}/webapi/entry.cgi"; Debug.Log($"[NasPublisher] 업로드 시도 — path: '{nasFolder}', file: '{fileName}'");
var form = new List<IMultipartFormSection> string uploadUrl = $"{nasBaseUrl}/webapi/entry.cgi" +
$"?api=SYNO.FileStation.Upload&version=2&method=upload" +
$"&_sid={UnityWebRequest.EscapeURL(_sid)}";
// PowerShell 테스트와 동일한 방식으로 multipart body 수동 구성
string boundary = System.Guid.NewGuid().ToString("N");
const string CRLF = "\r\n";
using var bodyStream = new MemoryStream();
void WriteText(string text)
{ {
new MultipartFormDataSection("api", "SYNO.FileStation.Upload"), var b = Encoding.UTF8.GetBytes(text);
new MultipartFormDataSection("version", "2"), bodyStream.Write(b, 0, b.Length);
new MultipartFormDataSection("method", "upload"), }
new MultipartFormDataSection("path", nasFolder), void WriteField(string name, string value)
new MultipartFormDataSection("overwrite", "true"), {
new MultipartFormDataSection("_sid", _sid), WriteText($"--{boundary}{CRLF}");
new MultipartFormFileSection("file", bytes, fileName, "application/octet-stream"), WriteText($"Content-Disposition: form-data; name=\"{name}\"{CRLF}{CRLF}");
}; WriteText(value);
WriteText(CRLF);
}
WriteField("path", nasFolder);
WriteField("create_parents", "true");
WriteField("overwrite", "true");
WriteText($"--{boundary}{CRLF}");
WriteText($"Content-Disposition: form-data; name=\"file\"; filename=\"{fileName}\"{CRLF}");
WriteText($"Content-Type: application/octet-stream{CRLF}{CRLF}");
bodyStream.Write(bytes, 0, bytes.Length);
WriteText(CRLF);
WriteText($"--{boundary}--{CRLF}");
byte[] bodyBytes = bodyStream.ToArray();
using var req = new UnityWebRequest(uploadUrl, "POST");
req.uploadHandler = new UploadHandlerRaw(bodyBytes);
req.downloadHandler = new DownloadHandlerBuffer();
req.SetRequestHeader("Content-Type", $"multipart/form-data; boundary={boundary}");
if (!string.IsNullOrEmpty(_synoToken))
req.SetRequestHeader("X-SYNO-TOKEN", _synoToken);
using var req = UnityWebRequest.Post(url, form);
yield return req.SendWebRequest(); yield return req.SendWebRequest();
if (req.result != UnityWebRequest.Result.Success) if (req.result != UnityWebRequest.Result.Success)
{
onError?.Invoke($"업로드 실패({fileName}): {req.error}"); onError?.Invoke($"업로드 실패({fileName}): {req.error}");
else yield break;
Debug.Log($"[NasPublisher] 업로드 완료: {fileName}"); }
Debug.Log($"[NasPublisher] 업로드 응답({fileName}): {req.downloadHandler.text}");
if (req.downloadHandler.text.Contains("\"success\":false"))
onError?.Invoke($"업로드 거부({fileName}): {req.downloadHandler.text}");
} }
// ── songs.json 패치 ─────────────────────────────────────── // ── songs.json 패치 ───────────────────────────────────────
@@ -178,12 +237,12 @@ public class NasPublisher : MonoBehaviour
// ── 유틸 ───────────────────────────────────────────────── // ── 유틸 ─────────────────────────────────────────────────
private static string ParseSid(string json) private static string ParseJsonString(string json, string key)
{ {
const string key = "\"sid\":\""; string search = $"\"{key}\":\"";
int start = json.IndexOf(key, StringComparison.Ordinal); int start = json.IndexOf(search, StringComparison.Ordinal);
if (start < 0) return null; if (start < 0) return null;
start += key.Length; start += search.Length;
int end = json.IndexOf('"', start); int end = json.IndexOf('"', start);
return end > start ? json.Substring(start, end - start) : null; return end > start ? json.Substring(start, end - start) : null;
} }
+6 -6
View File
@@ -43,18 +43,18 @@ public class SongInfo
[Serializable] [Serializable]
public class DifficultyMap public class DifficultyMap
{ {
public DifficultyInfo easy;
public DifficultyInfo normal; public DifficultyInfo normal;
public DifficultyInfo hard; public DifficultyInfo hard;
public DifficultyInfo expert; public DifficultyInfo expert;
public DifficultyInfo expertplus;
public DifficultyInfo Get(string key) => key switch public DifficultyInfo Get(string key) => key switch
{ {
"easy" => easy, "normal" => normal,
"normal" => normal, "hard" => hard,
"hard" => hard, "expert" => expert,
"expert" => expert, "expertplus" => expertplus,
_ => null _ => null
}; };
} }
+41
View File
@@ -0,0 +1,41 @@
using UnityEngine;
using UnityEngine.SceneManagement;
/// <summary>
/// Intro 씬의 XR Origin (Hands)에 추가하세요.
/// DontDestroyOnLoad로 모든 씬에 XR Rig를 유지하고,
/// 씬 전환 시 중복 카메라를 자동으로 비활성화합니다.
/// </summary>
[DisallowMultipleComponent]
public class PersistentXRRig : MonoBehaviour
{
private void Awake()
{
// 이미 다른 PersistentXRRig가 살아있으면 자기 자신을 제거 (싱글턴)
var existing = FindObjectsByType<PersistentXRRig>(FindObjectsSortMode.None);
if (existing.Length > 1)
{
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnDestroy()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
// SceneBuilder가 만든 일반 카메라(Main Camera)를 비활성화
// - XR Rig 자식 카메라는 유지
foreach (var cam in FindObjectsByType<Camera>(FindObjectsSortMode.None))
{
if (!cam.transform.IsChildOf(transform))
cam.gameObject.SetActive(false);
}
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 0d94d5e288e7ee34facc8caf424e5c72
+176 -18
View File
@@ -2,8 +2,10 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading;
using TMPro; using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
using UnityEngine.UI; using UnityEngine.UI;
@@ -12,7 +14,15 @@ public class SongCreatorManager : MonoBehaviour
[Header("음원 선택")] [Header("음원 선택")]
[SerializeField] private TMP_Dropdown audioDropdown; [SerializeField] private TMP_Dropdown audioDropdown;
[SerializeField] private Button refreshBtn; [SerializeField] private Button refreshBtn;
[SerializeField] private TMP_Text inputPathHint; // 파일 넣는 경로 안내 [SerializeField] private TMP_Text inputPathHint;
[Header("음원 추가 — 로컬 파일")]
[SerializeField] private Button filePickerBtn; // 파일 탐색 버튼
[SerializeField] private TMP_Text addStatusText; // 추가/다운로드 상태
[Header("음원 추가 — URL")]
[SerializeField] private TMP_InputField urlInput; // MP3 직접 URL
[SerializeField] private Button urlDownloadBtn; // URL 다운로드 시작
[Header("메타데이터")] [Header("메타데이터")]
[SerializeField] private TMP_InputField titleInput; [SerializeField] private TMP_InputField titleInput;
@@ -20,14 +30,16 @@ public class SongCreatorManager : MonoBehaviour
[SerializeField] private TMP_InputField bpmInput; [SerializeField] private TMP_InputField bpmInput;
[Header("난이도")] [Header("난이도")]
[SerializeField] private Toggle toggleEasy;
[SerializeField] private Toggle toggleNormal; [SerializeField] private Toggle toggleNormal;
[SerializeField] private Toggle toggleHard; [SerializeField] private Toggle toggleHard;
[SerializeField] private Toggle toggleExpert; [SerializeField] private Toggle toggleExpert;
[SerializeField] private Toggle toggleExpertPlus;
[Header("액션")] [Header("액션")]
[SerializeField] private Button generateButton; [SerializeField] private Button generateButton;
[SerializeField] private Button manualEditorButton; // 작은 버튼 [SerializeField] private Button manualEditorButton; // 작은 버튼
[SerializeField] private Button backButton;
[SerializeField] private string introSceneName = "Intro";
[Header("진행 상태")] [Header("진행 상태")]
[SerializeField] private GameObject progressGroup; [SerializeField] private GameObject progressGroup;
@@ -46,6 +58,7 @@ public class SongCreatorManager : MonoBehaviour
Path.Combine(Application.persistentDataPath, "input"); Path.Combine(Application.persistentDataPath, "input");
private readonly List<string> audioFiles = new(); private readonly List<string> audioFiles = new();
private string _pendingFilePath; // 파일 다이얼로그 결과 (백그라운드 스레드 → 메인 스레드 전달)
// ── Unity ──────────────────────────────────────────────── // ── Unity ────────────────────────────────────────────────
@@ -56,14 +69,40 @@ public class SongCreatorManager : MonoBehaviour
if (inputPathHint != null) if (inputPathHint != null)
inputPathHint.text = $"음원 경로: {InputPath}"; inputPathHint.text = $"음원 경로: {InputPath}";
// 씬에서 아무 토글도 안 선택되어 있으면 전부 켜기
bool anyOn = (toggleNormal != null && toggleNormal.isOn)
|| (toggleHard != null && toggleHard.isOn)
|| (toggleExpert != null && toggleExpert.isOn)
|| (toggleExpertPlus != null && toggleExpertPlus.isOn);
if (!anyOn)
{
if (toggleNormal != null) toggleNormal.isOn = true;
if (toggleHard != null) toggleHard.isOn = true;
if (toggleExpert != null) toggleExpert.isOn = true;
if (toggleExpertPlus != null) toggleExpertPlus.isOn = true;
}
refreshBtn.onClick.AddListener(RefreshAudioList); refreshBtn.onClick.AddListener(RefreshAudioList);
generateButton.onClick.AddListener(OnGenerateClicked); generateButton.onClick.AddListener(OnGenerateClicked);
manualEditorButton.onClick.AddListener(() => SceneManager.LoadScene(mapEditorScene)); manualEditorButton.onClick.AddListener(() => SceneManager.LoadScene(mapEditorScene));
backButton?.onClick.AddListener(() => SceneManager.LoadScene(introSceneName));
if (filePickerBtn != null) filePickerBtn.onClick.AddListener(OnFilePickerClicked);
if (urlDownloadBtn != null) urlDownloadBtn.onClick.AddListener(OnUrlDownloadClicked);
progressGroup.SetActive(false); progressGroup.SetActive(false);
RefreshAudioList(); RefreshAudioList();
} }
private void Update()
{
// 파일 다이얼로그는 STA 스레드에서 실행되므로 결과를 메인 스레드에서 처리
if (_pendingFilePath != null)
{
CopyToInput(_pendingFilePath);
_pendingFilePath = null;
}
}
// ── 음원 목록 갱신 ─────────────────────────────────────── // ── 음원 목록 갱신 ───────────────────────────────────────
private void RefreshAudioList() private void RefreshAudioList()
@@ -96,14 +135,18 @@ public class SongCreatorManager : MonoBehaviour
{ SetStatus("BPM을 올바르게 입력해주세요."); return; } { SetStatus("BPM을 올바르게 입력해주세요."); return; }
var diffs = new List<string>(); var diffs = new List<string>();
if (toggleEasy.isOn) diffs.Add("easy"); if (toggleNormal != null && toggleNormal.isOn) diffs.Add("normal");
if (toggleNormal.isOn) diffs.Add("normal"); if (toggleHard != null && toggleHard.isOn) diffs.Add("hard");
if (toggleHard.isOn) diffs.Add("hard"); if (toggleExpert != null && toggleExpert.isOn) diffs.Add("expert");
if (toggleExpert.isOn) diffs.Add("expert"); if (toggleExpertPlus != null && toggleExpertPlus.isOn) diffs.Add("expertplus");
if (diffs.Count == 0) { SetStatus("난이도를 하나 이상 선택해주세요."); return; } if (diffs.Count == 0) { SetStatus("난이도를 하나 이상 선택해주세요."); return; }
StartCoroutine(GenerateFlow(audioFiles[audioDropdown.value], bpm, diffs)); string audioPath = audioFiles[audioDropdown.value];
Debug.Log($"[SongCreator] 생성 시작 — 파일: {audioPath}, BPM: {bpm}, 난이도: {string.Join(",", diffs)}");
Debug.Log($"[SongCreator] beatSageUploader={beatSageUploader}, nasPublisher={nasPublisher}");
StartCoroutine(GenerateFlow(audioPath, bpm, diffs));
} }
// ── 생성 플로우 ─────────────────────────────────────────── // ── 생성 플로우 ───────────────────────────────────────────
@@ -112,40 +155,58 @@ public class SongCreatorManager : MonoBehaviour
{ {
SetInteractable(false); SetInteractable(false);
progressGroup.SetActive(true); progressGroup.SetActive(true);
Debug.Log("[SongCreator] GenerateFlow 시작");
// 1단계: Beat Sage 전송 → 변환 // 1단계: Beat Sage 전송 → 변환
Dictionary<string, List<NoteData>> maps = null; Dictionary<string, List<NoteData>> maps = null;
bool failed = false; bool failed = false;
Debug.Log("[SongCreator] BeatSage Upload 호출");
yield return beatSageUploader.Upload( yield return beatSageUploader.Upload(
audioPath, diffs, bpm, audioPath, diffs, bpm,
onProgress: p => onProgress: p =>
{ {
progressSlider.value = p * 0.8f; progressSlider.value = p * 0.8f;
SetStatus(beatSageUploader.CurrentStatus); SetStatus($"{beatSageUploader.CurrentStatus} ({(int)(p * 80)}%)");
}, },
onComplete: result => maps = result, onComplete: result =>
onError: err => { SetStatus($"Beat Sage 실패: {err}"); failed = true; }); {
maps = result;
Debug.Log($"[SongCreator] BeatSage 완료 — 난이도 수: {result?.Count}");
},
onError: err =>
{
Debug.LogError($"[SongCreator] BeatSage 오류: {err}");
SetStatus($"오류: {err}");
failed = true;
});
Debug.Log($"[SongCreator] BeatSage 단계 끝 — failed={failed}, maps={maps?.Count}");
if (failed) { SetInteractable(true); yield break; } if (failed) { SetInteractable(true); yield break; }
// 2단계: NAS 업로드 // 2단계: NAS 업로드
SetStatus("NAS에 업로드 중...");
SongInfo song = BuildSongInfo(audioPath, bpm, maps); SongInfo song = BuildSongInfo(audioPath, bpm, maps);
Debug.Log($"[SongCreator] NAS Publish 호출 — song.id={song.id}");
yield return nasPublisher.Publish( yield return nasPublisher.Publish(
song, audioPath, maps, song, audioPath, maps,
onProgress: p => onProgress: p =>
{ {
progressSlider.value = 0.8f + p * 0.2f; progressSlider.value = 0.8f + p * 0.2f;
SetStatus($"NAS 업로드 중... {(int)((0.8f + p * 0.2f) * 100)}%"); SetStatus($"[4/4] NAS 업로드 중... ({(int)((0.8f + p * 0.2f) * 100)}%)");
}, },
onComplete: () => onComplete: () =>
{ {
progressSlider.value = 1f; progressSlider.value = 1f;
SetStatus($"'{song.title}' 생성 완료!"); SetStatus($"완료! '{song.title}' 생성 성공 (100%)");
Debug.Log($"[SongCreator] NAS 업로드 완료");
}, },
onError: err => { SetStatus($"NAS 업로드 실패: {err}"); failed = true; }); onError: err =>
{
Debug.LogError($"[SongCreator] NAS 오류: {err}");
SetStatus($"NAS 업로드 실패: {err}");
failed = true;
});
SetInteractable(true); SetInteractable(true);
} }
@@ -163,10 +224,10 @@ public class SongCreatorManager : MonoBehaviour
var info = new DifficultyInfo { noteCount = kv.Value.Count }; var info = new DifficultyInfo { noteCount = kv.Value.Count };
switch (kv.Key) switch (kv.Key)
{ {
case "easy": diffMap.easy = info; break; case "normal": diffMap.normal = info; break;
case "normal": diffMap.normal = info; break; case "hard": diffMap.hard = info; break;
case "hard": diffMap.hard = info; break; case "expert": diffMap.expert = info; break;
case "expert": diffMap.expert = info; break; case "expertplus": diffMap.expertplus = info; break;
} }
} }
@@ -193,5 +254,102 @@ public class SongCreatorManager : MonoBehaviour
manualEditorButton.interactable = value; manualEditorButton.interactable = value;
audioDropdown.interactable = value; audioDropdown.interactable = value;
refreshBtn.interactable = value; refreshBtn.interactable = value;
if (filePickerBtn != null) filePickerBtn.interactable = value;
if (urlDownloadBtn != null) urlDownloadBtn.interactable = value;
}
// ── 로컬 파일 선택 ────────────────────────────────────────
private void OnFilePickerClicked()
{
#if UNITY_EDITOR
string path = UnityEditor.EditorUtility.OpenFilePanel("음원 파일 선택", "", "mp3");
if (!string.IsNullOrEmpty(path)) CopyToInput(path);
#elif UNITY_STANDALONE_WIN
var t = new Thread(() =>
{
var dlg = new System.Windows.Forms.OpenFileDialog
{
Filter = "MP3 파일|*.mp3",
Title = "음원 파일 선택"
};
if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
_pendingFilePath = dlg.FileName;
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
#else
SetAddStatus($"ADB로 파일을 추가하세요:\n{InputPath}");
#endif
}
private void CopyToInput(string srcPath)
{
try
{
string dest = Path.Combine(InputPath, Path.GetFileName(srcPath));
File.Copy(srcPath, dest, overwrite: true);
RefreshAudioList();
string nameNoExt = Path.GetFileNameWithoutExtension(srcPath);
int idx = audioFiles.FindIndex(f => Path.GetFileNameWithoutExtension(f) == nameNoExt);
if (idx >= 0) audioDropdown.value = idx;
SetAddStatus($"추가됨: {Path.GetFileName(srcPath)}");
}
catch (Exception e)
{
SetAddStatus($"파일 추가 실패: {e.Message}");
}
}
// ── URL 다운로드 ──────────────────────────────────────────
private void OnUrlDownloadClicked()
{
string url = urlInput != null ? urlInput.text.Trim() : "";
if (string.IsNullOrEmpty(url)) { SetAddStatus("URL을 입력해주세요."); return; }
StartCoroutine(DownloadFromUrl(url));
}
private IEnumerator DownloadFromUrl(string url)
{
SetAddStatus("다운로드 중...");
if (urlDownloadBtn != null) urlDownloadBtn.interactable = false;
string fileName;
try
{
string uriPath = new Uri(url).AbsolutePath;
fileName = Path.GetFileName(uriPath);
if (string.IsNullOrEmpty(fileName) || !fileName.EndsWith(".mp3", StringComparison.OrdinalIgnoreCase))
fileName = "download.mp3";
}
catch { fileName = "download.mp3"; }
string savePath = Path.GetFullPath(Path.Combine(InputPath, fileName));
using var req = UnityWebRequest.Get(url);
req.downloadHandler = new DownloadHandlerFile(savePath);
yield return req.SendWebRequest();
if (urlDownloadBtn != null) urlDownloadBtn.interactable = true;
if (req.result == UnityWebRequest.Result.Success)
{
RefreshAudioList();
string nameNoExt = Path.GetFileNameWithoutExtension(fileName);
int idx = audioFiles.FindIndex(f => Path.GetFileNameWithoutExtension(f) == nameNoExt);
if (idx >= 0) audioDropdown.value = idx;
SetAddStatus($"다운로드 완료: {fileName}");
}
else
{
if (File.Exists(savePath)) File.Delete(savePath);
SetAddStatus($"다운로드 실패: {req.error}");
}
}
private void SetAddStatus(string msg)
{
if (addStatusText != null) addStatusText.text = msg;
} }
} }
+9 -5
View File
@@ -13,15 +13,16 @@ public class SongDetailPanel : MonoBehaviour
[SerializeField] private TMP_Text infoText; [SerializeField] private TMP_Text infoText;
[Header("난이도 버튼")] [Header("난이도 버튼")]
[SerializeField] private Button btnEasy;
[SerializeField] private Button btnNormal; [SerializeField] private Button btnNormal;
[SerializeField] private Button btnHard; [SerializeField] private Button btnHard;
[SerializeField] private Button btnExpert; [SerializeField] private Button btnExpert;
[SerializeField] private Button btnExpertPlus;
[Header("액션 버튼")] [Header("액션 버튼")]
[SerializeField] private Button downloadButton; [SerializeField] private Button downloadButton;
[SerializeField] private Button deleteButton; [SerializeField] private Button deleteButton;
[SerializeField] private Button playButton; [SerializeField] private Button playButton;
[SerializeField] private Button closeButton;
[Header("진행률")] [Header("진행률")]
[SerializeField] private GameObject progressGroup; [SerializeField] private GameObject progressGroup;
@@ -41,10 +42,10 @@ public class SongDetailPanel : MonoBehaviour
private readonly (string key, System.Func<SongDetailPanel, Button> btn)[] diffSlots = private readonly (string key, System.Func<SongDetailPanel, Button> btn)[] diffSlots =
{ {
("easy", p => p.btnEasy), ("normal", p => p.btnNormal),
("normal", p => p.btnNormal), ("hard", p => p.btnHard),
("hard", p => p.btnHard), ("expert", p => p.btnExpert),
("expert", p => p.btnExpert), ("expertplus", p => p.btnExpertPlus),
}; };
// ── Public API ─────────────────────────────────────────── // ── Public API ───────────────────────────────────────────
@@ -101,6 +102,9 @@ public class SongDetailPanel : MonoBehaviour
playButton.onClick.RemoveAllListeners(); playButton.onClick.RemoveAllListeners();
playButton.onClick.AddListener(OnPlayClicked); playButton.onClick.AddListener(OnPlayClicked);
closeButton?.onClick.RemoveAllListeners();
closeButton?.onClick.AddListener(() => gameObject.SetActive(false));
} }
private void SelectDifficulty(string difficulty) private void SelectDifficulty(string difficulty)
+66 -6
View File
@@ -2,6 +2,7 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using UnityEngine; using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Networking; using UnityEngine.Networking;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
@@ -21,6 +22,10 @@ public class Spawner : MonoBehaviour
[Header("씬 설정")] [Header("씬 설정")]
public string songSelectSceneName = "SongSelect"; public string songSelectSceneName = "SongSelect";
[Header("뒤로가기 입력")]
[Tooltip("Quest B/Y 버튼을 뒤로가기로 쓸 InputAction (선택)")]
[SerializeField] private InputActionReference backAction;
private List<NoteData> mapNotes = new List<NoteData>(); private List<NoteData> mapNotes = new List<NoteData>();
private int nextNoteIndex = 0; private int nextNoteIndex = 0;
private float travelTime; private float travelTime;
@@ -28,6 +33,16 @@ public class Spawner : MonoBehaviour
// ── Unity ──────────────────────────────────────────────── // ── Unity ────────────────────────────────────────────────
void Awake()
{
// PlayOnAwake가 OnEnable에서 발동하기 전에 차단
if (audioSource != null)
{
audioSource.playOnAwake = false;
audioSource.clip = null;
}
}
void Start() void Start()
{ {
travelTime = distanceToHit / noteSpeed; travelTime = distanceToHit / noteSpeed;
@@ -44,6 +59,10 @@ public class Spawner : MonoBehaviour
void Update() void Update()
{ {
// 뒤로가기: Quest B/Y 버튼 (PC ESC는 DesktopUIMode가 처리)
bool goBack = backAction != null && backAction.action.WasPressedThisFrame();
if (goBack) { SceneManager.LoadScene(songSelectSceneName); return; }
if (!isReady || audioSource == null || !audioSource.isPlaying) return; if (!isReady || audioSource == null || !audioSource.isPlaying) return;
float currentTime = audioSource.time; float currentTime = audioSource.time;
@@ -62,25 +81,66 @@ public class Spawner : MonoBehaviour
SongInfo song = GameSession.SelectedSong; SongInfo song = GameSession.SelectedSong;
string difficulty = GameSession.SelectedDifficulty; string difficulty = GameSession.SelectedDifficulty;
// 맵 JSON 로드 // ── 기본 연결 확인 ───────────────────────────────────────
if (audioSource == null)
{
Debug.LogError("[Spawner] AudioSource 미연결 — Inspector에서 AudioSource 연결 필요");
yield break;
}
if (song == null)
{
Debug.LogError("[Spawner] GameSession.SelectedSong == null — SongSelect에서 곡을 선택하고 Play를 눌러야 합니다");
yield break;
}
if (string.IsNullOrEmpty(difficulty))
{
Debug.LogError("[Spawner] GameSession.SelectedDifficulty 비어있음");
yield break;
}
Debug.Log($"[Spawner] 시작: song={song.title}({song.id}), diff={difficulty}");
// ── 맵 JSON 로드 ─────────────────────────────────────────
string mapPath = GetMapPath(song, difficulty); string mapPath = GetMapPath(song, difficulty);
Debug.Log($"[Spawner] 맵 경로: {mapPath}");
if (string.IsNullOrEmpty(mapPath))
{
Debug.LogError($"[Spawner] songs.json에 '{difficulty}' mapFile 없음 — NAS 재업로드 필요");
yield break;
}
if (!File.Exists(mapPath)) if (!File.Exists(mapPath))
{ {
Debug.LogError($"[Spawner] 맵 파일 없음: {mapPath}"); Debug.LogError($"[Spawner] 맵 파일 없음: {mapPath}\n→ SongSelect에서 다운로드하세요");
yield break; yield break;
} }
LoadMapJson(mapPath); LoadMapJson(mapPath);
// 오디오 로드 (로컬 파일 → AudioClip) // ── 오디오 로드 ──────────────────────────────────────────
audioSource.Stop();
audioSource.clip = null;
string audioPath = GetAudioPath(song.id); string audioPath = GetAudioPath(song.id);
Debug.Log($"[Spawner] 오디오 경로: {audioPath}");
if (!File.Exists(audioPath))
{
Debug.LogError($"[Spawner] 오디오 파일 없음: {audioPath}\n→ SongSelect에서 다운로드하세요");
yield break;
}
yield return LoadAudioClip(audioPath); yield return LoadAudioClip(audioPath);
// LRU 갱신 if (audioSource.clip == null)
SongLibrary.Instance?.TouchSong(song.id); {
Debug.LogError("[Spawner] 오디오 클립 로드 실패 (파일 손상 또는 형식 오류)");
yield break;
}
SongLibrary.Instance?.TouchSong(song.id);
isReady = true; isReady = true;
audioSource.Play(); audioSource.Play();
Debug.Log($"[Spawner] 시작: {song.title} ({difficulty})"); Debug.Log($"[Spawner] 재생 시작: {song.title} / {difficulty} / 노트:{mapNotes.Count}개");
} }
private void LoadMapJson(string path) private void LoadMapJson(string path)
+37
View File
@@ -0,0 +1,37 @@
using UnityEngine;
/// <summary>
/// Quest가 아닌 환경(에디터, PC)에서 XR Interaction Simulator를 자동으로 로드합니다.
///
/// 설정 방법:
/// 1. Intro 씬의 XR Origin (또는 임의 GameObject)에 이 스크립트를 추가
/// 2. SimulatorPrefab 필드에 아래 경로의 프리팹을 연결:
/// Assets/Samples/XR Interaction Toolkit/3.3.1/XR Interaction Simulator/XR Interaction Simulator.prefab
///
/// 조작법 (XR Interaction Simulator):
/// - 마우스 우클릭 드래그 : 머리 방향 회전
/// - G 키 누른 채 마우스 이동 : 오른손 컨트롤러 이동
/// - Shift+G : 왼손 컨트롤러
/// - Space : 트리거(UI 클릭)
/// </summary>
public class XRSimulatorLoader : MonoBehaviour
{
[SerializeField] private GameObject simulatorPrefab;
private void Awake()
{
#if !UNITY_ANDROID || UNITY_EDITOR
if (simulatorPrefab != null)
{
Instantiate(simulatorPrefab);
Debug.Log("[XRSimulatorLoader] XR Interaction Simulator 시작");
}
else
{
Debug.LogWarning("[XRSimulatorLoader] simulatorPrefab이 비어 있습니다.\n" +
"Inspector에서 XR Interaction Simulator.prefab을 연결하세요.\n" +
"경로: Assets/Samples/XR Interaction Toolkit/3.3.1/XR Interaction Simulator/XR Interaction Simulator.prefab");
}
#endif
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7e76ce582f56913438ba761e845a91e0
@@ -1,11 +0,0 @@
*.fbx filter=lfs diff=lfs merge=lfs -text
*.obj filter=lfs diff=lfs merge=lfs -text
*.unity filter=lfs diff=lfs merge=lfs -text
*.prefab filter=lfs diff=lfs merge=lfs -text
*.psd filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text
git filter=lfs diff=lfs merge=lfs -text
lfs filter=lfs diff=lfs merge=lfs -text
track filter=lfs diff=lfs merge=lfs -text
@@ -1,77 +0,0 @@
# Created by https://www.toptal.com/developers/gitignore/api/unity
# Edit at https://www.toptal.com/developers/gitignore?templates=unity
### Unity ###
# This .gitignore file should be placed at the root of your Unity project directory
#
# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore
/[Ll]ibrary/
/[Tt]emp/
/[Oo]bj/
/[Bb]uild/
/[Bb]uilds/
/[Ll]ogs/
/[Uu]ser[Ss]ettings/
# MemoryCaptures can get excessive in size.
# They also could contain extremely sensitive data
/[Mm]emoryCaptures/
# Recordings can get excessive in size
/[Rr]ecordings/
# Uncomment this line if you wish to ignore the asset store tools plugin
# /[Aa]ssets/AssetStoreTools*
# Autogenerated Jetbrains Rider plugin
/[Aa]ssets/Plugins/Editor/JetBrains*
# Visual Studio cache directory
.vs/
# Gradle cache directory
.gradle/
# Autogenerated VS/MD/Consulo solution and project files
ExportedObj/
.consulo/
*.csproj
*.unityproj
*.sln
*.suo
*.tmp
*.user
*.userprefs
*.pidb
*.booproj
*.svd
*.pdb
*.mdb
*.opendb
*.VC.db
# Unity3D generated meta files
*.pidb.meta
*.pdb.meta
*.mdb.meta
# Unity3D generated file on crash reports
sysinfo.txt
# Builds
*.apk
*.aab
*.unitypackage
*.app
# Crashlytics generated file
crashlytics-build.properties
# Packed Addressables
/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin*
# Temporary auto-generated Android Assets
/[Aa]ssets/[Ss]treamingAssets/aa.meta
/[Aa]ssets/[Ss]treamingAssets/aa/*
# End of https://www.toptal.com/developers/gitignore/api/unity
@@ -1,6 +0,0 @@
{
"version": "1.0",
"components": [
"Microsoft.VisualStudio.Workload.ManagedGame"
]
}
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 1cefd1af5fec2454e8b447226a052dfd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,23 +0,0 @@
fileFormatVersion: 2
guid: 8dfafc704a06b3443988a3ad4685e047
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
preloadAudioData: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:
@@ -1,39 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!84 &8400000
RenderTexture:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: RenderTexture360
m_ImageContentsHash:
serializedVersion: 2
Hash: 00000000000000000000000000000000
m_IsAlphaChannelOptional: 0
serializedVersion: 6
m_Width: 1920
m_Height: 1080
m_AntiAliasing: 1
m_MipCount: -1
m_DepthStencilFormat: 94
m_ColorFormat: 8
m_MipMap: 0
m_GenerateMips: 1
m_SRGB: 0
m_UseDynamicScale: 0
m_UseDynamicScaleExplicit: 0
m_BindMS: 0
m_EnableCompatibleFormat: 1
m_EnableRandomWrite: 0
m_TextureSettings:
serializedVersion: 2
m_FilterMode: 1
m_Aniso: 0
m_MipBias: 0
m_WrapU: 1
m_WrapV: 1
m_WrapW: 1
m_Dimension: 2
m_VolumeDepth: 1
m_ShadowSamplingMode: 2
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 8aaa673c3694b3d4b8de8bfe01e0904e
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 8400000
userData:
assetBundleName:
assetBundleVariant:
@@ -1,145 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-8025372093999516296
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: mat360
m_Shader: {fileID: 108, guid: 0000000000000000f000000000000000, type: 0}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords:
- _MAPPING_LATITUDE_LONGITUDE_LAYOUT
- _MIRRORONBACK_ON
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 8400000, guid: 8aaa673c3694b3d4b8de8bfe01e0904e, type: 2}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _Exposure: 1
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _ImageType: 1
- _Layout: 0
- _Mapping: 1
- _Metallic: 0
- _MirrorOnBack: 1
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Rotation: 0
- _Smoothness: 0.5
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
- _Tint: {r: 0.5, g: 0.5, b: 0.5, a: 0.5}
m_BuildTextureStacks: []
m_AllowLocking: 1
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: e20afe50d6646ca4f8b3574c58737d07
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -1,18 +0,0 @@
fileFormatVersion: 2
guid: 50a6dbce0208cc94b886e82263a3ed12
VideoClipImporter:
externalObjects: {}
serializedVersion: 3
frameRange: 0
startFrame: -1
endFrame: -1
colorSpace: 0
deinterlace: 0
encodeAlpha: 0
flipVertical: 0
flipHorizontal: 0
importAudio: 1
targetSettings: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,798 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-7421096243121189777
MonoBehaviour:
m_ObjectHideFlags: 3
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: 81180773991d8724ab7f2d216912b564, type: 3}
m_Name: ChromaticAberration
m_EditorClassIdentifier:
active: 1
intensity:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-7418140390737849241
MonoBehaviour:
m_ObjectHideFlags: 3
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: 29fa0085f50d5e54f8144f766051a691, type: 3}
m_Name: FilmGrain
m_EditorClassIdentifier:
active: 1
type:
m_OverrideState: 1
m_Value: 0
intensity:
m_OverrideState: 1
m_Value: 0
response:
m_OverrideState: 1
m_Value: 0.8
texture:
m_OverrideState: 1
m_Value: {fileID: 0}
--- !u!114 &-6787609107417152939
MonoBehaviour:
m_ObjectHideFlags: 3
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: fb60a22f311433c4c962b888d1393f88, type: 3}
m_Name: PaniniProjection
m_EditorClassIdentifier:
active: 1
distance:
m_OverrideState: 1
m_Value: 0
cropToFit:
m_OverrideState: 1
m_Value: 1
--- !u!114 &-5954177847976826866
MonoBehaviour:
m_ObjectHideFlags: 3
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: 558a8e2b6826cf840aae193990ba9f2e, type: 3}
m_Name: ShadowsMidtonesHighlights
m_EditorClassIdentifier:
active: 1
shadows:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
midtones:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
highlights:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
shadowsStart:
m_OverrideState: 1
m_Value: 0
shadowsEnd:
m_OverrideState: 1
m_Value: 0.3
highlightsStart:
m_OverrideState: 1
m_Value: 0.55
highlightsEnd:
m_OverrideState: 1
m_Value: 1
--- !u!114 &-5844943586150005936
MonoBehaviour:
m_ObjectHideFlags: 3
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: e021b4c809a781e468c2988c016ebbea, type: 3}
m_Name: ColorLookup
m_EditorClassIdentifier:
active: 1
texture:
m_OverrideState: 1
m_Value: {fileID: 0}
dimension: 1
contribution:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-5386571787587349074
MonoBehaviour:
m_ObjectHideFlags: 3
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: 0b2db86121404754db890f4c8dfe81b2, type: 3}
m_Name: Bloom
m_EditorClassIdentifier:
active: 1
skipIterations:
m_OverrideState: 1
m_Value: 1
threshold:
m_OverrideState: 1
m_Value: 0.9
intensity:
m_OverrideState: 1
m_Value: 0
scatter:
m_OverrideState: 1
m_Value: 0.7
clamp:
m_OverrideState: 1
m_Value: 65472
tint:
m_OverrideState: 1
m_Value: {r: 1, g: 1, b: 1, a: 1}
highQualityFiltering:
m_OverrideState: 1
m_Value: 0
filter:
m_OverrideState: 1
m_Value: 0
downscale:
m_OverrideState: 1
m_Value: 0
maxIterations:
m_OverrideState: 1
m_Value: 6
dirtTexture:
m_OverrideState: 1
m_Value: {fileID: 0}
dimension: 1
dirtIntensity:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-1377927621367197308
MonoBehaviour:
m_ObjectHideFlags: 3
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: 221518ef91623a7438a71fef23660601, type: 3}
m_Name: WhiteBalance
m_EditorClassIdentifier:
active: 1
temperature:
m_OverrideState: 1
m_Value: 0
tint:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-1316422313757601441
MonoBehaviour:
m_ObjectHideFlags: 3
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: 899c54efeace73346a0a16faa3afe726, type: 3}
m_Name: Vignette
m_EditorClassIdentifier:
active: 1
color:
m_OverrideState: 1
m_Value: {r: 0, g: 0, b: 0, a: 1}
center:
m_OverrideState: 1
m_Value: {x: 0.5, y: 0.5}
intensity:
m_OverrideState: 1
m_Value: 0
smoothness:
m_OverrideState: 1
m_Value: 0.2
rounded:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-324871269950643110
MonoBehaviour:
m_ObjectHideFlags: 3
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: 06437c1ff663d574d9447842ba0a72e4, type: 3}
m_Name: ScreenSpaceLensFlare
m_EditorClassIdentifier:
active: 1
intensity:
m_OverrideState: 1
m_Value: 0
tintColor:
m_OverrideState: 1
m_Value: {r: 1, g: 1, b: 1, a: 1}
bloomMip:
m_OverrideState: 1
m_Value: 1
firstFlareIntensity:
m_OverrideState: 1
m_Value: 1
secondaryFlareIntensity:
m_OverrideState: 1
m_Value: 1
warpedFlareIntensity:
m_OverrideState: 1
m_Value: 1
warpedFlareScale:
m_OverrideState: 1
m_Value: {x: 1, y: 1}
samples:
m_OverrideState: 1
m_Value: 1
sampleDimmer:
m_OverrideState: 1
m_Value: 0.5
vignetteEffect:
m_OverrideState: 1
m_Value: 1
startingPosition:
m_OverrideState: 1
m_Value: 1.25
scale:
m_OverrideState: 1
m_Value: 1.5
streaksIntensity:
m_OverrideState: 1
m_Value: 0
streaksLength:
m_OverrideState: 1
m_Value: 0.5
streaksOrientation:
m_OverrideState: 1
m_Value: 0
streaksThreshold:
m_OverrideState: 1
m_Value: 0.25
resolution:
m_OverrideState: 1
m_Value: 4
chromaticAbberationIntensity:
m_OverrideState: 1
m_Value: 0.5
--- !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:
components:
- {fileID: 6058122952597297739}
- {fileID: 740159920569565729}
- {fileID: -1377927621367197308}
- {fileID: 620222539856011564}
- {fileID: -7421096243121189777}
- {fileID: 4128515664278618566}
- {fileID: -324871269950643110}
- {fileID: 919598218924102584}
- {fileID: 14229295411216620}
- {fileID: -6787609107417152939}
- {fileID: 9026691863995123148}
- {fileID: -5844943586150005936}
- {fileID: 3598312518371328039}
- {fileID: -5954177847976826866}
- {fileID: -1316422313757601441}
- {fileID: -7418140390737849241}
- {fileID: 1563326871748030085}
- {fileID: -5386571787587349074}
- {fileID: 2609520313565454508}
--- !u!114 &14229295411216620
MonoBehaviour:
m_ObjectHideFlags: 3
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: 5485954d14dfb9a4c8ead8edb0ded5b1, type: 3}
m_Name: LiftGammaGain
m_EditorClassIdentifier:
active: 1
lift:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
gamma:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
gain:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
--- !u!114 &620222539856011564
MonoBehaviour:
m_ObjectHideFlags: 3
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: c5e1dc532bcb41949b58bc4f2abfbb7e, type: 3}
m_Name: LensDistortion
m_EditorClassIdentifier:
active: 1
intensity:
m_OverrideState: 1
m_Value: 0
xMultiplier:
m_OverrideState: 1
m_Value: 1
yMultiplier:
m_OverrideState: 1
m_Value: 1
center:
m_OverrideState: 1
m_Value: {x: 0.5, y: 0.5}
scale:
m_OverrideState: 1
m_Value: 1
--- !u!114 &740159920569565729
MonoBehaviour:
m_ObjectHideFlags: 3
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: ccf1aba9553839d41ae37dd52e9ebcce, type: 3}
m_Name: MotionBlur
m_EditorClassIdentifier:
active: 1
mode:
m_OverrideState: 1
m_Value: 0
quality:
m_OverrideState: 1
m_Value: 0
intensity:
m_OverrideState: 1
m_Value: 0
clamp:
m_OverrideState: 1
m_Value: 0.05
--- !u!114 &919598218924102584
MonoBehaviour:
m_ObjectHideFlags: 3
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: c01700fd266d6914ababb731e09af2eb, type: 3}
m_Name: DepthOfField
m_EditorClassIdentifier:
active: 1
mode:
m_OverrideState: 1
m_Value: 0
gaussianStart:
m_OverrideState: 1
m_Value: 10
gaussianEnd:
m_OverrideState: 1
m_Value: 30
gaussianMaxRadius:
m_OverrideState: 1
m_Value: 1
highQualitySampling:
m_OverrideState: 1
m_Value: 0
focusDistance:
m_OverrideState: 1
m_Value: 10
aperture:
m_OverrideState: 1
m_Value: 5.6
focalLength:
m_OverrideState: 1
m_Value: 50
bladeCount:
m_OverrideState: 1
m_Value: 5
bladeCurvature:
m_OverrideState: 1
m_Value: 1
bladeRotation:
m_OverrideState: 1
m_Value: 0
--- !u!114 &1563326871748030085
MonoBehaviour:
m_ObjectHideFlags: 3
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: 3eb4b772797da9440885e8bd939e9560, type: 3}
m_Name: ColorCurves
m_EditorClassIdentifier:
active: 1
master:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 2
m_Loop: 0
m_ZeroValue: 0
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
red:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 2
m_Loop: 0
m_ZeroValue: 0
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
green:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 2
m_Loop: 0
m_ZeroValue: 0
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
blue:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 2
m_Loop: 0
m_ZeroValue: 0
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
hueVsHue:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 0
m_Loop: 1
m_ZeroValue: 0.5
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
hueVsSat:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 0
m_Loop: 1
m_ZeroValue: 0.5
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
satVsSat:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 0
m_Loop: 0
m_ZeroValue: 0.5
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
lumVsSat:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 0
m_Loop: 0
m_ZeroValue: 0.5
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
--- !u!114 &2609520313565454508
MonoBehaviour:
m_ObjectHideFlags: 3
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: 6bd486065ce11414fa40e631affc4900, type: 3}
m_Name: ProbeVolumesOptions
m_EditorClassIdentifier:
active: 1
normalBias:
m_OverrideState: 1
m_Value: 0.33
viewBias:
m_OverrideState: 1
m_Value: 0
scaleBiasWithMinProbeDistance:
m_OverrideState: 1
m_Value: 0
samplingNoise:
m_OverrideState: 1
m_Value: 0.1
animateSamplingNoise:
m_OverrideState: 1
m_Value: 1
leakReductionMode:
m_OverrideState: 1
m_Value: 2
minValidDotProductValue:
m_OverrideState: 1
m_Value: 0.1
occlusionOnlyReflectionNormalization:
m_OverrideState: 1
m_Value: 1
intensityMultiplier:
m_OverrideState: 1
m_Value: 1
skyOcclusionIntensityMultiplier:
m_OverrideState: 1
m_Value: 1
worldOffset:
m_OverrideState: 1
m_Value: {x: 0, y: 0, z: 0}
--- !u!114 &3598312518371328039
MonoBehaviour:
m_ObjectHideFlags: 3
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: cdfbdbb87d3286943a057f7791b43141, type: 3}
m_Name: ChannelMixer
m_EditorClassIdentifier:
active: 1
redOutRedIn:
m_OverrideState: 1
m_Value: 100
redOutGreenIn:
m_OverrideState: 1
m_Value: 0
redOutBlueIn:
m_OverrideState: 1
m_Value: 0
greenOutRedIn:
m_OverrideState: 1
m_Value: 0
greenOutGreenIn:
m_OverrideState: 1
m_Value: 100
greenOutBlueIn:
m_OverrideState: 1
m_Value: 0
blueOutRedIn:
m_OverrideState: 1
m_Value: 0
blueOutGreenIn:
m_OverrideState: 1
m_Value: 0
blueOutBlueIn:
m_OverrideState: 1
m_Value: 100
--- !u!114 &4128515664278618566
MonoBehaviour:
m_ObjectHideFlags: 3
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: 66f335fb1ffd8684294ad653bf1c7564, type: 3}
m_Name: ColorAdjustments
m_EditorClassIdentifier:
active: 1
postExposure:
m_OverrideState: 1
m_Value: 0
contrast:
m_OverrideState: 1
m_Value: 0
colorFilter:
m_OverrideState: 1
m_Value: {r: 1, g: 1, b: 1, a: 1}
hueShift:
m_OverrideState: 1
m_Value: 0
saturation:
m_OverrideState: 1
m_Value: 0
--- !u!114 &6058122952597297739
MonoBehaviour:
m_ObjectHideFlags: 3
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: 97c23e3b12dc18c42a140437e53d3951, type: 3}
m_Name: Tonemapping
m_EditorClassIdentifier:
active: 1
mode:
m_OverrideState: 1
m_Value: 0
neutralHDRRangeReductionMode:
m_OverrideState: 1
m_Value: 2
acesPreset:
m_OverrideState: 1
m_Value: 3
hueShiftAmount:
m_OverrideState: 1
m_Value: 0
detectPaperWhite:
m_OverrideState: 1
m_Value: 0
paperWhite:
m_OverrideState: 1
m_Value: 300
detectBrightnessLimits:
m_OverrideState: 1
m_Value: 1
minNits:
m_OverrideState: 1
m_Value: 0.005
maxNits:
m_OverrideState: 1
m_Value: 1000
--- !u!114 &9026691863995123148
MonoBehaviour:
m_ObjectHideFlags: 3
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: 70afe9e12c7a7ed47911bb608a23a8ff, type: 3}
m_Name: SplitToning
m_EditorClassIdentifier:
active: 1
shadows:
m_OverrideState: 1
m_Value: {r: 0.5, g: 0.5, b: 0.5, a: 1}
highlights:
m_OverrideState: 1
m_Value: {r: 0.5, g: 0.5, b: 0.5, a: 1}
balance:
m_OverrideState: 1
m_Value: 0
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: b6d8050cecbe75e4c8b3445d5ccd33ff
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: fe48604fef3073d4f8b57b3c0d0f1f0b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,137 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: CrossSectionMatB
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _Metallic: 0
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0.5
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0.45597476, g: 0.50431424, b: 1, a: 1}
- _Color: {r: 0.4559747, g: 0.50431424, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &3490122771063610102
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: c7d47ebf024a67d46a66640e09f82669
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -1,137 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: CrossSectionMatR
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _Metallic: 0
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0.5
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 0.46855342, b: 0.46855342, a: 1}
- _Color: {r: 1, g: 0.46855342, b: 0.46855342, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &3490122771063610102
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 791c5d8c7b8a4b54ebbe04a3670da951
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -1,137 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: matBlack
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _Metallic: 0
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0.5
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0.006289184, g: 0.006289184, b: 0.006289184, a: 1}
- _Color: {r: 0.006289184, g: 0.006289184, b: 0.006289184, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &5396700846410337812
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 3d8af250f1297084bb70a700df0dbc78
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -1,136 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: matBlue
m_Shader: {fileID: 10755, guid: 0000000000000000f000000000000000, type: 0}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _Metallic: 0
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0.5
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0, g: 0.08285145, b: 0.77254903, a: 1}
- _Color: {r: 0, g: 0.08285142, b: 0.77254903, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &3082830731586131883
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: feae95ea31c159b4f8bbef43d83faac0
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -1,47 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: matRed
m_Shader: {fileID: 10755, guid: 0000000000000000f000000000000000, type: 0}
m_Parent: {fileID: 2100000, guid: feae95ea31c159b4f8bbef43d83faac0, type: 2}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs: []
m_Ints: []
m_Floats: []
m_Colors:
- _BaseColor: {r: 0.7735849, g: 0, b: 0, a: 1}
- _Color: {r: 0.7735849, g: 0, b: 0, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &3082830731586131883
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 7ccf81800bf470f45a1fcbd91bddb4ea
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: c765c72154ad01f4b800f670ce87a334
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,23 +0,0 @@
fileFormatVersion: 2
guid: 98ab42040bcda63498e744197fc4f103
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
preloadAudioData: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: c606763f559f76f47a58476a80a18c45
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 742911e0277bf4244a520d287b4beec1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: f73dc255f4b6d6e44a12b0efbdb32e76
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,150 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace EzySlice {
/**
* A Basic Structure which contains intersection information
* for Plane->Triangle Intersection Tests
* TO-DO -> This structure can be optimized to hold less data
* via an optional indices array. Could lead for a faster
* intersection test aswell.
*/
public sealed class IntersectionResult {
// general tag to check if this structure is valid
private bool is_success;
// our intersection points/triangles
private readonly Triangle[] upper_hull;
private readonly Triangle[] lower_hull;
private readonly Vector3[] intersection_pt;
// our counters. We use raw arrays for performance reasons
private int upper_hull_count;
private int lower_hull_count;
private int intersection_pt_count;
public IntersectionResult() {
this.is_success = false;
this.upper_hull = new Triangle[2];
this.lower_hull = new Triangle[2];
this.intersection_pt = new Vector3[2];
this.upper_hull_count = 0;
this.lower_hull_count = 0;
this.intersection_pt_count = 0;
}
public Triangle[] upperHull {
get { return upper_hull; }
}
public Triangle[] lowerHull {
get { return lower_hull; }
}
public Vector3[] intersectionPoints {
get { return intersection_pt; }
}
public int upperHullCount {
get { return upper_hull_count; }
}
public int lowerHullCount {
get { return lower_hull_count; }
}
public int intersectionPointCount {
get { return intersection_pt_count; }
}
public bool isValid {
get { return is_success; }
}
/**
* Used by the intersector, adds a new triangle to the
* upper hull section
*/
public IntersectionResult AddUpperHull(Triangle tri) {
upper_hull[upper_hull_count++] = tri;
is_success = true;
return this;
}
/**
* Used by the intersector, adds a new triangle to the
* lower gull section
*/
public IntersectionResult AddLowerHull(Triangle tri) {
lower_hull[lower_hull_count++] = tri;
is_success = true;
return this;
}
/**
* Used by the intersector, adds a new intersection point
* which is shared by both upper->lower hulls
*/
public void AddIntersectionPoint(Vector3 pt) {
intersection_pt[intersection_pt_count++] = pt;
}
/**
* Clear the current state of this object
*/
public void Clear() {
is_success = false;
upper_hull_count = 0;
lower_hull_count = 0;
intersection_pt_count = 0;
}
/**
* Editor only DEBUG functionality. This should not be compiled in the final
* Version.
*/
public void OnDebugDraw() {
OnDebugDraw(Color.white);
}
public void OnDebugDraw(Color drawColor) {
#if UNITY_EDITOR
if (!isValid) {
return;
}
Color prevColor = Gizmos.color;
Gizmos.color = drawColor;
// draw the intersection points
for (int i = 0; i < intersectionPointCount; i++) {
Gizmos.DrawSphere(intersectionPoints[i], 0.1f);
}
// draw the upper hull in RED
for (int i = 0; i < upperHullCount; i++) {
upperHull[i].OnDebugDraw(Color.red);
}
// draw the lower hull in BLUE
for (int i = 0; i < lowerHullCount; i++) {
lowerHull[i].OnDebugDraw(Color.blue);
}
Gizmos.color = prevColor;
#endif
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 88a3861524ef7d941b29cb3825c06ddc
@@ -1,491 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace EzySlice {
/**
* Contains static functionality to perform geometric intersection tests.
*/
public sealed class Intersector {
/**
* Perform an intersection between Plane and Line, storing intersection point
* in reference q. Function returns true if intersection has been found or
* false otherwise.
*/
public static bool Intersect(Plane pl, Line ln, out Vector3 q) {
return Intersector.Intersect(pl, ln.positionA, ln.positionB, out q);
}
public const float Epsilon = 0.0001f;
/**
* Perform an intersection between Plane and Line made up of points a and b. Intersection
* point will be stored in reference q. Function returns true if intersection has been
* found or false otherwise.
*/
public static bool Intersect(Plane pl, Vector3 a, Vector3 b, out Vector3 q) {
Vector3 normal = pl.normal;
Vector3 ab = b - a;
float t = (pl.dist - Vector3.Dot(normal, a)) / Vector3.Dot(normal, ab);
// need to be careful and compensate for floating errors
if (t >= -Epsilon && t <= (1 + Epsilon)) {
q = a + t * ab;
return true;
}
q = Vector3.zero;
return false;
}
/**
* Support functionality
*/
public static float TriArea2D(float x1, float y1, float x2, float y2, float x3, float y3) {
return (x1 - x2) * (y2 - y3) - (x2 - x3) * (y1 - y2);
}
/**
* Perform an intersection between Plane and Triangle. This is a comprehensive function
* which alwo builds a HULL Hirearchy useful for decimation projects. This obviously
* comes at the cost of more complex code and runtime checks, but the returned results
* are much more flexible.
* Results will be filled into the IntersectionResult reference. Check result.isValid()
* for the final results.
*/
public static void Intersect(Plane pl, Triangle tri, IntersectionResult result) {
// clear the previous results from the IntersectionResult
result.Clear();
// grab local variables for easier access
Vector3 a = tri.positionA;
Vector3 b = tri.positionB;
Vector3 c = tri.positionC;
// check to see which side of the plane the points all
// lay in. SideOf operation is a simple dot product and some comparison
// operations, so these are a very quick checks
SideOfPlane sa = pl.SideOf(a);
SideOfPlane sb = pl.SideOf(b);
SideOfPlane sc = pl.SideOf(c);
// we cannot intersect if the triangle points all fall on the same side
// of the plane. This is an easy early out test as no intersections are possible.
if (sa == sb && sb == sc) {
return;
}
// detect cases where two points lay straight on the plane, meaning
// that the plane is actually parralel with one of the edges of the triangle
else if ((sa == SideOfPlane.ON && sa == sb) ||
(sa == SideOfPlane.ON && sa == sc) ||
(sb == SideOfPlane.ON && sb == sc)) {
return;
}
// detect cases where one point is on the plane and the other two are on the same side
else if ((sa == SideOfPlane.ON && sb != SideOfPlane.ON && sb == sc) ||
(sb == SideOfPlane.ON && sa != SideOfPlane.ON && sa == sc) ||
(sc == SideOfPlane.ON && sa != SideOfPlane.ON && sa == sb)) {
return;
}
// keep in mind that intersection points are shared by both
// the upper HULL and lower HULL hence they lie perfectly
// on the plane that cut them
Vector3 qa;
Vector3 qb;
// check the cases where the points of the triangle actually lie on the plane itself
// in these cases, there is only going to be 2 triangles, one for the upper HULL and
// the other on the lower HULL
// we just need to figure out which points to accept into the upper or lower hulls.
if (sa == SideOfPlane.ON) {
// if the point a is on the plane, test line b-c
if (Intersector.Intersect(pl, b, c, out qa)) {
// line b-c intersected, construct out triangles and return approprietly
result.AddIntersectionPoint(qa);
result.AddIntersectionPoint(a);
// our two generated triangles, we need to figure out which
// triangle goes into the UPPER hull and which goes into the LOWER hull
Triangle ta = new Triangle(a, b, qa);
Triangle tb = new Triangle(a, qa, c);
// generate UV coordinates if there is any
if (tri.hasUV) {
// the computed UV coordinate if the intersection point
Vector2 pq = tri.GenerateUV(qa);
Vector2 pa = tri.uvA;
Vector2 pb = tri.uvB;
Vector2 pc = tri.uvC;
ta.SetUV(pa, pb, pq);
tb.SetUV(pa, pq, pc);
}
// generate Normal coordinates if there is any
if (tri.hasNormal) {
// the computed Normal coordinate if the intersection point
Vector3 pq = tri.GenerateNormal(qa);
Vector3 pa = tri.normalA;
Vector3 pb = tri.normalB;
Vector3 pc = tri.normalC;
ta.SetNormal(pa, pb, pq);
tb.SetNormal(pa, pq, pc);
}
// generate Tangent coordinates if there is any
if (tri.hasTangent) {
// the computed Tangent coordinate if the intersection point
Vector4 pq = tri.GenerateTangent(qa);
Vector4 pa = tri.tangentA;
Vector4 pb = tri.tangentB;
Vector4 pc = tri.tangentC;
ta.SetTangent(pa, pb, pq);
tb.SetTangent(pa, pq, pc);
}
// b point lies on the upside of the plane
if (sb == SideOfPlane.UP) {
result.AddUpperHull(ta).AddLowerHull(tb);
}
// b point lies on the downside of the plane
else if (sb == SideOfPlane.DOWN) {
result.AddUpperHull(tb).AddLowerHull(ta);
}
}
}
// test the case where the b point lies on the plane itself
else if (sb == SideOfPlane.ON) {
// if the point b is on the plane, test line a-c
if (Intersector.Intersect(pl, a, c, out qa)) {
// line a-c intersected, construct out triangles and return approprietly
result.AddIntersectionPoint(qa);
result.AddIntersectionPoint(b);
// our two generated triangles, we need to figure out which
// triangle goes into the UPPER hull and which goes into the LOWER hull
Triangle ta = new Triangle(a, b, qa);
Triangle tb = new Triangle(qa, b, c);
// generate UV coordinates if there is any
if (tri.hasUV) {
// the computed UV coordinate if the intersection point
Vector2 pq = tri.GenerateUV(qa);
Vector2 pa = tri.uvA;
Vector2 pb = tri.uvB;
Vector2 pc = tri.uvC;
ta.SetUV(pa, pb, pq);
tb.SetUV(pq, pb, pc);
}
// generate Normal coordinates if there is any
if (tri.hasNormal) {
// the computed Normal coordinate if the intersection point
Vector3 pq = tri.GenerateNormal(qa);
Vector3 pa = tri.normalA;
Vector3 pb = tri.normalB;
Vector3 pc = tri.normalC;
ta.SetNormal(pa, pb, pq);
tb.SetNormal(pq, pb, pc);
}
// generate Tangent coordinates if there is any
if (tri.hasTangent) {
// the computed Tangent coordinate if the intersection point
Vector4 pq = tri.GenerateTangent(qa);
Vector4 pa = tri.tangentA;
Vector4 pb = tri.tangentB;
Vector4 pc = tri.tangentC;
ta.SetTangent(pa, pb, pq);
tb.SetTangent(pq, pb, pc);
}
// a point lies on the upside of the plane
if (sa == SideOfPlane.UP) {
result.AddUpperHull(ta).AddLowerHull(tb);
}
// a point lies on the downside of the plane
else if (sa == SideOfPlane.DOWN) {
result.AddUpperHull(tb).AddLowerHull(ta);
}
}
}
// test the case where the c point lies on the plane itself
else if (sc == SideOfPlane.ON) {
// if the point c is on the plane, test line a-b
if (Intersector.Intersect(pl, a, b, out qa)) {
// line a-c intersected, construct out triangles and return approprietly
result.AddIntersectionPoint(qa);
result.AddIntersectionPoint(c);
// our two generated triangles, we need to figure out which
// triangle goes into the UPPER hull and which goes into the LOWER hull
Triangle ta = new Triangle(a, qa, c);
Triangle tb = new Triangle(qa, b, c);
// generate UV coordinates if there is any
if (tri.hasUV) {
// the computed UV coordinate if the intersection point
Vector2 pq = tri.GenerateUV(qa);
Vector2 pa = tri.uvA;
Vector2 pb = tri.uvB;
Vector2 pc = tri.uvC;
ta.SetUV(pa, pq, pc);
tb.SetUV(pq, pb, pc);
}
// generate Normal coordinates if there is any
if (tri.hasNormal) {
// the computed Normal coordinate if the intersection point
Vector3 pq = tri.GenerateNormal(qa);
Vector3 pa = tri.normalA;
Vector3 pb = tri.normalB;
Vector3 pc = tri.normalC;
ta.SetNormal(pa, pq, pc);
tb.SetNormal(pq, pb, pc);
}
// generate Tangent coordinates if there is any
if (tri.hasTangent) {
// the computed Tangent coordinate if the intersection point
Vector4 pq = tri.GenerateTangent(qa);
Vector4 pa = tri.tangentA;
Vector4 pb = tri.tangentB;
Vector4 pc = tri.tangentC;
ta.SetTangent(pa, pq, pc);
tb.SetTangent(pq, pb, pc);
}
// a point lies on the upside of the plane
if (sa == SideOfPlane.UP) {
result.AddUpperHull(ta).AddLowerHull(tb);
}
// a point lies on the downside of the plane
else if (sa == SideOfPlane.DOWN) {
result.AddUpperHull(tb).AddLowerHull(ta);
}
}
}
// at this point, all edge cases have been tested and failed, we need to perform
// full intersection tests against the lines. From this point onwards we will generate
// 3 triangles
else if (sa != sb && Intersector.Intersect(pl, a, b, out qa)) {
// intersection found against a - b
result.AddIntersectionPoint(qa);
// since intersection was found against a - b, we need to check which other
// lines to check (we only need to check one more line) for intersection.
// the line we check against will be the line against the point which lies on
// the other side of the plane.
if (sa == sc) {
// we likely have an intersection against line b-c which will complete this loop
if (Intersector.Intersect(pl, b, c, out qb)) {
result.AddIntersectionPoint(qb);
// our three generated triangles. Two of these triangles will end
// up on either the UPPER or LOWER hulls.
Triangle ta = new Triangle(qa, b, qb);
Triangle tb = new Triangle(a, qa, qb);
Triangle tc = new Triangle(a, qb, c);
// generate UV coordinates if there is any
if (tri.hasUV) {
// the computed UV coordinate if the intersection point
Vector2 pqa = tri.GenerateUV(qa);
Vector2 pqb = tri.GenerateUV(qb);
Vector2 pa = tri.uvA;
Vector2 pb = tri.uvB;
Vector2 pc = tri.uvC;
ta.SetUV(pqa, pb, pqb);
tb.SetUV(pa, pqa, pqb);
tc.SetUV(pa, pqb, pc);
}
// generate Normal coordinates if there is any
if (tri.hasNormal) {
// the computed Normal coordinate if the intersection point
Vector3 pqa = tri.GenerateNormal(qa);
Vector3 pqb = tri.GenerateNormal(qb);
Vector3 pa = tri.normalA;
Vector3 pb = tri.normalB;
Vector3 pc = tri.normalC;
ta.SetNormal(pqa, pb, pqb);
tb.SetNormal(pa, pqa, pqb);
tc.SetNormal(pa, pqb, pc);
}
// generate Tangent coordinates if there is any
if (tri.hasTangent) {
// the computed Tangent coordinate if the intersection point
Vector4 pqa = tri.GenerateTangent(qa);
Vector4 pqb = tri.GenerateTangent(qb);
Vector4 pa = tri.tangentA;
Vector4 pb = tri.tangentB;
Vector4 pc = tri.tangentC;
ta.SetTangent(pqa, pb, pqb);
tb.SetTangent(pa, pqa, pqb);
tc.SetTangent(pa, pqb, pc);
}
if (sa == SideOfPlane.UP) {
result.AddUpperHull(tb).AddUpperHull(tc).AddLowerHull(ta);
} else {
result.AddLowerHull(tb).AddLowerHull(tc).AddUpperHull(ta);
}
}
} else {
// in this scenario, the point a is a "lone" point which lies in either upper
// or lower HULL. We need to perform another intersection to find the last point
if (Intersector.Intersect(pl, a, c, out qb)) {
result.AddIntersectionPoint(qb);
// our three generated triangles. Two of these triangles will end
// up on either the UPPER or LOWER hulls.
Triangle ta = new Triangle(a, qa, qb);
Triangle tb = new Triangle(qa, b, c);
Triangle tc = new Triangle(qb, qa, c);
// generate UV coordinates if there is any
if (tri.hasUV) {
// the computed UV coordinate if the intersection point
Vector2 pqa = tri.GenerateUV(qa);
Vector2 pqb = tri.GenerateUV(qb);
Vector2 pa = tri.uvA;
Vector2 pb = tri.uvB;
Vector2 pc = tri.uvC;
ta.SetUV(pa, pqa, pqb);
tb.SetUV(pqa, pb, pc);
tc.SetUV(pqb, pqa, pc);
}
// generate Normal coordinates if there is any
if (tri.hasNormal) {
// the computed Normal coordinate if the intersection point
Vector3 pqa = tri.GenerateNormal(qa);
Vector3 pqb = tri.GenerateNormal(qb);
Vector3 pa = tri.normalA;
Vector3 pb = tri.normalB;
Vector3 pc = tri.normalC;
ta.SetNormal(pa, pqa, pqb);
tb.SetNormal(pqa, pb, pc);
tc.SetNormal(pqb, pqa, pc);
}
// generate Tangent coordinates if there is any
if (tri.hasTangent) {
// the computed Tangent coordinate if the intersection point
Vector4 pqa = tri.GenerateTangent(qa);
Vector4 pqb = tri.GenerateTangent(qb);
Vector4 pa = tri.tangentA;
Vector4 pb = tri.tangentB;
Vector4 pc = tri.tangentC;
ta.SetTangent(pa, pqa, pqb);
tb.SetTangent(pqa, pb, pc);
tc.SetTangent(pqb, pqa, pc);
}
if (sa == SideOfPlane.UP) {
result.AddUpperHull(ta).AddLowerHull(tb).AddLowerHull(tc);
} else {
result.AddLowerHull(ta).AddUpperHull(tb).AddUpperHull(tc);
}
}
}
}
// if line a-b did not intersect (or the lie on the same side of the plane)
// this simplifies the problem a fair bit. This means we have an intersection
// in line a-c and b-c, which we can use to build a new UPPER and LOWER hulls
// we are expecting both of these intersection tests to pass, otherwise something
// went wrong (float errors? missed a checked case?)
else if (Intersector.Intersect(pl, c, a, out qa) && Intersector.Intersect(pl, c, b, out qb)) {
// in here we know that line a-b actually lie on the same side of the plane, this will
// simplify the rest of the logic. We also have our intersection points
// the computed UV coordinate of the intersection point
result.AddIntersectionPoint(qa);
result.AddIntersectionPoint(qb);
// our three generated triangles. Two of these triangles will end
// up on either the UPPER or LOWER hulls.
Triangle ta = new Triangle(qa, qb, c);
Triangle tb = new Triangle(a, qb, qa);
Triangle tc = new Triangle(a, b, qb);
// generate UV coordinates if there is any
if (tri.hasUV) {
// the computed UV coordinate if the intersection point
Vector2 pqa = tri.GenerateUV(qa);
Vector2 pqb = tri.GenerateUV(qb);
Vector2 pa = tri.uvA;
Vector2 pb = tri.uvB;
Vector2 pc = tri.uvC;
ta.SetUV(pqa, pqb, pc);
tb.SetUV(pa, pqb, pqa);
tc.SetUV(pa, pb, pqb);
}
// generate Normal coordinates if there is any
if (tri.hasNormal) {
// the computed Normal coordinate if the intersection point
Vector3 pqa = tri.GenerateNormal(qa);
Vector3 pqb = tri.GenerateNormal(qb);
Vector3 pa = tri.normalA;
Vector3 pb = tri.normalB;
Vector3 pc = tri.normalC;
ta.SetNormal(pqa, pqb, pc);
tb.SetNormal(pa, pqb, pqa);
tc.SetNormal(pa, pb, pqb);
}
// generate Tangent coordinates if there is any
if (tri.hasTangent) {
// the computed Tangent coordinate if the intersection point
Vector4 pqa = tri.GenerateTangent(qa);
Vector4 pqb = tri.GenerateTangent(qb);
Vector4 pa = tri.tangentA;
Vector4 pb = tri.tangentB;
Vector4 pc = tri.tangentC;
ta.SetTangent(pqa, pqb, pc);
tb.SetTangent(pa, pqb, pqa);
tc.SetTangent(pa, pb, pqb);
}
if (sa == SideOfPlane.UP) {
result.AddUpperHull(tb).AddUpperHull(tc).AddLowerHull(ta);
} else {
result.AddLowerHull(tb).AddLowerHull(tc).AddUpperHull(ta);
}
}
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 1ec78da1598b7d4499fe97c9ce3aa8ca
@@ -1,31 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace EzySlice {
public struct Line {
private readonly Vector3 m_pos_a;
private readonly Vector3 m_pos_b;
public Line(Vector3 pta, Vector3 ptb) {
this.m_pos_a = pta;
this.m_pos_b = ptb;
}
public float dist {
get { return Vector3.Distance(this.m_pos_a, this.m_pos_b); }
}
public float distSq {
get { return (this.m_pos_a - this.m_pos_b).sqrMagnitude; }
}
public Vector3 positionA {
get { return this.m_pos_a; }
}
public Vector3 positionB {
get { return this.m_pos_b; }
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 9e369e5c7f1fe2e42942f36dcd180f71
@@ -1,141 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace EzySlice {
/**
* Quick Internal structure which checks where the point lays on the
* Plane. UP = Upwards from the Normal, DOWN = Downwards from the Normal
* ON = Point lays straight on the plane
*/
public enum SideOfPlane {
UP,
DOWN,
ON
}
/**
* Represents a simple 3D Plane structure with a position
* and direction which extends infinitely in its axis. This provides
* an optimal structure for collision tests for the slicing framework.
*/
public struct Plane {
private Vector3 m_normal;
private float m_dist;
// this is for editor debugging only! do NOT try to access this
// variable at runtime, we will be stripping it out for final
// builds
#if UNITY_EDITOR
private Transform trans_ref;
#endif
public Plane(Vector3 pos, Vector3 norm) {
this.m_normal = norm;
this.m_dist = Vector3.Dot(norm, pos);
// this is for editor debugging only!
#if UNITY_EDITOR
trans_ref = null;
#endif
}
public Plane(Vector3 norm, float dot) {
this.m_normal = norm;
this.m_dist = dot;
// this is for editor debugging only!
#if UNITY_EDITOR
trans_ref = null;
#endif
}
public Plane(Vector3 a, Vector3 b, Vector3 c) {
m_normal = Vector3.Normalize(Vector3.Cross(b - a, c - a));
m_dist = -Vector3.Dot(m_normal, a);
// this is for editor debugging only!
#if UNITY_EDITOR
trans_ref = null;
#endif
}
public void Compute(Vector3 pos, Vector3 norm) {
this.m_normal = norm;
this.m_dist = Vector3.Dot(norm, pos);
}
public void Compute(Transform trans) {
Compute(trans.position, trans.up);
// this is for editor debugging only!
#if UNITY_EDITOR
trans_ref = trans;
#endif
}
public void Compute(GameObject obj) {
Compute(obj.transform);
}
public Vector3 normal {
get { return this.m_normal; }
}
public float dist {
get { return this.m_dist; }
}
/**
* Checks which side of the plane the point lays on.
*/
public SideOfPlane SideOf(Vector3 pt) {
float result = Vector3.Dot(m_normal, pt) - m_dist;
if (result > Intersector.Epsilon) {
return SideOfPlane.UP;
}
if (result < -Intersector.Epsilon) {
return SideOfPlane.DOWN;
}
return SideOfPlane.ON;
}
/**
* Editor only DEBUG functionality. This should not be compiled in the final
* Version.
*/
public void OnDebugDraw() {
OnDebugDraw(Color.white);
}
public void OnDebugDraw(Color drawColor) {
// NOTE -> Gizmos are only supported in the editor. We will keep these function
// signatures for consistancy however at final build, these will do nothing
// TO/DO -> Should we throw a runtime exception if this function tried to get executed
// at runtime?
#if UNITY_EDITOR
if (trans_ref == null) {
return;
}
Color prevColor = Gizmos.color;
Matrix4x4 prevMatrix = Gizmos.matrix;
// TO-DO
Gizmos.matrix = Matrix4x4.TRS(trans_ref.position, trans_ref.rotation, trans_ref.localScale);
Gizmos.color = drawColor;
Gizmos.DrawWireCube(Vector3.zero, new Vector3(1.0f, 0.0f, 1.0f));
Gizmos.color = prevColor;
Gizmos.matrix = prevMatrix;
#endif
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: a22a1d43b52627b48b3c98d8d322d36e
@@ -1,115 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace EzySlice {
/**
* TextureRegion defines a region of a specific texture which can be used
* for custom UV Mapping Routines.
*
* TextureRegions are always stored in normalized UV Coordinate space between
* 0.0f and 1.0f
*/
public struct TextureRegion {
private readonly float pos_start_x;
private readonly float pos_start_y;
private readonly float pos_end_x;
private readonly float pos_end_y;
public TextureRegion(float startX, float startY, float endX, float endY) {
this.pos_start_x = startX;
this.pos_start_y = startY;
this.pos_end_x = endX;
this.pos_end_y = endY;
}
public float startX { get { return this.pos_start_x; } }
public float startY { get { return this.pos_start_y; } }
public float endX { get { return this.pos_end_x; } }
public float endY { get { return this.pos_end_y; } }
public Vector2 start { get { return new Vector2(startX, startY); } }
public Vector2 end { get { return new Vector2(endX, endY); } }
/**
* Perform a mapping of a UV coordinate (computed in 0,1 space)
* into the new coordinates defined by the provided TextureRegion
*/
public Vector2 Map(Vector2 uv) {
return Map(uv.x, uv.y);
}
/**
* Perform a mapping of a UV coordinate (computed in 0,1 space)
* into the new coordinates defined by the provided TextureRegion
*/
public Vector2 Map(float x, float y) {
float mappedX = MAP(x, 0.0f, 1.0f, pos_start_x, pos_end_x);
float mappedY = MAP(y, 0.0f, 1.0f, pos_start_y, pos_end_y);
return new Vector2(mappedX, mappedY);
}
/**
* Our mapping function to map arbitrary values into our required texture region
*/
private static float MAP(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
}
/**
* Define our TextureRegion extension to easily calculate
* from a Texture2D Object.
*/
public static class TextureRegionExtension {
/**
* Helper function to quickly calculate the Texture Region from a material.
* This extension function will use the mainTexture component to perform the
* calculation.
*
* Will throw a null exception if the texture does not exist. See
* Texture.getTextureRegion() for function details.
*/
public static TextureRegion GetTextureRegion(this Material mat,
int pixX,
int pixY,
int pixWidth,
int pixHeight) {
return mat.mainTexture.GetTextureRegion(pixX, pixY, pixWidth, pixHeight);
}
/**
* Using a Texture2D, calculate and return a specific TextureRegion
* Coordinates are provided in pixel coordinates where 0,0 is the
* bottom left corner of the texture.
*
* The texture region will automatically be calculated to ensure that it
* will fit inside the provided texture.
*/
public static TextureRegion GetTextureRegion(this Texture tex,
int pixX,
int pixY,
int pixWidth,
int pixHeight) {
int textureWidth = tex.width;
int textureHeight = tex.height;
// ensure we are not referencing out of bounds coordinates
// relative to our texture
int calcWidth = Mathf.Min(textureWidth, pixWidth);
int calcHeight = Mathf.Min(textureHeight, pixHeight);
int calcX = Mathf.Min(Mathf.Abs(pixX), textureWidth);
int calcY = Mathf.Min(Mathf.Abs(pixY), textureHeight);
float startX = calcX / (float) textureWidth;
float startY = calcY / (float) textureHeight;
float endX = (calcX + calcWidth) / (float) textureWidth;
float endY = (calcY + calcHeight) / (float) textureHeight;
// texture region is a struct which is allocated on the stack
return new TextureRegion(startX, startY, endX, endY);
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: f532d256442d1d64e8d4b33763420e2a
@@ -1,349 +0,0 @@
using UnityEngine;
namespace EzySlice {
/**
* Represents a simple 3D Triangle structure with position
* and UV map. The UV is required if the slicer needs
* to recalculate the new UV position for texture mapping.
*/
public struct Triangle {
// the points which represent this triangle
// these have to be set and are immutable. Cannot be
// changed once set
private readonly Vector3 m_pos_a;
private readonly Vector3 m_pos_b;
private readonly Vector3 m_pos_c;
// the UV coordinates of this triangle
// these are optional and may not be set
private bool m_uv_set;
private Vector2 m_uv_a;
private Vector2 m_uv_b;
private Vector2 m_uv_c;
// the Normals of the Vertices
// these are optional and may not be set
private bool m_nor_set;
private Vector3 m_nor_a;
private Vector3 m_nor_b;
private Vector3 m_nor_c;
// the Tangents of the Vertices
// these are optional and may not be set
private bool m_tan_set;
private Vector4 m_tan_a;
private Vector4 m_tan_b;
private Vector4 m_tan_c;
public Triangle(Vector3 posa,
Vector3 posb,
Vector3 posc) {
this.m_pos_a = posa;
this.m_pos_b = posb;
this.m_pos_c = posc;
this.m_uv_set = false;
this.m_uv_a = Vector2.zero;
this.m_uv_b = Vector2.zero;
this.m_uv_c = Vector2.zero;
this.m_nor_set = false;
this.m_nor_a = Vector3.zero;
this.m_nor_b = Vector3.zero;
this.m_nor_c = Vector3.zero;
this.m_tan_set = false;
this.m_tan_a = Vector4.zero;
this.m_tan_b = Vector4.zero;
this.m_tan_c = Vector4.zero;
}
public Vector3 positionA {
get { return this.m_pos_a; }
}
public Vector3 positionB {
get { return this.m_pos_b; }
}
public Vector3 positionC {
get { return this.m_pos_c; }
}
public bool hasUV {
get { return this.m_uv_set; }
}
public void SetUV(Vector2 uvA, Vector2 uvB, Vector2 uvC) {
this.m_uv_a = uvA;
this.m_uv_b = uvB;
this.m_uv_c = uvC;
this.m_uv_set = true;
}
public Vector2 uvA {
get { return this.m_uv_a; }
}
public Vector2 uvB {
get { return this.m_uv_b; }
}
public Vector2 uvC {
get { return this.m_uv_c; }
}
public bool hasNormal {
get { return this.m_nor_set; }
}
public void SetNormal(Vector3 norA, Vector3 norB, Vector3 norC) {
this.m_nor_a = norA;
this.m_nor_b = norB;
this.m_nor_c = norC;
this.m_nor_set = true;
}
public Vector3 normalA {
get { return this.m_nor_a; }
}
public Vector3 normalB {
get { return this.m_nor_b; }
}
public Vector3 normalC {
get { return this.m_nor_c; }
}
public bool hasTangent {
get { return this.m_tan_set; }
}
public void SetTangent(Vector4 tanA, Vector4 tanB, Vector4 tanC) {
this.m_tan_a = tanA;
this.m_tan_b = tanB;
this.m_tan_c = tanC;
this.m_tan_set = true;
}
public Vector4 tangentA {
get { return this.m_tan_a; }
}
public Vector4 tangentB {
get { return this.m_tan_b; }
}
public Vector4 tangentC {
get { return this.m_tan_c; }
}
/**
* Compute and set the tangents of this triangle
* Derived From https://answers.unity.com/questions/7789/calculating-tangents-vector4.html
*/
public void ComputeTangents() {
// computing tangents requires both UV and normals set
if (!m_nor_set || !m_uv_set) {
return;
}
Vector3 v1 = m_pos_a;
Vector3 v2 = m_pos_b;
Vector3 v3 = m_pos_c;
Vector2 w1 = m_uv_a;
Vector2 w2 = m_uv_b;
Vector2 w3 = m_uv_c;
float x1 = v2.x - v1.x;
float x2 = v3.x - v1.x;
float y1 = v2.y - v1.y;
float y2 = v3.y - v1.y;
float z1 = v2.z - v1.z;
float z2 = v3.z - v1.z;
float s1 = w2.x - w1.x;
float s2 = w3.x - w1.x;
float t1 = w2.y - w1.y;
float t2 = w3.y - w1.y;
float r = 1.0f / (s1 * t2 - s2 * t1);
Vector3 sdir = new Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
Vector3 tdir = new Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
Vector3 n1 = m_nor_a;
Vector3 nt1 = sdir;
Vector3.OrthoNormalize(ref n1, ref nt1);
Vector4 tanA = new Vector4(nt1.x, nt1.y, nt1.z, (Vector3.Dot(Vector3.Cross(n1, nt1), tdir) < 0.0f) ? -1.0f : 1.0f);
Vector3 n2 = m_nor_b;
Vector3 nt2 = sdir;
Vector3.OrthoNormalize(ref n2, ref nt2);
Vector4 tanB = new Vector4(nt2.x, nt2.y, nt2.z, (Vector3.Dot(Vector3.Cross(n2, nt2), tdir) < 0.0f) ? -1.0f : 1.0f);
Vector3 n3 = m_nor_c;
Vector3 nt3 = sdir;
Vector3.OrthoNormalize(ref n3, ref nt3);
Vector4 tanC = new Vector4(nt3.x, nt3.y, nt3.z, (Vector3.Dot(Vector3.Cross(n3, nt3), tdir) < 0.0f) ? -1.0f : 1.0f);
// finally set the tangents of this object
SetTangent(tanA, tanB, tanC);
}
/**
* Calculate the Barycentric coordinate weight values u-v-w for Point p in respect to the provided
* triangle. This is useful for computing new UV coordinates for arbitrary points.
*/
public Vector3 Barycentric(Vector3 p) {
Vector3 a = m_pos_a;
Vector3 b = m_pos_b;
Vector3 c = m_pos_c;
Vector3 m = Vector3.Cross(b - a, c - a);
float nu;
float nv;
float ood;
float x = Mathf.Abs(m.x);
float y = Mathf.Abs(m.y);
float z = Mathf.Abs(m.z);
// compute areas of plane with largest projections
if (x >= y && x >= z) {
// area of PBC in yz plane
nu = Intersector.TriArea2D(p.y, p.z, b.y, b.z, c.y, c.z);
// area of PCA in yz plane
nv = Intersector.TriArea2D(p.y, p.z, c.y, c.z, a.y, a.z);
// 1/2*area of ABC in yz plane
ood = 1.0f / m.x;
} else if (y >= x && y >= z) {
// project in xz plane
nu = Intersector.TriArea2D(p.x, p.z, b.x, b.z, c.x, c.z);
nv = Intersector.TriArea2D(p.x, p.z, c.x, c.z, a.x, a.z);
ood = 1.0f / -m.y;
} else {
// project in xy plane
nu = Intersector.TriArea2D(p.x, p.y, b.x, b.y, c.x, c.y);
nv = Intersector.TriArea2D(p.x, p.y, c.x, c.y, a.x, a.y);
ood = 1.0f / m.z;
}
float u = nu * ood;
float v = nv * ood;
float w = 1.0f - u - v;
return new Vector3(u, v, w);
}
/**
* Generate a set of new UV coordinates for the provided point pt in respect to Triangle.
*
* Uses weight values for the computation, so this triangle must have UV's set to return
* the correct results. Otherwise Vector2.zero will be returned. check via hasUV().
*/
public Vector2 GenerateUV(Vector3 pt) {
// if not set, result will be zero, quick exit
if (!m_uv_set) {
return Vector2.zero;
}
Vector3 weights = Barycentric(pt);
return (weights.x * m_uv_a) + (weights.y * m_uv_b) + (weights.z * m_uv_c);
}
/**
* Generates a set of new Normal coordinates for the provided point pt in respect to Triangle.
*
* Uses weight values for the computation, so this triangle must have Normal's set to return
* the correct results. Otherwise Vector3.zero will be returned. check via hasNormal().
*/
public Vector3 GenerateNormal(Vector3 pt) {
// if not set, result will be zero, quick exit
if (!m_nor_set) {
return Vector3.zero;
}
Vector3 weights = Barycentric(pt);
return (weights.x * m_nor_a) + (weights.y * m_nor_b) + (weights.z * m_nor_c);
}
/**
* Generates a set of new Tangent coordinates for the provided point pt in respect to Triangle.
*
* Uses weight values for the computation, so this triangle must have Tangent's set to return
* the correct results. Otherwise Vector4.zero will be returned. check via hasTangent().
*/
public Vector4 GenerateTangent(Vector3 pt) {
// if not set, result will be zero, quick exit
if (!m_nor_set) {
return Vector4.zero;
}
Vector3 weights = Barycentric(pt);
return (weights.x * m_tan_a) + (weights.y * m_tan_b) + (weights.z * m_tan_c);
}
/**
* Helper function to split this triangle by the provided plane and store
* the results inside the IntersectionResult structure.
* Returns true on success or false otherwise
*/
public bool Split(Plane pl, IntersectionResult result) {
Intersector.Intersect(pl, this, result);
return result.isValid;
}
/**
* Check the triangle winding order, if it's Clock Wise or Counter Clock Wise
*/
public bool IsCW() {
return SignedSquare(m_pos_a, m_pos_b, m_pos_c) >= float.Epsilon;
}
/**
* Returns the Signed square of a given triangle, useful for checking the
* winding order
*/
public static float SignedSquare(Vector3 a, Vector3 b, Vector3 c) {
return (a.x * (b.y * c.z - b.z * c.y) -
a.y * (b.x * c.z - b.z * c.x) +
a.z * (b.x * c.y - b.y * c.x));
}
/**
* Editor only DEBUG functionality. This should not be compiled in the final
* Version.
*/
public void OnDebugDraw() {
OnDebugDraw(Color.white);
}
public void OnDebugDraw(Color drawColor) {
#if UNITY_EDITOR
Color prevColor = Gizmos.color;
Gizmos.color = drawColor;
Gizmos.DrawLine(positionA, positionB);
Gizmos.DrawLine(positionB, positionC);
Gizmos.DrawLine(positionC, positionA);
Gizmos.color = prevColor;
#endif
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: b17c76e3fa3389f46beadff35495afe8
@@ -1,199 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace EzySlice {
/**
* Contains static functionality for performing Triangulation on arbitrary vertices.
* Read the individual function descriptions for specific details.
*/
public sealed class Triangulator {
/**
* Represents a 3D Vertex which has been mapped onto a 2D surface
* and is mainly used in MonotoneChain to triangulate a set of vertices
* against a flat plane.
*/
internal struct Mapped2D {
private readonly Vector3 original;
private readonly Vector2 mapped;
public Mapped2D(Vector3 newOriginal, Vector3 u, Vector3 v) {
this.original = newOriginal;
this.mapped = new Vector2(Vector3.Dot(newOriginal, u), Vector3.Dot(newOriginal, v));
}
public Vector2 mappedValue {
get { return this.mapped; }
}
public Vector3 originalValue {
get { return this.original; }
}
}
/**
* Overloaded variant of MonotoneChain which will calculate UV coordinates of the Triangles
* between 0.0 and 1.0 (default).
*
* See MonotoneChain(vertices, normal, tri, TextureRegion) for full explanation
*/
public static bool MonotoneChain(List<Vector3> vertices, Vector3 normal, out List<Triangle> tri) {
// default texture region is in coordinates 0,0 to 1,1
return MonotoneChain(vertices, normal, out tri, new TextureRegion(0.0f, 0.0f, 1.0f, 1.0f));
}
/**
* O(n log n) Convex Hull Algorithm.
* Accepts a list of vertices as Vector3 and triangulates them according to a projection
* plane defined as planeNormal. Algorithm will output vertices, indices and UV coordinates
* as arrays
*/
public static bool MonotoneChain(List<Vector3> vertices, Vector3 normal, out List<Triangle> tri, TextureRegion texRegion) {
int count = vertices.Count;
// we cannot triangulate less than 3 points. Use minimum of 3 points
if (count < 3) {
tri = null;
return false;
}
// first, we map from 3D points into a 2D plane represented by the provided normal
Vector3 u = Vector3.Normalize(Vector3.Cross(normal, Vector3.up));
if (Vector3.zero == u) {
u = Vector3.Normalize(Vector3.Cross(normal, Vector3.forward));
}
Vector3 v = Vector3.Cross(u, normal);
// generate an array of mapped values
Mapped2D[] mapped = new Mapped2D[count];
// these values will be used to generate new UV coordinates later on
float maxDivX = float.MinValue;
float maxDivY = float.MinValue;
float minDivX = float.MaxValue;
float minDivY = float.MaxValue;
// map the 3D vertices into the 2D mapped values
for (int i = 0; i < count; i++) {
Vector3 vertToAdd = vertices[i];
Mapped2D newMappedValue = new Mapped2D(vertToAdd, u, v);
Vector2 mapVal = newMappedValue.mappedValue;
// grab our maximal values so we can map UV's in a proper range
maxDivX = Mathf.Max(maxDivX, mapVal.x);
maxDivY = Mathf.Max(maxDivY, mapVal.y);
minDivX = Mathf.Min(minDivX, mapVal.x);
minDivY = Mathf.Min(minDivY, mapVal.y);
mapped[i] = newMappedValue;
}
// sort our newly generated array values
Array.Sort<Mapped2D>(mapped, (a, b) => {
Vector2 x = a.mappedValue;
Vector2 p = b.mappedValue;
return (x.x < p.x || (x.x == p.x && x.y < p.y)) ? -1 : 1;
});
// our final hull mappings will end up in here
Mapped2D[] hulls = new Mapped2D[count + 1];
int k = 0;
// build the lower hull of the chain
for (int i = 0; i < count; i++) {
while (k >= 2) {
Vector2 mA = hulls[k - 2].mappedValue;
Vector2 mB = hulls[k - 1].mappedValue;
Vector2 mC = mapped[i].mappedValue;
if (Intersector.TriArea2D(mA.x, mA.y, mB.x, mB.y, mC.x, mC.y) > 0.0f) {
break;
}
k--;
}
hulls[k++] = mapped[i];
}
// build the upper hull of the chain
for (int i = count - 2, t = k + 1; i >= 0; i--) {
while (k >= t) {
Vector2 mA = hulls[k - 2].mappedValue;
Vector2 mB = hulls[k - 1].mappedValue;
Vector2 mC = mapped[i].mappedValue;
if (Intersector.TriArea2D(mA.x, mA.y, mB.x, mB.y, mC.x, mC.y) > 0.0f) {
break;
}
k--;
}
hulls[k++] = mapped[i];
}
// finally we can build our mesh, generate all the variables
// and fill them up
int vertCount = k - 1;
int triCount = (vertCount - 2) * 3;
// this should not happen, but here just in case
if (vertCount < 3) {
tri = null;
return false;
}
// ensure List does not dynamically grow, performing copy ops each time!
tri = new List<Triangle>(triCount / 3);
float width = maxDivX - minDivX;
float height = maxDivY - minDivY;
int indexCount = 1;
// generate both the vertices and uv's in this loop
for (int i = 0; i < triCount; i += 3) {
// the Vertices in our triangle
Mapped2D posA = hulls[0];
Mapped2D posB = hulls[indexCount];
Mapped2D posC = hulls[indexCount + 1];
// generate UV Maps
Vector2 uvA = posA.mappedValue;
Vector2 uvB = posB.mappedValue;
Vector2 uvC = posC.mappedValue;
uvA.x = (uvA.x - minDivX) / width;
uvA.y = (uvA.y - minDivY) / height;
uvB.x = (uvB.x - minDivX) / width;
uvB.y = (uvB.y - minDivY) / height;
uvC.x = (uvC.x - minDivX) / width;
uvC.y = (uvC.y - minDivY) / height;
Triangle newTriangle = new Triangle(posA.originalValue, posB.originalValue, posC.originalValue);
// ensure our UV coordinates are mapped into the requested TextureRegion
newTriangle.SetUV(texRegion.Map(uvA), texRegion.Map(uvB), texRegion.Map(uvC));
// the normals is the same for all vertices since the final mesh is completly flat
newTriangle.SetNormal(normal, normal, normal);
newTriangle.ComputeTangents();
tri.Add(newTriangle);
indexCount++;
}
return true;
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: c5405063d0e3ca84381aa2d7bfb70a29
@@ -1,141 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace EzySlice {
/**
* The final generated data structure from a slice operation. This provides easy access
* to utility functions and the final Mesh data for each section of the HULL.
*/
public sealed class SlicedHull {
private Mesh upper_hull;
private Mesh lower_hull;
public SlicedHull(Mesh upperHull, Mesh lowerHull) {
this.upper_hull = upperHull;
this.lower_hull = lowerHull;
}
public GameObject CreateUpperHull(GameObject original) {
return CreateUpperHull(original, null);
}
public GameObject CreateUpperHull(GameObject original, Material crossSectionMat) {
GameObject newObject = CreateUpperHull();
if (newObject != null) {
newObject.transform.localPosition = original.transform.localPosition;
newObject.transform.localRotation = original.transform.localRotation;
newObject.transform.localScale = original.transform.localScale;
Material[] shared = original.GetComponent<MeshRenderer>().sharedMaterials;
Mesh mesh = original.GetComponent<MeshFilter>().sharedMesh;
// nothing changed in the hierarchy, the cross section must have been batched
// with the submeshes, return as is, no need for any changes
if (mesh.subMeshCount == upper_hull.subMeshCount) {
// the the material information
newObject.GetComponent<Renderer>().sharedMaterials = shared;
return newObject;
}
// otherwise the cross section was added to the back of the submesh array because
// it uses a different material. We need to take this into account
Material[] newShared = new Material[shared.Length + 1];
// copy our material arrays across using native copy (should be faster than loop)
System.Array.Copy(shared, newShared, shared.Length);
newShared[shared.Length] = crossSectionMat;
// the the material information
newObject.GetComponent<Renderer>().sharedMaterials = newShared;
}
return newObject;
}
public GameObject CreateLowerHull(GameObject original) {
return CreateLowerHull(original, null);
}
public GameObject CreateLowerHull(GameObject original, Material crossSectionMat) {
GameObject newObject = CreateLowerHull();
if (newObject != null) {
newObject.transform.localPosition = original.transform.localPosition;
newObject.transform.localRotation = original.transform.localRotation;
newObject.transform.localScale = original.transform.localScale;
Material[] shared = original.GetComponent<MeshRenderer>().sharedMaterials;
Mesh mesh = original.GetComponent<MeshFilter>().sharedMesh;
// nothing changed in the hierarchy, the cross section must have been batched
// with the submeshes, return as is, no need for any changes
if (mesh.subMeshCount == lower_hull.subMeshCount) {
// the the material information
newObject.GetComponent<Renderer>().sharedMaterials = shared;
return newObject;
}
// otherwise the cross section was added to the back of the submesh array because
// it uses a different material. We need to take this into account
Material[] newShared = new Material[shared.Length + 1];
// copy our material arrays across using native copy (should be faster than loop)
System.Array.Copy(shared, newShared, shared.Length);
newShared[shared.Length] = crossSectionMat;
// the the material information
newObject.GetComponent<Renderer>().sharedMaterials = newShared;
}
return newObject;
}
/**
* Generate a new GameObject from the upper hull of the mesh
* This function will return null if upper hull does not exist
*/
public GameObject CreateUpperHull() {
return CreateEmptyObject("Upper_Hull", upper_hull);
}
/**
* Generate a new GameObject from the Lower hull of the mesh
* This function will return null if lower hull does not exist
*/
public GameObject CreateLowerHull() {
return CreateEmptyObject("Lower_Hull", lower_hull);
}
public Mesh upperHull {
get { return this.upper_hull; }
}
public Mesh lowerHull {
get { return this.lower_hull; }
}
/**
* Helper function which will create a new GameObject to be able to add
* a new mesh for rendering and return.
*/
private static GameObject CreateEmptyObject(string name, Mesh hull) {
if (hull == null) {
return null;
}
GameObject newObject = new GameObject(name);
newObject.AddComponent<MeshRenderer>();
MeshFilter filter = newObject.AddComponent<MeshFilter>();
filter.mesh = hull;
return newObject;
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: dd8558bd942d575479792e491438f2bf
@@ -1,481 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace EzySlice {
/**
* Contains methods for slicing GameObjects
*/
public sealed class Slicer {
/**
* An internal class for storing internal submesh values
*/
internal class SlicedSubmesh {
public readonly List<Triangle> upperHull = new List<Triangle>();
public readonly List<Triangle> lowerHull = new List<Triangle>();
/**
* Check if the submesh has had any UV's added.
* NOTE -> This should be supported properly
*/
public bool hasUV {
get {
// what is this abomination??
return upperHull.Count > 0 ? upperHull[0].hasUV : lowerHull.Count > 0 && lowerHull[0].hasUV;
}
}
/**
* Check if the submesh has had any Normals added.
* NOTE -> This should be supported properly
*/
public bool hasNormal {
get {
// what is this abomination??
return upperHull.Count > 0 ? upperHull[0].hasNormal : lowerHull.Count > 0 && lowerHull[0].hasNormal;
}
}
/**
* Check if the submesh has had any Tangents added.
* NOTE -> This should be supported properly
*/
public bool hasTangent {
get {
// what is this abomination??
return upperHull.Count > 0 ? upperHull[0].hasTangent : lowerHull.Count > 0 && lowerHull[0].hasTangent;
}
}
/**
* Check if proper slicing has occured for this submesh. Slice occured if there
* are triangles in both the upper and lower hulls
*/
public bool isValid {
get {
return upperHull.Count > 0 && lowerHull.Count > 0;
}
}
}
/**
* Helper function to accept a gameobject which will transform the plane
* approprietly before the slice occurs
* See -> Slice(Mesh, Plane) for more info
*/
public static SlicedHull Slice(GameObject obj, Plane pl, TextureRegion crossRegion, Material crossMaterial) {
// cannot continue without a proper filter
if (!obj.TryGetComponent<MeshFilter>(out var filter)) {
Debug.LogWarning("EzySlice::Slice -> Provided GameObject must have a MeshFilter Component.");
return null;
}
// cannot continue without a proper renderer
if (!obj.TryGetComponent<MeshRenderer>(out var renderer)) {
Debug.LogWarning("EzySlice::Slice -> Provided GameObject must have a MeshRenderer Component.");
return null;
}
Material[] materials = renderer.sharedMaterials;
Mesh mesh = filter.sharedMesh;
// cannot slice a mesh that doesn't exist
if (mesh == null) {
Debug.LogWarning("EzySlice::Slice -> Provided GameObject must have a Mesh that is not NULL.");
return null;
}
int submeshCount = mesh.subMeshCount;
// to make things straightforward, exit without slicing if the materials and mesh
// array don't match. This shouldn't happen anyway
if (materials.Length != submeshCount) {
Debug.LogWarning("EzySlice::Slice -> Provided Material array must match the length of submeshes.");
return null;
}
// we need to find the index of the material for the cross section.
// default to the end of the array
int crossIndex = materials.Length;
// for cases where the sliced material is null, we will append the cross section to the end
// of the submesh array, this is because the application may want to set/change the material
// after slicing has occured, so we don't assume anything
if (crossMaterial != null) {
for (int i = 0; i < crossIndex; i++) {
if (materials[i] == crossMaterial) {
crossIndex = i;
break;
}
}
}
return Slice(mesh, pl, crossRegion, crossIndex);
}
/**
* Slice the gameobject mesh (if any) using the Plane, which will generate
* a maximum of 2 other Meshes.
* This function will recalculate new UV coordinates to ensure textures are applied
* properly.
* Returns null if no intersection has been found or the GameObject does not contain
* a valid mesh to cut.
*/
public static SlicedHull Slice(Mesh sharedMesh, Plane pl, TextureRegion region, int crossIndex) {
if (sharedMesh == null) {
return null;
}
Vector3[] verts = sharedMesh.vertices;
Vector2[] uv = sharedMesh.uv;
Vector3[] norm = sharedMesh.normals;
Vector4[] tan = sharedMesh.tangents;
int submeshCount = sharedMesh.subMeshCount;
// each submesh will be sliced and placed in its own array structure
SlicedSubmesh[] slices = new SlicedSubmesh[submeshCount];
// the cross section hull is common across all submeshes
List<Vector3> crossHull = new List<Vector3>();
// we reuse this object for all intersection tests
IntersectionResult result = new IntersectionResult();
// see if we would like to split the mesh using uv, normals and tangents
bool genUV = verts.Length == uv.Length;
bool genNorm = verts.Length == norm.Length;
bool genTan = verts.Length == tan.Length;
// iterate over all the submeshes individually. vertices and indices
// are all shared within the submesh
for (int submesh = 0; submesh < submeshCount; submesh++) {
int[] indices = sharedMesh.GetTriangles(submesh);
int indicesCount = indices.Length;
SlicedSubmesh mesh = new SlicedSubmesh();
// loop through all the mesh vertices, generating upper and lower hulls
// and all intersection points
for (int index = 0; index < indicesCount; index += 3) {
int i0 = indices[index + 0];
int i1 = indices[index + 1];
int i2 = indices[index + 2];
Triangle newTri = new Triangle(verts[i0], verts[i1], verts[i2]);
// generate UV if available
if (genUV) {
newTri.SetUV(uv[i0], uv[i1], uv[i2]);
}
// generate normals if available
if (genNorm) {
newTri.SetNormal(norm[i0], norm[i1], norm[i2]);
}
// generate tangents if available
if (genTan) {
newTri.SetTangent(tan[i0], tan[i1], tan[i2]);
}
// slice this particular triangle with the provided
// plane
if (newTri.Split(pl, result)) {
int upperHullCount = result.upperHullCount;
int lowerHullCount = result.lowerHullCount;
int interHullCount = result.intersectionPointCount;
for (int i = 0; i < upperHullCount; i++) {
mesh.upperHull.Add(result.upperHull[i]);
}
for (int i = 0; i < lowerHullCount; i++) {
mesh.lowerHull.Add(result.lowerHull[i]);
}
for (int i = 0; i < interHullCount; i++) {
crossHull.Add(result.intersectionPoints[i]);
}
} else {
SideOfPlane sa = pl.SideOf(verts[i0]);
SideOfPlane sb = pl.SideOf(verts[i1]);
SideOfPlane sc = pl.SideOf(verts[i2]);
SideOfPlane side = SideOfPlane.ON;
if (sa != SideOfPlane.ON)
{
side = sa;
}
if (sb != SideOfPlane.ON)
{
Debug.Assert(side == SideOfPlane.ON || side == sb);
side = sb;
}
if (sc != SideOfPlane.ON)
{
Debug.Assert(side == SideOfPlane.ON || side == sc);
side = sc;
}
if (side == SideOfPlane.UP || side == SideOfPlane.ON) {
mesh.upperHull.Add(newTri);
} else {
mesh.lowerHull.Add(newTri);
}
}
}
// register into the index
slices[submesh] = mesh;
}
// check if slicing actually occured
for (int i = 0; i < slices.Length; i++) {
// check if at least one of the submeshes was sliced. If so, stop checking
// because we need to go through the generation step
if (slices[i] != null && slices[i].isValid) {
return CreateFrom(slices, CreateFrom(crossHull, pl.normal, region), crossIndex);
}
}
// no slicing occured, just return null to signify
return null;
}
/**
* Generates a single SlicedHull from a set of cut submeshes
*/
private static SlicedHull CreateFrom(SlicedSubmesh[] meshes, List<Triangle> cross, int crossSectionIndex) {
int submeshCount = meshes.Length;
int upperHullCount = 0;
int lowerHullCount = 0;
// get the total amount of upper, lower and intersection counts
for (int submesh = 0; submesh < submeshCount; submesh++) {
upperHullCount += meshes[submesh].upperHull.Count;
lowerHullCount += meshes[submesh].lowerHull.Count;
}
Mesh upperHull = CreateUpperHull(meshes, upperHullCount, cross, crossSectionIndex);
Mesh lowerHull = CreateLowerHull(meshes, lowerHullCount, cross, crossSectionIndex);
return new SlicedHull(upperHull, lowerHull);
}
private static Mesh CreateUpperHull(SlicedSubmesh[] mesh, int total, List<Triangle> crossSection, int crossSectionIndex) {
return CreateHull(mesh, total, crossSection, crossSectionIndex, true);
}
private static Mesh CreateLowerHull(SlicedSubmesh[] mesh, int total, List<Triangle> crossSection, int crossSectionIndex) {
return CreateHull(mesh, total, crossSection, crossSectionIndex, false);
}
/**
* Generate a single Mesh HULL of either the UPPER or LOWER hulls.
*/
private static Mesh CreateHull(SlicedSubmesh[] meshes, int total, List<Triangle> crossSection, int crossIndex, bool isUpper) {
if (total <= 0) {
return null;
}
int submeshCount = meshes.Length;
int crossCount = crossSection != null ? crossSection.Count : 0;
Mesh newMesh = new Mesh();
newMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
int arrayLen = (total + crossCount) * 3;
bool hasUV = meshes[0].hasUV;
bool hasNormal = meshes[0].hasNormal;
bool hasTangent = meshes[0].hasTangent;
// vertices and uv's are common for all submeshes
Vector3[] newVertices = new Vector3[arrayLen];
Vector2[] newUvs = hasUV ? new Vector2[arrayLen] : null;
Vector3[] newNormals = hasNormal ? new Vector3[arrayLen] : null;
Vector4[] newTangents = hasTangent ? new Vector4[arrayLen] : null;
// each index refers to our submesh triangles
List<int[]> triangles = new List<int[]>(submeshCount);
int vIndex = 0;
// first we generate all our vertices, uv's and triangles
for (int submesh = 0; submesh < submeshCount; submesh++) {
// pick the hull we will be playing around with
List<Triangle> hull = isUpper ? meshes[submesh].upperHull : meshes[submesh].lowerHull;
int hullCount = hull.Count;
int[] indices = new int[hullCount * 3];
// fill our mesh arrays
for (int i = 0, triIndex = 0; i < hullCount; i++, triIndex += 3) {
Triangle newTri = hull[i];
int i0 = vIndex + 0;
int i1 = vIndex + 1;
int i2 = vIndex + 2;
// add the vertices
newVertices[i0] = newTri.positionA;
newVertices[i1] = newTri.positionB;
newVertices[i2] = newTri.positionC;
// add the UV coordinates if any
if (hasUV) {
newUvs[i0] = newTri.uvA;
newUvs[i1] = newTri.uvB;
newUvs[i2] = newTri.uvC;
}
// add the Normals if any
if (hasNormal) {
newNormals[i0] = newTri.normalA;
newNormals[i1] = newTri.normalB;
newNormals[i2] = newTri.normalC;
}
// add the Tangents if any
if (hasTangent) {
newTangents[i0] = newTri.tangentA;
newTangents[i1] = newTri.tangentB;
newTangents[i2] = newTri.tangentC;
}
// triangles are returned in clocwise order from the
// intersector, no need to sort these
indices[triIndex] = i0;
indices[triIndex + 1] = i1;
indices[triIndex + 2] = i2;
vIndex += 3;
}
// add triangles to the index for later generation
triangles.Add(indices);
}
// generate the cross section required for this particular hull
if (crossSection != null && crossCount > 0) {
int[] crossIndices = new int[crossCount * 3];
for (int i = 0, triIndex = 0; i < crossCount; i++, triIndex += 3) {
Triangle newTri = crossSection[i];
int i0 = vIndex + 0;
int i1 = vIndex + 1;
int i2 = vIndex + 2;
// add the vertices
newVertices[i0] = newTri.positionA;
newVertices[i1] = newTri.positionB;
newVertices[i2] = newTri.positionC;
// add the UV coordinates if any
if (hasUV) {
newUvs[i0] = newTri.uvA;
newUvs[i1] = newTri.uvB;
newUvs[i2] = newTri.uvC;
}
// add the Normals if any
if (hasNormal) {
// invert the normals dependiong on upper or lower hull
if (isUpper) {
newNormals[i0] = -newTri.normalA;
newNormals[i1] = -newTri.normalB;
newNormals[i2] = -newTri.normalC;
} else {
newNormals[i0] = newTri.normalA;
newNormals[i1] = newTri.normalB;
newNormals[i2] = newTri.normalC;
}
}
// add the Tangents if any
if (hasTangent) {
newTangents[i0] = newTri.tangentA;
newTangents[i1] = newTri.tangentB;
newTangents[i2] = newTri.tangentC;
}
// add triangles in clockwise for upper
// and reversed for lower hulls, to ensure the mesh
// is facing the right direction
if (isUpper) {
crossIndices[triIndex] = i0;
crossIndices[triIndex + 1] = i1;
crossIndices[triIndex + 2] = i2;
} else {
crossIndices[triIndex] = i0;
crossIndices[triIndex + 1] = i2;
crossIndices[triIndex + 2] = i1;
}
vIndex += 3;
}
// add triangles to the index for later generation
if (triangles.Count <= crossIndex) {
triangles.Add(crossIndices);
} else {
// otherwise, we need to merge the triangles for the provided subsection
int[] prevTriangles = triangles[crossIndex];
int[] merged = new int[prevTriangles.Length + crossIndices.Length];
System.Array.Copy(prevTriangles, merged, prevTriangles.Length);
System.Array.Copy(crossIndices, 0, merged, prevTriangles.Length, crossIndices.Length);
// replace the previous array with the new merged array
triangles[crossIndex] = merged;
}
}
int totalTriangles = triangles.Count;
newMesh.subMeshCount = totalTriangles;
// fill the mesh structure
newMesh.vertices = newVertices;
if (hasUV) {
newMesh.uv = newUvs;
}
if (hasNormal) {
newMesh.normals = newNormals;
}
if (hasTangent) {
newMesh.tangents = newTangents;
}
// add the submeshes
for (int i = 0; i < totalTriangles; i++) {
newMesh.SetTriangles(triangles[i], i, false);
}
return newMesh;
}
/**
* Generate Two Meshes (an upper and lower) cross section from a set of intersection
* points and a plane normal. Intersection Points do not have to be in order.
*/
private static List<Triangle> CreateFrom(List<Vector3> intPoints, Vector3 planeNormal, TextureRegion region) {
return Triangulator.MonotoneChain(intPoints, planeNormal, out List<Triangle> tris, region) ? tris : null;
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 9613e2c4eb41eb4418609c52cd562e43
@@ -1,98 +0,0 @@
using System.Collections;
using UnityEngine;
namespace EzySlice {
/**
* Define Extension methods for easy access to slicer functionality
*/
public static class SlicerExtensions {
/**
* SlicedHull Return functions and appropriate overrides!
*/
public static SlicedHull Slice(this GameObject obj, Plane pl, Material crossSectionMaterial = null) {
return Slice(obj, pl, new TextureRegion(0.0f, 0.0f, 1.0f, 1.0f), crossSectionMaterial);
}
public static SlicedHull Slice(this GameObject obj, Vector3 position, Vector3 direction, Material crossSectionMaterial = null) {
return Slice(obj, position, direction, new TextureRegion(0.0f, 0.0f, 1.0f, 1.0f), crossSectionMaterial);
}
public static SlicedHull Slice(this GameObject obj, Vector3 position, Vector3 direction, TextureRegion textureRegion, Material crossSectionMaterial = null) {
Plane cuttingPlane = new Plane();
Matrix4x4 mat = obj.transform.worldToLocalMatrix;
Matrix4x4 transpose = mat.transpose;
Matrix4x4 inv = transpose.inverse;
Vector3 refUp = inv.MultiplyVector(direction).normalized;
Vector3 refPt = obj.transform.InverseTransformPoint(position);
cuttingPlane.Compute(refPt, refUp);
return Slice(obj, cuttingPlane, textureRegion, crossSectionMaterial);
}
public static SlicedHull Slice(this GameObject obj, Plane pl, TextureRegion textureRegion, Material crossSectionMaterial = null) {
return Slicer.Slice(obj, pl, textureRegion, crossSectionMaterial);
}
/**
* These functions (and overrides) will return the final indtaniated GameObjects types
*/
public static GameObject[] SliceInstantiate(this GameObject obj, Plane pl) {
return SliceInstantiate(obj, pl, new TextureRegion(0.0f, 0.0f, 1.0f, 1.0f));
}
public static GameObject[] SliceInstantiate(this GameObject obj, Vector3 position, Vector3 direction) {
return SliceInstantiate(obj, position, direction, null);
}
public static GameObject[] SliceInstantiate(this GameObject obj, Vector3 position, Vector3 direction, Material crossSectionMat) {
return SliceInstantiate(obj, position, direction, new TextureRegion(0.0f, 0.0f, 1.0f, 1.0f), crossSectionMat);
}
public static GameObject[] SliceInstantiate(this GameObject obj, Vector3 position, Vector3 direction, TextureRegion cuttingRegion, Material crossSectionMaterial = null) {
EzySlice.Plane cuttingPlane = new EzySlice.Plane();
Matrix4x4 mat = obj.transform.worldToLocalMatrix;
Matrix4x4 transpose = mat.transpose;
Matrix4x4 inv = transpose.inverse;
Vector3 refUp = inv.MultiplyVector(direction).normalized;
Vector3 refPt = obj.transform.InverseTransformPoint(position);
cuttingPlane.Compute(refPt, refUp);
return SliceInstantiate(obj, cuttingPlane, cuttingRegion, crossSectionMaterial);
}
public static GameObject[] SliceInstantiate(this GameObject obj, Plane pl, TextureRegion cuttingRegion, Material crossSectionMaterial = null) {
SlicedHull slice = Slicer.Slice(obj, pl, cuttingRegion, crossSectionMaterial);
if (slice == null) {
return null;
}
GameObject upperHull = slice.CreateUpperHull(obj, crossSectionMaterial);
GameObject lowerHull = slice.CreateLowerHull(obj, crossSectionMaterial);
if (upperHull != null && lowerHull != null) {
return new GameObject[] { upperHull, lowerHull };
}
// otherwise return only the upper hull
if (upperHull != null) {
return new GameObject[] { upperHull };
}
// otherwise return only the lower hull
if (lowerHull != null) {
return new GameObject[] { lowerHull };
}
// nothing to return, so return nothing!
return null;
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 2d5138f004062524c9d5f0da6dc1a4ec
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 1b9268b4a3e9a864cb9be419f857e44e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: b093e4224daf35243b83fd7ceeb435fd
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 62c1be2341d3fbb43b5c3420bce0bbae
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 2068c2f8d01072f47a4750fff4f7e480
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: cb9305de6342b6f4081fe86fc94c420d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,110 +0,0 @@
fileFormatVersion: 2
guid: ec5d2bead623da04c8558f147b002afe
ModelImporter:
serializedVersion: 24200
internalIDToNameTable: []
externalObjects: {}
materials:
materialImportMode: 2
materialName: 0
materialSearch: 1
materialLocation: 0
animations:
legacyGenerateAnimations: 4
bakeSimulation: 0
resampleCurves: 1
optimizeGameObjects: 0
removeConstantScaleCurves: 0
motionNodeName:
animationImportErrors:
animationImportWarnings:
animationRetargetingWarnings:
animationDoRetargetingWarnings: 0
importAnimatedCustomProperties: 0
importConstraints: 0
animationCompression: 1
animationRotationError: 0.5
animationPositionError: 0.5
animationScaleError: 0.5
animationWrapMode: 0
extraExposedTransformPaths: []
extraUserProperties: []
clipAnimations: []
isReadable: 0
meshes:
lODScreenPercentages: []
globalScale: 1
meshCompression: 0
addColliders: 0
useSRGBMaterialColor: 1
sortHierarchyByName: 1
importPhysicalCameras: 1
importVisibility: 1
importBlendShapes: 1
importCameras: 1
importLights: 1
nodeNameCollisionStrategy: 1
fileIdsGeneration: 2
swapUVChannels: 0
generateSecondaryUV: 0
useFileUnits: 1
keepQuads: 0
weldVertices: 1
bakeAxisConversion: 0
preserveHierarchy: 0
skinWeightsMode: 0
maxBonesPerVertex: 4
minBoneWeight: 0.001
optimizeBones: 1
generateMeshLods: 0
meshLodGenerationFlags: 0
maximumMeshLod: -1
meshOptimizationFlags: -1
indexFormat: 0
secondaryUVAngleDistortion: 8
secondaryUVAreaDistortion: 15.000001
secondaryUVHardAngle: 88
secondaryUVMarginMethod: 1
secondaryUVMinLightmapResolution: 40
secondaryUVMinObjectScale: 1
secondaryUVPackMargin: 4
useFileScale: 1
strictVertexDataChecks: 0
tangentSpace:
normalSmoothAngle: 60
normalImportMode: 0
tangentImportMode: 3
normalCalculationMode: 4
legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
blendShapeNormalImportMode: 1
normalSmoothingSource: 0
referencedClips: []
importAnimation: 1
humanDescription:
serializedVersion: 3
human: []
skeleton: []
armTwist: 0.5
foreArmTwist: 0.5
upperLegTwist: 0.5
legTwist: 0.5
armStretch: 0.05
legStretch: 0.05
feetSpacing: 0
globalScale: 1
rootMotionBoneName:
hasTranslationDoF: 0
hasExtraRoot: 0
skeletonHasParents: 1
lastHumanDescriptionAvatarSource: {instanceID: 0}
autoGenerateAvatarMappingIfUnspecified: 1
animationType: 2
humanoidOversampling: 1
avatarSetup: 0
addHumanoidExtraRootOnlyWhenUsingAvatar: 1
importBlendShapeDeformPercent: 1
remapMaterialsIfMaterialImportModeIsNone: 0
additionalBone: 0
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 0525ea140858f9e46ae278c81cc9150d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,143 +0,0 @@
fileFormatVersion: 2
guid: d41a613013e565f45b506725963523f8
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:
@@ -1,139 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Material.001
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 2800000, guid: d41a613013e565f45b506725963523f8, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 2800000, guid: d41a613013e565f45b506725963523f8, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 1
- _Glossiness: 0
- _GlossyReflections: 1
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _UVSec: 0
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0.9063317, g: 0.9063317, b: 0.9063317, a: 1}
- _Color: {r: 0.9063317, g: 0.9063317, b: 0.9063317, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &7237702746129366603
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 240f0667da603fe49a6c28cee959eaa4
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -1,139 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Material.002
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 2800000, guid: d41a613013e565f45b506725963523f8, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 2800000, guid: d41a613013e565f45b506725963523f8, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 1
- _Glossiness: 0
- _GlossyReflections: 1
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _UVSec: 0
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0.9063317, g: 0.9063317, b: 0.9063317, a: 1}
- _Color: {r: 0.9063317, g: 0.9063317, b: 0.9063317, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &1220628289462815948
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 055dbc1942c1cd447bfc2ff14742585f
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -1,139 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-2010901689382297289
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Material.005
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 1
- _Glossiness: 0
- _GlossyReflections: 1
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _UVSec: 0
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0.9063317, g: 0.9063317, b: 0.9063317, a: 1}
- _Color: {r: 0.9063317, g: 0.9063317, b: 0.9063317, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 9de7ecd36bd818947b6fe74fe082b330
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -1,158 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-5965136316952598079
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Material.B
m_Shader: {fileID: 211, guid: 0000000000000000f000000000000000, type: 0}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 0
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses:
- MOTIONVECTORS
- GRABPASS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 2800000, guid: d41a613013e565f45b506725963523f8, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 2800000, guid: d41a613013e565f45b506725963523f8, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BlendOp: 0
- _BumpScale: 1
- _CameraFadingEnabled: 0
- _CameraFarFadeDistance: 2
- _CameraNearFadeDistance: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _ColorMode: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DistortionBlend: 0.5
- _DistortionEnabled: 0
- _DistortionStrength: 1
- _DistortionStrengthScaled: 0
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EmissionEnabled: 0
- _EnvironmentReflections: 1
- _FlipbookMode: 0
- _GlossMapScale: 1
- _Glossiness: 0
- _GlossyReflections: 1
- _LightingEnabled: 0
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _QueueOffset: 0
- _ReceiveShadows: 1
- _SampleGI: 0
- _Smoothness: 0
- _SmoothnessTextureChannel: 0
- _SoftParticlesEnabled: 0
- _SoftParticlesFarFadeDistance: 1
- _SoftParticlesNearFadeDistance: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _UVSec: 0
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0.9063317, g: 0.9063317, b: 0.9063317, a: 1}
- _CameraFadeParams: {r: 0, g: Infinity, b: 0, a: 0}
- _Color: {r: 0.09171098, g: 0, b: 57.037918, a: 1}
- _ColorAddSubDiff: {r: 0, g: 0, b: 0, a: 0}
- _EmissionColor: {r: 2803.8748, g: 0, b: 0, a: 1}
- _SoftParticleFadeParams: {r: 0, g: 0, b: 0, a: 0}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: da1dcb1e3514740419194692754332fd
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -1,158 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-5965136316952598079
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Material.R
m_Shader: {fileID: 211, guid: 0000000000000000f000000000000000, type: 0}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 0
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses:
- MOTIONVECTORS
- GRABPASS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 2800000, guid: d41a613013e565f45b506725963523f8, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 2800000, guid: d41a613013e565f45b506725963523f8, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BlendOp: 0
- _BumpScale: 1
- _CameraFadingEnabled: 0
- _CameraFarFadeDistance: 2
- _CameraNearFadeDistance: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _ColorMode: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DistortionBlend: 0.5
- _DistortionEnabled: 0
- _DistortionStrength: 1
- _DistortionStrengthScaled: 0
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EmissionEnabled: 0
- _EnvironmentReflections: 1
- _FlipbookMode: 0
- _GlossMapScale: 1
- _Glossiness: 0
- _GlossyReflections: 1
- _LightingEnabled: 0
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _QueueOffset: 0
- _ReceiveShadows: 1
- _SampleGI: 0
- _Smoothness: 0
- _SmoothnessTextureChannel: 0
- _SoftParticlesEnabled: 0
- _SoftParticlesFarFadeDistance: 1
- _SoftParticlesNearFadeDistance: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _UVSec: 0
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0.9063317, g: 0.9063317, b: 0.9063317, a: 1}
- _CameraFadeParams: {r: 0, g: Infinity, b: 0, a: 0}
- _Color: {r: 89.63481, g: 0, b: 0, a: 1}
- _ColorAddSubDiff: {r: 0, g: 0, b: 0, a: 0}
- _EmissionColor: {r: 2803.8748, g: 0, b: 0, a: 1}
- _SoftParticleFadeParams: {r: 0, g: 0, b: 0, a: 0}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: d90b2a86e1b4f784f940a0c097f15d90
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -1,139 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Material
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 2800000, guid: d41a613013e565f45b506725963523f8, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 2800000, guid: d41a613013e565f45b506725963523f8, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 1
- _Glossiness: 0
- _GlossyReflections: 1
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _UVSec: 0
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0.9063317, g: 0.9063317, b: 0.9063317, a: 1}
- _Color: {r: 0.9063317, g: 0.9063317, b: 0.9063317, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &5899627680703092969
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 5055245c25557274a9dfd9e5e0bb8c73
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: f17e771607346bb4ea2d7cdc50a90cbf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: ce6293aea353e494f98858b3bcc881e7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 1bba8bf2738365a4ebe41aa48a804770
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 3b0cd98e7918f8948adc45d9aa40973c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: ab7acf0d9677fbe4db51bc36647d4fcb
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: fe662ea7d4bacf74ea2459d10b988a1b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,170 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-7628788528051855285
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Blue
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- _EMISSION
m_InvalidKeywords: []
m_LightmapFlags: 6
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _AlphaTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- PixelSnap: 0
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BlendOp: 0
- _BumpScale: 1
- _CameraFadingEnabled: 0
- _CameraFarFadeDistance: 2
- _CameraNearFadeDistance: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _ColorMask: 15
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DistortionBlend: 0.5
- _DistortionEnabled: 0
- _DistortionStrength: 1
- _DistortionStrengthScaled: 0
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EmissionEnabled: 0
- _EnableExternalAlpha: 0
- _EnvironmentReflections: 1
- _FlipbookMode: 0
- _GlossMapScale: 1
- _Glossiness: 0
- _GlossyReflections: 1
- _LightingEnabled: 1
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0.5
- _SmoothnessTextureChannel: 0
- _SoftParticlesEnabled: 0
- _SoftParticlesFarFadeDistance: 1
- _SoftParticlesNearFadeDistance: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Stencil: 0
- _StencilComp: 8
- _StencilOp: 0
- _StencilReadMask: 255
- _StencilWriteMask: 255
- _Surface: 0
- _UVSec: 0
- _UseUIAlphaClip: 0
- _WorkflowMode: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0, g: 0, b: 1, a: 1}
- _CameraFadeParams: {r: 0, g: Infinity, b: 0, a: 0}
- _Color: {r: 0, g: 0, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _Flip: {r: 1, g: 1, b: 1, a: 1}
- _RendererColor: {r: 1, g: 1, b: 1, a: 1}
- _SoftParticleFadeParams: {r: 0, g: 0, b: 0, a: 0}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1

Some files were not shown because too many files have changed in this diff Show More