From e45c1efac11dcbae963db7d12c299b60b258f69c Mon Sep 17 00:00:00 2001 From: Untold Engine Date: Sat, 29 Aug 2026 07:30:09 -0700 Subject: [PATCH] [Patch]Restore scene-authored loading in Environment panel --- Sources/UntoldEditor/Editor/EditorView.swift | 5 +- .../UntoldEditor/Editor/EnvironmentView.swift | 119 ++++++++++++++++++ 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/Sources/UntoldEditor/Editor/EditorView.swift b/Sources/UntoldEditor/Editor/EditorView.swift index 02776db..656888e 100644 --- a/Sources/UntoldEditor/Editor/EditorView.swift +++ b/Sources/UntoldEditor/Editor/EditorView.swift @@ -464,7 +464,10 @@ public struct EditorView: View { Group { switch rightPanelEnvTab { case .environment: - EnvironmentView(selectedAsset: $selectedAsset) + EnvironmentView( + selectedAsset: $selectedAsset, + onLoadSceneAuthored: editor_loadSceneAuthoredFromAsset + ) case .effects: PostProcessingEditorView() } diff --git a/Sources/UntoldEditor/Editor/EnvironmentView.swift b/Sources/UntoldEditor/Editor/EnvironmentView.swift index c128e97..f7df0a1 100644 --- a/Sources/UntoldEditor/Editor/EnvironmentView.swift +++ b/Sources/UntoldEditor/Editor/EnvironmentView.swift @@ -44,6 +44,33 @@ struct EnvironmentView: View { @State private var enableColorLUT: Bool = false @State private var intensity: Float = 1.0 @Binding var selectedAsset: Asset? + var onLoadSceneAuthored: (Asset) -> Void = { _ in } + + private var selectedSceneAuthoredAsset: Asset? { + guard let selectedAsset else { + return nil + } + + if let runtimeAsset = resolvedRuntimeAsset(for: selectedAsset), + runtimeAsset.category == AssetCategory.models.rawValue, + runtimeAsset.path.pathExtension.lowercased() == "untold" + { + return runtimeAsset + } + + if let manifestAsset = resolvedTiledSceneManifest(for: selectedAsset) { + return manifestAsset + } + + if selectedAsset.category == AssetCategory.streamModels.rawValue, + selectedAsset.path.pathExtension.lowercased() == "remotestream" + { + return selectedAsset + } + + return nil + } + var body: some View { VStack(alignment: .leading, spacing: 8) { // MARK: - Header @@ -57,6 +84,48 @@ struct EnvironmentView: View { Divider() + // MARK: - Scene Authored Data + + VStack(alignment: .leading, spacing: 6) { + Text("Scene Authored") + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.editorTextPrimary) + + Button(action: loadSceneAuthoredFromSelection) { + HStack(spacing: 6) { + Image(systemName: "camera.badge.ellipsis") + .font(.system(size: 12)) + Text("Load Scene Authored") + .font(.system(size: 12, weight: .semibold)) + } + .padding(.vertical, 4) + .padding(.horizontal, 8) + .frame(maxWidth: .infinity, alignment: .leading) + .background(selectedSceneAuthoredAsset == nil ? Color.editorSurface.opacity(0.45) : Color.editorAccent) + .foregroundColor(selectedSceneAuthoredAsset == nil ? .editorTextTertiary : .editorTextPrimary) + .cornerRadius(6) + .overlay( + RoundedRectangle(cornerRadius: 6) + .stroke(Color.editorDivider, lineWidth: 1) + ) + } + .buttonStyle(PlainButtonStyle()) + .disabled(selectedSceneAuthoredAsset == nil) + .help("Load Blender-authored cameras and lights from the selected .untold asset or tiled scene manifest") + + HStack { + Text("Source") + .foregroundColor(.editorTextSecondary) + Spacer() + Text(selectedSceneAuthoredAsset?.name ?? "None") + .foregroundColor(.editorTextTertiary) + .lineLimit(1) + } + .font(.system(size: 11)) + } + + Divider() + // MARK: - Add IBL Button (Compact) Button(action: { @@ -163,6 +232,56 @@ struct EnvironmentView: View { intensity = ambientIntensity } } + + private func resolvedRuntimeAsset(for asset: Asset) -> Asset? { + guard asset.isFolder else { return asset } + guard asset.category == AssetCategory.models.rawValue || asset.category == AssetCategory.animations.rawValue else { + return nil + } + guard let runtimeAssetURL = primaryRuntimeAsset(in: asset.path) else { + return nil + } + + return Asset( + name: runtimeAssetURL.lastPathComponent, + category: asset.category, + path: runtimeAssetURL, + isFolder: false + ) + } + + private func resolvedTiledSceneManifest(for asset: Asset) -> Asset? { + guard asset.category == AssetCategory.streamModels.rawValue else { + return nil + } + + if asset.isFolder { + guard let manifestURL = primaryTiledSceneManifest(in: asset.path) else { + return nil + } + + return Asset( + name: manifestURL.lastPathComponent, + category: asset.category, + path: manifestURL, + isFolder: false + ) + } + + guard isTiledSceneManifest(asset.path) else { + return nil + } + + return asset + } + + private func loadSceneAuthoredFromSelection() { + guard let asset = selectedSceneAuthoredAsset else { + return + } + + onLoadSceneAuthored(asset) + } } private struct UndoableEffectToggle: View {