From 748437d1dd9d82ef8721b7065dd9a0ca0379e679 Mon Sep 17 00:00:00 2001 From: Cemal Kurt Date: Sat, 7 Mar 2026 20:40:19 +0300 Subject: [PATCH 1/9] [macOS] Remove vertexNormal dependency from SphericalHarmonics shaders Metal RHI strictly validates that vertex shader inputs match the vertex descriptor provided by the geometry. The SphericalHarmonics shaders declared a vertexNormal input, but custom Qt3D geometries (Grid3D, BoundingBox, Locator3D) and some loaded meshes don't provide normal attributes, causing Metal pipeline creation to fail with a crash. Instead of requiring normals as a vertex attribute, compute flat face normals in the fragment shader using dFdx/dFdy screen-space derivatives of the world position. This produces correct lighting for flat-shaded geometry and works regardless of whether the mesh provides normals. Co-Authored-By: Claude Opus 4.6 --- .../Materials/shaders/SphericalHarmonics.frag | 11 ++++++----- .../Materials/shaders/SphericalHarmonics.vert | 5 ++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/meshroom/ui/qml/Viewer3D/Materials/shaders/SphericalHarmonics.frag b/meshroom/ui/qml/Viewer3D/Materials/shaders/SphericalHarmonics.frag index 2c97b3672d..f7a0c55981 100644 --- a/meshroom/ui/qml/Viewer3D/Materials/shaders/SphericalHarmonics.frag +++ b/meshroom/ui/qml/Viewer3D/Materials/shaders/SphericalHarmonics.frag @@ -1,6 +1,6 @@ #version 450 core -layout(location = 0) in vec3 normal; +layout(location = 0) in vec3 worldPos; layout(location = 0) out vec4 fragColor; layout(std140, binding = 0) uniform qt3d_render_view_uniforms { @@ -47,18 +47,19 @@ vec3 resolveSH_Opt(vec3 premulCoefficients[9], vec3 dir) result += premulCoefficients[5] * (dir.x * dir.z); result += premulCoefficients[6] * (dir.y * dir.z); result += premulCoefficients[7] * (dirSq.x - dirSq.y); - result += premulCoefficients[8] * (3 * dirSq.z - 1); + result += premulCoefficients[8] * (3.0 * dirSq.z - 1.0); return result; } void main() { - if(displayNormals) { - // Display normals mode + // Compute flat face normal from screen-space position derivatives + vec3 normal = normalize(cross(dFdx(worldPos), dFdy(worldPos))); + + if (displayNormals) { fragColor = vec4(normal, 1.0); } else { - // Calculate the color from spherical harmonics coeffs fragColor = vec4(resolveSH_Opt(shCoeffs, normal), 1.0); } } diff --git a/meshroom/ui/qml/Viewer3D/Materials/shaders/SphericalHarmonics.vert b/meshroom/ui/qml/Viewer3D/Materials/shaders/SphericalHarmonics.vert index fea3b3d087..ad3018b93e 100644 --- a/meshroom/ui/qml/Viewer3D/Materials/shaders/SphericalHarmonics.vert +++ b/meshroom/ui/qml/Viewer3D/Materials/shaders/SphericalHarmonics.vert @@ -1,9 +1,8 @@ #version 450 core layout(location = 0) in vec3 vertexPosition; -layout(location = 1) in vec3 vertexNormal; -layout(location = 0) out vec3 normal; +layout(location = 0) out vec3 worldPos; layout(std140, binding = 0) uniform qt3d_render_view_uniforms { mat4 viewMatrix; @@ -35,6 +34,6 @@ layout(std140, binding = 1) uniform qt3d_command_uniforms { void main() { - normal = vertexNormal; + worldPos = (modelMatrix * vec4(vertexPosition, 1.0)).xyz; gl_Position = mvp * vec4(vertexPosition, 1.0); } From d4c64081f9cc6a326097060f4ca1c5bc2c98f4a2 Mon Sep 17 00:00:00 2001 From: Cemal Kurt Date: Sat, 7 Mar 2026 20:40:29 +0300 Subject: [PATCH 2/9] [macOS] Add normal attributes to custom Qt3D geometries Built-in Qt3D materials (PhongMaterial, DiffuseSpecularMaterial) expect a vertexNormal attribute in the vertex descriptor. On Metal RHI, if a geometry lacks this attribute, the render pipeline state creation fails with a crash because Metal strictly validates that all shader inputs are satisfied by the vertex descriptor. Add default upward-facing (0, 1, 0) normal attributes to: - Grid3D.qml (dynamic count matching grid vertices) - BoundingBox.qml (24 vertices) - Locator3D.qml (6 vertices) These normals ensure Metal pipeline compatibility while maintaining correct visual appearance for these helper geometries. Co-Authored-By: Claude Opus 4.6 --- meshroom/ui/qml/Viewer3D/BoundingBox.qml | 18 ++++++++++++++++++ meshroom/ui/qml/Viewer3D/Grid3D.qml | 19 +++++++++++++++++++ meshroom/ui/qml/Viewer3D/Locator3D.qml | 17 +++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/meshroom/ui/qml/Viewer3D/BoundingBox.qml b/meshroom/ui/qml/Viewer3D/BoundingBox.qml index 543d90d345..34cb9aae1c 100644 --- a/meshroom/ui/qml/Viewer3D/BoundingBox.qml +++ b/meshroom/ui/qml/Viewer3D/BoundingBox.qml @@ -84,6 +84,24 @@ Entity { ]) } } + Attribute { + attributeType: Attribute.VertexAttribute + vertexBaseType: Attribute.Float + vertexSize: 3 + count: 24 + name: defaultNormalAttributeName + buffer: Buffer { + data: { + var f32 = new Float32Array(24 * 3) + for (var i = 0; i < 24; i++) { + f32[3 * i] = 0.0 + f32[3 * i + 1] = 1.0 + f32[3 * i + 2] = 0.0 + } + return f32 + } + } + } boundingVolumePositionAttribute: boundingBoxPosition } } diff --git a/meshroom/ui/qml/Viewer3D/Grid3D.qml b/meshroom/ui/qml/Viewer3D/Grid3D.qml index 06ec8fdc0c..03e2a41f91 100644 --- a/meshroom/ui/qml/Viewer3D/Grid3D.qml +++ b/meshroom/ui/qml/Viewer3D/Grid3D.qml @@ -46,6 +46,25 @@ Entity { } } } + Attribute { + id: gridNormal + attributeType: Attribute.VertexAttribute + vertexBaseType: Attribute.Float + vertexSize: 3 + count: gridPosition.count + name: defaultNormalAttributeName + buffer: Buffer { + data: { + var f32 = new Float32Array(gridPosition.count * 3) + for (var i = 0; i < gridPosition.count; i++) { + f32[3 * i] = 0.0 + f32[3 * i + 1] = 1.0 + f32[3 * i + 2] = 0.0 + } + return f32 + } + } + } boundingVolumePositionAttribute: gridPosition } }, diff --git a/meshroom/ui/qml/Viewer3D/Locator3D.qml b/meshroom/ui/qml/Viewer3D/Locator3D.qml index f08a92b230..c65c83ed11 100644 --- a/meshroom/ui/qml/Viewer3D/Locator3D.qml +++ b/meshroom/ui/qml/Viewer3D/Locator3D.qml @@ -30,6 +30,23 @@ Entity { ]) } } + Attribute { + attributeType: Attribute.VertexAttribute + vertexBaseType: Attribute.Float + vertexSize: 3 + count: 6 + name: defaultNormalAttributeName + buffer: Buffer { + data: new Float32Array([ + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0 + ]) + } + } Attribute { attributeType: Attribute.VertexAttribute vertexBaseType: Attribute.Float From 926ba92d5726013a9ed0b11950990caef20373b9 Mon Sep 17 00:00:00 2001 From: Cemal Kurt Date: Sat, 7 Mar 2026 20:40:39 +0300 Subject: [PATCH 3/9] [macOS] Add ensureNormals() for dynamically loaded meshes Meshes loaded via SceneLoader (OBJ, PLY, etc.) may lack normal attributes. When these meshes are rendered with built-in Qt3D materials that require vertexNormal, Metal RHI crashes due to missing vertex descriptor entries. Add Scene3DHelper.ensureNormals(entity) that traverses all QGeometry children of a loaded entity and adds default (0, 1, 0) normal attributes to any geometry missing them. Call this method in MediaLoader's sceneLoaderPostProcess before material setup. Co-Authored-By: Claude Opus 4.6 --- meshroom/ui/components/scene3D.py | 44 +++++++++++++++++++++++- meshroom/ui/qml/Viewer3D/MediaLoader.qml | 4 +++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/meshroom/ui/components/scene3D.py b/meshroom/ui/components/scene3D.py index afeac42f50..0a0419b04d 100644 --- a/meshroom/ui/components/scene3D.py +++ b/meshroom/ui/components/scene3D.py @@ -1,6 +1,7 @@ +import struct from math import acos, pi, sqrt, atan2, cos, sin, asin -from PySide6.QtCore import QObject, Slot, QSize, Signal, QPointF +from PySide6.QtCore import QObject, Slot, QSize, Signal, QPointF, QByteArray from PySide6.Qt3DCore import Qt3DCore from PySide6.Qt3DRender import Qt3DRender from PySide6.QtGui import QVector3D, QQuaternion, QVector2D, QVector4D, QMatrix4x4 @@ -52,6 +53,47 @@ def vertexColorCount(self, entity): count += sum([attr.count() for attr in geo.attributes() if attr.name() == "vertexColor"]) return count + @Slot(Qt3DCore.QEntity) + def ensureNormals(self, entity): + """ + Add default normal attributes to geometries that don't have them. + This prevents Metal RHI pipeline crashes when built-in Qt3D materials + (which require vertexNormal) are used with meshes lacking normals. + """ + for geo in entity.findChildren(Qt3DCore.QGeometry): + hasNormals = any(attr.name() == "vertexNormal" for attr in geo.attributes()) + if hasNormals: + continue + + # Find the vertexPosition attribute to get vertex count + posAttr = None + for attr in geo.attributes(): + if attr.name() == "vertexPosition": + posAttr = attr + break + if not posAttr: + continue + + vertexCount = posAttr.count() + + # Create a buffer filled with (0, 1, 0) default normals + normalData = QByteArray(struct.pack(' Date: Sat, 7 Mar 2026 20:40:51 +0300 Subject: [PATCH 4/9] [macOS] Add OpenGL 3.2 Core fallback techniques for custom shaders Add OpenGL 3.2 Core Profile technique alternatives to the existing RHI techniques in WireframeEffect and SphericalHarmonicsEffect. This provides shader compatibility on platforms where Qt3D uses the OpenGL backend instead of RHI/Metal. New shader files: - SphericalHarmonics_gl.vert/frag: GLSL 330 versions using uniforms instead of UBO layout qualifiers - robustwireframe_gl.vert/geom/frag: GLSL 330 wireframe rendering with geometry shader for barycentric coordinate computation Co-Authored-By: Claude Opus 4.6 --- .../Materials/SphericalHarmonicsEffect.qml | 19 ++++++++++ .../Viewer3D/Materials/WireframeEffect.qml | 23 ++++++++++++ .../shaders/SphericalHarmonics_gl.frag | 36 +++++++++++++++++++ .../shaders/SphericalHarmonics_gl.vert | 14 ++++++++ .../Materials/shaders/robustwireframe_gl.frag | 19 ++++++++++ .../Materials/shaders/robustwireframe_gl.geom | 23 ++++++++++++ .../Materials/shaders/robustwireframe_gl.vert | 12 +++++++ 7 files changed, 146 insertions(+) create mode 100644 meshroom/ui/qml/Viewer3D/Materials/shaders/SphericalHarmonics_gl.frag create mode 100644 meshroom/ui/qml/Viewer3D/Materials/shaders/SphericalHarmonics_gl.vert create mode 100644 meshroom/ui/qml/Viewer3D/Materials/shaders/robustwireframe_gl.frag create mode 100644 meshroom/ui/qml/Viewer3D/Materials/shaders/robustwireframe_gl.geom create mode 100644 meshroom/ui/qml/Viewer3D/Materials/shaders/robustwireframe_gl.vert diff --git a/meshroom/ui/qml/Viewer3D/Materials/SphericalHarmonicsEffect.qml b/meshroom/ui/qml/Viewer3D/Materials/SphericalHarmonicsEffect.qml index 1ecb3be0f3..d030a00d96 100644 --- a/meshroom/ui/qml/Viewer3D/Materials/SphericalHarmonicsEffect.qml +++ b/meshroom/ui/qml/Viewer3D/Materials/SphericalHarmonicsEffect.qml @@ -31,6 +31,25 @@ Effect { } } ] + }, + Technique { + graphicsApiFilter { + api: GraphicsApiFilter.OpenGL + profile: GraphicsApiFilter.CoreProfile + majorVersion: 3 + minorVersion: 2 + } + + filterKeys: [ FilterKey { name: "renderingStyle"; value: "forward" } ] + + renderPasses: [ + RenderPass { + shaderProgram: ShaderProgram { + vertexShaderCode: loadSource(Qt.resolvedUrl("shaders/SphericalHarmonics_gl.vert")) + fragmentShaderCode: loadSource(Qt.resolvedUrl("shaders/SphericalHarmonics_gl.frag")) + } + } + ] } ] } diff --git a/meshroom/ui/qml/Viewer3D/Materials/WireframeEffect.qml b/meshroom/ui/qml/Viewer3D/Materials/WireframeEffect.qml index 0ae376e6a1..68af5a0c3b 100644 --- a/meshroom/ui/qml/Viewer3D/Materials/WireframeEffect.qml +++ b/meshroom/ui/qml/Viewer3D/Materials/WireframeEffect.qml @@ -29,6 +29,29 @@ Effect { } } ] + }, + Technique { + graphicsApiFilter { + api: GraphicsApiFilter.OpenGL + profile: GraphicsApiFilter.CoreProfile + majorVersion: 3 + minorVersion: 2 + } + + filterKeys: [ FilterKey { name: "renderingStyle"; value: "forward" } ] + + parameters: [ + ] + + renderPasses: [ + RenderPass { + shaderProgram: ShaderProgram { + vertexShaderCode: loadSource(Qt.resolvedUrl("shaders/robustwireframe_gl.vert")) + geometryShaderCode: loadSource(Qt.resolvedUrl("shaders/robustwireframe_gl.geom")) + fragmentShaderCode: loadSource(Qt.resolvedUrl("shaders/robustwireframe_gl.frag")) + } + } + ] } ] } diff --git a/meshroom/ui/qml/Viewer3D/Materials/shaders/SphericalHarmonics_gl.frag b/meshroom/ui/qml/Viewer3D/Materials/shaders/SphericalHarmonics_gl.frag new file mode 100644 index 0000000000..4f261b9efc --- /dev/null +++ b/meshroom/ui/qml/Viewer3D/Materials/shaders/SphericalHarmonics_gl.frag @@ -0,0 +1,36 @@ +#version 330 core + +in vec3 worldPos; + +out vec4 fragColor; + +uniform vec3 shCoeffs[9]; +uniform bool displayNormals; + +vec3 resolveSH_Opt(vec3 premulCoefficients[9], vec3 dir) +{ + vec3 result = premulCoefficients[0] * dir.x; + result += premulCoefficients[1] * dir.y; + result += premulCoefficients[2] * dir.z; + result += premulCoefficients[3]; + vec3 dirSq = dir * dir; + result += premulCoefficients[4] * (dir.x * dir.y); + result += premulCoefficients[5] * (dir.x * dir.z); + result += premulCoefficients[6] * (dir.y * dir.z); + result += premulCoefficients[7] * (dirSq.x - dirSq.y); + result += premulCoefficients[8] * (3.0 * dirSq.z - 1.0); + return result; +} + +void main() +{ + // Compute flat face normal from screen-space position derivatives + vec3 normal = normalize(cross(dFdx(worldPos), dFdy(worldPos))); + + if (displayNormals) { + fragColor = vec4(normal, 1.0); + } + else { + fragColor = vec4(resolveSH_Opt(shCoeffs, normal), 1.0); + } +} diff --git a/meshroom/ui/qml/Viewer3D/Materials/shaders/SphericalHarmonics_gl.vert b/meshroom/ui/qml/Viewer3D/Materials/shaders/SphericalHarmonics_gl.vert new file mode 100644 index 0000000000..fadf78fd7b --- /dev/null +++ b/meshroom/ui/qml/Viewer3D/Materials/shaders/SphericalHarmonics_gl.vert @@ -0,0 +1,14 @@ +#version 330 core + +in vec3 vertexPosition; + +out vec3 worldPos; + +uniform mat4 modelMatrix; +uniform mat4 mvp; + +void main() +{ + worldPos = (modelMatrix * vec4(vertexPosition, 1.0)).xyz; + gl_Position = mvp * vec4(vertexPosition, 1.0); +} diff --git a/meshroom/ui/qml/Viewer3D/Materials/shaders/robustwireframe_gl.frag b/meshroom/ui/qml/Viewer3D/Materials/shaders/robustwireframe_gl.frag new file mode 100644 index 0000000000..5132c60e85 --- /dev/null +++ b/meshroom/ui/qml/Viewer3D/Materials/shaders/robustwireframe_gl.frag @@ -0,0 +1,19 @@ +#version 330 core + +in vec3 barycentric; + +out vec4 fragColor; + +void main() +{ + float mindist = min(min(barycentric.x, barycentric.y), barycentric.z); + + if (mindist < 0.05) + { + fragColor = vec4(1.0, 1.0, 1.0, 1.0); + } + else + { + discard; + } +} diff --git a/meshroom/ui/qml/Viewer3D/Materials/shaders/robustwireframe_gl.geom b/meshroom/ui/qml/Viewer3D/Materials/shaders/robustwireframe_gl.geom new file mode 100644 index 0000000000..2a19f09ff1 --- /dev/null +++ b/meshroom/ui/qml/Viewer3D/Materials/shaders/robustwireframe_gl.geom @@ -0,0 +1,23 @@ +#version 330 core + +layout(triangles) in; +layout(triangle_strip, max_vertices = 3) out; + +out vec3 barycentric; + +void main() +{ + barycentric = vec3(1.0, 0.0, 0.0); + gl_Position = gl_in[0].gl_Position; + EmitVertex(); + + barycentric = vec3(0.0, 1.0, 0.0); + gl_Position = gl_in[1].gl_Position; + EmitVertex(); + + barycentric = vec3(0.0, 0.0, 1.0); + gl_Position = gl_in[2].gl_Position; + EmitVertex(); + + EndPrimitive(); +} diff --git a/meshroom/ui/qml/Viewer3D/Materials/shaders/robustwireframe_gl.vert b/meshroom/ui/qml/Viewer3D/Materials/shaders/robustwireframe_gl.vert new file mode 100644 index 0000000000..bea7dd9556 --- /dev/null +++ b/meshroom/ui/qml/Viewer3D/Materials/shaders/robustwireframe_gl.vert @@ -0,0 +1,12 @@ +#version 330 core + +in vec3 vertexPosition; + +uniform mat4 modelMatrix; +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; + +void main() +{ + gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(vertexPosition, 1.0); +} From a2513cf4879e2f7cd29e40ca3f7442309206fb92 Mon Sep 17 00:00:00 2001 From: Cemal Kurt Date: Sat, 7 Mar 2026 20:41:00 +0300 Subject: [PATCH 5/9] [macOS] Fix textured material lighting in MaterialSwitcher Override the textured material's ambient, shininess, and specular properties with low fixed values instead of inheriting from the loaded mesh's MTL material properties. Many OBJ files include high ambient values that wash out textures when viewed in the 3D viewer. Using low ambient (#111), no specular (#000), and minimal shininess (1.0) ensures textures are displayed with proper diffuse-only lighting. Co-Authored-By: Claude Opus 4.6 --- meshroom/ui/qml/Viewer3D/MaterialSwitcher.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/meshroom/ui/qml/Viewer3D/MaterialSwitcher.qml b/meshroom/ui/qml/Viewer3D/MaterialSwitcher.qml index 9a9614befa..fe89a46d1f 100644 --- a/meshroom/ui/qml/Viewer3D/MaterialSwitcher.qml +++ b/meshroom/ui/qml/Viewer3D/MaterialSwitcher.qml @@ -95,9 +95,9 @@ Entity { DiffuseMapMaterial { id: textured objectName: "TexturedMaterial" - ambient: root.ambient - shininess: root.shininess - specular: root.specular + ambient: "#111" + shininess: 1.0 + specular: "#000" diffuse: TextureLoader { magnificationFilter: Texture.Linear mirrored: false From 517ff1dda0ab5dc0e4ab9b9640d86a68ad963dd6 Mon Sep 17 00:00:00 2001 From: Cemal Kurt Date: Sat, 7 Mar 2026 20:41:13 +0300 Subject: [PATCH 6/9] [macOS] Add QML plugin path discovery for qtAliceVision Add QML import paths for PySide6's bundled QML modules and the AliceVision bundle's PlugIns directory. This enables the QML engine to discover and load qtAliceVision, SfmDataEntity, and DepthMapEntity QML plugins that provide camera visualization, SfM data overlay, and depth map rendering in the 3D viewer. The PySide6 QML path ensures Qt3D and QtQuick.Scene3D modules are found, while the AV_BUNDLE/PlugIns path provides the AliceVision- specific visualization plugins. Co-Authored-By: Claude Opus 4.6 --- meshroom/ui/app.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/meshroom/ui/app.py b/meshroom/ui/app.py index 665f3f2dac..05a3963439 100644 --- a/meshroom/ui/app.py +++ b/meshroom/ui/app.py @@ -277,6 +277,20 @@ def __init__(self, inputArgs): qInstallMessageHandler(MessageHandler.handler) self.engine.addImportPath(qmlDir) + # Add PySide6's bundled QML modules path (Qt3D, QtQuick.Scene3D, etc.) + pyside6QmlPath = os.path.join(os.path.dirname(QtCore.__file__), "Qt", "qml") + if os.path.isdir(pyside6QmlPath): + self.engine.addImportPath(pyside6QmlPath) + + # Add qtAliceVision QML plugins from the AliceVision bundle + for envVar in ("AV_BUNDLE", "MESHROOM_INSTALL_DIR"): + bundleBase = os.environ.get(envVar, "") + if bundleBase: + pluginsPath = os.path.join(bundleBase, "PlugIns") + if os.path.isdir(pluginsPath): + self.engine.addImportPath(pluginsPath) + logging.info(f"Added QML import path: {pluginsPath}") + break # expose available node types that can be instantiated self.engine.rootContext().setContextProperty("_nodeTypes", {n: {"category": pluginManager.getRegisteredNodePlugins()[n].nodeDescriptor.category} for n in sorted(pluginManager.getRegisteredNodePlugins().keys())}) From 1cc3f83322239eef922e3ff3210060cf8e915c50 Mon Sep 17 00:00:00 2001 From: Cemal Kurt Date: Sat, 7 Mar 2026 20:41:23 +0300 Subject: [PATCH 7/9] [macOS] Add macOS support to setupEnvironment and RHI backend - Guard QT_PLUGIN_PATH and QML2_IMPORT_PATH setup behind directory existence checks. On macOS with PySide6, these directories may not exist in the standard locations and setting invalid paths causes plugin loading failures. - Add macOS-specific DYLD_FALLBACK_LIBRARY_PATH handling (analogous to LD_LIBRARY_PATH on Linux) so AliceVision framework libraries can be found at runtime. - Use Metal as the default RHI backend on macOS (QSG_RHI_BACKEND=metal) via setdefault so it can be overridden, while keeping OpenGL as the default on other platforms. Co-Authored-By: Claude Opus 4.6 --- meshroom/__init__.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/meshroom/__init__.py b/meshroom/__init__.py index 874719e012..b84d461485 100644 --- a/meshroom/__init__.py +++ b/meshroom/__init__.py @@ -160,10 +160,15 @@ def addToEnvPath(var, val, index=-1): env = { "PATH": aliceVisionBinDir, - "QT_PLUGIN_PATH": [qtPluginsDir], - "QML2_IMPORT_PATH": [os.path.join(qtPluginsDir, "qml")] } + # Only add Qt plugin/QML paths if the directories actually exist + if os.path.isdir(qtPluginsDir): + env["QT_PLUGIN_PATH"] = [qtPluginsDir] + qmlImportDir = os.path.join(qtPluginsDir, "qml") + if os.path.isdir(qmlImportDir): + env["QML2_IMPORT_PATH"] = [qmlImportDir] + for key, value in env.items(): logging.debug(f"Add to {key}: {value}") addToEnvPath(key, value, 0) @@ -196,6 +201,13 @@ def addToEnvPath(var, val, index=-1): addToEnvPath("PATH", os.environ.get("ALICEVISION_BIN_PATH", "")) if sys.platform == "win32": addToEnvPath("PATH", os.environ.get("ALICEVISION_LIBPATH", "")) + elif sys.platform == "darwin": + aliceVisionLibPath = os.environ.get("ALICEVISION_LIBPATH", "") + addToEnvPath("DYLD_FALLBACK_LIBRARY_PATH", aliceVisionLibPath) + if rootDir: + aliceVisionLibDir = os.path.join(rootDir, "aliceVision", "lib") + if os.path.isdir(aliceVisionLibDir): + addToEnvPath("DYLD_FALLBACK_LIBRARY_PATH", aliceVisionLibDir) else: addToEnvPath("LD_LIBRARY_PATH", os.environ.get("ALICEVISION_LIBPATH", "")) @@ -203,4 +215,7 @@ def addToEnvPath(var, val, index=-1): os.environ["QML_XHR_ALLOW_FILE_READ"] = '1' os.environ["QML_XHR_ALLOW_FILE_WRITE"] = '1' os.environ["PYSEQ_STRICT_PAD"] = '1' -os.environ["QSG_RHI_BACKEND"] = "opengl" +if sys.platform == "darwin": + os.environ.setdefault("QSG_RHI_BACKEND", "metal") +else: + os.environ["QSG_RHI_BACKEND"] = "opengl" From 167a26ce21c02871bdb617b07ddfc86378e149d8 Mon Sep 17 00:00:00 2001 From: Cemal Kurt Date: Sat, 7 Mar 2026 20:41:31 +0300 Subject: [PATCH 8/9] [macOS] Fix cgroup CPU detection on non-Linux platforms The cgroup CPU count detection reads /proc/self/cgroup which only exists on Linux. On macOS and Windows, this causes a crash when Meshroom tries to determine available CPU cores for task parallelism. Add an early return using os.cpu_count() on non-Linux platforms. Co-Authored-By: Claude Opus 4.6 --- meshroom/core/cgroup.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/meshroom/core/cgroup.py b/meshroom/core/cgroup.py index 7d78a8f895..6e580d2841 100755 --- a/meshroom/core/cgroup.py +++ b/meshroom/core/cgroup.py @@ -63,6 +63,12 @@ def parseNumericList(numericListString): # Try to retrieve limits of cores for the current process' cgroup def getCgroupCpuCount(): + # On non-Linux systems (macOS, Windows), cgroups don't exist. + # Fall back to os.cpu_count() to detect available cores. + import sys + if sys.platform != "linux": + return os.cpu_count() or -1 + # First of all, get pid of process pid = os.getpid() From 6bd7eb7c30c9e2e4c8d01d283e82551a790ef9a8 Mon Sep 17 00:00:00 2001 From: Cemal Kurt Date: Sat, 7 Mar 2026 20:41:40 +0300 Subject: [PATCH 9/9] [macOS] Add groupDesc backward compatibility to GroupAttribute Some AliceVision node descriptors from the MTL-AliceVision bundle use the older 'groupDesc' parameter name instead of 'items' in GroupAttribute constructors. Add backward compatibility by accepting 'groupDesc' as a deprecated alias and defaulting positional params to allow keyword-only usage patterns. Co-Authored-By: Claude Opus 4.6 --- meshroom/core/desc/attribute.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/meshroom/core/desc/attribute.py b/meshroom/core/desc/attribute.py index 5ca5a605cf..0da55bdf09 100644 --- a/meshroom/core/desc/attribute.py +++ b/meshroom/core/desc/attribute.py @@ -196,12 +196,15 @@ def matchDescription(self, value, strict=True): class GroupAttribute(Attribute): """ A macro Attribute composed of several Attributes """ @deprecated.depreciateParam("group", "Param 'group' on {name} should not be used anymore. Please use 'commandLineGroup' instead") - def __init__(self, items, name, label, description, group="allParams", commandLineGroup=_setParamSentinel, + def __init__(self, items=None, name="", label="", description="", group="allParams", commandLineGroup=_setParamSentinel, advanced=False, semantic="", enabled=True, joinChar=" ", brackets=None, visible=True, - exposed=False): + exposed=False, groupDesc=None): """ :param items: the description of the Attributes composing this group + :param groupDesc: deprecated alias for 'items' """ + if items is None and groupDesc is not None: + items = groupDesc self._items = items self._joinChar = joinChar self._brackets = brackets