Skip to content
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
# Changelog
## v0.12.14 - 2026-05-22
### 🐞 Fixes
- [Patch] Migrate editor ray picking to ScenePickingSystem (c059ced…)
- [Patch] Added FPS Stats (4e80c55…)
- [Patch] Implemented undo redo feature (9ddf6e3…)
- [Patch] Fix ci build failure (9c7b8e4…)
- [Patch] add support to assetbrowser to import remote asset (5438b79…)
- [Patch] Fix anti-aliasing failure (8ac8c8b…)
- [Patch] Fix anti-aliasing failure (b3eb245…)
- [Patch] Fixed the quick load preview (36825d8…)
- [Patch] fixed rotation gizmo (9d23a4a…)
- [Patch] Modify quick preview (59f1ae2…)
- [Patch] Fixed gizmo parent-child selection (77432da…)
- [Patch] Fixed the direction handler (a19d897…)
- [Patch] Remove debug view from editor (5a592ef…)
- [Patch] Removed export tools (4e279db…)
## v0.12.10 - 2026-04-29
### 🐞 Fixes
- [Patch] Fixed bundle script to include required helper scripts (8bec369…)
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let package = Package(
// Use a branch during active development:
// .package(url: "https://github.com/untoldengine/UntoldEngine.git", branch: "develop"),
// Or pin to a release:
.package(url: "https://github.com/untoldengine/UntoldEngine.git", exact: "0.12.10"),
.package(url: "https://github.com/untoldengine/UntoldEngine.git", exact: "0.12.14"),
],
targets: [
.executableTarget(
Expand Down
428 changes: 135 additions & 293 deletions Sources/UntoldEditor/Editor/AssetBrowserView.swift

Large diffs are not rendered by default.

13 changes: 9 additions & 4 deletions Sources/UntoldEditor/Editor/AssetNodeEditingPolicy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,21 @@ func editableAssetRootEntity(for entityId: EntityID) -> EntityID {
assetRootEntityId(for: entityId) ?? entityId
}

func sceneTransformEntity(for entityId: EntityID) -> EntityID {
entityId
}

func isAssetInstanceRoot(_ entityId: EntityID) -> Bool {
scene.get(component: AssetInstanceComponent.self, for: entityId) != nil
}

func canEditSceneTransform(entityId: EntityID) -> Bool {
isDerivedAssetNode(entityId) == false
hasComponent(entityId: entityId, componentType: LocalTransformComponent.self)
}

func selectableTransformEntity(for entityId: EntityID) -> EntityID {
let editableRoot = editableAssetRootEntity(for: entityId)
return canEditSceneTransform(entityId: editableRoot) ? editableRoot : .invalid
let transformEntity = sceneTransformEntity(for: entityId)
return canEditSceneTransform(entityId: transformEntity) ? transformEntity : .invalid
}

func isBindableAssetMeshNode(_ entityId: EntityID) -> Bool {
Expand All @@ -55,7 +59,8 @@ func canShowComponentInInspector(componentType: Any.Type, for entityId: EntityID

if EditorAuthoringMode.sceneCompositionOnly {
if isDerivedAssetNode(entityId) {
return false
return key == ObjectIdentifier(RenderComponent.self)
|| key == ObjectIdentifier(LocalTransformComponent.self)
}

return key == ObjectIdentifier(RenderComponent.self)
Expand Down
47 changes: 39 additions & 8 deletions Sources/UntoldEditor/Editor/EditorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -713,17 +713,13 @@ public struct EditorView: View {

// MARK: - Quick Preview

private func editor_handleQuickPreview() {
private func editor_handleQuickPreview(mode: QuickPreviewImportMode) {
let openPanel = NSOpenPanel()
openPanel.title = "Quick Preview - Select 3D File"
openPanel.allowedContentTypes = [
UTType(filenameExtension: "untold")!,
UTType(filenameExtension: "ply")!,
UTType(filenameExtension: "json")!,
]
openPanel.title = mode.filePickerTitle
openPanel.allowedContentTypes = mode.allowedContentTypes
openPanel.allowsMultipleSelection = false
openPanel.canChooseDirectories = false
openPanel.message = "Select an Untold asset, PLY Gaussian, or tiled scene manifest to preview"
openPanel.message = mode.filePickerMessage

guard openPanel.runModal() == .OK, let fileURL = openPanel.url else {
return
Expand All @@ -737,6 +733,8 @@ public struct EditorView: View {
return
}

deleteExistingQuickPreviewEntities()

// Create a new entity for the preview
removeGizmo()
let entityId = createEntity()
Expand All @@ -753,6 +751,9 @@ public struct EditorView: View {
}

if fileExtension == "untold" {
clearSceneBatches()
GeometryStreamingSystem.shared.enabled = false

// Load Untold runtime asset using absolute path
setEntityMeshAsync(entityId: entityId, filename: absolutePath, withExtension: fileExtension) { success in
if success {
Expand All @@ -762,10 +763,16 @@ public struct EditorView: View {
}
}
} else if fileExtension == "ply" {
clearSceneBatches()
GeometryStreamingSystem.shared.enabled = false

// Load Gaussian PLY using absolute path
setEntityGaussian(entityId: entityId, filename: absolutePath, withExtension: fileExtension)
print("✅ Quick Preview Gaussian loaded: \(fileName).\(fileExtension)")
} else if fileExtension == "json" {
clearSceneBatches()
GeometryStreamingSystem.shared.enabled = true

setEntityStreamScene(entityId: entityId, url: fileURL) { success in
DispatchQueue.main.async {
if success {
Expand Down Expand Up @@ -809,6 +816,30 @@ public struct EditorView: View {
print("⚠️ Note: Quick Preview entities cannot be saved to scenes (absolute paths not serialized)")
}

private func deleteExistingQuickPreviewEntities() {
let previewEntityIds = getAllGameEntities()
.filter { hasComponent(entityId: $0, componentType: QuickPreviewComponent.self) }

guard previewEntityIds.isEmpty == false else {
return
}

for entityId in previewEntityIds {
destroyEntity(entityId: entityId)
}

if let selectedId = selectionManager.selectedEntity,
previewEntityIds.contains(selectedId)
{
selectionManager.selectedEntity = nil
activeEntity = .invalid
}

editor_entities = getAllGameEntities()
selectionManager.objectWillChange.send()
sceneGraphModel.refreshHierarchy()
}

// MARK: - Quick Preview Save Validation

/// Checks if the scene contains any Quick Preview entities.
Expand Down
Loading
Loading