Skip to content
Draft
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
22 changes: 22 additions & 0 deletions meshroom/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-


from meshroom.api.core import (
setLoglevel,
initialize,
listPlugins,
unregisterPlugin,
registerPlugin,
listNodes,
unregisterNode,
registerNode,
)

from meshroom.api.scene import (
loadGraph,
getNodes,
getBackdropNodes,
getNode,
getNodesInsideBackdrop,
getNodeAttributes,
)
80 changes: 80 additions & 0 deletions meshroom/api/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# -*- coding: utf-8 -*-

"""
Functions that are designed to provide a simple interface
to Meshroom plugins.
"""

import logging
from typing import Union
import meshroom
from meshroom.core import pluginManager


LOGGER = logging.getLogger("MeshroomApi")


def setLoglevel(level: Union[int, str]):
if isinstance(level, str):
level = logging._nameToLevel.get(level.upper(), None)
if not isinstance(level, int):
LOGGER.warning(f"Cannot set level {level} : not an integer.")
return
logging.getLogger().setLevel(level)
levelName = logging.getLevelName(int(level))
LOGGER.info(f"Meshroom log level has been set to {levelName}.")
Comment thread
Alxiice marked this conversation as resolved.


def initialize(plugins=False, rezPlugins=False, nodes=False, submitters=False, pipelines=False):
if plugins:
meshroom.core.initPlugins()
if rezPlugins:
meshroom.core.initRezPlugins()
if nodes:
meshroom.core.initNodes()
nodes = pluginManager.getRegisteredNodePlugins()
LOGGER.info(f"{len(nodes)} Registered NodePlugins")
for n in nodes.values():
LOGGER.info(f"Registered NodePlugin {n.nodeDescriptor.__module__}")
if submitters:
meshroom.core.initSubmitters()
if pipelines:
meshroom.core.initPipelines()


def listPlugins():
plugins = pluginManager.getPlugins()
return plugins


def registerPlugin(plugin):
pluginManager.addPlugin(plugin, registerNodePlugins=True)
LOGGER.info(f"Register Plugin {plugin.name}")


def unregisterPlugin(name):
plugin = pluginManager.getPlugin(name)
if not plugin:
LOGGER.warning(f"No Plugin named {name}")
return
LOGGER.info(f"Unregister Plugin {plugin.name}")
pluginManager.removePlugin(plugin)


def listNodes():
nodes = pluginManager.getRegisteredNodePlugins()
return nodes


def registerNode(nodePlugin):
pluginManager.registerNode(nodePlugin)
LOGGER.info(f"Register NodePlugin {nodePlugin.nodeDescriptor.__module__}")


def unregisterNode(name):
node = pluginManager.getRegisteredNodePlugin(name)
if not node:
LOGGER.warning(f"No NodePlugin named {name}")
return
LOGGER.info(f"Unregister NodePlugin {node.nodeDescriptor.__module__}")
pluginManager.unregisterNode(node)
85 changes: 85 additions & 0 deletions meshroom/api/scene.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# -*- coding: utf-8 -*-

"""
Functions that are designed to provide a simple interface
to Meshroom scenes. This can be used to parse a scene and
get infos about the scene, about specific nodes, etc.
"""

import logging
from typing import Optional

from meshroom.core import graph as meshroomGraph
from meshroom.core.graph import Graph
from meshroom.core.node import BaseNode, BackdropNode
from meshroom.core.attribute import Attribute


LOGGER = logging.getLogger("MeshroomApi")


def loadGraph(filePath, strictCompatibility=False) -> Graph:
g = meshroomGraph.loadGraph(filePath, strictCompatibility=strictCompatibility)
compatibilityNodesNames = [n.name for n in g.compatibilityNodes]
if compatibilityNodesNames:
LOGGER.warning(f"Scene ({filePath}) loaded with compatibility nodes : {compatibilityNodesNames}")
return g
Comment thread
Alxiice marked this conversation as resolved.


def getNodes(graph: Graph, filterTypes: Optional[list[str]]=None) -> list[BaseNode]:
nodes: list[BaseNode] = [n for n in graph.nodes]
if filterTypes:
nodes = [n for n in nodes if n.nodeType in filterTypes]
return nodes


def getBackdropNodes(graph: Graph) -> list[BackdropNode]:
return getNodes(graph, filterTypes=["Backdrop"])


def getNode(graph: Graph, instanceName: str) -> BaseNode:
return graph.node(instanceName)


def getNodesInsideBackdrop(graph: Graph, backdropNode: BackdropNode):
""" List nodes inside a backdrop node

HACK: Except for Backdrop nodes we don't know nodes height and width.
- As of now the width is fixed to 160 so we will use this
- For the height the node header has an height of approximately 20 and it will
likely not change. A node without any exposed param will be at least the double so
we will take a height of 40.

This might not work well, but this will work well enough for controlled cases.
"""

class Rect:
def __init__(self, node):
self.x1 = node.x
self.y1 = node.y
w = node.getNodeWidth() or 160
self.x2 = w + self.x1
h = node.getNodeHeight() or 40
self.y2 = h + self.y1

backdropRect = Rect(backdropNode)

def isNodeInsideBackdrop(node: BaseNode):
nodeRect = Rect(node)
isinside = \
backdropRect.x1 < nodeRect.x1 < nodeRect.x2 < backdropRect.x2 and \
backdropRect.y1 < nodeRect.y1 < nodeRect.y2 < backdropRect.y2
return isinside

nodes = [n for n in getNodes(graph) if n.name != backdropNode.name]
nodesInside = [n for n in nodes if isNodeInsideBackdrop(n)]
return nodesInside


def getNodeAttributes(node: BaseNode, internalAttributes=False, allAttributes=False) -> list[Attribute]:
attributes = []
if allAttributes or not internalAttributes:
attributes.extend(node.getAttributes().values())
if allAttributes or internalAttributes:
attributes.extend(node.getInternalAttributes().values())
return attributes
Comment thread
Alxiice marked this conversation as resolved.
75 changes: 75 additions & 0 deletions tests/resources/templateGraphWithBackdrops.mg
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{
"header": {
"releaseVersion": "2026.1.0+develop",
"fileVersion": "2.1",
"nodesVersions": {
"InputFile": "1.0",
"InputInt": "1.0",
"InputString": "1.0"
},
"template": true
},
"graph": {
"A_1": {
"nodeType": "InputString",
"position": [
-109,
39.0
],
"inputs": {
"string": "string_2"
}
},
"Backdrop_1": {
"nodeType": "Backdrop",
"position": [
-130,
-131
],
"internalInputs": {
"nodeWidth": 200,
"nodeHeight": 311
}
},
"InputString_1": {
"nodeType": "InputString",
"position": [
-109,
-28
],
"inputs": {
"string": "string_1"
}
},
"InsideBackdrop_1": {
"nodeType": "InputFile",
"position": [
-109,
108
],
"inputs": {
"inputFile": "/path1"
}
},
"Int_1": {
"nodeType": "InputInt",
"position": [
-109,
-91
],
"inputs": {
"integer": 3
}
},
"OutsideBackdrop_1": {
"nodeType": "InputFile",
"position": [
-109,
219.0
],
"inputs": {
"inputFile": "/path2"
}
}
}
}
Loading
Loading