Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 32 additions & 16 deletions .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -274,35 +274,51 @@ jobs:
cmake --build . --parallel ${{ env.NPROC }} --config ${{ matrix.CONFIG }} `
--target Open3DViewer
cmake --build . --parallel ${{ env.NPROC }} --config ${{ matrix.CONFIG }} `
--target INSTALL
$cmakeCachePath = Join-Path $env:BUILD_DIR "CMakeCache.txt"
$Env:OPEN3D_VERSION_FULL = (Select-String -Path $cmakeCachePath -Pattern "OPEN3D_VERSION_FULL").Line.Split('=')[1]
$open3dAppPath = Join-Path $env:INSTALL_DIR "bin\Open3D"
Compress-Archive -Path $open3dAppPath -DestinationPath `
"$Env:GITHUB_WORKSPACE/open3d-$Env:OPEN3D_VERSION_FULL-app-windows-amd64.zip"
echo "VIEWER_ZIP_NAME=open3d-$Env:OPEN3D_VERSION_FULL-app-windows-amd64.zip" | Out-File -FilePath `
$Env:GITHUB_ENV -Encoding utf8 -Append

- name: Generate viewer attestation
if: ${{ github.ref == 'refs/heads/main' && matrix.BUILD_SHARED_LIBS == 'OFF' && matrix.STATIC_RUNTIME == 'ON' && matrix.device == 'cpu' && matrix.CONFIG == 'Release' }}
uses: actions/attest@v4
with:
subject-path: ${{ github.workspace }}/${{ env.VIEWER_ZIP_NAME }}
--target install
$version = (Select-String -Path "${{ env.BUILD_DIR }}/CMakeCache.txt" `
-Pattern "OPEN3D_VERSION_FULL").Line.Split('=')[1]
$viewerZip = "${{ env.BUILD_DIR }}/open3d-$version-app-windows-amd64.zip"
Compress-Archive -Path "${{ env.INSTALL_DIR }}/bin/Open3D" `
-DestinationPath $viewerZip
"VIEWER_ZIP_PATH=$viewerZip" | Out-File -FilePath $Env:GITHUB_ENV `
-Encoding utf8 -Append

- name: Upload Viewer
if: ${{ matrix.BUILD_SHARED_LIBS == 'OFF' && matrix.STATIC_RUNTIME == 'ON' && matrix.device == 'cpu' && matrix.CONFIG == 'Release' }}
uses: actions/upload-artifact@v4
with:
name: open3d-app-windows-amd64
path: ${{ github.workspace }}/${{ env.VIEWER_ZIP_NAME }}
path: ${{ env.VIEWER_ZIP_PATH }}
if-no-files-found: error

- name: Update devel release with viewer
if: ${{ github.ref == 'refs/heads/main' && matrix.BUILD_SHARED_LIBS == 'OFF' && matrix.STATIC_RUNTIME == 'ON' && matrix.device == 'cpu' && matrix.CONFIG == 'Release' }}
env:
GH_TOKEN: ${{ github.token }}
run: |
bash .github/workflows/update_release.sh "${{ env.VIEWER_ZIP_NAME }}"
bash .github/workflows/update_release.sh "${{ env.VIEWER_ZIP_PATH }}"

- name: Setup WinApp CLI
if: ${{ matrix.BUILD_SHARED_LIBS == 'OFF' && matrix.STATIC_RUNTIME == 'ON' && matrix.device == 'cpu' && matrix.CONFIG == 'Release' }}
uses: microsoft/setup-WinAppCli@v0.1

- name: Build MSIX
working-directory: ${{ env.BUILD_DIR }}
if: ${{ matrix.BUILD_SHARED_LIBS == 'OFF' && matrix.STATIC_RUNTIME == 'ON' && matrix.device == 'cpu' && matrix.CONFIG == 'Release' }}
run: |
$ErrorActionPreference = 'Stop'
cmake --build . --parallel ${{ env.NPROC }} --config ${{ matrix.CONFIG }} `
--target Open3DViewerMSIX

- name: Upload MSIX test package
if: ${{ matrix.BUILD_SHARED_LIBS == 'OFF' && matrix.STATIC_RUNTIME == 'ON' && matrix.device == 'cpu' && matrix.CONFIG == 'Release' }}
uses: actions/upload-artifact@v4
with:
name: open3d-app-windows-msix-amd64
path: |
${{ env.MSIX_PATH }}
${{ env.BUILD_DIR }}\msix-package\Open3D.cer
if-no-files-found: error

- name: Run C++ unit tests
if: ${{ matrix.device != 'cuda' }}
Expand Down
25 changes: 25 additions & 0 deletions cpp/apps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ macro(open3d_add_app_gui SRC_DIR APP_NAME TARGET_NAME)
COMMAND ${CMAKE_COMMAND} -E copy_directory "${GUI_RESOURCE_DIR}" "${APP_DIR}/${RESOURCE_DIR_NAME}"
)
if (UNIX)
install(FILES "$<TARGET_FILE:TBB::tbb>"
DESTINATION "${CMAKE_INSTALL_PREFIX}/bin/${APP_NAME}")
install(DIRECTORY "${APP_DIR}"
DESTINATION "${CMAKE_INSTALL_PREFIX}/bin"
USE_SOURCE_PERMISSIONS)
Expand Down Expand Up @@ -131,6 +133,17 @@ macro(open3d_add_app_common SRC_DIR APP_NAME TARGET_NAME)
elseif (WIN32)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../${APP_NAME}")
add_executable(${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
# Suppress the console window while keeping the standard main() entry point (no WinMain).
set_target_properties(${TARGET_NAME} PROPERTIES WIN32_EXECUTABLE TRUE)
if (CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
target_link_options(${TARGET_NAME} PRIVATE
"-Xlinker" "/ENTRY:mainCRTStartup")
elseif (MSVC)
target_link_options(${TARGET_NAME} PRIVATE "/ENTRY:mainCRTStartup")
else()
target_link_options(${TARGET_NAME} PRIVATE
"-Xlinker" "/ENTRY:mainCRTStartup")
endif()
else()
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../${APP_NAME}")
add_executable(${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})
Expand All @@ -155,6 +168,18 @@ endmacro()
if (BUILD_GUI)
open3d_add_app_common(Open3DViewer Open3D Open3DViewer)
open3d_add_app_gui(Open3DViewer Open3D Open3DViewer)
if (WIN32)
add_custom_target(Open3DViewerMSIX
COMMAND "${CMAKE_COMMAND}" --install "${CMAKE_BINARY_DIR}"
--config "$<CONFIG>"
COMMAND powershell.exe -NoProfile -ExecutionPolicy Bypass
-File "${PROJECT_SOURCE_DIR}/cpp/apps/Open3DViewer/WindowsMSIX/build_msix.ps1"
-InstallDir "${CMAKE_INSTALL_PREFIX}/bin/Open3D"
-Version "${PROJECT_VERSION}"
-OutDir "${CMAKE_BINARY_DIR}/msix-package"
VERBATIM)
add_dependencies(Open3DViewerMSIX Open3DViewer)
endif()
endif()

open3d_add_app_common(OfflineReconstruction OfflineReconstruction OfflineReconstruction)
10 changes: 10 additions & 0 deletions cpp/apps/Open3DViewer/Open3DViewer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,14 @@
<glob pattern="*.xyzrgb"/>
</mime-type>

<mime-type type="application/x-splat">
<comment>3D Gaussian Splat</comment>
<glob pattern="*.splat"/>
</mime-type>

<mime-type type="application/x-spz">
<comment>3D Gaussian Splat (compressed)</comment>
<glob pattern="*.spz"/>
</mime-type>

</mime-info>
85 changes: 85 additions & 0 deletions cpp/apps/Open3DViewer/WindowsMSIX/AppxManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
MSIX manifest for the Open3D Viewer (Windows x64, side-load distribution).

Design decisions:
- Checked in as source; only the Version attribute is substituted at pack time
in CI (placeholder @OPEN3D_MSIX_VERSION@ → four-part version, e.g. 0.18.0.0).
- Publisher "CN=Open3D" is matched by the self-signed dev cert generated in CI
using the manifest publisher value
- runFullTrust is the only required capability for a packaged Win32 desktop app.
- File associations are derived from cpp/apps/Open3DViewer/Open3DViewer.xml.
The viewer receives the opened file path as argv[1] (standard packaged app
full-trust activation behaviour).
- MSIX PNGs are kept with the shared GUI resources and installed under
resources/ alongside the viewer's other runtime resources.
-->
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">

<Identity
Name="Open3DViewer"
Publisher="CN=Open3D"
Version="@OPEN3D_MSIX_VERSION@"
ProcessorArchitecture="x64" />

<Properties>
<DisplayName>Open3D</DisplayName>
<PublisherDisplayName>Open3D</PublisherDisplayName>
<Logo>resources\StoreLogo.png</Logo>
</Properties>

<Dependencies>
<!-- Windows 10 1809 (RS5) minimum; tested on Server 2022 (windows-2022 CI runner). -->
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.20348.0" />
</Dependencies>

<Resources>
<Resource Language="en-us" />
</Resources>

<Applications>
<Application Id="Open3DViewer"
Executable="Open3D.exe"
EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements
DisplayName="Open3D"
Description="Open3D Viewer for 3D data, meshes, point clouds and Gaussian Splats."
Square150x150Logo="resources\Square150x150Logo.png"
Square44x44Logo="resources\Square44x44Logo.png"
BackgroundColor="transparent" />
<Extensions>
<!-- Register file associations for all formats supported by Open3D.
Source of truth: cpp/apps/Open3DViewer/Open3DViewer.xml -->
<uap:Extension Category="windows.fileTypeAssociation">
<uap:FileTypeAssociation Name="open3dformats">
<uap:Logo>resources\Square44x44Logo.png</uap:Logo>
<uap:DisplayName>Open3D 3D file</uap:DisplayName>
<uap:SupportedFileTypes>
<uap:FileType>.ply</uap:FileType>
<uap:FileType>.stl</uap:FileType>
<uap:FileType>.obj</uap:FileType>
<uap:FileType>.off</uap:FileType>
<uap:FileType>.fbx</uap:FileType>
<uap:FileType>.glb</uap:FileType>
<uap:FileType>.gltf</uap:FileType>
<uap:FileType>.pcd</uap:FileType>
<uap:FileType>.pts</uap:FileType>
<uap:FileType>.xyz</uap:FileType>
<uap:FileType>.splat</uap:FileType>
<uap:FileType>.spz</uap:FileType>
</uap:SupportedFileTypes>
</uap:FileTypeAssociation>
</uap:Extension>
</Extensions>
</Application>
</Applications>

<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>

</Package>
62 changes: 62 additions & 0 deletions cpp/apps/Open3DViewer/WindowsMSIX/build_msix.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<#
.SYNOPSIS
Stages, signs, and packs the Open3D Viewer as a side-load MSIX.

.PARAMETER InstallDir
Directory the viewer was installed to (contains Open3D.exe + resources/).
.PARAMETER Version
Three-part Open3D version from CMake.
.PARAMETER OutDir
Directory to write the staged files, cert, and final .msix/.cer into.
#>
param(
[Parameter(Mandatory = $true)][string]$InstallDir,
[Parameter(Mandatory = $true)][string]$Version,
[Parameter(Mandatory = $true)][string]$OutDir
)

$ErrorActionPreference = 'Stop'

$OPEN3D_VERSION = $Version
$MSIX_VERSION = "$Version.0"

# Stage the already-installed viewer and add the manifest.
$STAGING = Join-Path $OutDir "msix-staging"
Remove-Item -Recurse -Force $STAGING -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force -Path $STAGING | Out-Null
Copy-Item (Join-Path $InstallDir "Open3D.exe") $STAGING
Copy-Item (Join-Path $InstallDir "tbb*.dll") $STAGING
Copy-Item -Recurse (Join-Path $InstallDir "resources") $STAGING
Copy-Item (Join-Path $PSScriptRoot "AppxManifest.xml") $STAGING

# Substitute version placeholder in the staged manifest.
$manifestPath = Join-Path $STAGING "AppxManifest.xml"
(Get-Content $manifestPath) -replace '@OPEN3D_MSIX_VERSION@', $MSIX_VERSION |
Set-Content $manifestPath

# Generate self-signed cert whose subject matches Publisher="CN=Open3D".
# Export .cer (public key only) so users can install it to trust the package.
$MSIX_NAME = "Open3DViewer-$OPEN3D_VERSION-x64.msix"
$MSIX_PATH = Join-Path $OutDir $MSIX_NAME
$PFX_PATH = Join-Path $OutDir "Open3D.pfx"
try {
winapp cert generate `
--manifest $manifestPath `
--output $PFX_PATH `
--export-cer `
--if-exists overwrite

# Pack and sign the MSIX. The private key is removed immediately after use.
winapp pack $STAGING `
--output $MSIX_PATH `
--cert $PFX_PATH
}
finally {
Remove-Item $PFX_PATH -Force -ErrorAction SilentlyContinue
}

# If running in GitHub Actions, export the MSIX name and path to the environment.
if ($env:GITHUB_ENV) {
echo "MSIX_NAME=$MSIX_NAME" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
echo "MSIX_PATH=$MSIX_PATH" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
}
1 change: 1 addition & 0 deletions cpp/open3d/visualization/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ if (BUILD_GUI)
else()
target_sources(visualization_impl PRIVATE
rendering/gaussian_splat/ComputeGPUVulkan.cpp
rendering/GpuAdapterSelection.cpp
rendering/gaussian_splat/GaussianSplatOpenGLContext.cpp
rendering/gaussian_splat/GaussianSplatVulkanBackend.cpp
rendering/gaussian_splat/GaussianSplatVulkanInteropContext.cpp
Expand Down
18 changes: 18 additions & 0 deletions cpp/open3d/visualization/gui/GLFWWindowSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
#endif
#include "open3d/visualization/gui/Native.h"
#include "open3d/visualization/gui/Window.h"
#if defined(__linux__)
#include "open3d/visualization/rendering/GpuAdapterSelection.h"
#include "open3d/visualization/rendering/gaussian_splat/GaussianSplatVulkanInteropContext.h"
#endif
#include "open3d/visualization/rendering/filament/FilamentEngine.h"
#include "open3d/visualization/rendering/filament/FilamentRenderer.h"

Expand Down Expand Up @@ -111,6 +115,16 @@ void GLFWWindowSystem::Initialize() {
glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE);
#endif
#if defined(__linux__)
// GLFW/GLX caches the vendor selection during glfwInit(). Select the
// Vulkan adapter and apply PRIME steering before initializing GLFW so the
// helper OpenGL context can be created on the same GPU.
auto& vk_ctx = rendering::GaussianSplatVulkanInteropContext::GetInstance();
if (!vk_ctx.IsValid() && vk_ctx.Initialize() && vk_ctx.IsValid()) {
const rendering::GpuAdapterInfo adapter =
rendering::GetAdapterInfo(vk_ctx.GetPhysicalDevice());
rendering::SteerNextGLContextToAdapter(adapter);
}

// Filament (April 2026) selects PlatformGLX exclusively on Linux
// (compile-time decision in PlatformFactory.cpp). Force GLFW to X11 so the
// native window handle is an X11 Window (XID), matching what PlatformGLX
Expand Down Expand Up @@ -172,6 +186,10 @@ GLFWWindowSystem::OSWindow GLFWWindowSystem::CreateOSWindow(Window* o3d_window,

auto* glfw_window = glfwCreateWindow(width, height, title, NULL, NULL);

#if defined(_WIN32)
SetNativeWindowIcon(glfw_window);
#endif

glfwSetWindowUserPointer(glfw_window, o3d_window);
glfwSetWindowSizeCallback(glfw_window, ResizeCallback);
glfwSetWindowPosCallback(glfw_window, WindowMovedCallback);
Expand Down
5 changes: 5 additions & 0 deletions cpp/open3d/visualization/gui/Native.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ namespace visualization {
namespace gui {

void* GetNativeDrawable(GLFWwindow* glfw_window);
// GLFW uses the generic Windows application icon unless the native window is
// assigned the icon embedded in the executable.
#ifdef _WIN32
void SetNativeWindowIcon(GLFWwindow* glfw_window);
#endif // _WIN32
// Note that Windows cannot post an expose event so it must draw immediately.
// Therefore this function cannot be called while drawing.
void PostNativeExposeEvent(GLFWwindow* glfw_window);
Expand Down
24 changes: 24 additions & 0 deletions cpp/open3d/visualization/gui/NativeWin32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,30 @@ void* GetNativeDrawable(GLFWwindow* glfw_window) {
return glfwGetWin32Window(glfw_window);
}

void SetNativeWindowIcon(GLFWwindow* glfw_window) {
HWND window = glfwGetWin32Window(glfw_window);
if (!window) {
return;
}
HINSTANCE instance = GetModuleHandle(nullptr);
HICON small_icon = static_cast<HICON>(LoadImage(
instance, "IDI_ICON1", IMAGE_ICON, GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON), 0));
HICON large_icon = static_cast<HICON>(LoadImage(
instance, "IDI_ICON1", IMAGE_ICON, GetSystemMetrics(SM_CXICON),
GetSystemMetrics(SM_CYICON), 0));
if (small_icon && large_icon) {
SendMessage(window, WM_SETICON, ICON_SMALL,
reinterpret_cast<LPARAM>(small_icon));
SendMessage(window, WM_SETICON, ICON_BIG,
reinterpret_cast<LPARAM>(large_icon));
SetClassLongPtr(window, GCLP_HICON,
reinterpret_cast<LONG_PTR>(large_icon));
SetClassLongPtr(window, GCLP_HICONSM,
reinterpret_cast<LONG_PTR>(small_icon));
}
}

void PostNativeExposeEvent(GLFWwindow* glfw_window) {
InvalidateRect(glfwGetWin32Window(glfw_window), NULL, FALSE);
// InvalidateRect() does not actually post an event to the message queue.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading