Skip to content

Commit

Permalink
feat: add visionOS support
Browse files Browse the repository at this point in the history
  • Loading branch information
okwasniewski committed Aug 9, 2024
1 parent d0c9707 commit 8731cae
Show file tree
Hide file tree
Showing 18 changed files with 452 additions and 14 deletions.
2 changes: 1 addition & 1 deletion Apps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ if(NOT ANDROID)
add_subdirectory(Playground)
endif()

if((WIN32 AND NOT WINDOWS_STORE) OR (APPLE AND NOT IOS) OR (UNIX AND NOT ANDROID))
if((WIN32 AND NOT WINDOWS_STORE) OR (APPLE AND NOT IOS AND NOT VISIONOS) OR (UNIX AND NOT ANDROID))
add_subdirectory(UnitTests)
endif()

Expand Down
46 changes: 43 additions & 3 deletions Apps/Playground/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,24 @@ if(APPLE)
"iOS/LibNativeBridge.mm")
set_source_files_properties(${SCRIPTS} ${BABYLON_SCRIPTS} ${DEPENDENCIES} PROPERTIES MACOSX_PACKAGE_LOCATION "Scripts")
set_source_files_properties(${REFERENCE_IMAGES} PROPERTIES MACOSX_PACKAGE_LOCATION "ReferenceImages")
set(ADDITIONAL_LIBRARIES ${ADDITIONAL_LIBRARIES} PRIVATE NativeCamera)
elseif(VISIONOS)
set(PLIST_FILE
"${CMAKE_CURRENT_LIST_DIR}/visionOS/Info.plist")
set(RESOURCE_FILES ${SCRIPTS})
set(SOURCES
${SOURCES}
"visionOS/App.swift"
"visionOS/LibNativeBridge.cpp"
"visionOS/LibNativeBridge.hpp"
)
set(ADDITIONAL_LIBRARIES PRIVATE NativeXr)
set_source_files_properties(${SCRIPTS} ${BABYLON_SCRIPTS} ${DEPENDENCIES} PROPERTIES MACOSX_PACKAGE_LOCATION "Scripts")
set_source_files_properties(${REFERENCE_IMAGES} PROPERTIES MACOSX_PACKAGE_LOCATION "ReferenceImages")
else()
set(PLIST_FILE "${CMAKE_CURRENT_LIST_DIR}/macOS/Info.plist")
set(STORYBOARD "${CMAKE_CURRENT_LIST_DIR}/macOS/Base.lproj/Main.storyboard")
set(ADDITIONAL_LIBRARIES ${ADDITIONAL_LIBRARIES} PRIVATE NativeCamera)
set(RESOURCE_FILES ${STORYBOARD})
set(SOURCES
${SOURCES}
Expand All @@ -49,9 +64,7 @@ if(APPLE)
set_source_files_properties(${SCRIPTS} ${BABYLON_SCRIPTS} ${DEPENDENCIES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/Scripts")
set_source_files_properties(${REFERENCE_IMAGES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/ReferenceImages")
endif()
set(ADDITIONAL_LIBRARIES ${ADDITIONAL_LIBRARIES}
PRIVATE ${JAVASCRIPTCORE_LIBRARY}
PRIVATE NativeCamera)
set(ADDITIONAL_LIBRARIES ${ADDITIONAL_LIBRARIES} PRIVATE ${JAVASCRIPTCORE_LIBRARY})
set(RESOURCE_FILES ${STORYBOARD})
elseif(UNIX)
set(SOURCES
Expand Down Expand Up @@ -162,6 +175,30 @@ if(APPLE)
set(CMAKE_Swift_COMPILER_FORCED TRUE)
set(CMAKE_Swift_LANGUAGE_VERSION 4.0)
enable_language(Swift)
elseif(VISIONOS)
set_target_properties(Playground PROPERTIES
MACOSX_BUNDLE true
MACOSX_BUNDLE_INFO_PLIST "${PLIST_FILE}"
XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES
RESOURCE "${RESOURCE_FILES}"

XCODE_ATTRIBUTE_XROS_DEPLOYMENT_TARGET ${DEPLOYMENT_TARGET}
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.BabylonNative.Playground.visionOS"

XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "${CMAKE_CURRENT_LIST_DIR}/visionOS/LibNativeBridge.hpp"
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
XCODE_ATTRIBUTE_ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES YES
XCODE_ATTRIBUTE_SWIFT_OBJC_INTEROP_MODE "objcxx"

# CMake seems to add a custom flag "-Wno-unknown-pragmas" to the Swift compiler. That flag is used for Clang,
# So we need to make sure we override it with nothing here in order to compile Swift.
XCODE_ATTRIBUTE_OTHER_SWIFT_FLAGS "")

# Swift support
set(CMAKE_Swift_COMPILER_FORCED TRUE)
set(CMAKE_Swift_LANGUAGE_VERSION 5.0)
enable_language(Swift)
else()
target_link_libraries(Playground PUBLIC "-framework MetalKit")

Expand Down Expand Up @@ -205,3 +242,6 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${REFERENCE_IMAGES})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SCRIPTS})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES})
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Playground)

set_property(TARGET Playground PROPERTY XCODE_GENERATE_SCHEME YES)
set_property(TARGET Playground PROPERTY XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED YES)
100 changes: 100 additions & 0 deletions Apps/Playground/visionOS/App.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import SwiftUI
import CompositorServices

struct ContentStageConfiguration: CompositorLayerConfiguration {
func makeConfiguration(capabilities: LayerRenderer.Capabilities, configuration: inout LayerRenderer.Configuration) {
configuration.depthFormat = .depth32Float
configuration.colorFormat = .bgra8Unorm_srgb

let foveationEnabled = capabilities.supportsFoveation
configuration.isFoveationEnabled = foveationEnabled

let options: LayerRenderer.Capabilities.SupportedLayoutsOptions = foveationEnabled ? [.foveationEnabled] : []
let supportedLayouts = capabilities.supportedLayouts(options: options)

configuration.layout = supportedLayouts.contains(.layered) ? .layered : .dedicated
}
}

class Renderer {
let layerRenderer: LayerRenderer
var libNativeBridge: LibNativeBridge

init(_ layerRenderer: LayerRenderer) {
self.layerRenderer = layerRenderer
self.libNativeBridge = LibNativeBridge(layerRenderer)
}

func startRenderLoop() {
let renderThread = Thread {
self.renderLoop()
}
renderThread.name = "Render Thread"
renderThread.start()
}


func renderLoop() {
while true {
if layerRenderer.state == .invalidated {
print("Layer is invalidated")

libNativeBridge.shutdown()
return
} else if layerRenderer.state == .paused {
layerRenderer.waitUntilRunning()
continue
} else {
autoreleasepool {
libNativeBridge.initialize()
libNativeBridge.render()
}
}
}
}
}


@main
struct ExampleApp: App {
@State private var showImmersiveSpace = false
@State private var immersiveSpaceIsShown = false

@Environment(\.openImmersiveSpace) var openImmersiveSpace
@Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace

var body: some Scene {
WindowGroup {
VStack {
Toggle("Show Immersive Space", isOn: $showImmersiveSpace)
.toggleStyle(.button)
.padding(.top, 50)
}
.onChange(of: showImmersiveSpace) { _, newValue in
Task {
if newValue {
switch await openImmersiveSpace(id: "ImmersiveSpace") {
case .opened:
immersiveSpaceIsShown = true
case .error, .userCancelled:
fallthrough
@unknown default:
immersiveSpaceIsShown = false
showImmersiveSpace = false
}
} else if immersiveSpaceIsShown {
await dismissImmersiveSpace()
immersiveSpaceIsShown = false
}
}
}

}
ImmersiveSpace(id: "ImmersiveSpace") {
CompositorLayer(configuration: ContentStageConfiguration()) { layerRenderer in
let renderer = Renderer(layerRenderer)
renderer.startRenderLoop()
}
}.immersionStyle(selection: .constant(.mixed), in: .mixed, .full)
}
}
33 changes: 33 additions & 0 deletions Apps/Playground/visionOS/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Need photo library permission for debug code to save photo captures</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationPreferredDefaultSceneSessionRole</key>
<string>UIWindowSceneSessionRoleApplication</string>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict/>
</dict>
</dict>
</plist>
110 changes: 110 additions & 0 deletions Apps/Playground/visionOS/LibNativeBridge.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#import "LibNativeBridge.hpp"
#import <Babylon/AppRuntime.h>
#import <Babylon/Graphics/Device.h>
#import <Babylon/ScriptLoader.h>
#import <Babylon/Plugins/NativeEngine.h>
#import <Babylon/Plugins/NativeInput.h>
#import <Babylon/Plugins/NativeOptimizations.h>
#import <Babylon/Plugins/NativeXr.h>
#import <Babylon/Polyfills/Canvas.h>
#import <Babylon/Polyfills/Console.h>
#import <Babylon/Polyfills/Window.h>
#import <Babylon/Polyfills/XMLHttpRequest.h>

std::optional<Babylon::Graphics::Device> device{};
std::optional<Babylon::Graphics::DeviceUpdate> update{};
std::optional<Babylon::AppRuntime> runtime{};
std::optional<Babylon::Polyfills::Canvas> nativeCanvas{};
std::optional<Babylon::Plugins::NativeXr> nativeXr{};
Babylon::Plugins::NativeInput* nativeInput{};
bool isXrActive{};


bool LibNativeBridge::initialize() {
if (m_initialized) {
return true;
}

Babylon::Graphics::Configuration graphicsConfig{};
graphicsConfig.Window = m_layerRenderer;
// Pass in visionOS default widht and height.
#if TARGET_OS_SIMULATOR
graphicsConfig.Width = static_cast<size_t>(2732);
graphicsConfig.Height = static_cast<size_t>(2048);
#else
graphicsConfig.Width = static_cast<size_t>(1920);
graphicsConfig.Height = static_cast<size_t>(1824);
#endif

device.emplace(graphicsConfig);
update.emplace(device->GetUpdate("update"));

device->StartRenderingCurrentFrame();
update->Start();

runtime.emplace();

runtime->Dispatch([](Napi::Env env)
{
device->AddToJavaScript(env);

Babylon::Polyfills::Console::Initialize(env, [](const char* message, auto) {
NSLog(@"%s", message);
});

nativeCanvas.emplace(Babylon::Polyfills::Canvas::Initialize(env));

Babylon::Polyfills::Window::Initialize(env);

Babylon::Polyfills::XMLHttpRequest::Initialize(env);

Babylon::Plugins::NativeEngine::Initialize(env);

Babylon::Plugins::NativeOptimizations::Initialize(env);
// nativeXr.emplace(Babylon::Plugins::NativeXr::Initialize(env));
// nativeXr->UpdateWindow((__bridge void*)m_layerRenderer);

nativeInput = &Babylon::Plugins::NativeInput::CreateForJavaScript(env);
});

Babylon::ScriptLoader loader{ *runtime };
loader.LoadScript("app:///Scripts/ammo.js");
loader.LoadScript("app:///Scripts/recast.js");
loader.LoadScript("app:///Scripts/babylon.max.js");
loader.LoadScript("app:///Scripts/babylonjs.loaders.js");
loader.LoadScript("app:///Scripts/babylonjs.materials.js");
loader.LoadScript("app:///Scripts/babylon.gui.js");
loader.LoadScript("app:///Scripts/experience.js");
m_initialized = true;

return true;
}

void LibNativeBridge::render() {
if (device && m_initialized)
{
update->Finish();
device->FinishRenderingCurrentFrame();
device->StartRenderingCurrentFrame();
update->Start();
}
}

void LibNativeBridge::shutdown() {
if (!m_initialized) {
return;
}

if (device)
{
update->Finish();
device->FinishRenderingCurrentFrame();
}

nativeInput = {};
nativeCanvas.reset();
runtime.reset();
update.reset();
device.reset();
m_initialized = false;
}
25 changes: 25 additions & 0 deletions Apps/Playground/visionOS/LibNativeBridge.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef BgfxAdapter_hpp
#define BgfxAdapter_hpp

#include <CompositorServices/CompositorServices.h>
#include <ARKit/ARKit.h>

class LibNativeBridge {
private:
bool m_initialized = false;
cp_layer_renderer_t m_layerRenderer = NULL;

public:
LibNativeBridge(cp_layer_renderer_t layerRenderer) : m_layerRenderer(layerRenderer) {
}

~LibNativeBridge() {
shutdown();
}

bool initialize(void);
void shutdown(void);
void render(void);
};

#endif /* BgfxAdapter_hpp */
3 changes: 2 additions & 1 deletion Apps/UnitTests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,5 @@ endforeach()

set_property(TARGET UnitTests PROPERTY FOLDER Apps)
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES} ${SCRIPTS})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../node_modules PREFIX node_modules FILES ${EXTERNAL_SCRIPTS})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../node_modules PREFIX node_modules FILES ${EXTERNAL_SCRIPTS})
set_property(TARGET UnitTests PROPERTY XCODE_GENERATE_SCHEME YES)
Loading

0 comments on commit 8731cae

Please # to comment.