diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b1694d324b..4f02966241 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -473,7 +473,9 @@ jobs: CIBW_SKIP: '*musllinux*' CIBW_ARCHS: 'auto64' CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 - CIBW_BEFORE_ALL_LINUX: yum install -y libXt-devel + CIBW_BEFORE_ALL_LINUX: yum install -y libXt-devel doxygen + CIBW_BEFORE_ALL_MACOS: brew install doxygen + CIBW_BEFORE_ALL_WINDOWS: choco install doxygen.install -y CIBW_BUILD_VERBOSITY: 1 CIBW_ENVIRONMENT: CMAKE_BUILD_PARALLEL_LEVEL=2 MACOSX_DEPLOYMENT_TARGET: '11.0' diff --git a/CMakeLists.txt b/CMakeLists.txt index 357270cc89..11a55867ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,6 +135,7 @@ set(MATERIALX_PYTHON_EXECUTABLE "" CACHE FILEPATH "Python executable to be used in building the MaterialX Python package (e.g. 'C:/Python39/python.exe').") set(MATERIALX_PYTHON_PYBIND11_DIR "" CACHE PATH "Path to a folder containing the PyBind11 source to be used in building MaterialX Python.") +option(MATERIALX_PYTHON_FORCE_REPLACE_DOCS "Force replace existing docstrings when generating Python binding documentation from Doxygen." OFF) # Settings to define installation layout set(MATERIALX_INSTALL_INCLUDE_PATH "include" CACHE STRING "Install header include path (e.g. 'inc', 'include').") @@ -210,6 +211,7 @@ mark_as_advanced(MATERIALX_DYNAMIC_ANALYSIS) mark_as_advanced(MATERIALX_PYTHON_VERSION) mark_as_advanced(MATERIALX_PYTHON_EXECUTABLE) mark_as_advanced(MATERIALX_PYTHON_PYBIND11_DIR) +mark_as_advanced(MATERIALX_PYTHON_FORCE_REPLACE_DOCS) mark_as_advanced(MATERIALX_OSL_BINARY_OSLC) mark_as_advanced(MATERIALX_OSL_BINARY_TESTRENDER) mark_as_advanced(MATERIALX_OSL_INCLUDE_PATH) @@ -548,16 +550,16 @@ if(MATERIALX_BUILD_TESTS) add_subdirectory(source/MaterialXTest) endif() +if (MATERIALX_BUILD_DOCS) + add_subdirectory(documents) +endif() + # Add Python subdirectories if(MATERIALX_BUILD_PYTHON) add_subdirectory(source/PyMaterialX) add_subdirectory(python) endif() -if(MATERIALX_BUILD_DOCS) - add_subdirectory(documents) -endif() - if(MATERIALX_BUILD_JS) add_subdirectory(source/JsMaterialX) endif() diff --git a/documents/Doxyfile.in b/documents/Doxyfile.in index d3897739cf..890d2d7e04 100644 --- a/documents/Doxyfile.in +++ b/documents/Doxyfile.in @@ -23,3 +23,6 @@ FULL_SIDEBAR = NO QUIET = YES WARN_IF_UNDOCUMENTED = NO + +GENERATE_XML = YES +XML_OUTPUT = doxygen_xml diff --git a/pyproject.toml b/pyproject.toml index 2f5f1cbe17..12aba95f51 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,17 +56,25 @@ logging.level = "DEBUG" # where the package is. wheel.packages = ["python/MaterialX"] +sdist.include = [ + "/documents", +] + sdist.exclude = [ "/build", "/dist", "/resources", "/javascript", - "/documents", "/.github", "MANIFEST.in", "/source/JsMaterialX", ] +wheel.exclude = [ + "/documents", + "documents/", +] + [tool.scikit-build.metadata.version] # https://scikit-build-core.readthedocs.io/en/latest/configuration.html#dynamic-metadata provider = "scikit_build_core.metadata.regex" @@ -81,6 +89,8 @@ result = "{major}.{minor}.{build}" [tool.scikit-build.cmake.define] MATERIALX_BUILD_SHARED_LIBS = 'OFF' # Be explicit MATERIALX_BUILD_PYTHON = 'ON' +MATERIALX_BUILD_DOCS = 'ON' +MATERIALX_PYTHON_FORCE_REPLACE_DOCS = 'ON' MATERIALX_TEST_RENDER = 'OFF' MATERIALX_WARNINGS_AS_ERRORS = 'ON' MATERIALX_BUILD_TESTS = 'OFF' diff --git a/python/Scripts/pybind_docs.py b/python/Scripts/pybind_docs.py new file mode 100644 index 0000000000..207fbfe843 --- /dev/null +++ b/python/Scripts/pybind_docs.py @@ -0,0 +1,514 @@ +#!/usr/bin/env python +""" +pybind11 documentation insertion tool. + +Extracts documentation from Doxygen XML and inserts it into pybind11 bindings +using string matching via signature lookup table. + +Logic: +- Builds a multi-key lookup for all functions (MaterialX::, mx::, Class::method, method) +- Handles free functions without by assuming MaterialX namespace +- Adds class context tracking to correctly document lambda-based bindings +- Supports .def(...) and .def_static(...); skips .def_readonly_static(...) +""" + +import argparse +import re +import json +import xml.etree.ElementTree as ET +from pathlib import Path +from typing import Dict, Optional + +# Defaults (can be overridden by CLI) +DOXYGEN_XML_DIR = Path("build/documents/doxygen_xml") +PYBIND_DIR = Path("source/PyMaterialX") + + +class DocExtractor: + """Extracts documentation from Doxygen XML files and builds a lookup table.""" + + def __init__(self, xml_dir: Path): + self.xml_dir = xml_dir + self.class_docs: Dict[str, str] = {} + self.func_docs: Dict[str, Dict] = {} + # Multi-key lookup: all name variants point to the same doc + self.func_lookup: Dict[str, Dict] = {} + + def extract(self): + if not self.xml_dir.exists(): + raise FileNotFoundError(f"Doxygen XML directory not found: {self.xml_dir}") + + for xml_file in self.xml_dir.glob("*.xml"): + self._process_xml_file(xml_file) + + self._build_lookup_table() + print(f"Extracted {len(self.class_docs)} classes and {len(self.func_docs)} functions") + print(f"Built lookup table with {len(self.func_lookup)} keys") + + def _process_xml_file(self, xml_file: Path): + tree = ET.parse(xml_file) + root = tree.getroot() + + # Class / struct documentation + for compound in root.findall(".//compounddef[@kind='class']") + root.findall(".//compounddef[@kind='struct']"): + self._extract_class_doc(compound) + + # Function documentation + for member in root.findall(".//memberdef[@kind='function']"): + self._extract_func_doc(member) + + def _extract_class_doc(self, compound): + name = self._get_text(compound.find("compoundname")) + brief = self._get_text(compound.find("briefdescription/para")) + detail = self._extract_detail(compound.find("detaileddescription")) + doc = "\n\n".join(filter(None, [brief, detail])) + if doc: + normalized = self._normalize_name(name) + self.class_docs[normalized] = doc + + def _extract_func_doc(self, member): + name = self._get_text(member.find("name")) + qualified = self._get_text(member.find("qualifiedname")) + + # Many free functions have no ; use the bare name + # and normalize to MaterialX::name so lookups can resolve. + if not qualified and name: + qualified = name + + if not qualified: + return + + brief = self._get_text(member.find("briefdescription/para")) + detail = self._extract_detail(member.find("detaileddescription")) + params = self._extract_params(member) + returns = self._get_text(member.find(".//simplesect[@kind='return']")) + + normalized = self._normalize_name(qualified) + self.func_docs[normalized] = { + "brief": brief, + "detail": detail, + "params": params, + "returns": returns, + } + + def _build_lookup_table(self): + for qualified_name, doc in self.func_docs.items(): + for variant in self._generate_name_variants(qualified_name): + if variant not in self.func_lookup: + self.func_lookup[variant] = doc + + def _generate_name_variants(self, qualified_name: str) -> list: + variants = [qualified_name] + parts = qualified_name.split("::") + # Class::method + if len(parts) >= 2: + variants.append("::".join(parts[-2:])) + # method + if len(parts) >= 1: + variants.append(parts[-1]) + # mx:: variant if MaterialX:: + if qualified_name.startswith("MaterialX::"): + mx_variant = qualified_name.replace("MaterialX::", "mx::", 1) + variants.append(mx_variant) + if len(parts) >= 3: + variants.append(f"mx::{parts[-2]}::{parts[-1]}") + return variants + + def _normalize_name(self, name: str) -> str: + if not name: + return name + return name if name.startswith("MaterialX::") else f"MaterialX::{name}" + + def _get_text(self, elem) -> str: + if elem is None: + return "" + text = "".join(elem.itertext()) + return re.sub(r"\s+", " ", text).strip() + + def _extract_detail(self, elem, exclude_tags={"parameterlist", "simplesect"}) -> str: + if elem is None: + return "" + parts = [] + for para in elem.findall("para"): + if not any(para.find(tag) is not None for tag in exclude_tags): + t = self._get_text(para) + if t: + parts.append(t) + return "\n\n".join(parts) + + def _extract_params(self, member) -> Dict[str, str]: + params = {} + for param_item in member.findall(".//parameterlist[@kind='param']/parameteritem"): + name = self._get_text(param_item.find("parameternamelist/parametername")) + desc = self._get_text(param_item.find("parameterdescription")) + if name: + params[name] = desc + return params + + +class DocInserter: + """Inserts documentation into pybind11 binding files.""" + + def __init__(self, extractor: DocExtractor, pybind_dir: Path, force_replace: bool = False): + self.extractor = extractor + self.pybind_dir = pybind_dir + self.force_replace = force_replace + + self.class_pattern = re.compile(r"py::class_<") + self.def_pattern = re.compile(r"\.def(?:_static)?\s*\(") + # Match .def and .def_static; skip .def_readonly_static (constants) + self.def_pattern = re.compile(r"\.def(?:_static)?\s*\(") + self.skip_pattern = re.compile(r"\.def_readonly_static\s*\(") + + def process_all_files(self): + cpp_files = list(self.pybind_dir.rglob("*.cpp")) + patched = 0 + for cpp_file in cpp_files: + if self._process_file(cpp_file): + patched += 1 + print(f"\nProcessed {len(cpp_files)} files, patched {patched}") + + def _process_file(self, cpp_file: Path) -> bool: + content = cpp_file.read_text(encoding="utf-8") + original = content + + content = self._insert_class_docs(content) + content = self._insert_method_docs(content) + + if content != original: + cpp_file.write_text(content, encoding="utf-8") + print(f" - {cpp_file.relative_to(self.pybind_dir.parent)}") + return True + else: + print(f" - {cpp_file.relative_to(self.pybind_dir.parent)}") + return False + + def _insert_class_docs(self, content: str) -> str: + result = [] + pos = 0 + + for match in self.class_pattern.finditer(content): + result.append(content[pos:match.start()]) + + start = match.start() + template_end = self._find_template_end(content, start) + if template_end == -1: + result.append(content[start:match.end()]) + pos = match.end() + continue + + paren_start = content.find('(', template_end) + if paren_start == -1: + result.append(content[start:match.end()]) + pos = match.end() + continue + + paren_end = self._find_matching_paren(content, paren_start) + if paren_end == -1: + result.append(content[start:match.end()]) + pos = match.end() + continue + + args_text = content[paren_start + 1:paren_end] + class_name = self._extract_class_name(args_text) + + if class_name: + doc = self.extractor.class_docs.get(self.extractor._normalize_name(class_name)) + if doc: + args = self._split_args(args_text) + if len(args) >= 3 and not self.force_replace: + result.append(content[start:paren_end + 1]) + pos = paren_end + 1 + continue + + escaped = self._escape_for_cpp(doc) + if len(args) >= 3 and self.force_replace: + new_args = args[:2] + [f'"{escaped}"'] + args[3:] + result.append(content[start:paren_start + 1]) + result.append(", ".join(new_args)) + result.append(")") + else: + result.append(content[start:paren_end]) + result.append(f', "{escaped}")') + pos = paren_end + 1 + continue + + result.append(content[start:paren_end + 1]) + pos = paren_end + 1 + + result.append(content[pos:]) + return "".join(result) + + def _insert_method_docs(self, content: str) -> str: + # Build a map of line numbers to class contexts + class_contexts = self._extract_class_contexts(content) + + result = [] + pos = 0 + + for match in self.def_pattern.finditer(content): + if self.skip_pattern.match(content, match.start()): + continue + + result.append(content[pos:match.start()]) + + start = match.start() + paren_start = content.find('(', start) + if paren_start == -1: + result.append(content[start:match.end()]) + pos = match.end() + continue + + paren_end = self._find_matching_paren(content, paren_start) + if paren_end == -1: + result.append(content[start:match.end()]) + pos = match.end() + continue + + args_text = content[paren_start + 1:paren_end] + args = self._split_args(args_text) + + if len(args) < 2: + result.append(content[start:paren_end + 1]) + pos = paren_end + 1 + continue + + has_doc = self._has_docstring(args) + if has_doc and not self.force_replace: + result.append(content[start:paren_end + 1]) + pos = paren_end + 1 + continue + + callable_ref = args[1].strip() + + current_line = content[:start].count('\n') + class_context = class_contexts.get(current_line) + + doc_entry = self._find_doc_for_callable(callable_ref, class_context) + + if doc_entry: + docstring = self._build_docstring(doc_entry) + escaped = self._escape_for_cpp(docstring) + + if has_doc and self.force_replace: + doc_idx = self._find_docstring_arg_index(args) + if doc_idx is not None: + new_args = args[:doc_idx] + [f'"{escaped}"'] + args[doc_idx + 1:] + result.append(content[start:paren_start + 1]) + result.append(", ".join(new_args)) + result.append(")") + pos = paren_end + 1 + continue + + result.append(content[start:paren_end]) + result.append(f', "{escaped}")') + pos = paren_end + 1 + continue + + result.append(content[start:paren_end + 1]) + pos = paren_end + 1 + + result.append(content[pos:]) + return "".join(result) + + def _extract_class_contexts(self, content: str) -> Dict[int, str]: + contexts = {} + for match in self.class_pattern.finditer(content): + start = match.start() + template_end = self._find_template_end(content, start) + if template_end == -1: + continue + template_start = content.find('<', start) + 1 + template_content = content[template_start:template_end - 1] + class_type = template_content.split(',')[0].strip() + class_name = class_type.split('::')[-1] if '::' in class_type else class_type + + start_line = content[:start].count('\n') + end_pos = content.find(';', start) + if end_pos != -1: + end_line = content[:end_pos].count('\n') + for line in range(start_line, end_line + 1): + contexts[line] = class_name + return contexts + + def _find_doc_for_callable(self, callable_ref: str, class_context: Optional[str] = None) -> Optional[Dict]: + callable_ref = callable_ref.strip() + + # Function pointers like &mx::Class::method or &MaterialX::name + if callable_ref.startswith('&'): + name = callable_ref[1:].strip() + name = re.sub(r'[,\s]+$', '', name) + return self.extractor.func_lookup.get(name) + + # Lambdas: look for elem.method( or obj->method( + method_match = re.search(r'[\.\->](\w+)\s*\(', callable_ref) + if method_match: + method_name = method_match.group(1) + if class_context: + for prefix in ("", "mx::", "MaterialX::"): + qualified = f"{prefix}{class_context}::{method_name}" if prefix else f"{class_context}::{method_name}" + doc = self.extractor.func_lookup.get(qualified) + if doc: + return doc + return self.extractor.func_lookup.get(method_name) + + return None + + def _build_docstring(self, doc_entry: Dict) -> str: + parts = [] + if doc_entry.get("brief"): + parts.append(doc_entry["brief"]) + if doc_entry.get("detail"): + parts.append(doc_entry["detail"]) + params = doc_entry.get("params", {}) + if params: + param_lines = ["Args:"] + for name, desc in params.items(): + param_lines.append(f" {name}: {desc}" if desc else f" {name}:") + parts.append("\n".join(param_lines)) + if doc_entry.get("returns"): + parts.append(f"Returns:\n {doc_entry['returns']}") + return "\n\n".join(parts) + + def _escape_for_cpp(self, s: str) -> str: + if not s: + return "" + s = s.replace("\\", "\\\\").replace('"', '\\"') + s = s.replace("\n", "\\n") + return s + + def _find_template_end(self, content: str, start: int) -> int: + pos = content.find('<', start) + if pos == -1: + return -1 + depth = 1 + i = pos + 1 + in_string = False + while i < len(content) and depth > 0: + c = content[i] + if c == '"' and content[i - 1] != '\\': + in_string = not in_string + elif not in_string: + if c == '<': + depth += 1 + elif c == '>': + depth -= 1 + i += 1 + return i if depth == 0 else -1 + + def _find_matching_paren(self, content: str, start: int) -> int: + depth = 0 + in_string = False + escape = False + for i in range(start, len(content)): + c = content[i] + if escape: + escape = False + continue + if c == '\\': + escape = True + continue + if c == '"': + in_string = not in_string + continue + if not in_string: + if c == '(': + depth += 1 + elif c == ')': + depth -= 1 + if depth == 0: + return i + return -1 + + def _split_args(self, args_text: str) -> list: + args = [] + current = [] + depth = 0 + in_string = False + escape = False + for c in args_text: + if escape: + current.append(c) + escape = False + continue + if c == '\\': + current.append(c) + escape = True + continue + if c == '"': + in_string = not in_string + current.append(c) + continue + if not in_string: + if c in '(<': + depth += 1 + elif c in ')>': + depth -= 1 + elif c == ',' and depth == 0: + args.append("".join(current).strip()) + current = [] + continue + current.append(c) + if current: + args.append("".join(current).strip()) + return args + + def _extract_class_name(self, args_text: str) -> Optional[str]: + args = self._split_args(args_text) + if len(args) >= 2: + return args[1].strip().strip('"') + return None + + def _has_docstring(self, args: list) -> bool: + for arg in args[2:]: + a = arg.strip() + if not a.startswith("py::arg") and a.startswith('"'): + return True + return False + + def _find_docstring_arg_index(self, args: list) -> Optional[int]: + for i, arg in enumerate(args[2:], start=2): + a = arg.strip() + if not a.startswith("py::arg") and a.startswith('"'): + return i + return None + + +def main(): + parser = argparse.ArgumentParser(description="Extract Doxygen docs and insert into pybind11 bindings (simplified)") + parser.add_argument("-d", "--doxygen_xml_dir", type=Path, default=Path("build/documents/doxygen_xml"), help="Path to Doxygen XML output directory") + parser.add_argument("-p", "--pybind_dir", type=Path, default=Path("source/PyMaterialX"), help="Path to pybind11 bindings directory") + parser.add_argument("-f", "--force", action="store_true", help="Force replace existing docstrings") + parser.add_argument("-j", "--write_json", action="store_true", help="Write extracted docs to JSON files") + + args = parser.parse_args() + + if not args.doxygen_xml_dir.exists(): + print(f"Error: Doxygen XML directory not found: {args.doxygen_xml_dir}") + return 1 + if not args.pybind_dir.exists(): + print(f"Error: Pybind directory not found: {args.pybind_dir}") + return 1 + + print("Extracting documentation from Doxygen XML...") + extractor = DocExtractor(args.doxygen_xml_dir) + extractor.extract() + + if args.write_json: + print("\nWriting JSON files...") + Path("class_docs.json").write_text(json.dumps(extractor.class_docs, indent=2), encoding="utf-8") + Path("func_docs.json").write_text(json.dumps(extractor.func_docs, indent=2), encoding="utf-8") + print(" - class_docs.json") + print(" - func_docs.json") + + print(f"\n{'Replacing' if args.force else 'Inserting'} documentation in pybind11 files...") + inserter = DocInserter(extractor, args.pybind_dir, args.force) + inserter.process_all_files() + + print("\nDone!") + return 0 + + +if __name__ == "__main__": + exit(main()) + diff --git a/source/PyMaterialX/CMakeLists.txt b/source/PyMaterialX/CMakeLists.txt index 8e5d70c1ce..cafbb79245 100644 --- a/source/PyMaterialX/CMakeLists.txt +++ b/source/PyMaterialX/CMakeLists.txt @@ -67,3 +67,30 @@ if (MATERIALX_BUILD_RENDER) add_subdirectory(PyMaterialXRenderMsl) endif() endif() + +if (MATERIALX_BUILD_DOCS) + # Ensure Doxygen docs are generated, then extract docs for pybind11 bindings. + set(PYBIND_DOCS_ARGS -d ${CMAKE_BINARY_DIR}/documents/doxygen_xml -p ${CMAKE_SOURCE_DIR}/source/PyMaterialX) + if(MATERIALX_PYTHON_FORCE_REPLACE_DOCS) + list(APPEND PYBIND_DOCS_ARGS --force) + endif() + add_custom_target(PyBindDocs + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/python/Scripts/pybind_docs.py ${PYBIND_DOCS_ARGS} + COMMENT "Generating PyMaterialX binding docs from Doxygen XML" + VERBATIM + ) + + # Run MaterialXDocs before attempting to generate pybind docs + add_dependencies(PyBindDocs MaterialXDocs) + + # Make pybind modules depend on the generated docs so the binding docs + # are integrated prior to building the Python extension modules. + foreach(_py_mod IN ITEMS PyMaterialXCore PyMaterialXFormat PyMaterialXGenShader + PyMaterialXGenGlsl PyMaterialXGenMsl PyMaterialXGenMdl + PyMaterialXGenOsl PyMaterialXRender PyMaterialXRenderGlsl + PyMaterialXRenderOsl PyMaterialXRenderMsl) + if(TARGET ${_py_mod}) + add_dependencies(${_py_mod} PyBindDocs) + endif() + endforeach() +endif() diff --git a/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp b/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp index 7990387f96..160effe781 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp @@ -14,17 +14,16 @@ namespace mx = MaterialX; void bindPyDefinition(py::module& mod) { - py::class_(mod, "NodeDef") - .def("setNodeString", &mx::NodeDef::setNodeString) - .def("hasNodeString", &mx::NodeDef::hasNodeString) - .def("getNodeString", &mx::NodeDef::getNodeString) - .def("setNodeGroup", &mx::NodeDef::setNodeGroup) - .def("hasNodeGroup", &mx::NodeDef::hasNodeGroup) - .def("getNodeGroup", &mx::NodeDef::getNodeGroup) - .def("getImplementation", &mx::NodeDef::getImplementation) - .def("getImplementation", &mx::NodeDef::getImplementation, - py::arg("target") = mx::EMPTY_STRING) - .def("isVersionCompatible", &mx::NodeDef::isVersionCompatible) + py::class_(mod, "NodeDef", "A node definition element within a Document.\n\nA NodeDef provides the declaration of a node interface, which may then be instantiated as a Node.") + .def("setNodeString", &mx::NodeDef::setNodeString, "Set the node string of the NodeDef.") + .def("hasNodeString", &mx::NodeDef::hasNodeString, "Return true if the given NodeDef has a node string.") + .def("getNodeString", &mx::NodeDef::getNodeString, "Return the node string of the NodeDef.") + .def("setNodeGroup", &mx::NodeDef::setNodeGroup, "Set the node group of the NodeDef.") + .def("hasNodeGroup", &mx::NodeDef::hasNodeGroup, "Return true if the given NodeDef has a node group.") + .def("getNodeGroup", &mx::NodeDef::getNodeGroup, "Return the node group of the NodeDef.") + .def("getImplementation", &mx::NodeDef::getImplementation, "Return the first implementation for this nodedef, optionally filtered by the given target name.\n\nArgs:\n target: An optional target name, which will be used to filter the implementations that are considered.\n\nReturns:\n An implementation for this nodedef, or an empty shared pointer if none was found. Note that a node implementation may be either an Implementation element or a NodeGraph element.") + .def("getImplementation", &mx::NodeDef::getImplementation, py::arg("target") = mx::EMPTY_STRING, "Return the first implementation for this nodedef, optionally filtered by the given target name.\n\nArgs:\n target: An optional target name, which will be used to filter the implementations that are considered.\n\nReturns:\n An implementation for this nodedef, or an empty shared pointer if none was found. Note that a node implementation may be either an Implementation element or a NodeGraph element.") + .def("isVersionCompatible", &mx::NodeDef::isVersionCompatible, "Return true if the given version string is compatible with this NodeDef.\n\nThis may be used to test, for example, whether a NodeDef and Node may be used together.") .def_readonly_static("CATEGORY", &mx::NodeDef::CATEGORY) .def_readonly_static("NODE_ATTRIBUTE", &mx::NodeDef::NODE_ATTRIBUTE) .def_readonly_static("TEXTURE_NODE_GROUP", &mx::NodeDef::TEXTURE_NODE_GROUP) @@ -36,70 +35,69 @@ void bindPyDefinition(py::module& mod) .def_readonly_static("ORGANIZATION_NODE_GROUP", &mx::NodeDef::ORGANIZATION_NODE_GROUP) .def_readonly_static("TRANSLATION_NODE_GROUP", &mx::NodeDef::TRANSLATION_NODE_GROUP); - py::class_(mod, "Implementation") - .def("setFile", &mx::Implementation::setFile) - .def("hasFile", &mx::Implementation::hasFile) - .def("getFile", &mx::Implementation::getFile) - .def("setFunction", &mx::Implementation::setFunction) - .def("hasFunction", &mx::Implementation::hasFunction) - .def("getFunction", &mx::Implementation::getFunction) - .def("setNodeDef", &mx::Implementation::setNodeDef) - .def("getNodeDef", &mx::Implementation::getNodeDef) - .def("setNodeGraph", &mx::Implementation::setNodeGraph) - .def("hasNodeGraph", &mx::Implementation::hasNodeGraph) - .def("getNodeGraph", &mx::Implementation::getNodeGraph) + py::class_(mod, "Implementation", "An implementation element within a Document.\n\nAn Implementation is used to associate external source code with a specific NodeDef, providing a definition for the node that may either be universal or restricted to a specific target.") + .def("setFile", &mx::Implementation::setFile, "Set the file string for the Implementation.") + .def("hasFile", &mx::Implementation::hasFile, "Return true if the given Implementation has a file string.") + .def("getFile", &mx::Implementation::getFile, "Return the file string for the Implementation.") + .def("setFunction", &mx::Implementation::setFunction, "Set the function string for the Implementation.") + .def("hasFunction", &mx::Implementation::hasFunction, "Return true if the given Implementation has a function string.") + .def("getFunction", &mx::Implementation::getFunction, "Return the function string for the Implementation.") + .def("setNodeDef", &mx::Implementation::setNodeDef, "Set the NodeDef element referenced by the Implementation.") + .def("getNodeDef", &mx::Implementation::getNodeDef, "Return the NodeDef element referenced by the Implementation.") + .def("setNodeGraph", &mx::Implementation::setNodeGraph, "Set the nodegraph string for the Implementation.") + .def("hasNodeGraph", &mx::Implementation::hasNodeGraph, "Return true if the given Implementation has a nodegraph string.") + .def("getNodeGraph", &mx::Implementation::getNodeGraph, "Return the nodegraph string for the Implementation.") .def_readonly_static("CATEGORY", &mx::Implementation::CATEGORY) .def_readonly_static("FILE_ATTRIBUTE", &mx::Implementation::FILE_ATTRIBUTE) .def_readonly_static("FUNCTION_ATTRIBUTE", &mx::Implementation::FUNCTION_ATTRIBUTE); - py::class_(mod, "TypeDef") - .def("setSemantic", &mx::TypeDef::setSemantic) - .def("hasSemantic", &mx::TypeDef::hasSemantic) - .def("getSemantic", &mx::TypeDef::getSemantic) - .def("setContext", &mx::TypeDef::setContext) - .def("hasContext", &mx::TypeDef::hasContext) - .def("getContext", &mx::TypeDef::getContext) - .def("addMember", &mx::TypeDef::addMember, - py::arg("name") = mx::EMPTY_STRING) - .def("getMember", &mx::TypeDef::getMember) - .def("getMembers", &mx::TypeDef::getMembers) - .def("removeMember", &mx::TypeDef::removeMember) + py::class_(mod, "TypeDef", "A type definition element within a Document.") + .def("setSemantic", &mx::TypeDef::setSemantic, "Set the semantic string of the TypeDef.") + .def("hasSemantic", &mx::TypeDef::hasSemantic, "Return true if the given TypeDef has a semantic string.") + .def("getSemantic", &mx::TypeDef::getSemantic, "Return the semantic string of the TypeDef.") + .def("setContext", &mx::TypeDef::setContext, "Set the context string of the TypeDef.") + .def("hasContext", &mx::TypeDef::hasContext, "Return true if the given TypeDef has a context string.") + .def("getContext", &mx::TypeDef::getContext, "Return the context string of the TypeDef.") + .def("addMember", &mx::TypeDef::addMember, py::arg("name") = mx::EMPTY_STRING, "Add a Member to the TypeDef.\n\nArgs:\n name: The name of the new Member. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new Member.") + .def("getMember", &mx::TypeDef::getMember, "Return the Member, if any, with the given name.") + .def("getMembers", &mx::TypeDef::getMembers, "Return a vector of all Member elements in the TypeDef.") + .def("removeMember", &mx::TypeDef::removeMember, "Remove the Member, if any, with the given name.") .def_readonly_static("CATEGORY", &mx::TypeDef::CATEGORY) .def_readonly_static("SEMANTIC_ATTRIBUTE", &mx::TypeDef::SEMANTIC_ATTRIBUTE) .def_readonly_static("CONTEXT_ATTRIBUTE", &mx::TypeDef::CONTEXT_ATTRIBUTE); - py::class_(mod, "Member") + py::class_(mod, "Member", "A member element within a TypeDef.") .def_readonly_static("CATEGORY", &mx::TypeDef::CATEGORY); - py::class_(mod, "Unit") + py::class_(mod, "Unit", "A unit declaration within a UnitDef.") .def_readonly_static("CATEGORY", &mx::Unit::CATEGORY); - py::class_(mod, "UnitDef") - .def("setUnitType", &mx::UnitDef::setUnitType) - .def("hasUnitType", &mx::UnitDef::hasUnitType) - .def("getUnitType", &mx::UnitDef::getUnitType) - .def("addUnit", &mx::UnitDef::addUnit) - .def("getUnit", &mx::UnitDef::getUnit) - .def("getUnits", &mx::UnitDef::getUnits) + py::class_(mod, "UnitDef", "A unit definition element within a Document.") + .def("setUnitType", &mx::UnitDef::setUnitType, "Set the element's unittype string.") + .def("hasUnitType", &mx::UnitDef::hasUnitType, "Return true if the given element has a unittype string.") + .def("getUnitType", &mx::UnitDef::getUnitType, "Return the element's type string.") + .def("addUnit", &mx::UnitDef::addUnit, "Add a Unit to the UnitDef.\n\nArgs:\n name: The name of the new Unit. An exception is thrown if the name provided is an empty string.\n\nReturns:\n A shared pointer to the new Unit.") + .def("getUnit", &mx::UnitDef::getUnit, "Return the Unit, if any, with the given name.") + .def("getUnits", &mx::UnitDef::getUnits, "Return a vector of all Unit elements in the UnitDef.") .def_readonly_static("CATEGORY", &mx::UnitDef::CATEGORY) .def_readonly_static("UNITTYPE_ATTRIBUTE", &mx::UnitDef::UNITTYPE_ATTRIBUTE); - py::class_(mod, "UnitTypeDef") - .def("getUnitDefs", &mx::UnitTypeDef::getUnitDefs) + py::class_(mod, "UnitTypeDef", "A unit type definition element within a Document.") + .def("getUnitDefs", &mx::UnitTypeDef::getUnitDefs, "Find all UnitDefs for the UnitTypeDef.") .def_readonly_static("CATEGORY", &mx::UnitTypeDef::CATEGORY); - py::class_(mod, "AttributeDef") - .def("setAttrName", &mx::AttributeDef::setAttrName) - .def("hasAttrName", &mx::AttributeDef::hasAttrName) - .def("getAttrName", &mx::AttributeDef::getAttrName) - .def("setValueString", &mx::AttributeDef::setValueString) - .def("hasValueString", &mx::AttributeDef::hasValueString) - .def("getValueString", &mx::AttributeDef::getValueString) - .def("setExportable", &mx::AttributeDef::setExportable) - .def("getExportable", &mx::AttributeDef::getExportable) + py::class_(mod, "AttributeDef", "An attribute definition element within a Document.") + .def("setAttrName", &mx::AttributeDef::setAttrName, "Set the element's attrname string.") + .def("hasAttrName", &mx::AttributeDef::hasAttrName, "Return true if this element has an attrname string.") + .def("getAttrName", &mx::AttributeDef::getAttrName, "Return the element's attrname string.") + .def("setValueString", &mx::AttributeDef::setValueString, "Set the value string of an element.") + .def("hasValueString", &mx::AttributeDef::hasValueString, "Return true if the given element has a value string.") + .def("getValueString", &mx::AttributeDef::getValueString, "Get the value string of a element.") + .def("setExportable", &mx::AttributeDef::setExportable, "Set the exportable boolean for the element.") + .def("getExportable", &mx::AttributeDef::getExportable, "Return the exportable boolean for the element.\n\nDefaults to false if exportable is not set.") .def_readonly_static("CATEGORY", &mx::AttributeDef::CATEGORY); - py::class_(mod, "TargetDef") - .def("getMatchingTargets", &mx::TargetDef::getMatchingTargets) + py::class_(mod, "TargetDef", "A definition of an implementation target.") + .def("getMatchingTargets", &mx::TargetDef::getMatchingTargets, "Return a vector of target names that is matching this targetdef either by itself of by its inheritance.\n\nThe vector is ordered by priority starting with this targetdef itself and then upwards in the inheritance hierarchy.") .def_readonly_static("CATEGORY", &mx::TargetDef::CATEGORY); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp b/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp index e021bc92de..d76b03425a 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp @@ -24,100 +24,89 @@ class PyBindDocument : public mx::Document void bindPyDocument(py::module& mod) { - mod.def("createDocument", &mx::createDocument); + mod.def("createDocument", &mx::createDocument, "Create a new document of the given subclass.\n\nCreate a new Document."); - py::class_(mod, "Document") - .def("initialize", &mx::Document::initialize) - .def("copy", &mx::Document::copy) - .def("setDataLibrary", &mx::Document::setDataLibrary) - .def("getDataLibrary", &mx::Document::getDataLibrary) - .def("hasDataLibrary", &mx::Document::hasDataLibrary) - .def("importLibrary", &mx::Document::importLibrary) - .def("getReferencedSourceUris", &mx::Document::getReferencedSourceUris) - .def("addNodeGraph", &mx::Document::addNodeGraph, - py::arg("name") = mx::EMPTY_STRING) - .def("getNodeGraph", &mx::Document::getNodeGraph) - .def("getNodeGraphs", &mx::Document::getNodeGraphs) - .def("removeNodeGraph", &mx::Document::removeNodeGraph) - .def("getMatchingPorts", &mx::Document::getMatchingPorts) - .def("addGeomInfo", &mx::Document::addGeomInfo, - py::arg("name") = mx::EMPTY_STRING, py::arg("geom") = mx::UNIVERSAL_GEOM_NAME) - .def("getGeomInfo", &mx::Document::getGeomInfo) - .def("getGeomInfos", &mx::Document::getGeomInfos) - .def("removeGeomInfo", &mx::Document::removeGeomInfo) - .def("getGeomPropValue", &mx::Document::getGeomPropValue, - py::arg("geomPropName"), py::arg("geom") = mx::UNIVERSAL_GEOM_NAME) - .def("addGeomPropDef", &mx::Document::addGeomPropDef) - .def("getGeomPropDef", &mx::Document::getGeomPropDef) - .def("getGeomPropDefs", &mx::Document::getGeomPropDefs) - .def("removeGeomPropDef", &mx::Document::removeGeomPropDef) - .def("getMaterialOutputs", &mx::Document::getMaterialOutputs) - .def("addLook", &mx::Document::addLook, - py::arg("name") = mx::EMPTY_STRING) - .def("getLook", &mx::Document::getLook) - .def("getLooks", &mx::Document::getLooks) - .def("removeLook", &mx::Document::removeLook) - .def("addLookGroup", &mx::Document::addLookGroup, - py::arg("name") = mx::EMPTY_STRING) - .def("getLookGroup", &mx::Document::getLookGroup) - .def("getLookGroups", &mx::Document::getLookGroups) - .def("removeLookGroup", &mx::Document::removeLookGroup) - .def("addCollection", &mx::Document::addCollection, - py::arg("name") = mx::EMPTY_STRING) - .def("getCollection", &mx::Document::getCollection) - .def("getCollections", &mx::Document::getCollections) - .def("removeCollection", &mx::Document::removeCollection) - .def("addTypeDef", &mx::Document::addTypeDef, - py::arg("name") = mx::EMPTY_STRING) - .def("getTypeDef", &mx::Document::getTypeDef) - .def("getTypeDefs", &mx::Document::getTypeDefs) - .def("removeTypeDef", &mx::Document::removeTypeDef) - .def("addNodeDef", &mx::Document::addNodeDef, - py::arg("name") = mx::EMPTY_STRING, py::arg("type") = mx::DEFAULT_TYPE_STRING, py::arg("node") = mx::EMPTY_STRING) + py::class_(mod, "Document", "A MaterialX document, which represents the top-level element in the MaterialX ownership hierarchy.\n\nUse the factory function createDocument() to create a Document instance.") + .def("initialize", &mx::Document::initialize, "Initialize the document, removing any existing content.") + .def("copy", &mx::Document::copy, "Create a deep copy of the document.") + .def("setDataLibrary", &mx::Document::setDataLibrary, "Store a reference to a data library in this document.") + .def("getDataLibrary", &mx::Document::getDataLibrary, "Return the data library, if any, referenced by this document.") + .def("hasDataLibrary", &mx::Document::hasDataLibrary, "Return true if this document has a data library.") + .def("importLibrary", &mx::Document::importLibrary, "Import the given data library into this document.\n\nArgs:\n library: The data library to be imported.") + .def("getReferencedSourceUris", &mx::Document::getReferencedSourceUris, "Get a list of source URIs referenced by the document.") + .def("addNodeGraph", &mx::Document::addNodeGraph, py::arg("name") = mx::EMPTY_STRING, "Add a NodeGraph to the document.\n\nArgs:\n name: The name of the new NodeGraph. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new NodeGraph.") + .def("getNodeGraph", &mx::Document::getNodeGraph, "Return the NodeGraph, if any, with the given name.") + .def("getNodeGraphs", &mx::Document::getNodeGraphs, "Return a vector of all NodeGraph elements in the document.") + .def("removeNodeGraph", &mx::Document::removeNodeGraph, "Remove the NodeGraph, if any, with the given name.") + .def("getMatchingPorts", &mx::Document::getMatchingPorts, "Return a vector of all port elements that match the given node name.\n\nPort elements support spatially-varying upstream connections to nodes, and include both Input and Output elements.") + .def("addGeomInfo", &mx::Document::addGeomInfo, py::arg("name") = mx::EMPTY_STRING, py::arg("geom") = mx::UNIVERSAL_GEOM_NAME, "Add a GeomInfo to the document.\n\nArgs:\n name: The name of the new GeomInfo. If no name is specified, then a unique name will automatically be generated.\n geom: An optional geometry string for the GeomInfo.\n\nReturns:\n A shared pointer to the new GeomInfo.") + .def("getGeomInfo", &mx::Document::getGeomInfo, "Return the GeomInfo, if any, with the given name.") + .def("getGeomInfos", &mx::Document::getGeomInfos, "Return a vector of all GeomInfo elements in the document.") + .def("removeGeomInfo", &mx::Document::removeGeomInfo, "Remove the GeomInfo, if any, with the given name.") + .def("getGeomPropValue", &mx::Document::getGeomPropValue, py::arg("geomPropName"), py::arg("geom") = mx::UNIVERSAL_GEOM_NAME, "Return the value of a geometric property for the given geometry string.") + .def("addGeomPropDef", &mx::Document::addGeomPropDef, "Add a GeomPropDef to the document.\n\nArgs:\n name: The name of the new GeomPropDef.\n geomprop: The geometric property to use for the GeomPropDef.\n\nReturns:\n A shared pointer to the new GeomPropDef.") + .def("getGeomPropDef", &mx::Document::getGeomPropDef, "Return the GeomPropDef, if any, with the given name.") + .def("getGeomPropDefs", &mx::Document::getGeomPropDefs, "Return a vector of all GeomPropDef elements in the document.") + .def("removeGeomPropDef", &mx::Document::removeGeomPropDef, "Remove the GeomPropDef, if any, with the given name.") + .def("getMaterialOutputs", &mx::Document::getMaterialOutputs, "Return material-type outputs for all nodegraphs in the document.") + .def("addLook", &mx::Document::addLook, py::arg("name") = mx::EMPTY_STRING, "Add a Look to the document.\n\nArgs:\n name: The name of the new Look. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new Look.") + .def("getLook", &mx::Document::getLook, "Return the Look, if any, with the given name.") + .def("getLooks", &mx::Document::getLooks, "Return a vector of all Look elements in the document.") + .def("removeLook", &mx::Document::removeLook, "Remove the Look, if any, with the given name.") + .def("addLookGroup", &mx::Document::addLookGroup, py::arg("name") = mx::EMPTY_STRING, "Add a LookGroup to the document.\n\nArgs:\n name: The name of the new LookGroup. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new LookGroup.") + .def("getLookGroup", &mx::Document::getLookGroup, "Return the LookGroup, if any, with the given name.") + .def("getLookGroups", &mx::Document::getLookGroups, "Return a vector of all LookGroup elements in the document.") + .def("removeLookGroup", &mx::Document::removeLookGroup, "Remove the LookGroup, if any, with the given name.") + .def("addCollection", &mx::Document::addCollection, py::arg("name") = mx::EMPTY_STRING, "Add a Collection to the document.\n\nArgs:\n name: The name of the new Collection. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new Collection.") + .def("getCollection", &mx::Document::getCollection, "Return the Collection, if any, with the given name.") + .def("getCollections", &mx::Document::getCollections, "Return a vector of all Collection elements in the document.") + .def("removeCollection", &mx::Document::removeCollection, "Remove the Collection, if any, with the given name.") + .def("addTypeDef", &mx::Document::addTypeDef, py::arg("name") = mx::EMPTY_STRING, "Add a TypeDef to the document.\n\nArgs:\n name: The name of the new TypeDef. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new TypeDef.") + .def("getTypeDef", &mx::Document::getTypeDef, "Return the TypeDef, if any, with the given name.") + .def("getTypeDefs", &mx::Document::getTypeDefs, "Return a vector of all TypeDef elements in the document.") + .def("removeTypeDef", &mx::Document::removeTypeDef, "Remove the TypeDef, if any, with the given name.") + .def("addNodeDef", &mx::Document::addNodeDef, py::arg("name") = mx::EMPTY_STRING, py::arg("type") = mx::DEFAULT_TYPE_STRING, py::arg("node") = mx::EMPTY_STRING, "Add a NodeDef to the document.\n\nArgs:\n name: The name of the new NodeDef. If no name is specified, then a unique name will automatically be generated.\n type: An optional type string. If specified, then the new NodeDef will be assigned an Output of the given type.\n node: An optional node string.\n\nReturns:\n A shared pointer to the new NodeDef.") .def("addNodeDefFromGraph", (mx::NodeDefPtr (mx::Document::*)(mx::NodeGraphPtr, const std::string&, const std::string&, const std::string&)) & mx::Document::addNodeDefFromGraph) .def("addNodeDefFromGraph", (mx::NodeDefPtr(mx::Document::*)(mx::NodeGraphPtr, const std::string&, const std::string&, const std::string&, bool, const std::string&, const std::string& )) & PyBindDocument::old_addNodeDefFromGraph) - .def("getNodeDef", &mx::Document::getNodeDef) - .def("getNodeDefs", &mx::Document::getNodeDefs) - .def("removeNodeDef", &mx::Document::removeNodeDef) - .def("getMatchingNodeDefs", &mx::Document::getMatchingNodeDefs) - .def("addAttributeDef", &mx::Document::addAttributeDef) - .def("getAttributeDef", &mx::Document::getAttributeDef) - .def("getAttributeDefs", &mx::Document::getAttributeDefs) - .def("removeAttributeDef", &mx::Document::removeAttributeDef) - .def("addTargetDef", &mx::Document::addTargetDef) - .def("getTargetDef", &mx::Document::getTargetDef) - .def("getTargetDefs", &mx::Document::getTargetDefs) - .def("removeTargetDef", &mx::Document::removeTargetDef) - .def("addPropertySet", &mx::Document::addPropertySet, - py::arg("name") = mx::EMPTY_STRING) - .def("getPropertySet", &mx::Document::getPropertySet) - .def("getPropertySets", &mx::Document::getPropertySets) - .def("removePropertySet", &mx::Document::removePropertySet) - .def("addVariantSet", &mx::Document::addVariantSet, - py::arg("name") = mx::EMPTY_STRING) - .def("getVariantSet", &mx::Document::getVariantSet) - .def("getVariantSets", &mx::Document::getVariantSets) - .def("removeVariantSet", &mx::Document::removeVariantSet) - .def("addImplementation", &mx::Document::addImplementation, - py::arg("name") = mx::EMPTY_STRING) - .def("getImplementation", &mx::Document::getImplementation) - .def("getImplementations", &mx::Document::getImplementations) - .def("removeImplementation", &mx::Document::removeImplementation) - .def("getMatchingImplementations", &mx::Document::getMatchingImplementations) - .def("addUnitDef", &mx::Document::addUnitDef) - .def("getUnitDef", &mx::Document::getUnitDef) - .def("getUnitDefs", &mx::Document::getUnitDefs) - .def("removeUnitDef", &mx::Document::removeUnitDef) - .def("addUnitTypeDef", &mx::Document::addUnitTypeDef) - .def("getUnitTypeDef", &mx::Document::getUnitTypeDef) - .def("getUnitTypeDefs", &mx::Document::getUnitTypeDefs) - .def("removeUnitTypeDef", &mx::Document::removeUnitTypeDef) - .def("upgradeVersion", &mx::Document::upgradeVersion) - .def("setColorManagementSystem", &mx::Document::setColorManagementSystem) - .def("hasColorManagementSystem", &mx::Document::hasColorManagementSystem) - .def("getColorManagementSystem", &mx::Document::getColorManagementSystem) - .def("setColorManagementConfig", &mx::Document::setColorManagementConfig) - .def("hasColorManagementConfig", &mx::Document::hasColorManagementConfig) - .def("getColorManagementConfig", &mx::Document::getColorManagementConfig); + .def("getNodeDef", &mx::Document::getNodeDef, "Return the NodeDef, if any, with the given name.") + .def("getNodeDefs", &mx::Document::getNodeDefs, "Return a vector of all NodeDef elements in the document.") + .def("removeNodeDef", &mx::Document::removeNodeDef, "Remove the NodeDef, if any, with the given name.") + .def("getMatchingNodeDefs", &mx::Document::getMatchingNodeDefs, "Return a vector of all NodeDef elements that match the given node name.") + .def("addAttributeDef", &mx::Document::addAttributeDef, "Add an AttributeDef to the document.\n\nArgs:\n name: The name of the new AttributeDef. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new AttributeDef.") + .def("getAttributeDef", &mx::Document::getAttributeDef, "Return the AttributeDef, if any, with the given name.") + .def("getAttributeDefs", &mx::Document::getAttributeDefs, "Return a vector of all AttributeDef elements in the document.") + .def("removeAttributeDef", &mx::Document::removeAttributeDef, "Remove the AttributeDef, if any, with the given name.") + .def("addTargetDef", &mx::Document::addTargetDef, "Add an TargetDef to the document.\n\nArgs:\n name: The name of the new TargetDef. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new TargetDef.") + .def("getTargetDef", &mx::Document::getTargetDef, "Return the AttributeDef, if any, with the given name.") + .def("getTargetDefs", &mx::Document::getTargetDefs, "Return a vector of all TargetDef elements in the document.") + .def("removeTargetDef", &mx::Document::removeTargetDef, "Remove the TargetDef, if any, with the given name.") + .def("addPropertySet", &mx::Document::addPropertySet, py::arg("name") = mx::EMPTY_STRING, "Add a PropertySet to the document.\n\nArgs:\n name: The name of the new PropertySet. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new PropertySet.") + .def("getPropertySet", &mx::Document::getPropertySet, "Return the PropertySet, if any, with the given name.") + .def("getPropertySets", &mx::Document::getPropertySets, "Return a vector of all PropertySet elements in the document.") + .def("removePropertySet", &mx::Document::removePropertySet, "Remove the PropertySet, if any, with the given name.") + .def("addVariantSet", &mx::Document::addVariantSet, py::arg("name") = mx::EMPTY_STRING, "Add a VariantSet to the document.\n\nArgs:\n name: The name of the new VariantSet. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new VariantSet.") + .def("getVariantSet", &mx::Document::getVariantSet, "Return the VariantSet, if any, with the given name.") + .def("getVariantSets", &mx::Document::getVariantSets, "Return a vector of all VariantSet elements in the document.") + .def("removeVariantSet", &mx::Document::removeVariantSet, "Remove the VariantSet, if any, with the given name.") + .def("addImplementation", &mx::Document::addImplementation, py::arg("name") = mx::EMPTY_STRING, "Add an Implementation to the document.\n\nArgs:\n name: The name of the new Implementation. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new Implementation.") + .def("getImplementation", &mx::Document::getImplementation, "Return the Implementation, if any, with the given name.") + .def("getImplementations", &mx::Document::getImplementations, "Return a vector of all Implementation elements in the document.") + .def("removeImplementation", &mx::Document::removeImplementation, "Remove the Implementation, if any, with the given name.") + .def("getMatchingImplementations", &mx::Document::getMatchingImplementations, "Return a vector of all node implementations that match the given NodeDef string.\n\nNote that a node implementation may be either an Implementation element or NodeGraph element.") + .def("addUnitDef", &mx::Document::addUnitDef, "") + .def("getUnitDef", &mx::Document::getUnitDef, "Return the UnitDef, if any, with the given name.") + .def("getUnitDefs", &mx::Document::getUnitDefs, "Return a vector of all Member elements in the TypeDef.") + .def("removeUnitDef", &mx::Document::removeUnitDef, "Remove the UnitDef, if any, with the given name.") + .def("addUnitTypeDef", &mx::Document::addUnitTypeDef, "") + .def("getUnitTypeDef", &mx::Document::getUnitTypeDef, "Return the UnitTypeDef, if any, with the given name.") + .def("getUnitTypeDefs", &mx::Document::getUnitTypeDefs, "Return a vector of all UnitTypeDef elements in the document.") + .def("removeUnitTypeDef", &mx::Document::removeUnitTypeDef, "Remove the UnitTypeDef, if any, with the given name.") + .def("upgradeVersion", &mx::Document::upgradeVersion, "Upgrade the content of this document from earlier supported versions to the library version.") + .def("setColorManagementSystem", &mx::Document::setColorManagementSystem, "Set the color management system string.") + .def("hasColorManagementSystem", &mx::Document::hasColorManagementSystem, "Return true if a color management system string has been set.") + .def("getColorManagementSystem", &mx::Document::getColorManagementSystem, "Return the color management system string.") + .def("setColorManagementConfig", &mx::Document::setColorManagementConfig, "Set the color management config string.") + .def("hasColorManagementConfig", &mx::Document::hasColorManagementConfig, "Return true if a color management config string has been set.") + .def("getColorManagementConfig", &mx::Document::getColorManagementConfig, "Return the color management config string."); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyElement.cpp b/source/PyMaterialX/PyMaterialXCore/PyElement.cpp index 05f858d4c3..f297feb90a 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyElement.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyElement.cpp @@ -26,7 +26,7 @@ namespace mx = MaterialX; void bindPyElement(py::module& mod) { - py::class_(mod, "Element") + py::class_(mod, "Element", "The base class for MaterialX elements.\n\nAn Element is a named object within a Document, which may possess any number of child elements and attributes.") .def(py::self == py::self) .def(py::self != py::self) .def("isEquivalent", [](const mx::Element& elem, mx::ConstElementPtr& rhs, const mx::ElementEquivalenceOptions& options) @@ -34,81 +34,76 @@ void bindPyElement(py::module& mod) std::string message; bool res = elem.isEquivalent(rhs, options, &message); return std::pair(res, message); - }) - .def("setCategory", &mx::Element::setCategory) - .def("getCategory", &mx::Element::getCategory) - .def("setName", &mx::Element::setName) - .def("getName", &mx::Element::getName) - .def("getNamePath", &mx::Element::getNamePath, - py::arg("relativeTo") = nullptr) - .def("getDescendant", &mx::Element::getDescendant) - .def("setFilePrefix", &mx::Element::setFilePrefix) - .def("hasFilePrefix", &mx::Element::hasFilePrefix) - .def("getFilePrefix", &mx::Element::getFilePrefix) - .def("getActiveFilePrefix", &mx::Element::getActiveFilePrefix) - .def("setGeomPrefix", &mx::Element::setGeomPrefix) - .def("hasGeomPrefix", &mx::Element::hasGeomPrefix) - .def("getGeomPrefix", &mx::Element::getGeomPrefix) - .def("getActiveGeomPrefix", &mx::Element::getActiveGeomPrefix) - .def("setColorSpace", &mx::Element::setColorSpace) - .def("hasColorSpace", &mx::Element::hasColorSpace) - .def("getColorSpace", &mx::Element::getColorSpace) - .def("getActiveColorSpace", &mx::Element::getActiveColorSpace) - .def("setInheritString", &mx::Element::setInheritString) - .def("hasInheritString", &mx::Element::hasInheritString) - .def("getInheritString", &mx::Element::getInheritString) - .def("setInheritsFrom", &mx::Element::setInheritsFrom) - .def("getInheritsFrom", &mx::Element::getInheritsFrom) - .def("hasInheritedBase", &mx::Element::hasInheritedBase) - .def("hasInheritanceCycle", &mx::Element::hasInheritanceCycle) - .def("setNamespace", &mx::Element::setNamespace) - .def("hasNamespace", &mx::Element::hasNamespace) - .def("getNamespace", &mx::Element::getNamespace) - .def("getQualifiedName", &mx::Element::getQualifiedName) - .def("setDocString", &mx::Element::setDocString) - .def("getDocString", &mx::Element::getDocString) - .def("addChildOfCategory", &mx::Element::addChildOfCategory, - py::arg("category"), py::arg("name") = mx::EMPTY_STRING) - .def("changeChildCategory", &mx::Element::changeChildCategory) - .def("_getChild", &mx::Element::getChild) - .def("getChildren", &mx::Element::getChildren) - .def("setChildIndex", &mx::Element::setChildIndex) - .def("getChildIndex", &mx::Element::getChildIndex) - .def("removeChild", &mx::Element::removeChild) - .def("setAttribute", &mx::Element::setAttribute) - .def("hasAttribute", &mx::Element::hasAttribute) - .def("getAttribute", &mx::Element::getAttribute) - .def("getAttributeNames", &mx::Element::getAttributeNames) - .def("removeAttribute", &mx::Element::removeAttribute) - .def("getSelf", static_cast(&mx::Element::getSelf)) - .def("getParent", static_cast(&mx::Element::getParent)) - .def("getRoot", static_cast(&mx::Element::getRoot)) - .def("getDocument", static_cast(&mx::Element::getDocument)) - .def("traverseTree", &mx::Element::traverseTree) - .def("traverseGraph", &mx::Element::traverseGraph) - .def("getUpstreamEdge", &mx::Element::getUpstreamEdge, - py::arg("index") = 0) - .def("getUpstreamEdgeCount", &mx::Element::getUpstreamEdgeCount) - .def("getUpstreamElement", &mx::Element::getUpstreamElement, - py::arg("index") = 0) - .def("traverseInheritance", &mx::Element::traverseInheritance) - .def("setSourceUri", &mx::Element::setSourceUri) - .def("hasSourceUri", &mx::Element::hasSourceUri) - .def("getSourceUri", &mx::Element::getSourceUri) - .def("getActiveSourceUri", &mx::Element::getActiveSourceUri) + }, "Return true if the given element tree, including all descendents, is considered to be equivalent to this one based on the equivalence criteria provided.\n\nArgs:\n rhs: Element to compare against\n options: Equivalence criteria\n message: Optional text description of differences\n\nReturns:\n True if the elements are equivalent. False otherwise.") + .def("setCategory", &mx::Element::setCategory, "Set the element's category string.") + .def("getCategory", &mx::Element::getCategory, "Return the element's category string.\n\nThe category of a MaterialX element represents its role within the document, with common examples being \"material\", \"nodegraph\", and \"image\".") + .def("setName", &mx::Element::setName, "Set the element's name string.") + .def("getName", &mx::Element::getName, "Return the element's name string.") + .def("getNamePath", &mx::Element::getNamePath, py::arg("relativeTo") = nullptr, "Return the element's hierarchical name path, relative to the root document.\n\nArgs:\n relativeTo: If a valid ancestor element is specified, then the returned path will be relative to this ancestor.") + .def("getDescendant", &mx::Element::getDescendant, "Return the element specified by the given hierarchical name path, relative to the current element.\n\nArgs:\n namePath: The relative name path of the specified element.") + .def("setFilePrefix", &mx::Element::setFilePrefix, "Set the element's file prefix string.") + .def("hasFilePrefix", &mx::Element::hasFilePrefix, "Return true if the given element has a file prefix string.") + .def("getFilePrefix", &mx::Element::getFilePrefix, "Return the element's file prefix string.") + .def("getActiveFilePrefix", &mx::Element::getActiveFilePrefix, "Return the file prefix string that is active at the scope of this element, taking all ancestor elements into account.") + .def("setGeomPrefix", &mx::Element::setGeomPrefix, "Set the element's geom prefix string.") + .def("hasGeomPrefix", &mx::Element::hasGeomPrefix, "Return true if the given element has a geom prefix string.") + .def("getGeomPrefix", &mx::Element::getGeomPrefix, "Return the element's geom prefix string.") + .def("getActiveGeomPrefix", &mx::Element::getActiveGeomPrefix, "Return the geom prefix string that is active at the scope of this element, taking all ancestor elements into account.") + .def("setColorSpace", &mx::Element::setColorSpace, "Set the element's color space string.") + .def("hasColorSpace", &mx::Element::hasColorSpace, "Return true if the given element has a color space string.") + .def("getColorSpace", &mx::Element::getColorSpace, "Return the element's color space string.") + .def("getActiveColorSpace", &mx::Element::getActiveColorSpace, "Return the color space string that is active at the scope of this element, taking all ancestor elements into account.") + .def("setInheritString", &mx::Element::setInheritString, "Set the inherit string of this element.") + .def("hasInheritString", &mx::Element::hasInheritString, "Return true if this element has an inherit string.") + .def("getInheritString", &mx::Element::getInheritString, "Return the inherit string of this element.") + .def("setInheritsFrom", &mx::Element::setInheritsFrom, "Set the element that this one directly inherits from.") + .def("getInheritsFrom", &mx::Element::getInheritsFrom, "Return the element, if any, that this one directly inherits from.") + .def("hasInheritedBase", &mx::Element::hasInheritedBase, "Return true if this element has the given element as an inherited base, taking the full inheritance chain into account.") + .def("hasInheritanceCycle", &mx::Element::hasInheritanceCycle, "Return true if the inheritance chain for this element contains a cycle.") + .def("setNamespace", &mx::Element::setNamespace, "Set the namespace string of this element.") + .def("hasNamespace", &mx::Element::hasNamespace, "Return true if this element has a namespace string.") + .def("getNamespace", &mx::Element::getNamespace, "Return the namespace string of this element.") + .def("getQualifiedName", &mx::Element::getQualifiedName, "Return a qualified version of the given name, taking the namespace at the scope of this element into account.") + .def("setDocString", &mx::Element::setDocString, "Set the documentation string of this element.") + .def("getDocString", &mx::Element::getDocString, "Return the documentation string of this element.") + .def("addChildOfCategory", &mx::Element::addChildOfCategory, py::arg("category"), py::arg("name") = mx::EMPTY_STRING, "Add a child element of the given category and name.\n\nArgs:\n category: The category string of the new child element. If the category string is recognized, then the corresponding Element subclass is generated; otherwise, a GenericElement is generated.\n name: The name of the new child element. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new child element.") + .def("changeChildCategory", &mx::Element::changeChildCategory, "Change the category of the given child element.\n\nArgs:\n child: The child element that will be modified.\n category: The new category string for the child element.\n\nReturns:\n A shared pointer to a new child element, containing the contents of the original child but with a new category and subclass.") + .def("_getChild", &mx::Element::getChild, "Return the child element, if any, with the given name.") + .def("getChildren", &mx::Element::getChildren, "Return a constant vector of all child elements.\n\nThe returned vector maintains the order in which children were added.") + .def("setChildIndex", &mx::Element::setChildIndex, "Set the index of the child, if any, with the given name.\n\nIf the given index is out of bounds, then an exception is thrown.") + .def("getChildIndex", &mx::Element::getChildIndex, "Return the index of the child, if any, with the given name.\n\nIf no child with the given name is found, then -1 is returned.") + .def("removeChild", &mx::Element::removeChild, "Remove the child element, if any, with the given name.") + .def("setAttribute", &mx::Element::setAttribute, "Set the value string of the given attribute.") + .def("hasAttribute", &mx::Element::hasAttribute, "Return true if the given attribute is present.") + .def("getAttribute", &mx::Element::getAttribute, "Return the value string of the given attribute.\n\nIf the given attribute is not present, then an empty string is returned.") + .def("getAttributeNames", &mx::Element::getAttributeNames, "Return a vector of stored attribute names, in the order they were set.") + .def("removeAttribute", &mx::Element::removeAttribute, "Remove the given attribute, if present.") + .def("getSelf", static_cast(&mx::Element::getSelf), "Return a shared pointer instance of this object.") + .def("getParent", static_cast(&mx::Element::getParent), "Return the parent graph that owns this node.\n\nIf this node is a root graph it has no parent and nullptr will be returned.") + .def("getRoot", static_cast(&mx::Element::getRoot), "Return the root element of our tree.") + .def("getDocument", static_cast(&mx::Element::getDocument), "Return the document associated with this ShaderMaterial.") + .def("traverseTree", &mx::Element::traverseTree, "Traverse the tree from the given element to each of its descendants in depth-first order, using pre-order visitation.\n\nReturns:\n A TreeIterator object.") + .def("traverseGraph", &mx::Element::traverseGraph, "Traverse the dataflow graph from the given element to each of its upstream sources in depth-first order, using pre-order visitation.\n\nReturns:\n A GraphIterator object.") + .def("getUpstreamEdge", &mx::Element::getUpstreamEdge, py::arg("index") = 0, "Return the Edge with the given index that lies directly upstream from this element in the dataflow graph.\n\nArgs:\n index: An optional index of the edge to be returned, where the valid index range may be determined with getUpstreamEdgeCount.\n\nReturns:\n The upstream Edge, if valid, or an empty Edge object.") + .def("getUpstreamEdgeCount", &mx::Element::getUpstreamEdgeCount, "Return the number of queryable upstream edges for this element.") + .def("getUpstreamElement", &mx::Element::getUpstreamElement, py::arg("index") = 0, "Return the Element with the given index that lies directly upstream from this one in the dataflow graph.\n\nArgs:\n index: An optional index of the element to be returned, where the valid index range may be determined with getUpstreamEdgeCount.\n\nReturns:\n The upstream Element, if valid, or an empty ElementPtr.") + .def("traverseInheritance", &mx::Element::traverseInheritance, "Traverse the inheritance chain from the given element to each element from which it inherits.\n\nReturns:\n An InheritanceIterator object.") + .def("setSourceUri", &mx::Element::setSourceUri, "Set the element's source URI.\n\nArgs:\n sourceUri: A URI string representing the resource from which this element originates. This string may be used by serialization and deserialization routines to maintain hierarchies of include references.") + .def("hasSourceUri", &mx::Element::hasSourceUri, "Return true if this element has a source URI.") + .def("getSourceUri", &mx::Element::getSourceUri, "Return the element's source URI.") + .def("getActiveSourceUri", &mx::Element::getActiveSourceUri, "Return the source URI that is active at the scope of this element, taking all ancestor elements into account.") .def("validate", [](const mx::Element& elem) { std::string message; bool res = elem.validate(&message); return std::pair(res, message); - }) - .def("copyContentFrom", &mx::Element::copyContentFrom) - .def("clearContent", &mx::Element::clearContent) - .def("createValidChildName", &mx::Element::createValidChildName) - .def("createStringResolver", &mx::Element::createStringResolver, - py::arg("geom") = mx::EMPTY_STRING) - .def("asString", &mx::Element::asString) - .def("__str__", &mx::Element::asString) + }, "Validate that the given document is consistent with the MaterialX specification.\n\nArgs:\n message: An optional output string, to which a description of each error will be appended.\n\nReturns:\n True if the document passes all tests, false otherwise.") + .def("copyContentFrom", &mx::Element::copyContentFrom, "Copy all attributes and descendants from the given element to this one.\n\nArgs:\n source: The element from which content is copied.") + .def("clearContent", &mx::Element::clearContent, "Clear all attributes and descendants from this element.") + .def("createValidChildName", &mx::Element::createValidChildName, "Using the input name as a starting point, modify it to create a valid, unique name for a child element.") + .def("createStringResolver", &mx::Element::createStringResolver, py::arg("geom") = mx::EMPTY_STRING, "Construct a StringResolver at the scope of this element.\n\nArgs:\n geom: An optional geometry name, which will be used to select the applicable set of geometry token substitutions. By default, no geometry token substitutions are applied. If the universal geometry name \"/\" is given, then all geometry token substitutions are applied,\n\nReturns:\n A shared pointer to a StringResolver.") + .def("asString", &mx::Element::asString, "Return a single-line description of this element, including its category, name, and attributes.") + .def("__str__", &mx::Element::asString, "Return a single-line description of this element, including its category, name, and attributes.") .def_readonly_static("NAME_ATTRIBUTE", &mx::Element::NAME_ATTRIBUTE) .def_readonly_static("FILE_PREFIX_ATTRIBUTE", &mx::Element::FILE_PREFIX_ATTRIBUTE) .def_readonly_static("GEOM_PREFIX_ATTRIBUTE", &mx::Element::GEOM_PREFIX_ATTRIBUTE) @@ -135,38 +130,37 @@ void bindPyElement(py::module& mod) BIND_ELEMENT_FUNC_INSTANCE(TypeDef) BIND_ELEMENT_FUNC_INSTANCE(Visibility); - py::class_(mod, "TypedElement") - .def("setType", &mx::TypedElement::setType) - .def("hasType", &mx::TypedElement::hasType) - .def("getType", &mx::TypedElement::getType) - .def("isColorType", &mx::TypedElement::isColorType) - .def("isMultiOutputType", &mx::TypedElement::isMultiOutputType) - .def("getTypeDef", &mx::TypedElement::getTypeDef) + py::class_(mod, "TypedElement", "The base class for typed elements.") + .def("setType", &mx::TypedElement::setType, "Set the element's type string.") + .def("hasType", &mx::TypedElement::hasType, "Return true if the given element has a type string.") + .def("getType", &mx::TypedElement::getType, "Return the element's type string.") + .def("isColorType", &mx::TypedElement::isColorType, "Return true if the element is of color type.") + .def("isMultiOutputType", &mx::TypedElement::isMultiOutputType, "Return true if the element is of multi-output type.") + .def("getTypeDef", &mx::TypedElement::getTypeDef, "Return the TypeDef declaring the type string of this element.\n\nIf no matching TypeDef is found, then an empty shared pointer is returned.") .def_readonly_static("TYPE_ATTRIBUTE", &mx::TypedElement::TYPE_ATTRIBUTE); - py::class_(mod, "ValueElement") - .def("setValueString", &mx::ValueElement::setValueString) - .def("hasValueString", &mx::ValueElement::hasValueString) - .def("getValueString", &mx::ValueElement::getValueString) - .def("getResolvedValueString", &mx::ValueElement::getResolvedValueString, - py::arg("resolver") = nullptr) - .def("setInterfaceName", &mx::ValueElement::setInterfaceName) - .def("hasInterfaceName", &mx::ValueElement::hasInterfaceName) - .def("getInterfaceName", &mx::ValueElement::getInterfaceName) - .def("setImplementationName", &mx::ValueElement::setImplementationName) - .def("hasImplementationName", &mx::ValueElement::hasImplementationName) - .def("getImplementationName", &mx::ValueElement::getImplementationName) - .def("_getValue", &mx::ValueElement::getValue) - .def("_getDefaultValue", &mx::ValueElement::getDefaultValue) - .def("setUnit", &mx::ValueElement::setUnit) - .def("hasUnit", &mx::ValueElement::hasUnit) - .def("getUnit", &mx::ValueElement::getUnit) - .def("getActiveUnit", &mx::ValueElement::getActiveUnit) - .def("setUnitType", &mx::ValueElement::setUnitType) - .def("hasUnitType", &mx::ValueElement::hasUnitType) - .def("getUnitType", &mx::ValueElement::getUnitType) - .def("getIsUniform", &mx::ValueElement::getIsUniform) - .def("setIsUniform", &mx::ValueElement::setIsUniform) + py::class_(mod, "ValueElement", "The base class for elements that support typed values.") + .def("setValueString", &mx::ValueElement::setValueString, "Set the value string of an element.") + .def("hasValueString", &mx::ValueElement::hasValueString, "Return true if the given element has a value string.") + .def("getValueString", &mx::ValueElement::getValueString, "Get the value string of a element.") + .def("getResolvedValueString", &mx::ValueElement::getResolvedValueString, py::arg("resolver") = nullptr, "Return the resolved value string of an element, applying any string substitutions that are defined at the element's scope.\n\nArgs:\n resolver: An optional string resolver, which will be used to apply string substitutions. By default, a new string resolver will be created at this scope and applied to the return value.") + .def("setInterfaceName", &mx::ValueElement::setInterfaceName, "Set the interface name of an element.") + .def("hasInterfaceName", &mx::ValueElement::hasInterfaceName, "Return true if the given element has an interface name.") + .def("getInterfaceName", &mx::ValueElement::getInterfaceName, "Return the interface name of an element.") + .def("setImplementationName", &mx::ValueElement::setImplementationName, "Set the implementation name of an element.") + .def("hasImplementationName", &mx::ValueElement::hasImplementationName, "Return true if the given element has an implementation name.") + .def("getImplementationName", &mx::ValueElement::getImplementationName, "Return the implementation name of an element.") + .def("_getValue", &mx::ValueElement::getValue, "Return the typed value of an element as a generic value object, which may be queried to access its data.\n\nReturns:\n A shared pointer to the typed value of this element, or an empty shared pointer if no value is present.") + .def("_getDefaultValue", &mx::ValueElement::getDefaultValue, "Return the default value for this element as a generic value object, which may be queried to access its data.\n\nReturns:\n A shared pointer to a typed value, or an empty shared pointer if no default value was found.") + .def("setUnit", &mx::ValueElement::setUnit, "Set the unit string of an element.") + .def("hasUnit", &mx::ValueElement::hasUnit, "Return true if the given element has a unit string.") + .def("getUnit", &mx::ValueElement::getUnit, "Return the unit string of an element.") + .def("getActiveUnit", &mx::ValueElement::getActiveUnit, "Return the unit defined by the associated NodeDef if this element is a child of a Node.") + .def("setUnitType", &mx::ValueElement::setUnitType, "Set the unit type of an element.") + .def("hasUnitType", &mx::ValueElement::hasUnitType, "Return true if the given element has a unit type.") + .def("getUnitType", &mx::ValueElement::getUnitType, "Return the unit type of an element.") + .def("getIsUniform", &mx::ValueElement::getIsUniform, "The the uniform attribute flag for this element.") + .def("setIsUniform", &mx::ValueElement::setIsUniform, "Set the uniform attribute flag on this element.") .def_readonly_static("VALUE_ATTRIBUTE", &mx::ValueElement::VALUE_ATTRIBUTE) .def_readonly_static("INTERFACE_NAME_ATTRIBUTE", &mx::ValueElement::INTERFACE_NAME_ATTRIBUTE) .def_readonly_static("IMPLEMENTATION_NAME_ATTRIBUTE", &mx::ValueElement::IMPLEMENTATION_NAME_ATTRIBUTE) @@ -199,42 +193,42 @@ void bindPyElement(py::module& mod) BIND_VALUE_ELEMENT_FUNC_INSTANCE(floatarray, mx::FloatVec) BIND_VALUE_ELEMENT_FUNC_INSTANCE(stringarray, mx::StringVec); - py::class_(mod, "Token") + py::class_(mod, "Token", "A token element representing a string value.\n\nToken elements are used to define input and output values for string substitutions in image filenames.") .def_readonly_static("CATEGORY", &mx::Token::CATEGORY); - py::class_(mod, "CommentElement") + py::class_(mod, "CommentElement", "An element representing a block of descriptive text within a document, which will be stored a comment when the document is written out.\n\nThe comment text may be accessed with the methods Element::setDocString and Element::getDocString.") .def_readonly_static("CATEGORY", &mx::CommentElement::CATEGORY); - py::class_(mod, "NewlineElement") + py::class_(mod, "NewlineElement", "An element representing a newline within a document.") .def_readonly_static("CATEGORY", &mx::NewlineElement::CATEGORY); - py::class_(mod, "GenericElement") + py::class_(mod, "GenericElement", "A generic element subclass, for instantiating elements with unrecognized categories.") .def_readonly_static("CATEGORY", &mx::GenericElement::CATEGORY); - py::class_(mod, "ElementEquivalenceOptions") + py::class_(mod, "ElementEquivalenceOptions", "A set of options for comparing the functional equivalence of elements.") .def_readwrite("performValueComparisons", &mx::ElementEquivalenceOptions::performValueComparisons) .def_readwrite("floatFormat", &mx::ElementEquivalenceOptions::floatFormat) .def_readwrite("floatPrecision", &mx::ElementEquivalenceOptions::floatPrecision) .def_readwrite("attributeExclusionList", &mx::ElementEquivalenceOptions::attributeExclusionList) .def(py::init<>()); - py::class_(mod, "StringResolver") - .def("setFilePrefix", &mx::StringResolver::setFilePrefix) - .def("getFilePrefix", &mx::StringResolver::getFilePrefix) - .def("setGeomPrefix", &mx::StringResolver::setGeomPrefix) - .def("getGeomPrefix", &mx::StringResolver::getGeomPrefix) - .def("setUdimString", &mx::StringResolver::setUdimString) - .def("setUvTileString", &mx::StringResolver::setUvTileString) - .def("setFilenameSubstitution", &mx::StringResolver::setFilenameSubstitution) - .def("getFilenameSubstitutions", &mx::StringResolver::getFilenameSubstitutions) - .def("setGeomNameSubstitution", &mx::StringResolver::setGeomNameSubstitution) - .def("getGeomNameSubstitutions", &mx::StringResolver::getGeomNameSubstitutions) - .def("resolve", &mx::StringResolver::resolve); + py::class_(mod, "StringResolver", "A helper object for applying string modifiers to data values in the context of a specific element and geometry.\n\nA StringResolver may be constructed through the Element::createStringResolver method, which initializes it in the context of a specific element, geometry, and material.\n\nCalling the StringResolver::resolve method applies all modifiers to a particular string value.\n\nMethods such as StringResolver::setFilePrefix may be used to edit the stored string modifiers before calling StringResolver::resolve.") + .def("setFilePrefix", &mx::StringResolver::setFilePrefix, "Set the file prefix for this context.") + .def("getFilePrefix", &mx::StringResolver::getFilePrefix, "Return the file prefix for this context.") + .def("setGeomPrefix", &mx::StringResolver::setGeomPrefix, "Set the geom prefix for this context.") + .def("getGeomPrefix", &mx::StringResolver::getGeomPrefix, "Return the geom prefix for this context.") + .def("setUdimString", &mx::StringResolver::setUdimString, "Set the UDIM substring substitution for filename data values.\n\nThis string will be used to replace the standard token.") + .def("setUvTileString", &mx::StringResolver::setUvTileString, "Set the UV-tile substring substitution for filename data values.\n\nThis string will be used to replace the standard token.") + .def("setFilenameSubstitution", &mx::StringResolver::setFilenameSubstitution, "Set an arbitrary substring substitution for filename data values.") + .def("getFilenameSubstitutions", &mx::StringResolver::getFilenameSubstitutions, "Return the map of filename substring substitutions.") + .def("setGeomNameSubstitution", &mx::StringResolver::setGeomNameSubstitution, "Set an arbitrary substring substitution for geometry name data values.") + .def("getGeomNameSubstitutions", &mx::StringResolver::getGeomNameSubstitutions, "Return the map of geometry name substring substitutions.") + .def("resolve", &mx::StringResolver::resolve, "Given an input string and type, apply all appropriate modifiers and return the resulting string."); py::class_(mod, "ElementPredicate"); py::register_exception(mod, "ExceptionOrphanedElement"); - mod.def("targetStringsMatch", &mx::targetStringsMatch); - mod.def("prettyPrint", &mx::prettyPrint); + mod.def("targetStringsMatch", &mx::targetStringsMatch, "Given two target strings, each containing a string array of target names, return true if they have any targets in common.\n\nAn empty target string matches all targets."); + mod.def("prettyPrint", &mx::prettyPrint, "Pretty print the given element tree, calling asString recursively on each element in depth-first order."); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp b/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp index 712851fac2..4f3ea67ace 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp @@ -15,27 +15,26 @@ namespace mx = MaterialX; void bindPyGeom(py::module& mod) { - py::class_(mod, "GeomElement") - .def("setGeom", &mx::GeomElement::setGeom) - .def("hasGeom", &mx::GeomElement::hasGeom) - .def("getGeom", &mx::GeomElement::getGeom) - .def("setCollectionString", &mx::GeomElement::setCollectionString) - .def("hasCollectionString", &mx::GeomElement::hasCollectionString) - .def("getCollectionString", &mx::GeomElement::getCollectionString) - .def("setCollection", &mx::GeomElement::setCollection) - .def("getCollection", &mx::GeomElement::getCollection); + py::class_(mod, "GeomElement", "The base class for geometric elements, which support bindings to geometries and geometric collections.") + .def("setGeom", &mx::GeomElement::setGeom, "Set the geometry string of this element.") + .def("hasGeom", &mx::GeomElement::hasGeom, "Return true if this element has a geometry string.") + .def("getGeom", &mx::GeomElement::getGeom, "Return the geometry string of this element.") + .def("setCollectionString", &mx::GeomElement::setCollectionString, "Set the collection string of this element.") + .def("hasCollectionString", &mx::GeomElement::hasCollectionString, "Return true if this element has a collection string.") + .def("getCollectionString", &mx::GeomElement::getCollectionString, "Return the collection string of this element.") + .def("setCollection", &mx::GeomElement::setCollection, "Assign a Collection to this element.") + .def("getCollection", &mx::GeomElement::getCollection, "Return the Collection that is assigned to this element."); - py::class_(mod, "GeomInfo") - .def("addGeomProp", &mx::GeomInfo::addGeomProp) - .def("getGeomProp", &mx::GeomInfo::getGeomProp) - .def("getGeomProps", &mx::GeomInfo::getGeomProps) - .def("removeGeomProp", &mx::GeomInfo::removeGeomProp) - .def("addToken", &mx::GeomInfo::addToken, - py::arg("name") = mx::DEFAULT_TYPE_STRING) - .def("getToken", &mx::GeomInfo::getToken) - .def("getTokens", &mx::GeomInfo::getTokens) - .def("removeToken", &mx::GeomInfo::removeToken) - .def("setTokenValue", &mx::GeomInfo::setTokenValue) + py::class_(mod, "GeomInfo", "A geometry info element within a Document.") + .def("addGeomProp", &mx::GeomInfo::addGeomProp, "Add a GeomProp to this element.\n\nArgs:\n name: The name of the new GeomProp. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new GeomProp.") + .def("getGeomProp", &mx::GeomInfo::getGeomProp, "Return the GeomProp, if any, with the given name.") + .def("getGeomProps", &mx::GeomInfo::getGeomProps, "Return a vector of all GeomProp elements.") + .def("removeGeomProp", &mx::GeomInfo::removeGeomProp, "Remove the GeomProp, if any, with the given name.") + .def("addToken", &mx::GeomInfo::addToken, py::arg("name") = mx::DEFAULT_TYPE_STRING, "Add a Token to this element.\n\nArgs:\n name: The name of the new Token. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new Token.") + .def("getToken", &mx::GeomInfo::getToken, "Return the Token, if any, with the given name.") + .def("getTokens", &mx::GeomInfo::getTokens, "Return a vector of all Token elements.") + .def("removeToken", &mx::GeomInfo::removeToken, "Remove the Token, if any, with the given name.") + .def("setTokenValue", &mx::GeomInfo::setTokenValue, "Set the string value of a Token by its name, creating a child element to hold the Token if needed.") BIND_GEOMINFO_FUNC_INSTANCE(integer, int) BIND_GEOMINFO_FUNC_INSTANCE(boolean, bool) BIND_GEOMINFO_FUNC_INSTANCE(float, float) @@ -53,42 +52,42 @@ void bindPyGeom(py::module& mod) BIND_GEOMINFO_FUNC_INSTANCE(stringarray, mx::StringVec) .def_readonly_static("CATEGORY", &mx::GeomInfo::CATEGORY); - py::class_(mod, "GeomProp") + py::class_(mod, "GeomProp", "A geometric property element within a GeomInfo.") .def_readonly_static("CATEGORY", &mx::GeomProp::CATEGORY); - py::class_(mod, "GeomPropDef") - .def("setGeomProp", &mx::GeomPropDef::setGeomProp) - .def("hasGeomProp", &mx::GeomPropDef::hasGeomProp) - .def("getGeomProp", &mx::GeomPropDef::getGeomProp) - .def("setSpace", &mx::GeomPropDef::setSpace) - .def("hasSpace", &mx::GeomPropDef::hasSpace) - .def("getSpace", &mx::GeomPropDef::getSpace) - .def("setIndex", &mx::GeomPropDef::setIndex) - .def("hasIndex", &mx::GeomPropDef::hasIndex) - .def("getIndex", &mx::GeomPropDef::getIndex) - .def("setGeomProp", &mx::GeomPropDef::setGeomProp) - .def("hasGeomProp", &mx::GeomPropDef::hasGeomProp) - .def("getGeomProp", &mx::GeomPropDef::getGeomProp) + py::class_(mod, "GeomPropDef", "An element representing a declaration of geometric property data.\n\nA GeomPropDef element contains a reference to a geometric node and a set of modifiers for that node. For example, a world-space normal can be declared as a reference to the \"normal\" geometric node with a space setting of \"world\", or a specific set of texture coordinates can be declared as a reference to the \"texcoord\" geometric node with an index setting of \"1\".") + .def("setGeomProp", &mx::GeomPropDef::setGeomProp, "Set the geometric property string of this element.") + .def("hasGeomProp", &mx::GeomPropDef::hasGeomProp, "Return true if this element has a geometric property string.") + .def("getGeomProp", &mx::GeomPropDef::getGeomProp, "Return the geometric property string of this element.") + .def("setSpace", &mx::GeomPropDef::setSpace, "Set the geometric space string of this element.") + .def("hasSpace", &mx::GeomPropDef::hasSpace, "Return true if this element has a geometric space string.") + .def("getSpace", &mx::GeomPropDef::getSpace, "Return the geometric space string of this element.") + .def("setIndex", &mx::GeomPropDef::setIndex, "Set the index string of this element.") + .def("hasIndex", &mx::GeomPropDef::hasIndex, "Return true if this element has an index string.") + .def("getIndex", &mx::GeomPropDef::getIndex, "Return the index string of this element.") + .def("setGeomProp", &mx::GeomPropDef::setGeomProp, "Set the geometric property string of this element.") + .def("hasGeomProp", &mx::GeomPropDef::hasGeomProp, "Return true if this element has a geometric property string.") + .def("getGeomProp", &mx::GeomPropDef::getGeomProp, "Return the geometric property string of this element.") .def_readonly_static("CATEGORY", &mx::GeomPropDef::CATEGORY); - py::class_(mod, "Collection") - .def("setIncludeGeom", &mx::Collection::setIncludeGeom) - .def("hasIncludeGeom", &mx::Collection::hasIncludeGeom) - .def("getIncludeGeom", &mx::Collection::getIncludeGeom) - .def("setExcludeGeom", &mx::Collection::setExcludeGeom) - .def("hasExcludeGeom", &mx::Collection::hasExcludeGeom) - .def("getExcludeGeom", &mx::Collection::getExcludeGeom) - .def("setIncludeCollectionString", &mx::Collection::setIncludeCollectionString) - .def("hasIncludeCollectionString", &mx::Collection::hasIncludeCollectionString) - .def("getIncludeCollectionString", &mx::Collection::getIncludeCollectionString) - .def("setIncludeCollection", &mx::Collection::setIncludeCollection) - .def("setIncludeCollections", &mx::Collection::setIncludeCollections) - .def("getIncludeCollections", &mx::Collection::getIncludeCollections) - .def("hasIncludeCycle", &mx::Collection::hasIncludeCycle) - .def("matchesGeomString", &mx::Collection::matchesGeomString) + py::class_(mod, "Collection", "A collection element within a Document.") + .def("setIncludeGeom", &mx::Collection::setIncludeGeom, "Set the include geometry string of this element.") + .def("hasIncludeGeom", &mx::Collection::hasIncludeGeom, "Return true if this element has an include geometry string.") + .def("getIncludeGeom", &mx::Collection::getIncludeGeom, "Return the include geometry string of this element.") + .def("setExcludeGeom", &mx::Collection::setExcludeGeom, "Set the exclude geometry string of this element.") + .def("hasExcludeGeom", &mx::Collection::hasExcludeGeom, "Return true if this element has an exclude geometry string.") + .def("getExcludeGeom", &mx::Collection::getExcludeGeom, "Return the exclude geometry string of this element.") + .def("setIncludeCollectionString", &mx::Collection::setIncludeCollectionString, "Set the include collection string of this element.") + .def("hasIncludeCollectionString", &mx::Collection::hasIncludeCollectionString, "Return true if this element has an include collection string.") + .def("getIncludeCollectionString", &mx::Collection::getIncludeCollectionString, "Return the include collection string of this element.") + .def("setIncludeCollection", &mx::Collection::setIncludeCollection, "Set the collection that is directly included by this element.") + .def("setIncludeCollections", &mx::Collection::setIncludeCollections, "Set the vector of collections that are directly included by this element.") + .def("getIncludeCollections", &mx::Collection::getIncludeCollections, "Return the vector of collections that are directly included by this element.") + .def("hasIncludeCycle", &mx::Collection::hasIncludeCycle, "Return true if the include chain for this element contains a cycle.") + .def("matchesGeomString", &mx::Collection::matchesGeomString, "Return true if this collection and the given geometry string have any geometries in common.") .def_readonly_static("CATEGORY", &mx::Collection::CATEGORY); - mod.def("geomStringsMatch", &mx::geomStringsMatch); + mod.def("geomStringsMatch", &mx::geomStringsMatch, "Given two geometry strings, each containing an array of geom names, return true if they have any geometries in common.\n\nAn empty geometry string matches no geometries, while the universal geometry string \"/\" matches all non-empty geometries.\n\nIf the contains argument is set to true, then we require that a geom path in the first string completely contains a geom path in the second string."); mod.attr("GEOM_PATH_SEPARATOR") = mx::GEOM_PATH_SEPARATOR; mod.attr("UNIVERSAL_GEOM_NAME") = mx::UNIVERSAL_GEOM_NAME; diff --git a/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp b/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp index 42e978d282..42c7851089 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp @@ -17,84 +17,79 @@ namespace mx = MaterialX; void bindPyInterface(py::module& mod) { - py::class_(mod, "PortElement") - .def("setNodeName", &mx::PortElement::setNodeName) - .def("getNodeName", &mx::PortElement::getNodeName) - .def("setNodeGraphString", &mx::PortElement::setNodeGraphString) - .def("hasNodeGraphString", &mx::PortElement::hasNodeGraphString) - .def("getNodeGraphString", &mx::PortElement::getNodeGraphString) - .def("setOutputString", &mx::PortElement::setOutputString) - .def("hasOutputString", &mx::PortElement::hasOutputString) - .def("getOutputString", &mx::PortElement::getOutputString) - .def("setConnectedNode", &mx::PortElement::setConnectedNode) - .def("getConnectedNode", &mx::PortElement::getConnectedNode) - .def("setConnectedOutput", &mx::PortElement::setConnectedOutput) - .def("getConnectedOutput", &mx::PortElement::getConnectedOutput); + py::class_(mod, "PortElement", "The base class for port elements such as Input and Output.\n\nPort elements support spatially-varying upstream connections to nodes.") + .def("setNodeName", &mx::PortElement::setNodeName, "Set the node name string of this element, creating a connection to the Node with the given name within the same NodeGraph.") + .def("getNodeName", &mx::PortElement::getNodeName, "Return the node name string of this element.") + .def("setNodeGraphString", &mx::PortElement::setNodeGraphString, "Set the node graph string of this element.") + .def("hasNodeGraphString", &mx::PortElement::hasNodeGraphString, "Return true if this element has a node graph string.") + .def("getNodeGraphString", &mx::PortElement::getNodeGraphString, "Return the node graph string of this element.") + .def("setOutputString", &mx::PortElement::setOutputString, "Set the output string of this element.") + .def("hasOutputString", &mx::PortElement::hasOutputString, "Return true if this element has an output string.") + .def("getOutputString", &mx::PortElement::getOutputString, "Return the output string of this element.") + .def("setConnectedNode", &mx::PortElement::setConnectedNode, "Set the node to which this element is connected.\n\nThe given node must belong to the same node graph. If the node argument is null, then any existing node connection will be cleared.") + .def("getConnectedNode", &mx::PortElement::getConnectedNode, "Return the node, if any, to which this element is connected.") + .def("setConnectedOutput", &mx::PortElement::setConnectedOutput, "Set the output to which this input is connected.\n\nIf the output argument is null, then any existing output connection will be cleared.") + .def("getConnectedOutput", &mx::PortElement::getConnectedOutput, "Return the output, if any, to which this input is connected."); - py::class_(mod, "Input") - .def("setDefaultGeomPropString", &mx::Input::setDefaultGeomPropString) - .def("hasDefaultGeomPropString", &mx::Input::hasDefaultGeomPropString) - .def("getDefaultGeomPropString", &mx::Input::getDefaultGeomPropString) - .def("getDefaultGeomProp", &mx::Input::getDefaultGeomProp) - .def("getConnectedNode", &mx::Input::getConnectedNode) - .def("setConnectedInterfaceName", &mx::Input::setConnectedInterfaceName) - .def("getInterfaceInput", &mx::Input::getInterfaceInput) + py::class_(mod, "Input", "An input element within a Node or NodeDef.\n\nAn Input holds either a uniform value or a connection to a spatially-varying Output, either of which may be modified within the scope of a Material.") + .def("setDefaultGeomPropString", &mx::Input::setDefaultGeomPropString, "Set the defaultgeomprop string for the input.") + .def("hasDefaultGeomPropString", &mx::Input::hasDefaultGeomPropString, "Return true if the given input has a defaultgeomprop string.") + .def("getDefaultGeomPropString", &mx::Input::getDefaultGeomPropString, "Return the defaultgeomprop string for the input.") + .def("getDefaultGeomProp", &mx::Input::getDefaultGeomProp, "Return the GeomPropDef element to use, if defined for this input.") + .def("getConnectedNode", &mx::Input::getConnectedNode, "Return the node, if any, to which this input is connected.") + .def("setConnectedInterfaceName", &mx::Input::setConnectedInterfaceName, "Connects this input to a corresponding interface with the given name.\n\nIf the interface name specified is an empty string then any existing connection is removed.") + .def("getInterfaceInput", &mx::Input::getInterfaceInput, "Return the input on the parent graph corresponding to the interface name for this input.") .def_readonly_static("CATEGORY", &mx::Input::CATEGORY); - py::class_(mod, "Output") - .def("hasUpstreamCycle", &mx::Output::hasUpstreamCycle) + py::class_(mod, "Output", "A spatially-varying output element within a NodeGraph or NodeDef.") + .def("hasUpstreamCycle", &mx::Output::hasUpstreamCycle, "Return true if a cycle exists in any upstream path from this element.") .def_readonly_static("CATEGORY", &mx::Output::CATEGORY) .def_readonly_static("DEFAULT_INPUT_ATTRIBUTE", &mx::Output::DEFAULT_INPUT_ATTRIBUTE); - py::class_(mod, "InterfaceElement") - .def("setNodeDefString", &mx::InterfaceElement::setNodeDefString) - .def("hasNodeDefString", &mx::InterfaceElement::hasNodeDefString) - .def("getNodeDefString", &mx::InterfaceElement::getNodeDefString) - .def("addInput", &mx::InterfaceElement::addInput, - py::arg("name") = mx::EMPTY_STRING, py::arg("type") = mx::DEFAULT_TYPE_STRING) - .def("getInput", &mx::InterfaceElement::getInput) - .def("getInputs", &mx::InterfaceElement::getInputs) - .def("getInputCount", &mx::InterfaceElement::getInputCount) - .def("removeInput", &mx::InterfaceElement::removeInput) - .def("getActiveInput", &mx::InterfaceElement::getActiveInput) - .def("getActiveInputs", &mx::InterfaceElement::getActiveInputs) - .def("addOutput", &mx::InterfaceElement::addOutput, - py::arg("name") = mx::EMPTY_STRING, py::arg("type") = mx::DEFAULT_TYPE_STRING) - .def("getOutput", &mx::InterfaceElement::getOutput) - .def("getOutputs", &mx::InterfaceElement::getOutputs) - .def("getOutputCount", &mx::InterfaceElement::getOutputCount) - .def("removeOutput", &mx::InterfaceElement::removeOutput) - .def("getActiveOutput", &mx::InterfaceElement::getActiveOutput) - .def("getActiveOutputs", &mx::InterfaceElement::getActiveOutputs) - .def("setConnectedOutput", &mx::InterfaceElement::setConnectedOutput) - .def("getConnectedOutput", &mx::InterfaceElement::getConnectedOutput) - .def("addToken", &mx::InterfaceElement::addToken, - py::arg("name") = mx::DEFAULT_TYPE_STRING) - .def("getToken", &mx::InterfaceElement::getToken) - .def("getTokens", &mx::InterfaceElement::getTokens) - .def("removeToken", &mx::InterfaceElement::removeToken) - .def("getActiveToken", &mx::InterfaceElement::getActiveToken) - .def("getActiveTokens", &mx::InterfaceElement::getActiveTokens) - .def("getActiveValueElement", &mx::InterfaceElement::getActiveValueElement) - .def("getActiveValueElements", &mx::InterfaceElement::getActiveValueElements) - .def("_getInputValue", &mx::InterfaceElement::getInputValue) - .def("setTokenValue", &mx::InterfaceElement::setTokenValue) - .def("getTokenValue", &mx::InterfaceElement::getTokenValue) - .def("setTarget", &mx::InterfaceElement::setTarget) - .def("hasTarget", &mx::InterfaceElement::hasTarget) - .def("getTarget", &mx::InterfaceElement::getTarget) - .def("setVersionString", &mx::InterfaceElement::setVersionString) - .def("hasVersionString", &mx::InterfaceElement::hasVersionString) - .def("getVersionString", &mx::InterfaceElement::getVersionString) - .def("setVersionIntegers", &mx::InterfaceElement::setVersionIntegers) - .def("getVersionIntegers", &mx::InterfaceElement::getVersionIntegers) - .def("setDefaultVersion", &mx::InterfaceElement::setDefaultVersion) - .def("getDefaultVersion", &mx::InterfaceElement::getDefaultVersion) - .def("getDeclaration", &mx::InterfaceElement::getDeclaration, - py::arg("target") = mx::EMPTY_STRING) - .def("clearContent", &mx::InterfaceElement::clearContent) - .def("hasExactInputMatch", &mx::InterfaceElement::hasExactInputMatch, - py::arg("declaration"), py::arg("message") = nullptr) + py::class_(mod, "InterfaceElement", "The base class for interface elements such as Node, NodeDef, and NodeGraph.\n\nAn InterfaceElement supports a set of Input and Output elements, with an API for setting their values.") + .def("setNodeDefString", &mx::InterfaceElement::setNodeDefString, "Set the NodeDef string for the interface.") + .def("hasNodeDefString", &mx::InterfaceElement::hasNodeDefString, "Return true if the given interface has a NodeDef string.") + .def("getNodeDefString", &mx::InterfaceElement::getNodeDefString, "Return the NodeDef string for the interface.") + .def("addInput", &mx::InterfaceElement::addInput, py::arg("name") = mx::EMPTY_STRING, py::arg("type") = mx::DEFAULT_TYPE_STRING, "Add an Input to this interface.\n\nArgs:\n name: The name of the new Input. If no name is specified, then a unique name will automatically be generated.\n type: An optional type string.\n\nReturns:\n A shared pointer to the new Input.") + .def("getInput", &mx::InterfaceElement::getInput, "Return the Input, if any, with the given name.") + .def("getInputs", &mx::InterfaceElement::getInputs, "Return a vector of all Input elements.") + .def("getInputCount", &mx::InterfaceElement::getInputCount, "Return the number of Input elements.") + .def("removeInput", &mx::InterfaceElement::removeInput, "Remove the Input, if any, with the given name.") + .def("getActiveInput", &mx::InterfaceElement::getActiveInput, "Return the first Input with the given name that belongs to this interface, taking interface inheritance into account.") + .def("getActiveInputs", &mx::InterfaceElement::getActiveInputs, "Return a vector of all Input elements that belong to this interface, taking inheritance into account.") + .def("addOutput", &mx::InterfaceElement::addOutput, py::arg("name") = mx::EMPTY_STRING, py::arg("type") = mx::DEFAULT_TYPE_STRING, "Add an Output to this interface.\n\nArgs:\n name: The name of the new Output. If no name is specified, then a unique name will automatically be generated.\n type: An optional type string.\n\nReturns:\n A shared pointer to the new Output.") + .def("getOutput", &mx::InterfaceElement::getOutput, "Return the Output, if any, with the given name.") + .def("getOutputs", &mx::InterfaceElement::getOutputs, "Return a vector of all Output elements.") + .def("getOutputCount", &mx::InterfaceElement::getOutputCount, "Return the number of Output elements.") + .def("removeOutput", &mx::InterfaceElement::removeOutput, "Remove the Output, if any, with the given name.") + .def("getActiveOutput", &mx::InterfaceElement::getActiveOutput, "Return the first Output with the given name that belongs to this interface, taking interface inheritance into account.") + .def("getActiveOutputs", &mx::InterfaceElement::getActiveOutputs, "Return a vector of all Output elements that belong to this interface, taking inheritance into account.") + .def("setConnectedOutput", &mx::InterfaceElement::setConnectedOutput, "Set the output to which the given input is connected, creating a child input if needed.\n\nIf the node argument is null, then any existing output connection on the input will be cleared.") + .def("getConnectedOutput", &mx::InterfaceElement::getConnectedOutput, "Return the output connected to the given input.\n\nIf the given input is not present, then an empty OutputPtr is returned.") + .def("addToken", &mx::InterfaceElement::addToken, py::arg("name") = mx::DEFAULT_TYPE_STRING, "Add a Token to this interface.\n\nArgs:\n name: The name of the new Token. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new Token.") + .def("getToken", &mx::InterfaceElement::getToken, "Return the Token, if any, with the given name.") + .def("getTokens", &mx::InterfaceElement::getTokens, "Return a vector of all Token elements.") + .def("removeToken", &mx::InterfaceElement::removeToken, "Remove the Token, if any, with the given name.") + .def("getActiveToken", &mx::InterfaceElement::getActiveToken, "Return the first Token with the given name that belongs to this interface, taking interface inheritance into account.") + .def("getActiveTokens", &mx::InterfaceElement::getActiveTokens, "Return a vector of all Token elements that belong to this interface, taking inheritance into account.") + .def("getActiveValueElement", &mx::InterfaceElement::getActiveValueElement, "Return the first value element with the given name that belongs to this interface, taking interface inheritance into account.\n\nExamples of value elements are Input, Output, and Token.") + .def("getActiveValueElements", &mx::InterfaceElement::getActiveValueElements, "Return a vector of all value elements that belong to this interface, taking inheritance into account.\n\nExamples of value elements are Input, Output, and Token.") + .def("_getInputValue", &mx::InterfaceElement::getInputValue, "Return the typed value of an input by its name, taking both the calling element and its declaration into account.\n\nArgs:\n name: The name of the input to be evaluated.\n target: An optional target name, which will be used to filter the declarations that are considered.\n\nReturns:\n If the given input is found in this interface or its declaration, then a shared pointer to its value is returned; otherwise, an empty shared pointer is returned.") + .def("setTokenValue", &mx::InterfaceElement::setTokenValue, "Set the string value of a Token by its name, creating a child element to hold the Token if needed.") + .def("getTokenValue", &mx::InterfaceElement::getTokenValue, "Return the string value of a Token by its name, or an empty string if the given Token is not present.") + .def("setTarget", &mx::InterfaceElement::setTarget, "Set the target string of this interface.") + .def("hasTarget", &mx::InterfaceElement::hasTarget, "Return true if the given interface has a target string.") + .def("getTarget", &mx::InterfaceElement::getTarget, "Return the target string of this interface.") + .def("setVersionString", &mx::InterfaceElement::setVersionString, "Set the version string of this interface.") + .def("hasVersionString", &mx::InterfaceElement::hasVersionString, "Return true if this interface has a version string.") + .def("getVersionString", &mx::InterfaceElement::getVersionString, "Return the version string of this interface.") + .def("setVersionIntegers", &mx::InterfaceElement::setVersionIntegers, "Set the major and minor versions as an integer pair.") + .def("getVersionIntegers", &mx::InterfaceElement::getVersionIntegers, "Return the major and minor versions as an integer pair.") + .def("setDefaultVersion", &mx::InterfaceElement::setDefaultVersion, "Set the default version flag of this element.") + .def("getDefaultVersion", &mx::InterfaceElement::getDefaultVersion, "Return the default version flag of this element.") + .def("getDeclaration", &mx::InterfaceElement::getDeclaration, py::arg("target") = mx::EMPTY_STRING, "Return the first declaration of this interface, optionally filtered by the given target name.\n\nArgs:\n target: An optional target name, which will be used to filter the declarations that are considered.\n\nReturns:\n A shared pointer to declaration, or an empty shared pointer if no declaration was found.") + .def("clearContent", &mx::InterfaceElement::clearContent, "Clear all attributes and descendants from this element.") + .def("hasExactInputMatch", &mx::InterfaceElement::hasExactInputMatch, py::arg("declaration"), py::arg("message") = nullptr, "Return true if this instance has an exact input match with the given declaration, where each input of this the instance corresponds to a declaration input of the same name and type.\n\nIf an exact input match is not found, and the optional message argument is provided, then an error message will be appended to the given string.") BIND_INTERFACE_TYPE_INSTANCE(integer, int) BIND_INTERFACE_TYPE_INSTANCE(boolean, bool) BIND_INTERFACE_TYPE_INSTANCE(float, float) diff --git a/source/PyMaterialX/PyMaterialXCore/PyLook.cpp b/source/PyMaterialX/PyMaterialXCore/PyLook.cpp index 5d1e1e8af3..f3acac53b6 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyLook.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyLook.cpp @@ -15,72 +15,66 @@ namespace mx = MaterialX; void bindPyLook(py::module& mod) { - py::class_(mod, "Look") - .def("addMaterialAssign", &mx::Look::addMaterialAssign, - py::arg("name") = mx::EMPTY_STRING, py::arg("material") = mx::EMPTY_STRING) - .def("getMaterialAssign", &mx::Look::getMaterialAssign) - .def("getMaterialAssigns", &mx::Look::getMaterialAssigns) - .def("getActiveMaterialAssigns", &mx::Look::getActiveMaterialAssigns) - .def("removeMaterialAssign", &mx::Look::removeMaterialAssign) - .def("addPropertyAssign", &mx::Look::addPropertyAssign, - py::arg("name") = mx::EMPTY_STRING) - .def("getPropertyAssign", &mx::Look::getPropertyAssign) - .def("getPropertyAssigns", &mx::Look::getPropertyAssigns) - .def("getActivePropertyAssigns", &mx::Look::getActivePropertyAssigns) - .def("removePropertyAssign", &mx::Look::removePropertyAssign) - .def("addPropertySetAssign", &mx::Look::addPropertySetAssign, - py::arg("name") = mx::EMPTY_STRING) - .def("getPropertySetAssign", &mx::Look::getPropertySetAssign) - .def("getPropertySetAssigns", &mx::Look::getPropertySetAssigns) - .def("getActivePropertySetAssigns", &mx::Look::getActivePropertySetAssigns) - .def("removePropertySetAssign", &mx::Look::removePropertySetAssign) - .def("addVariantAssign", &mx::Look::addVariantAssign, - py::arg("name") = mx::EMPTY_STRING) - .def("getVariantAssign", &mx::Look::getVariantAssign) - .def("getVariantAssigns", &mx::Look::getVariantAssigns) - .def("getActiveVariantAssigns", &mx::Look::getActiveVariantAssigns) - .def("removeVariantAssign", &mx::Look::removeVariantAssign) - .def("addVisibility", &mx::Look::addVisibility, - py::arg("name") = mx::EMPTY_STRING) - .def("getVisibility", &mx::Look::getVisibility) - .def("getVisibilities", &mx::Look::getVisibilities) - .def("getActiveVisibilities", &mx::Look::getActiveVisibilities) - .def("removeVisibility", &mx::Look::removeVisibility) + py::class_(mod, "Look", "A look element within a Document.") + .def("addMaterialAssign", &mx::Look::addMaterialAssign, py::arg("name") = mx::EMPTY_STRING, py::arg("material") = mx::EMPTY_STRING, "Add a MaterialAssign to the look.\n\nArgs:\n name: The name of the new MaterialAssign. If no name is specified, then a unique name will automatically be generated.\n material: An optional material string, which should match the name of the material node to be assigned.\n\nReturns:\n A shared pointer to the new MaterialAssign.") + .def("getMaterialAssign", &mx::Look::getMaterialAssign, "Return the MaterialAssign, if any, with the given name.") + .def("getMaterialAssigns", &mx::Look::getMaterialAssigns, "Return a vector of all MaterialAssign elements in the look.") + .def("getActiveMaterialAssigns", &mx::Look::getActiveMaterialAssigns, "Return a vector of all MaterialAssign elements that belong to this look, taking look inheritance into account.") + .def("removeMaterialAssign", &mx::Look::removeMaterialAssign, "Remove the MaterialAssign, if any, with the given name.") + .def("addPropertyAssign", &mx::Look::addPropertyAssign, py::arg("name") = mx::EMPTY_STRING, "Add a PropertyAssign to the look.\n\nArgs:\n name: The name of the new PropertyAssign. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new PropertyAssign.") + .def("getPropertyAssign", &mx::Look::getPropertyAssign, "Return the PropertyAssign, if any, with the given name.") + .def("getPropertyAssigns", &mx::Look::getPropertyAssigns, "Return a vector of all PropertyAssign elements in the look.") + .def("getActivePropertyAssigns", &mx::Look::getActivePropertyAssigns, "Return a vector of all PropertyAssign elements that belong to this look, taking look inheritance into account.") + .def("removePropertyAssign", &mx::Look::removePropertyAssign, "Remove the PropertyAssign, if any, with the given name.") + .def("addPropertySetAssign", &mx::Look::addPropertySetAssign, py::arg("name") = mx::EMPTY_STRING, "Add a PropertySetAssign to the look.\n\nArgs:\n name: The name of the new PropertySetAssign. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new PropertySetAssign.") + .def("getPropertySetAssign", &mx::Look::getPropertySetAssign, "Return the PropertySetAssign, if any, with the given name.") + .def("getPropertySetAssigns", &mx::Look::getPropertySetAssigns, "Return a vector of all PropertySetAssign elements in the look.") + .def("getActivePropertySetAssigns", &mx::Look::getActivePropertySetAssigns, "Return a vector of all PropertySetAssign elements that belong to this look, taking look inheritance into account.") + .def("removePropertySetAssign", &mx::Look::removePropertySetAssign, "Remove the PropertySetAssign, if any, with the given name.") + .def("addVariantAssign", &mx::Look::addVariantAssign, py::arg("name") = mx::EMPTY_STRING, "Add a VariantAssign to the look.\n\nArgs:\n name: The name of the new VariantAssign. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new VariantAssign.") + .def("getVariantAssign", &mx::Look::getVariantAssign, "Return the VariantAssign, if any, with the given name.") + .def("getVariantAssigns", &mx::Look::getVariantAssigns, "Return a vector of all VariantAssign elements in the look.") + .def("getActiveVariantAssigns", &mx::Look::getActiveVariantAssigns, "Return a vector of all VariantAssign elements that belong to this look, taking look inheritance into account.") + .def("removeVariantAssign", &mx::Look::removeVariantAssign, "Remove the VariantAssign, if any, with the given name.") + .def("addVisibility", &mx::Look::addVisibility, py::arg("name") = mx::EMPTY_STRING, "Add a Visibility to the look.\n\nArgs:\n name: The name of the new Visibility. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new Visibility.") + .def("getVisibility", &mx::Look::getVisibility, "Return the Visibility, if any, with the given name.") + .def("getVisibilities", &mx::Look::getVisibilities, "Return a vector of all Visibility elements in the look.") + .def("getActiveVisibilities", &mx::Look::getActiveVisibilities, "Return a vector of all Visibility elements that belong to this look, taking look inheritance into account.") + .def("removeVisibility", &mx::Look::removeVisibility, "Remove the Visibility, if any, with the given name.") .def_readonly_static("CATEGORY", &mx::Look::CATEGORY); - py::class_(mod, "LookGroup") - .def("getLooks", &mx::LookGroup::getLooks) - .def("setLooks", &mx::LookGroup::setLooks) - .def("getActiveLook", &mx::LookGroup::getActiveLook) - .def("setActiveLook", &mx::LookGroup::setActiveLook) + py::class_(mod, "LookGroup", "A look group element within a Document.") + .def("getLooks", &mx::LookGroup::getLooks, "Get comma-separated list of looks.") + .def("setLooks", &mx::LookGroup::setLooks, "Set comma-separated list of looks.") + .def("getActiveLook", &mx::LookGroup::getActiveLook, "Return the active look, if any.") + .def("setActiveLook", &mx::LookGroup::setActiveLook, "Set the active look.") .def_readonly_static("CATEGORY", &mx::LookGroup::CATEGORY) .def_readonly_static("LOOKS_ATTRIBUTE", &mx::LookGroup::LOOKS_ATTRIBUTE) .def_readonly_static("ACTIVE_ATTRIBUTE", &mx::LookGroup::ACTIVE_ATTRIBUTE); - py::class_(mod, "MaterialAssign") - .def("setMaterial", &mx::MaterialAssign::setMaterial) - .def("hasMaterial", &mx::MaterialAssign::hasMaterial) - .def("getMaterial", &mx::MaterialAssign::getMaterial) - .def("getMaterialOutputs", &mx::MaterialAssign::getMaterialOutputs) - .def("setExclusive", &mx::MaterialAssign::setExclusive) - .def("getExclusive", &mx::MaterialAssign::getExclusive) - .def("getReferencedMaterial", &mx::MaterialAssign::getReferencedMaterial) + py::class_(mod, "MaterialAssign", "A material assignment element within a Look.") + .def("setMaterial", &mx::MaterialAssign::setMaterial, "Set the material string for the MaterialAssign.") + .def("hasMaterial", &mx::MaterialAssign::hasMaterial, "Return true if the given MaterialAssign has a material string.") + .def("getMaterial", &mx::MaterialAssign::getMaterial, "Return the material string for the MaterialAssign.") + .def("getMaterialOutputs", &mx::MaterialAssign::getMaterialOutputs, "Return the outputs on any referenced material.") + .def("setExclusive", &mx::MaterialAssign::setExclusive, "Set the exclusive boolean for the MaterialAssign.") + .def("getExclusive", &mx::MaterialAssign::getExclusive, "Return the exclusive boolean for the MaterialAssign.") + .def("getReferencedMaterial", &mx::MaterialAssign::getReferencedMaterial, "Return the material node, if any, referenced by the MaterialAssign.") .def_readonly_static("CATEGORY", &mx::MaterialAssign::CATEGORY); - py::class_(mod, "Visibility") - .def("setViewerGeom", &mx::Visibility::setViewerGeom) - .def("hasViewerGeom", &mx::Visibility::hasViewerGeom) - .def("getViewerGeom", &mx::Visibility::getViewerGeom) - .def("setViewerCollection", &mx::Visibility::setViewerCollection) - .def("hasViewerCollection", &mx::Visibility::hasViewerCollection) - .def("getViewerCollection", &mx::Visibility::getViewerCollection) - .def("setVisibilityType", &mx::Visibility::setVisibilityType) - .def("hasVisibilityType", &mx::Visibility::hasVisibilityType) - .def("getVisibilityType", &mx::Visibility::getVisibilityType) - .def("setVisible", &mx::Visibility::setVisible) - .def("getVisible", &mx::Visibility::getVisible) + py::class_(mod, "Visibility", "A visibility element within a Look.\n\nA Visibility describes the visibility relationship between two geometries or geometric collections.") + .def("setViewerGeom", &mx::Visibility::setViewerGeom, "Set the viewer geom string of the element.") + .def("hasViewerGeom", &mx::Visibility::hasViewerGeom, "Return true if the given element has a viewer geom string.") + .def("getViewerGeom", &mx::Visibility::getViewerGeom, "Return the viewer geom string of the element.") + .def("setViewerCollection", &mx::Visibility::setViewerCollection, "Set the viewer geom string of the element.") + .def("hasViewerCollection", &mx::Visibility::hasViewerCollection, "Return true if the given element has a viewer collection string.") + .def("getViewerCollection", &mx::Visibility::getViewerCollection, "Return the viewer collection string of the element.") + .def("setVisibilityType", &mx::Visibility::setVisibilityType, "Set the visibility type string of the element.") + .def("hasVisibilityType", &mx::Visibility::hasVisibilityType, "Return true if the given element has a visibility type string.") + .def("getVisibilityType", &mx::Visibility::getVisibilityType, "Return the visibility type string of the element.") + .def("setVisible", &mx::Visibility::setVisible, "Set the visible boolean of the element.") + .def("getVisible", &mx::Visibility::getVisible, "Return the visible boolean of the element.") .def_readonly_static("CATEGORY", &mx::Visibility::CATEGORY); - mod.def("getGeometryBindings", &mx::getGeometryBindings, - py::arg("materialNode") , py::arg("geom") = mx::UNIVERSAL_GEOM_NAME); + mod.def("getGeometryBindings", &mx::getGeometryBindings, py::arg("materialNode"), py::arg("geom") = mx::UNIVERSAL_GEOM_NAME, "Return a vector of all MaterialAssign elements that bind this material node to the given geometry string.\n\nArgs:\n materialNode: Node to examine\n geom: The geometry for which material bindings should be returned. By default, this argument is the universal geometry string \"/\", and all material bindings are returned.\n\nReturns:\n Vector of MaterialAssign elements"); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyMaterial.cpp b/source/PyMaterialX/PyMaterialXCore/PyMaterial.cpp index 31656c67f9..3a4500e0f5 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyMaterial.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyMaterial.cpp @@ -13,7 +13,6 @@ namespace mx = MaterialX; void bindPyMaterial(py::module& mod) { - mod.def("getShaderNodes", &mx::getShaderNodes, - py::arg("materialNode"), py::arg("nodeType") = mx::SURFACE_SHADER_TYPE_STRING, py::arg("target") = mx::EMPTY_STRING); - mod.def("getConnectedOutputs", &mx::getConnectedOutputs); + mod.def("getShaderNodes", &mx::getShaderNodes, py::arg("materialNode"), py::arg("nodeType") = mx::SURFACE_SHADER_TYPE_STRING, py::arg("target") = mx::EMPTY_STRING, "Return a vector of all shader nodes connected to the given material node's inputs, filtered by the given shader type and target.\n\nArgs:\n materialNode: The node to examine.\n nodeType: THe shader node type to return. Defaults to the surface shader type.\n target: An optional target name, which will be used to filter the returned nodes."); + mod.def("getConnectedOutputs", &mx::getConnectedOutputs, "Return a vector of all outputs connected to the given node's inputs."); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyNode.cpp b/source/PyMaterialX/PyMaterialXCore/PyNode.cpp index 55448dee80..8fabf495c2 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyNode.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyNode.cpp @@ -14,66 +14,58 @@ void bindPyNode(py::module& mod) { py::class_(mod, "NodePredicate"); - py::class_(mod, "Node") - .def("setConnectedNode", &mx::Node::setConnectedNode) - .def("getConnectedNode", &mx::Node::getConnectedNode) - .def("setConnectedNodeName", &mx::Node::setConnectedNodeName) - .def("getConnectedNodeName", &mx::Node::getConnectedNodeName) - .def("getNodeDef", &mx::Node::getNodeDef, - py::arg("target") = mx::EMPTY_STRING, py::arg("allowRoughMatch") = false) - .def("getImplementation", &mx::Node::getImplementation, - py::arg("target") = mx::EMPTY_STRING) - .def("getDownstreamPorts", &mx::Node::getDownstreamPorts) - .def("addInputFromNodeDef", &mx::Node::addInputFromNodeDef) - .def("addInputsFromNodeDef", &mx::Node::addInputsFromNodeDef) + py::class_(mod, "Node", "A node element within a NodeGraph or Document.\n\nA Node represents an instance of a NodeDef within a graph, and its Input elements apply specific values and connections to that instance.") + .def("setConnectedNode", &mx::Node::setConnectedNode, "Set the node to which the given input is connected, creating a child input if needed.\n\nIf the node argument is null, then any existing node connection on the input will be cleared.") + .def("getConnectedNode", &mx::Node::getConnectedNode, "Return the Node connected to the given input.\n\nIf the given input is not present, then an empty NodePtr is returned.") + .def("setConnectedNodeName", &mx::Node::setConnectedNodeName, "Set the name of the Node connected to the given input, creating a child element for the input if needed.") + .def("getConnectedNodeName", &mx::Node::getConnectedNodeName, "Return the name of the Node connected to the given input.\n\nIf the given input is not present, then an empty string is returned.") + .def("getNodeDef", &mx::Node::getNodeDef, py::arg("target") = mx::EMPTY_STRING, py::arg("allowRoughMatch") = false, "Return the first NodeDef that declares this node, optionally filtered by the given target name.\n\nArgs:\n target: An optional target name, which will be used to filter the nodedefs that are considered.\n allowRoughMatch: If specified, then a rough match will be allowed when an exact match is not found. An exact match requires that each node input corresponds to a nodedef input of the same name and type.\n\nReturns:\n A NodeDef for this node, or an empty shared pointer if none was found.") + .def("getImplementation", &mx::Node::getImplementation, py::arg("target") = mx::EMPTY_STRING, "Return the first implementation for this node, optionally filtered by the given target and language names.\n\nArgs:\n target: An optional target name, which will be used to filter the implementations that are considered.\n\nReturns:\n An implementation for this node, or an empty shared pointer if none was found. Note that a node implementation may be either an Implementation element or a NodeGraph element.") + .def("getDownstreamPorts", &mx::Node::getDownstreamPorts, "Return a vector of all downstream ports that connect to this node, ordered by the names of the port elements.") + .def("addInputFromNodeDef", &mx::Node::addInputFromNodeDef, "Add an input based on the corresponding input for the associated node definition.\n\nIf the input already exists on the node it will just be returned.") + .def("addInputsFromNodeDef", &mx::Node::addInputsFromNodeDef, "Add inputs based on the corresponding associated node definition.") .def_readonly_static("CATEGORY", &mx::Node::CATEGORY); - py::class_(mod, "GraphElement") - .def("addNode", &mx::GraphElement::addNode, - py::arg("category"), py::arg("name") = mx::EMPTY_STRING, py::arg("type") = mx::DEFAULT_TYPE_STRING) - .def("addNodeInstance", &mx::GraphElement::addNodeInstance, - py::arg("nodeDef"), py::arg("name") = mx::EMPTY_STRING) - .def("getNode", &mx::GraphElement::getNode) - .def("getNodes", &mx::GraphElement::getNodes, - py::arg("category") = mx::EMPTY_STRING) - .def("removeNode", &mx::GraphElement::removeNode) - .def("addMaterialNode", &mx::GraphElement::addMaterialNode, - py::arg("name") = mx::EMPTY_STRING, py::arg("shaderNode") = nullptr) - .def("getMaterialNodes", &mx::GraphElement::getMaterialNodes) - .def("addBackdrop", &mx::GraphElement::addBackdrop, - py::arg("name") = mx::EMPTY_STRING) - .def("getBackdrop", &mx::GraphElement::getBackdrop) - .def("getBackdrops", &mx::GraphElement::getBackdrops) - .def("removeBackdrop", &mx::GraphElement::removeBackdrop) - .def("flattenSubgraphs", &mx::GraphElement::flattenSubgraphs, - py::arg("target") = mx::EMPTY_STRING, py::arg("filter") = nullptr) - .def("topologicalSort", &mx::GraphElement::topologicalSort) - .def("addGeomNode", &mx::GraphElement::addGeomNode) - .def("asStringDot", &mx::GraphElement::asStringDot); + py::class_(mod, "GraphElement", "The base class for graph elements such as NodeGraph and Document.") + .def("addNode", &mx::GraphElement::addNode, py::arg("category"), py::arg("name") = mx::EMPTY_STRING, py::arg("type") = mx::DEFAULT_TYPE_STRING, "Add a Node to the graph.\n\nArgs:\n category: The category of the new Node.\n name: The name of the new Node. If no name is specified, then a unique name will automatically be generated.\n type: An optional type string.\n\nReturns:\n A shared pointer to the new Node.") + .def("addNodeInstance", &mx::GraphElement::addNodeInstance, py::arg("nodeDef"), py::arg("name") = mx::EMPTY_STRING, "Add a Node that is an instance of the given NodeDef.") + .def("getNode", &mx::GraphElement::getNode, "Return the Node, if any, with the given name.") + .def("getNodes", &mx::GraphElement::getNodes, py::arg("category") = mx::EMPTY_STRING, "Return a vector of all Nodes in the graph, optionally filtered by the given category string.") + .def("removeNode", &mx::GraphElement::removeNode, "Remove the Node, if any, with the given name.") + .def("addMaterialNode", &mx::GraphElement::addMaterialNode, py::arg("name") = mx::EMPTY_STRING, py::arg("shaderNode") = nullptr, "Add a material node to the graph, optionally connecting it to the given shader node.") + .def("getMaterialNodes", &mx::GraphElement::getMaterialNodes, "Return a vector of all material nodes.") + .def("addBackdrop", &mx::GraphElement::addBackdrop, py::arg("name") = mx::EMPTY_STRING, "Add a Backdrop to the graph.") + .def("getBackdrop", &mx::GraphElement::getBackdrop, "Return the Backdrop, if any, with the given name.") + .def("getBackdrops", &mx::GraphElement::getBackdrops, "Return a vector of all Backdrop elements in the graph.") + .def("removeBackdrop", &mx::GraphElement::removeBackdrop, "Remove the Backdrop, if any, with the given name.") + .def("flattenSubgraphs", &mx::GraphElement::flattenSubgraphs, py::arg("target") = mx::EMPTY_STRING, py::arg("filter") = nullptr, "Flatten all subgraphs at the root scope of this graph element, recursively replacing each graph-defined node with its equivalent node network.\n\nArgs:\n target: An optional target string to be used in specifying which node definitions are used in this process.\n filter: An optional node predicate specifying which nodes should be included and excluded from this process.") + .def("topologicalSort", &mx::GraphElement::topologicalSort, "Return a vector of all children (nodes and outputs) sorted in topological order.") + .def("addGeomNode", &mx::GraphElement::addGeomNode, "If not yet present, add a geometry node to this graph matching the given property definition and name prefix.") + .def("asStringDot", &mx::GraphElement::asStringDot, "Convert this graph to a string in the DOT language syntax.\n\nThis can be used to visualise the graph using GraphViz (http://www.graphviz.org).\n\nIf declarations for the contained nodes are provided as nodedefs in the owning document, then they will be used to provide additional formatting details."); - py::class_(mod, "NodeGraph") - .def("getMaterialOutputs", &mx::NodeGraph::getMaterialOutputs) - .def("setNodeDef", &mx::NodeGraph::setNodeDef) - .def("getNodeDef", &mx::NodeGraph::getNodeDef) - .def("getDeclaration", &mx::NodeGraph::getDeclaration) - .def("addInterfaceName", &mx::NodeGraph::addInterfaceName) - .def("removeInterfaceName", &mx::NodeGraph::removeInterfaceName) - .def("modifyInterfaceName", &mx::NodeGraph::modifyInterfaceName) - .def("getDownstreamPorts", &mx::NodeGraph::getDownstreamPorts) + py::class_(mod, "NodeGraph", "A node graph element within a Document.") + .def("getMaterialOutputs", &mx::NodeGraph::getMaterialOutputs, "Return all material-type outputs of the nodegraph.") + .def("setNodeDef", &mx::NodeGraph::setNodeDef, "Set the NodeDef element referenced by this NodeGraph.") + .def("getNodeDef", &mx::NodeGraph::getNodeDef, "Return the NodeDef element referenced by this NodeGraph.") + .def("getDeclaration", &mx::NodeGraph::getDeclaration, "Return the first declaration of this interface, optionally filtered by the given target name.") + .def("addInterfaceName", &mx::NodeGraph::addInterfaceName, "Add an interface name to an existing NodeDef associated with this NodeGraph.\n\nArgs:\n inputPath: Path to an input descendant of this graph.\n interfaceName: The new interface name.\n\nReturns:\n Interface input.") + .def("removeInterfaceName", &mx::NodeGraph::removeInterfaceName, "Remove an interface name from an existing NodeDef associated with this NodeGraph.\n\nArgs:\n inputPath: Path to an input descendant of this graph.") + .def("modifyInterfaceName", &mx::NodeGraph::modifyInterfaceName, "Modify the interface name on an existing NodeDef associated with this NodeGraph.\n\nArgs:\n inputPath: Path to an input descendant of this graph.\n interfaceName: The new interface name.") + .def("getDownstreamPorts", &mx::NodeGraph::getDownstreamPorts, "Return a vector of all downstream ports that connect to this graph, ordered by the names of the port elements.") .def_readonly_static("CATEGORY", &mx::NodeGraph::CATEGORY); - py::class_(mod, "Backdrop") - .def("setContainsString", &mx::Backdrop::setContainsString) - .def("hasContainsString", &mx::Backdrop::hasContainsString) - .def("getContainsString", &mx::Backdrop::getContainsString) - .def("setWidth", &mx::Backdrop::setWidth) - .def("hasWidth", &mx::Backdrop::hasWidth) - .def("getWidth", &mx::Backdrop::getWidth) - .def("setHeight", &mx::Backdrop::setHeight) - .def("hasHeight", &mx::Backdrop::hasHeight) - .def("getHeight", &mx::Backdrop::getHeight) - .def("setContainsElements", &mx::Backdrop::setContainsElements) - .def("getContainsElements", &mx::Backdrop::getContainsElements) + py::class_(mod, "Backdrop", "A layout element used to contain, group and document nodes within a graph.") + .def("setContainsString", &mx::Backdrop::setContainsString, "Set the contains string for this backdrop.") + .def("hasContainsString", &mx::Backdrop::hasContainsString, "Return true if this backdrop has a contains string.") + .def("getContainsString", &mx::Backdrop::getContainsString, "Return the contains string for this backdrop.") + .def("setWidth", &mx::Backdrop::setWidth, "Set the width attribute of the backdrop.") + .def("hasWidth", &mx::Backdrop::hasWidth, "Return true if this backdrop has a width attribute.") + .def("getWidth", &mx::Backdrop::getWidth, "Return the width attribute of the backdrop.") + .def("setHeight", &mx::Backdrop::setHeight, "Set the height attribute of the backdrop.") + .def("hasHeight", &mx::Backdrop::hasHeight, "Return true if this backdrop has a height attribute.") + .def("getHeight", &mx::Backdrop::getHeight, "Return the height attribute of the backdrop.") + .def("setContainsElements", &mx::Backdrop::setContainsElements, "Set the vector of elements that this backdrop contains.") + .def("getContainsElements", &mx::Backdrop::getContainsElements, "Return the vector of elements that this backdrop contains.") .def_readonly_static("CATEGORY", &mx::Backdrop::CATEGORY) .def_readonly_static("CONTAINS_ATTRIBUTE", &mx::Backdrop::CONTAINS_ATTRIBUTE) .def_readonly_static("WIDTH_ATTRIBUTE", &mx::Backdrop::WIDTH_ATTRIBUTE) diff --git a/source/PyMaterialX/PyMaterialXCore/PyProperty.cpp b/source/PyMaterialX/PyMaterialXCore/PyProperty.cpp index a9b213a19b..3018c50d68 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyProperty.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyProperty.cpp @@ -15,28 +15,28 @@ namespace mx = MaterialX; void bindPyProperty(py::module& mod) { - py::class_(mod, "Property") + py::class_(mod, "Property", "A property element within a PropertySet.") .def_readonly_static("CATEGORY", &mx::Property::CATEGORY); - py::class_(mod, "PropertyAssign") - .def("setProperty", &mx::PropertyAssign::setProperty) - .def("hasProperty", &mx::PropertyAssign::hasProperty) - .def("getProperty", &mx::PropertyAssign::getProperty) - .def("setGeom", &mx::PropertyAssign::setGeom) - .def("hasGeom", &mx::PropertyAssign::hasGeom) - .def("getGeom", &mx::PropertyAssign::getGeom) - .def("setCollectionString", &mx::PropertyAssign::setCollectionString) - .def("hasCollectionString", &mx::PropertyAssign::hasCollectionString) - .def("getCollectionString", &mx::PropertyAssign::getCollectionString) - .def("setCollection", &mx::PropertyAssign::setCollection) - .def("getCollection", &mx::PropertyAssign::getCollection) + py::class_(mod, "PropertyAssign", "A property assignment element within a Look.") + .def("setProperty", &mx::PropertyAssign::setProperty, "Set the property string of this element.") + .def("hasProperty", &mx::PropertyAssign::hasProperty, "Return true if this element has a property string.") + .def("getProperty", &mx::PropertyAssign::getProperty, "Return the property string of this element.") + .def("setGeom", &mx::PropertyAssign::setGeom, "Set the geometry string of this element.") + .def("hasGeom", &mx::PropertyAssign::hasGeom, "Return true if this element has a geometry string.") + .def("getGeom", &mx::PropertyAssign::getGeom, "Return the geometry string of this element.") + .def("setCollectionString", &mx::PropertyAssign::setCollectionString, "Set the collection string of this element.") + .def("hasCollectionString", &mx::PropertyAssign::hasCollectionString, "Return true if this element has a collection string.") + .def("getCollectionString", &mx::PropertyAssign::getCollectionString, "Return the collection string of this element.") + .def("setCollection", &mx::PropertyAssign::setCollection, "Assign a Collection to this element.") + .def("getCollection", &mx::PropertyAssign::getCollection, "Return the Collection that is assigned to this element.") .def_readonly_static("CATEGORY", &mx::PropertyAssign::CATEGORY); - py::class_(mod, "PropertySet") - .def("addProperty", &mx::PropertySet::addProperty) - .def("getProperties", &mx::PropertySet::getProperties) - .def("removeProperty", &mx::PropertySet::removeProperty) - .def("_getPropertyValue", &mx::PropertySet::getPropertyValue) + py::class_(mod, "PropertySet", "A property set element within a Document.") + .def("addProperty", &mx::PropertySet::addProperty, "Add a Property to the set.\n\nArgs:\n name: The name of the new Property. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new Property.") + .def("getProperties", &mx::PropertySet::getProperties, "Return a vector of all Property elements in the set.") + .def("removeProperty", &mx::PropertySet::removeProperty, "Remove the Property with the given name, if present.") + .def("_getPropertyValue", &mx::PropertySet::getPropertyValue, "Return the typed value of a property by its name.\n\nArgs:\n name: The name of the property to be evaluated.\n\nReturns:\n If the given property is found, then a shared pointer to its value is returned; otherwise, an empty shared pointer is returned.") BIND_PROPERTYSET_TYPE_INSTANCE(integer, int) BIND_PROPERTYSET_TYPE_INSTANCE(boolean, bool) BIND_PROPERTYSET_TYPE_INSTANCE(float, float) @@ -54,11 +54,11 @@ void bindPyProperty(py::module& mod) BIND_PROPERTYSET_TYPE_INSTANCE(stringarray, mx::StringVec) .def_readonly_static("CATEGORY", &mx::Property::CATEGORY); - py::class_(mod, "PropertySetAssign") - .def("setPropertySetString", &mx::PropertySetAssign::setPropertySetString) - .def("hasPropertySetString", &mx::PropertySetAssign::hasPropertySetString) - .def("getPropertySetString", &mx::PropertySetAssign::getPropertySetString) - .def("setPropertySet", &mx::PropertySetAssign::setPropertySet) - .def("getPropertySet", &mx::PropertySetAssign::getPropertySet) + py::class_(mod, "PropertySetAssign", "A property set assignment element within a Look.") + .def("setPropertySetString", &mx::PropertySetAssign::setPropertySetString, "Set the property set string of this element.") + .def("hasPropertySetString", &mx::PropertySetAssign::hasPropertySetString, "Return true if this element has a property set string.") + .def("getPropertySetString", &mx::PropertySetAssign::getPropertySetString, "Return the property set string of this element.") + .def("setPropertySet", &mx::PropertySetAssign::setPropertySet, "Assign a property set to this element.") + .def("getPropertySet", &mx::PropertySetAssign::getPropertySet, "Return the property set that is assigned to this element.") .def_readonly_static("CATEGORY", &mx::PropertySetAssign::CATEGORY); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyTraversal.cpp b/source/PyMaterialX/PyMaterialXCore/PyTraversal.cpp index f83200b680..0a9bc16206 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyTraversal.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyTraversal.cpp @@ -14,59 +14,59 @@ namespace mx = MaterialX; void bindPyTraversal(py::module& mod) { - py::class_(mod, "Edge") - .def("getDownstreamElement", &mx::Edge::getDownstreamElement) - .def("getConnectingElement", &mx::Edge::getConnectingElement) - .def("getUpstreamElement", &mx::Edge::getUpstreamElement) - .def("getName", &mx::Edge::getName); + py::class_(mod, "Edge", "An edge between two connected Elements, returned during graph traversal.") + .def("getDownstreamElement", &mx::Edge::getDownstreamElement, "Return the downstream element of the edge.") + .def("getConnectingElement", &mx::Edge::getConnectingElement, "Return the connecting element of the edge, if any.") + .def("getUpstreamElement", &mx::Edge::getUpstreamElement, "Return the upstream element of the edge.") + .def("getName", &mx::Edge::getName, "Return the name of this edge, if any."); - py::class_(mod, "TreeIterator") - .def("getElement", &mx::TreeIterator::getElement) - .def("getElementDepth", &mx::TreeIterator::getElementDepth) - .def("setPruneSubtree", &mx::TreeIterator::setPruneSubtree) - .def("getPruneSubtree", &mx::TreeIterator::getPruneSubtree) + py::class_(mod, "TreeIterator", "An iterator object representing the state of a tree traversal.") + .def("getElement", &mx::TreeIterator::getElement, "Return the current element in the traversal.") + .def("getElementDepth", &mx::TreeIterator::getElementDepth, "Return the element depth of the current traversal, where the starting element represents a depth of zero.") + .def("setPruneSubtree", &mx::TreeIterator::setPruneSubtree, "Set the prune subtree flag, which controls whether the current subtree is pruned from traversal.\n\nArgs:\n prune: If set to true, then the current subtree will be pruned.") + .def("getPruneSubtree", &mx::TreeIterator::getPruneSubtree, "Return the prune subtree flag, which controls whether the current subtree is pruned from traversal.") .def("__iter__", [](mx::TreeIterator& it) -> mx::TreeIterator& { return it.begin(1); - }) + }, "Interpret this object as an iteration range, and return its begin iterator.", "Return a reference to this iterator to begin traversal.", "Return a reference to this iterator to begin traversal.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.") .def("__next__", [](mx::TreeIterator& it) { if (++it == it.end()) throw py::stop_iteration(); return *it; - }); + }, "Return the end iterator."); - py::class_(mod, "GraphIterator") - .def("getDownstreamElement", &mx::GraphIterator::getDownstreamElement) - .def("getConnectingElement", &mx::GraphIterator::getConnectingElement) - .def("getUpstreamElement", &mx::GraphIterator::getUpstreamElement) - .def("getUpstreamIndex", &mx::GraphIterator::getUpstreamIndex) - .def("getElementDepth", &mx::GraphIterator::getElementDepth) - .def("getNodeDepth", &mx::GraphIterator::getNodeDepth) - .def("setPruneSubgraph", &mx::GraphIterator::setPruneSubgraph) - .def("getPruneSubgraph", &mx::GraphIterator::getPruneSubgraph) + py::class_(mod, "GraphIterator", "An iterator object representing the state of an upstream graph traversal.") + .def("getDownstreamElement", &mx::GraphIterator::getDownstreamElement, "Return the downstream element of the current edge.") + .def("getConnectingElement", &mx::GraphIterator::getConnectingElement, "Return the connecting element, if any, of the current edge.") + .def("getUpstreamElement", &mx::GraphIterator::getUpstreamElement, "Return the upstream element of the current edge.") + .def("getUpstreamIndex", &mx::GraphIterator::getUpstreamIndex, "Return the index of the current edge within the range of upstream edges available to the downstream element.") + .def("getElementDepth", &mx::GraphIterator::getElementDepth, "Return the element depth of the current traversal, where a single edge between two elements represents a depth of one.") + .def("getNodeDepth", &mx::GraphIterator::getNodeDepth, "Return the node depth of the current traversal, where a single edge between two nodes represents a depth of one.") + .def("setPruneSubgraph", &mx::GraphIterator::setPruneSubgraph, "Set the prune subgraph flag, which controls whether the current subgraph is pruned from traversal.\n\nArgs:\n prune: If set to true, then the current subgraph will be pruned.") + .def("getPruneSubgraph", &mx::GraphIterator::getPruneSubgraph, "Return the prune subgraph flag, which controls whether the current subgraph is pruned from traversal.") .def("__iter__", [](mx::GraphIterator& it) -> mx::GraphIterator& { return it.begin(1); - }) + }, "Interpret this object as an iteration range, and return its begin iterator.", "Return a reference to this iterator to begin traversal.", "Return a reference to this iterator to begin traversal.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.") .def("__next__", [](mx::GraphIterator& it) { if (++it == it.end()) throw py::stop_iteration(); return *it; - }); + }, "Return the end iterator."); - py::class_(mod, "InheritanceIterator") + py::class_(mod, "InheritanceIterator", "An iterator object representing the current state of an inheritance traversal.") .def("__iter__", [](mx::InheritanceIterator& it) -> mx::InheritanceIterator& { return it.begin(1); - }) + }, "Interpret this object as an iteration range, and return its begin iterator.", "Return a reference to this iterator to begin traversal.", "Return a reference to this iterator to begin traversal.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.", "Interpret this object as an iteration range, and return its begin iterator.") .def("__next__", [](mx::InheritanceIterator& it) { if (++it == it.end()) throw py::stop_iteration(); return *it; - }); + }, "Return the end iterator."); py::register_exception(mod, "ExceptionFoundCycle"); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyTypes.cpp b/source/PyMaterialX/PyMaterialXCore/PyTypes.cpp index fec7867b3a..973abd9f03 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyTypes.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyTypes.cpp @@ -37,16 +37,16 @@ using IndexPair = std::pair; .def("__itruediv__", py::overload_cast(&V::operator/=), py::return_value_policy::reference_internal) \ .def(py::self * float()) \ .def(py::self / float()) \ -.def("getMagnitude", &V::getMagnitude) \ -.def("getNormalized", &V::getNormalized) \ -.def("dot", &V::dot) \ +.def("getMagnitude", &V::getMagnitude, "Return the magnitude of the vector.") \ +.def("getNormalized", &V::getNormalized, "Return a normalized version of the given path, collapsing current path and parent path references so that 'a/.\n\n/b' and 'c/../d/../a/b' become 'a/b'.") \ +.def("dot", &V::dot, "Return the dot product of two vectors.") \ .def("__getitem__", [](const V& v, size_t i) \ { return v[i]; } ) \ .def("__setitem__", [](V& v, size_t i, float f) \ { v[i] = f; } ) \ .def("__str__", [](const V& v) \ { return mx::toValueString(v); }) \ -.def("copy", [](const V& v) { return V(v); }) \ +.def("copy", [](const V& v) { return V(v); }, "Create a deep copy of the document.") \ .def_static("__len__", &V::numElements) #define BIND_MATRIX_SUBCLASS(M, N) \ @@ -74,77 +74,77 @@ using IndexPair = std::pair; { m[i.first][i.second] = f; }) \ .def("__str__", [](const M& m) \ { return mx::toValueString(m); }) \ -.def("copy", [](const M& m) { return M(m); }) \ -.def("isEquivalent", &M::isEquivalent) \ -.def("getTranspose", &M::getTranspose) \ -.def("getDeterminant", &M::getDeterminant) \ -.def("getAdjugate", &M::getAdjugate) \ -.def("getInverse", &M::getInverse) \ -.def_static("createScale", &M::createScale) \ -.def_static("createTranslation", &M::createTranslation) \ -.def_static("numRows", &M::numRows) \ -.def_static("numColumns", &M::numColumns) \ +.def("copy", [](const M& m) { return M(m); }, "Create a deep copy of the document.") \ +.def("isEquivalent", &M::isEquivalent, "Return true if the given matrix is equivalent to this one within a given floating-point tolerance.") \ +.def("getTranspose", &M::getTranspose, "Return the transpose of the matrix.") \ +.def("getDeterminant", &M::getDeterminant, "Return the determinant of the matrix.") \ +.def("getAdjugate", &M::getAdjugate, "Return the adjugate of the matrix.") \ +.def("getInverse", &M::getInverse, "Return the inverse of the matrix.") \ +.def_static("createScale", &M::createScale, "Create a scale matrix.") \ +.def_static("createTranslation", &M::createTranslation, "Create a translation matrix.") \ +.def_static("numRows", &M::numRows, "Return the number of rows in this matrix.") \ +.def_static("numColumns", &M::numColumns, "Return the number of columns in this matrix.") \ .def_static("__len__", &M::numRows) void bindPyTypes(py::module& mod) { - py::class_(mod, "VectorBase"); - py::class_(mod, "MatrixBase"); + py::class_(mod, "VectorBase", "The base class for vectors of scalar values."); + py::class_(mod, "MatrixBase", "The base class for square matrices of scalar values."); - py::class_(mod, "Vector2") + py::class_(mod, "Vector2", "A vector of two floating-point values.") BIND_VECTOR_SUBCLASS(mx::Vector2, 2) .def(py::init()) - .def("cross", &mx::Vector2::cross) + .def("cross", &mx::Vector2::cross, "Return the cross product of two vectors.") .def("asTuple", [](const mx::Vector2& v) { return std::make_tuple(v[0], v[1]); }); - py::class_(mod, "Vector3") + py::class_(mod, "Vector3", "A vector of three floating-point values.") BIND_VECTOR_SUBCLASS(mx::Vector3, 3) .def(py::init()) - .def("cross", &mx::Vector3::cross) + .def("cross", &mx::Vector3::cross, "Return the cross product of two vectors.") .def("asTuple", [](const mx::Vector3& v) { return std::make_tuple(v[0], v[1], v[2]); }); - py::class_(mod, "Vector4") + py::class_(mod, "Vector4", "A vector of four floating-point values.") BIND_VECTOR_SUBCLASS(mx::Vector4, 4) .def(py::init()) .def("asTuple", [](const mx::Vector4& v) { return std::make_tuple(v[0], v[1], v[2], v[3]); }); - py::class_(mod, "Color3") + py::class_(mod, "Color3", "A three-component color value.") BIND_VECTOR_SUBCLASS(mx::Color3, 3) .def(py::init()) - .def("linearToSrgb", &mx::Color3::linearToSrgb) - .def("srgbToLinear", &mx::Color3::srgbToLinear) + .def("linearToSrgb", &mx::Color3::linearToSrgb, "Transform the given color from linear RGB to the sRGB encoding, returning the result as a new value.") + .def("srgbToLinear", &mx::Color3::srgbToLinear, "Transform the given color from the sRGB encoding to linear RGB, returning the result as a new value.") .def("asTuple", [](const mx::Color3& v) { return std::make_tuple(v[0], v[1], v[2]); }); - py::class_(mod, "Color4") + py::class_(mod, "Color4", "A four-component color value.") BIND_VECTOR_SUBCLASS(mx::Color4, 4) .def(py::init()) .def("asTuple", [](const mx::Color4& v) { return std::make_tuple(v[0], v[1], v[2], v[3]); }); - py::class_(mod, "Matrix33") + py::class_(mod, "Matrix33", "A 3x3 matrix of floating-point values.\n\nVector transformation methods follow the row-vector convention, with matrix-vector multiplication computed as v' = vM.") BIND_MATRIX_SUBCLASS(mx::Matrix33, 3) .def(py::init()) - .def("multiply", &mx::Matrix33::multiply) - .def("transformPoint", &mx::Matrix33::transformPoint) - .def("transformVector", &mx::Matrix33::transformVector) - .def("transformNormal", &mx::Matrix33::transformNormal) - .def_static("createRotation", &mx::Matrix33::createRotation) + .def("multiply", &mx::Matrix33::multiply, "Return the product of this matrix and a 3D vector.") + .def("transformPoint", &mx::Matrix33::transformPoint, "Transform the given 2D point.") + .def("transformVector", &mx::Matrix33::transformVector, "Transform the given 2D direction vector.") + .def("transformNormal", &mx::Matrix33::transformNormal, "Transform the given 3D normal vector.") + .def_static("createRotation", &mx::Matrix33::createRotation, "Create a rotation matrix.\n\nArgs:\n angle: Angle in radians") .def_readonly_static("IDENTITY", &mx::Matrix33::IDENTITY); - py::class_(mod, "Matrix44") + py::class_(mod, "Matrix44", "A 4x4 matrix of floating-point values.\n\nVector transformation methods follow the row-vector convention, with matrix-vector multiplication computed as v' = vM.") BIND_MATRIX_SUBCLASS(mx::Matrix44, 4) .def(py::init()) - .def("multiply", &mx::Matrix44::multiply) - .def("transformPoint", &mx::Matrix44::transformPoint) - .def("transformVector", &mx::Matrix44::transformVector) - .def("transformNormal", &mx::Matrix44::transformNormal) - .def_static("createRotationX", &mx::Matrix44::createRotationX) - .def_static("createRotationY", &mx::Matrix44::createRotationY) - .def_static("createRotationZ", &mx::Matrix44::createRotationZ) + .def("multiply", &mx::Matrix44::multiply, "Return the product of this matrix and a 4D vector.") + .def("transformPoint", &mx::Matrix44::transformPoint, "Transform the given 3D point.") + .def("transformVector", &mx::Matrix44::transformVector, "Transform the given 3D direction vector.") + .def("transformNormal", &mx::Matrix44::transformNormal, "Transform the given 3D normal vector.") + .def_static("createRotationX", &mx::Matrix44::createRotationX, "Create a rotation matrix about the X-axis.\n\nArgs:\n angle: Angle in radians") + .def_static("createRotationY", &mx::Matrix44::createRotationY, "Create a rotation matrix about the Y-axis.\n\nArgs:\n angle: Angle in radians") + .def_static("createRotationZ", &mx::Matrix44::createRotationZ, "Create a rotation matrix about the Z-axis.\n\nArgs:\n angle: Angle in radians") .def_readonly_static("IDENTITY", &mx::Matrix44::IDENTITY); mod.attr("DEFAULT_TYPE_STRING") = mx::DEFAULT_TYPE_STRING; diff --git a/source/PyMaterialX/PyMaterialXCore/PyUnitConverter.cpp b/source/PyMaterialX/PyMaterialXCore/PyUnitConverter.cpp index bf3bcbf0f9..d4f749bf4e 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyUnitConverter.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyUnitConverter.cpp @@ -66,28 +66,28 @@ PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); void bindPyUnitConverters(py::module& mod) { - py::class_(mod, "UnitConverter") - .def("convert", (float (mx::UnitConverter::*)(float, const std::string&, const std::string&)const) &mx::UnitConverter::convert) - .def("convert", (mx::Vector2 (mx::UnitConverter::*)(const mx::Vector2&, const std::string&, const std::string&)const) &mx::UnitConverter::convert) - .def("convert", (mx::Vector3 (mx::UnitConverter::*)(const mx::Vector3&, const std::string&, const std::string&)const) &mx::UnitConverter::convert) - .def("convert", (mx::Vector4 (mx::UnitConverter::*)(const mx::Vector4&, const std::string&, const std::string&)const) &mx::UnitConverter::convert) - .def("getUnitAsInteger", &mx::UnitConverter::getUnitAsInteger) - .def("getUnitFromInteger", &mx::UnitConverter::getUnitFromInteger); + py::class_(mod, "UnitConverter", "An abstract base class for unit converters.\n\nEach unit converter instance is responsible for a single unit type.") + .def("convert", (float (mx::UnitConverter::*)(float, const std::string&, const std::string&)const) &mx::UnitConverter::convert, "Convert a given value in a given unit to a desired unit.\n\nArgs:\n input: Input value to convert\n inputUnit: Unit of input value\n outputUnit: Unit for output value") + .def("convert", (mx::Vector2 (mx::UnitConverter::*)(const mx::Vector2&, const std::string&, const std::string&)const) &mx::UnitConverter::convert, "Convert a given value in a given unit to a desired unit.\n\nArgs:\n input: Input value to convert\n inputUnit: Unit of input value\n outputUnit: Unit for output value") + .def("convert", (mx::Vector3 (mx::UnitConverter::*)(const mx::Vector3&, const std::string&, const std::string&)const) &mx::UnitConverter::convert, "Convert a given value in a given unit to a desired unit.\n\nArgs:\n input: Input value to convert\n inputUnit: Unit of input value\n outputUnit: Unit for output value") + .def("convert", (mx::Vector4 (mx::UnitConverter::*)(const mx::Vector4&, const std::string&, const std::string&)const) &mx::UnitConverter::convert, "Convert a given value in a given unit to a desired unit.\n\nArgs:\n input: Input value to convert\n inputUnit: Unit of input value\n outputUnit: Unit for output value") + .def("getUnitAsInteger", &mx::UnitConverter::getUnitAsInteger, "Given a unit name return a value that it can map to as an integer Returns -1 value if not found.") + .def("getUnitFromInteger", &mx::UnitConverter::getUnitFromInteger, "Given an integer index return the unit name in the map used by the converter Returns Empty string if not found."); - py::class_(mod, "LinearUnitConverter") - .def_static("create", &mx::LinearUnitConverter::create) - .def("getUnitScale", &mx::LinearUnitConverter::getUnitScale) - .def("convert", (float (mx::LinearUnitConverter::*)(float, const std::string&, const std::string&)const) &mx::LinearUnitConverter::convert) - .def("convert", (mx::Vector2 (mx::LinearUnitConverter::*)(const mx::Vector2&, const std::string&, const std::string&)const) &mx::LinearUnitConverter::convert) - .def("convert", (mx::Vector3 (mx::LinearUnitConverter::*)(const mx::Vector3&, const std::string&, const std::string&)const) &mx::LinearUnitConverter::convert) - .def("convert", (mx::Vector4 (mx::LinearUnitConverter::*)(const mx::Vector4&, const std::string&, const std::string&)const) &mx::LinearUnitConverter::convert) - .def("getUnitAsInteger", &mx::LinearUnitConverter::getUnitAsInteger) - .def("getUnitFromInteger", &mx::LinearUnitConverter::getUnitFromInteger); + py::class_(mod, "LinearUnitConverter", "A converter class for linear units that require only a scalar multiplication.") + .def_static("create", &mx::LinearUnitConverter::create, "Creator.") + .def("getUnitScale", &mx::LinearUnitConverter::getUnitScale, "Return the mappings from unit names to the scale value defined by a linear converter.") + .def("convert", (float (mx::LinearUnitConverter::*)(float, const std::string&, const std::string&)const) &mx::LinearUnitConverter::convert, "Convert a given value in a given unit to a desired unit.\n\nArgs:\n input: Input value to convert\n inputUnit: Unit of input value\n outputUnit: Unit for output value") + .def("convert", (mx::Vector2 (mx::LinearUnitConverter::*)(const mx::Vector2&, const std::string&, const std::string&)const) &mx::LinearUnitConverter::convert, "Convert a given value in a given unit to a desired unit.\n\nArgs:\n input: Input value to convert\n inputUnit: Unit of input value\n outputUnit: Unit for output value") + .def("convert", (mx::Vector3 (mx::LinearUnitConverter::*)(const mx::Vector3&, const std::string&, const std::string&)const) &mx::LinearUnitConverter::convert, "Convert a given value in a given unit to a desired unit.\n\nArgs:\n input: Input value to convert\n inputUnit: Unit of input value\n outputUnit: Unit for output value") + .def("convert", (mx::Vector4 (mx::LinearUnitConverter::*)(const mx::Vector4&, const std::string&, const std::string&)const) &mx::LinearUnitConverter::convert, "Convert a given value in a given unit to a desired unit.\n\nArgs:\n input: Input value to convert\n inputUnit: Unit of input value\n outputUnit: Unit for output value") + .def("getUnitAsInteger", &mx::LinearUnitConverter::getUnitAsInteger, "Given a unit name return a value that it can map to as an integer.\n\nReturns -1 value if not found") + .def("getUnitFromInteger", &mx::LinearUnitConverter::getUnitFromInteger, "Given an integer index return the unit name in the map used by the converter.\n\nReturns Empty string if not found"); - py::class_(mod, "UnitConverterRegistry") - .def_static("create", &mx::UnitConverterRegistry::create) - .def("addUnitConverter", &mx::UnitConverterRegistry::addUnitConverter) - .def("removeUnitConverter", &mx::UnitConverterRegistry::removeUnitConverter) - .def("getUnitConverter", &mx::UnitConverterRegistry::getUnitConverter) - .def("clearUnitConverters", &mx::UnitConverterRegistry::clearUnitConverters); + py::class_(mod, "UnitConverterRegistry", "A registry for unit converters.") + .def_static("create", &mx::UnitConverterRegistry::create, "Creator.") + .def("addUnitConverter", &mx::UnitConverterRegistry::addUnitConverter, "Add a unit converter for a given UnitDef.\n\nReturns false if a converter has already been registered for the given UnitDef") + .def("removeUnitConverter", &mx::UnitConverterRegistry::removeUnitConverter, "Remove a unit converter for a given UnitDef.\n\nReturns false if a converter does not exist for the given UnitDef") + .def("getUnitConverter", &mx::UnitConverterRegistry::getUnitConverter, "Get a unit converter for a given UnitDef Returns any empty pointer if a converter does not exist for the given UnitDef.") + .def("clearUnitConverters", &mx::UnitConverterRegistry::clearUnitConverters, "Clear all unit converters from the registry."); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyUtil.cpp b/source/PyMaterialX/PyMaterialXCore/PyUtil.cpp index 826c5adf84..85ad22eac2 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyUtil.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyUtil.cpp @@ -15,17 +15,17 @@ namespace mx = MaterialX; void bindPyUtil(py::module& mod) { - mod.def("getVersionString", &mx::getVersionString); - mod.def("getVersionIntegers", &mx::getVersionIntegers); - mod.def("createValidName", &mx::createValidName, py::arg("name"), py::arg("replaceChar") = '_'); - mod.def("isValidName", &mx::isValidName); - mod.def("incrementName", &mx::incrementName); - mod.def("splitString", &mx::splitString); - mod.def("joinStrings", &mx::joinStrings); - mod.def("replaceSubstrings", &mx::replaceSubstrings); - mod.def("stringStartsWith", &mx::stringStartsWith); - mod.def("stringEndsWith", &mx::stringEndsWith); - mod.def("splitNamePath", &mx::splitNamePath); - mod.def("createNamePath", &mx::createNamePath); - mod.def("parentNamePath", &mx::parentNamePath); + mod.def("getVersionString", &mx::getVersionString, "Return the version of the MaterialX library as a string."); + mod.def("getVersionIntegers", &mx::getVersionIntegers, "Return the major, minor, and build versions of the MaterialX library as an integer tuple."); + mod.def("createValidName", &mx::createValidName, py::arg("name"), py::arg("replaceChar") = '_', "Create a valid MaterialX name from the given string."); + mod.def("isValidName", &mx::isValidName, "Return true if the given string is a valid MaterialX name."); + mod.def("incrementName", &mx::incrementName, "Increment the numeric suffix of a name."); + mod.def("splitString", &mx::splitString, "Split a string into a vector of substrings using the given set of separator characters."); + mod.def("joinStrings", &mx::joinStrings, "Join a vector of substrings into a single string, placing the given separator between each substring."); + mod.def("replaceSubstrings", &mx::replaceSubstrings, "Apply the given substring substitutions to the input string."); + mod.def("stringStartsWith", &mx::stringStartsWith, "Return true if the given string starts with the given prefix."); + mod.def("stringEndsWith", &mx::stringEndsWith, "Return true if the given string ends with the given suffix."); + mod.def("splitNamePath", &mx::splitNamePath, "Split a name path into string vector."); + mod.def("createNamePath", &mx::createNamePath, "Create a name path from a string vector."); + mod.def("parentNamePath", &mx::parentNamePath, "Given a name path, return the parent name path."); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyValue.cpp b/source/PyMaterialX/PyMaterialXCore/PyValue.cpp index c36bd6c288..05fcd44278 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyValue.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyValue.cpp @@ -10,8 +10,8 @@ #define BIND_TYPE_INSTANCE(NAME, T) \ py::class_, std::shared_ptr< mx::TypedValue >, mx::Value>(mod, "TypedValue_" #NAME) \ - .def("getData", &mx::TypedValue::getData) \ - .def("getValueString", &mx::TypedValue::getValueString) \ + .def("getData", &mx::TypedValue::getData, "Return the raw float vector.") \ + .def("getValueString", &mx::TypedValue::getValueString, "Return the value set on this port as a string, or an empty string if there is no value.") \ .def_static("createValue", &mx::Value::createValue) \ .def_readonly_static("TYPE", &mx::TypedValue::TYPE); @@ -20,13 +20,10 @@ namespace mx = MaterialX; void bindPyValue(py::module& mod) { - py::class_(mod, "Value") - .def("getValueString", &mx::Value::getValueString) - .def("getTypeString", &mx::Value::getTypeString) - .def_static("createValueFromStrings", &mx::Value::createValueFromStrings, - py::arg("value"), - py::arg("type"), - py::arg("typeDefPtr") = nullptr); + py::class_(mod, "Value", "A generic, discriminated value, whose type may be queried dynamically.") + .def("getValueString", &mx::Value::getValueString, "Return the value string for this value.") + .def("getTypeString", &mx::Value::getTypeString, "Return the type string for this value.") + .def_static("createValueFromStrings", &mx::Value::createValueFromStrings, py::arg("value"), py::arg("type"), py::arg("typeDefPtr") = nullptr, "Create a new value instance from value and type strings.\n\nReturns:\n A shared pointer to a typed value, or an empty shared pointer if the conversion to the given data type cannot be performed."); BIND_TYPE_INSTANCE(integer, int) BIND_TYPE_INSTANCE(boolean, bool) diff --git a/source/PyMaterialX/PyMaterialXCore/PyVariant.cpp b/source/PyMaterialX/PyMaterialXCore/PyVariant.cpp index 13c7fa572c..03942ae93c 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyVariant.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyVariant.cpp @@ -12,23 +12,22 @@ namespace mx = MaterialX; void bindPyVariant(py::module& mod) { - py::class_(mod, "Variant") + py::class_(mod, "Variant", "A variant element within a VariantSet.") .def_readonly_static("CATEGORY", &mx::Variant::CATEGORY); - py::class_(mod, "VariantSet") - .def("addVariant", &mx::VariantSet::addVariant, - py::arg("name") = mx::EMPTY_STRING) - .def("getVariant", &mx::VariantSet::getVariant) - .def("getVariants", &mx::VariantSet::getVariants) - .def("removeVariant", &mx::VariantSet::removeVariant) + py::class_(mod, "VariantSet", "A variant set element within a Document.") + .def("addVariant", &mx::VariantSet::addVariant, py::arg("name") = mx::EMPTY_STRING, "Add a Variant to the variant set.\n\nArgs:\n name: The name of the new Variant. If no name is specified, then a unique name will automatically be generated.\n\nReturns:\n A shared pointer to the new Variant.") + .def("getVariant", &mx::VariantSet::getVariant, "Return the Variant, if any, with the given name.") + .def("getVariants", &mx::VariantSet::getVariants, "Return a vector of all Variant elements in the look.") + .def("removeVariant", &mx::VariantSet::removeVariant, "Remove the Variant, if any, with the given name.") .def_readonly_static("CATEGORY", &mx::VariantSet::CATEGORY); - py::class_(mod, "VariantAssign") - .def("setVariantSetString", &mx::VariantAssign::setVariantSetString) - .def("hasVariantSetString", &mx::VariantAssign::hasVariantSetString) - .def("getVariantSetString", &mx::VariantAssign::getVariantSetString) - .def("setVariantString", &mx::VariantAssign::setVariantString) - .def("hasVariantString", &mx::VariantAssign::hasVariantString) - .def("getVariantString", &mx::VariantAssign::getVariantString) + py::class_(mod, "VariantAssign", "A variant assignment element within a Look.") + .def("setVariantSetString", &mx::VariantAssign::setVariantSetString, "Set the element's variant set string.") + .def("hasVariantSetString", &mx::VariantAssign::hasVariantSetString, "Return true if the given element has a variant set string.") + .def("getVariantSetString", &mx::VariantAssign::getVariantSetString, "Return the element's variant set string.") + .def("setVariantString", &mx::VariantAssign::setVariantString, "Set the element's variant string.") + .def("hasVariantString", &mx::VariantAssign::hasVariantString, "Return true if the given element has a variant string.") + .def("getVariantString", &mx::VariantAssign::getVariantString, "Return the element's variant string.") .def_readonly_static("CATEGORY", &mx::VariantAssign::CATEGORY); } diff --git a/source/PyMaterialX/PyMaterialXFormat/PyFile.cpp b/source/PyMaterialX/PyMaterialXFormat/PyFile.cpp index c451037566..f4c0f489d7 100644 --- a/source/PyMaterialX/PyMaterialXFormat/PyFile.cpp +++ b/source/PyMaterialX/PyMaterialXFormat/PyFile.cpp @@ -24,44 +24,42 @@ void bindPyFile(py::module& mod) .value("FormatNative", mx::FilePath::Format::FormatNative) .export_values(); - py::class_(mod, "FilePath") + py::class_(mod, "FilePath", "A generic file path, supporting both syntactic and file system operations.") .def(py::init<>()) .def(py::init()) .def(py::self == py::self) .def(py::self != py::self) .def(py::self / py::self) - .def("asString", &mx::FilePath::asString, - py::arg("format") = mx::FilePath::Format::FormatNative) - .def("isEmpty", &mx::FilePath::isEmpty) - .def("isAbsolute", &mx::FilePath::isAbsolute) - .def("getBaseName", &mx::FilePath::getBaseName) - .def("getParentPath", &mx::FilePath::getParentPath) - .def("getExtension", &mx::FilePath::getExtension) - .def("addExtension", &mx::FilePath::addExtension) - .def("removeExtension", &mx::FilePath::removeExtension) - .def("size", &mx::FilePath::size) - .def("getNormalized", &mx::FilePath::getNormalized) - .def("exists", &mx::FilePath::exists) - .def("isDirectory", &mx::FilePath::isDirectory) - .def("getFilesInDirectory", &mx::FilePath::getFilesInDirectory) - .def("getSubDirectories", &mx::FilePath::getSubDirectories) - .def("createDirectory", &mx::FilePath::createDirectory) - .def_static("getCurrentPath", &mx::FilePath::getCurrentPath) - .def_static("getModulePath", &mx::FilePath::getModulePath); + .def("asString", &mx::FilePath::asString, py::arg("format") = mx::FilePath::Format::FormatNative, "Return this path as a standard string with the given format.") + .def("isEmpty", &mx::FilePath::isEmpty, "Return true if the given path is empty.") + .def("isAbsolute", &mx::FilePath::isAbsolute, "Return true if the given path is absolute.") + .def("getBaseName", &mx::FilePath::getBaseName, "Return the base name of the given path, with leading directory information removed.") + .def("getParentPath", &mx::FilePath::getParentPath, "Return the parent directory of the given path, if any.\n\nIf no parent directory is present, then the empty path is returned.") + .def("getExtension", &mx::FilePath::getExtension, "Return the file extension of the given path.") + .def("addExtension", &mx::FilePath::addExtension, "Add a file extension to the given path.") + .def("removeExtension", &mx::FilePath::removeExtension, "Remove the file extension, if any, from the given path.") + .def("size", &mx::FilePath::size, "Return the number of strings in the path.") + .def("getNormalized", &mx::FilePath::getNormalized, "Return a normalized version of the given path, collapsing current path and parent path references so that 'a/.\n\n/b' and 'c/../d/../a/b' become 'a/b'.") + .def("exists", &mx::FilePath::exists, "Return true if the given path exists on the file system.") + .def("isDirectory", &mx::FilePath::isDirectory, "Return true if the given path is a directory on the file system.") + .def("getFilesInDirectory", &mx::FilePath::getFilesInDirectory, "Return a vector of all files in the given directory with the given extension.\n\nIf extension is empty all files are returned.") + .def("getSubDirectories", &mx::FilePath::getSubDirectories, "Return a vector of all directories at or beneath the given path.") + .def("createDirectory", &mx::FilePath::createDirectory, "Create a directory on the file system at the given path.") + .def_static("getCurrentPath", &mx::FilePath::getCurrentPath, "Return the current working directory of the file system.") + .def_static("getModulePath", &mx::FilePath::getModulePath, "Return the directory containing the executable module."); - py::class_(mod, "FileSearchPath") + py::class_(mod, "FileSearchPath", "A sequence of file paths, which may be queried to find the first instance of a given filename on the file system.") .def(py::init<>()) .def(py::init(), py::arg("searchPath"), py::arg("sep") = mx::PATH_LIST_SEPARATOR) - .def("asString", &mx::FileSearchPath::asString, - py::arg("sep") = mx::PATH_LIST_SEPARATOR) - .def("append", static_cast(&mx::FileSearchPath::append)) - .def("append", static_cast(&mx::FileSearchPath::append)) - .def("prepend", &mx::FileSearchPath::prepend) - .def("clear", &mx::FileSearchPath::clear) - .def("size", &mx::FileSearchPath::size) - .def("isEmpty", &mx::FileSearchPath::isEmpty) - .def("find", &mx::FileSearchPath::find); + .def("asString", &mx::FileSearchPath::asString, py::arg("sep") = mx::PATH_LIST_SEPARATOR, "Convert this sequence to a string using the given separator.") + .def("append", static_cast(&mx::FileSearchPath::append), "Append the given search path to the sequence.") + .def("append", static_cast(&mx::FileSearchPath::append), "Append the given search path to the sequence.") + .def("prepend", &mx::FileSearchPath::prepend, "Prepend the given path to the sequence.") + .def("clear", &mx::FileSearchPath::clear, "Clear all paths from the sequence.") + .def("size", &mx::FileSearchPath::size, "Return the number of paths in the sequence.") + .def("isEmpty", &mx::FileSearchPath::isEmpty, "Return true if the search path is empty.") + .def("find", &mx::FileSearchPath::find, "Given an input filename, iterate through each path in this sequence, returning the first combined path found on the file system.\n\nOn success, the combined path is returned; otherwise the original filename is returned unmodified."); py::implicitly_convertible(); py::implicitly_convertible(); diff --git a/source/PyMaterialX/PyMaterialXFormat/PyUtil.cpp b/source/PyMaterialX/PyMaterialXFormat/PyUtil.cpp index dcd78bbcb5..7668da1221 100644 --- a/source/PyMaterialX/PyMaterialXFormat/PyUtil.cpp +++ b/source/PyMaterialX/PyMaterialXFormat/PyUtil.cpp @@ -14,16 +14,11 @@ namespace mx = MaterialX; void bindPyUtil(py::module& mod) { - mod.def("readFile", &mx::readFile); - mod.def("getSubdirectories", &mx::getSubdirectories); - mod.def("loadDocuments", &mx::loadDocuments, - py::arg("rootPath"), py::arg("searchPath"), py::arg("skipFiles"), py::arg("includeFiles"), py::arg("documents"), py::arg("documentsPaths"), - py::arg("readOptions") = (mx::XmlReadOptions*) nullptr, py::arg("errors") = (mx::StringVec*) nullptr); - mod.def("loadLibrary", &mx::loadLibrary, - py::arg("file"), py::arg("doc"), py::arg("searchPath") = mx::FileSearchPath(), py::arg("readOptions") = (mx::XmlReadOptions*) nullptr); - mod.def("loadLibraries", &mx::loadLibraries, - py::arg("libraryFolders"), py::arg("searchPath"), py::arg("doc"), py::arg("excludeFiles") = mx::StringSet(), py::arg("readOptions") = (mx::XmlReadOptions*) nullptr); - mod.def("flattenFilenames", &mx::flattenFilenames, - py::arg("doc"), py::arg("searchPath") = mx::FileSearchPath(), py::arg("customResolver") = (mx::StringResolverPtr) nullptr); - mod.def("getSourceSearchPath", &mx::getSourceSearchPath); + mod.def("readFile", &mx::readFile, "Read the given file and return a string containing its contents; if the read is not successful, then the empty string is returned."); + mod.def("getSubdirectories", &mx::getSubdirectories, "Get all subdirectories for a given set of directories and search paths."); + mod.def("loadDocuments", &mx::loadDocuments, py::arg("rootPath"), py::arg("searchPath"), py::arg("skipFiles"), py::arg("includeFiles"), py::arg("documents"), py::arg("documentsPaths"), py::arg("readOptions") = (mx::XmlReadOptions*) nullptr, py::arg("errors") = (mx::StringVec*) nullptr, "Scans for all documents under a root path and returns documents which can be loaded."); + mod.def("loadLibrary", &mx::loadLibrary, py::arg("file"), py::arg("doc"), py::arg("searchPath") = mx::FileSearchPath(), py::arg("readOptions") = (mx::XmlReadOptions*) nullptr, "Load a given MaterialX library into a document."); + mod.def("loadLibraries", &mx::loadLibraries, py::arg("libraryFolders"), py::arg("searchPath"), py::arg("doc"), py::arg("excludeFiles") = mx::StringSet(), py::arg("readOptions") = (mx::XmlReadOptions*) nullptr, "Load all MaterialX files within the given library folders into a document, using the given search path to locate the folders on the file system."); + mod.def("flattenFilenames", &mx::flattenFilenames, py::arg("doc"), py::arg("searchPath") = mx::FileSearchPath(), py::arg("customResolver") = (mx::StringResolverPtr) nullptr, "Flatten all filenames in the given document, applying string resolvers at the scope of each element and removing all fileprefix attributes.\n\nArgs:\n doc: The document to modify.\n searchPath: An optional search path for relative to absolute path conversion.\n customResolver: An optional custom resolver to apply."); + mod.def("getSourceSearchPath", &mx::getSourceSearchPath, "Return a file search path containing the parent folder of each source URI in the given document."); } diff --git a/source/PyMaterialX/PyMaterialXFormat/PyXmlIo.cpp b/source/PyMaterialX/PyMaterialXFormat/PyXmlIo.cpp index 9b4b32cf6a..c08875b3f4 100644 --- a/source/PyMaterialX/PyMaterialXFormat/PyXmlIo.cpp +++ b/source/PyMaterialX/PyMaterialXFormat/PyXmlIo.cpp @@ -13,7 +13,7 @@ namespace mx = MaterialX; void bindPyXmlIo(py::module& mod) { - py::class_(mod, "XmlReadOptions") + py::class_(mod, "XmlReadOptions", "A set of options for controlling the behavior of XML read functions.") .def(py::init()) .def_readwrite("readXIncludeFunction", &mx::XmlReadOptions::readXIncludeFunction) .def_readwrite("readComments", &mx::XmlReadOptions::readComments) @@ -21,23 +21,20 @@ void bindPyXmlIo(py::module& mod) .def_readwrite("upgradeVersion", &mx::XmlReadOptions::upgradeVersion) .def_readwrite("parentXIncludes", &mx::XmlReadOptions::parentXIncludes); - py::class_(mod, "XmlWriteOptions") + py::class_(mod, "XmlWriteOptions", "A set of options for controlling the behavior of XML write functions.") .def(py::init()) .def_readwrite("writeXIncludeEnable", &mx::XmlWriteOptions::writeXIncludeEnable) .def_readwrite("elementPredicate", &mx::XmlWriteOptions::elementPredicate); - mod.def("readFromXmlFileBase", &mx::readFromXmlFile, - py::arg("doc"), py::arg("filename"), py::arg("searchPath") = mx::FileSearchPath(), py::arg("readOptions") = (mx::XmlReadOptions*) nullptr); - mod.def("readFromXmlString", &mx::readFromXmlString, - py::arg("doc"), py::arg("str"), py::arg("searchPath") = mx::FileSearchPath(), py::arg("readOptions") = (mx::XmlReadOptions*) nullptr); + mod.def("readFromXmlFileBase", &mx::readFromXmlFile, py::arg("doc"), py::arg("filename"), py::arg("searchPath") = mx::FileSearchPath(), py::arg("readOptions") = (mx::XmlReadOptions*) nullptr, "Read a Document as XML from the given filename.\n\nArgs:\n doc: The Document into which data is read.\n filename: The filename from which data is read. This argument can be supplied either as a FilePath or a standard string.\n searchPath: An optional sequence of file paths that will be applied in order when searching for the given file and its includes. This argument can be supplied either as a FileSearchPath, or as a standard string with paths separated by the PATH_SEPARATOR character.\n readOptions: An optional pointer to an XmlReadOptions object. If provided, then the given options will affect the behavior of the read function. Defaults to a null pointer."); + mod.def("readFromXmlString", &mx::readFromXmlString, py::arg("doc"), py::arg("str"), py::arg("searchPath") = mx::FileSearchPath(), py::arg("readOptions") = (mx::XmlReadOptions*) nullptr, "Read a Document as XML from the given string.\n\nArgs:\n doc: The Document into which data is read.\n str: The string from which data is read.\n searchPath: An optional sequence of file paths that will be applied in order when searching for the given file and its includes. This argument can be supplied either as a FileSearchPath, or as a standard string with paths separated by the PATH_SEPARATOR character.\n readOptions: An optional pointer to an XmlReadOptions object. If provided, then the given options will affect the behavior of the read function. Defaults to a null pointer."); mod.def("writeToXmlFile", mx::writeToXmlFile, py::arg("doc"), py::arg("filename"), py::arg("writeOptions") = (mx::XmlWriteOptions*) nullptr); mod.def("writeToXmlString", mx::writeToXmlString, py::arg("doc"), py::arg("writeOptions") = nullptr); mod.def("prependXInclude", mx::prependXInclude); - mod.def("getEnvironmentPath", &mx::getEnvironmentPath, - py::arg("sep") = mx::PATH_LIST_SEPARATOR); + mod.def("getEnvironmentPath", &mx::getEnvironmentPath, py::arg("sep") = mx::PATH_LIST_SEPARATOR, "Return a FileSearchPath object from search path environment variable."); mod.attr("PATH_LIST_SEPARATOR") = mx::PATH_LIST_SEPARATOR; mod.attr("MATERIALX_SEARCH_PATH_ENV_VAR") = mx::MATERIALX_SEARCH_PATH_ENV_VAR; diff --git a/source/PyMaterialX/PyMaterialXGenGlsl/PyGlslShaderGenerator.cpp b/source/PyMaterialX/PyMaterialXGenGlsl/PyGlslShaderGenerator.cpp index 87726352ff..fd95bc5522 100644 --- a/source/PyMaterialX/PyMaterialXGenGlsl/PyGlslShaderGenerator.cpp +++ b/source/PyMaterialX/PyMaterialXGenGlsl/PyGlslShaderGenerator.cpp @@ -41,51 +41,51 @@ namespace void bindPyGlslShaderGenerator(py::module& mod) { - py::class_(mod, "GlslShaderGenerator") - .def_static("create", &GlslShaderGenerator_create) - .def("generate", &mx::GlslShaderGenerator::generate) - .def("getTarget", &mx::GlslShaderGenerator::getTarget) - .def("getVersion", &mx::GlslShaderGenerator::getVersion); + py::class_(mod, "GlslShaderGenerator", "Base class for GLSL (OpenGL Shading Language) code generation.\n\nA generator for a specific GLSL target should be derived from this class.") + .def_static("create", &GlslShaderGenerator_create, "Creator function.\n\nIf a TypeSystem is not provided it will be created internally. Optionally pass in an externally created TypeSystem here, if you want to keep type descriptions alive after the lifetime of the shader generator.") + .def("generate", &mx::GlslShaderGenerator::generate, "Generate a shader starting from the given element, translating the element and all dependencies upstream into shader code.") + .def("getTarget", &mx::GlslShaderGenerator::getTarget, "Return a unique identifier for the target this generator is for.") + .def("getVersion", &mx::GlslShaderGenerator::getVersion, "Return the version string for the GLSL version this generator is for."); } void bindPyGlslResourceBindingContext(py::module &mod) { - py::class_(mod, "GlslResourceBindingContext") - .def_static("create", &mx::GlslResourceBindingContext::create) + py::class_(mod, "GlslResourceBindingContext", "Class representing a resource binding for Glsl shader resources.") + .def_static("create", &mx::GlslResourceBindingContext::create, "") .def(py::init()) - .def("emitDirectives", &mx::GlslResourceBindingContext::emitDirectives) - .def("emitResourceBindings", &mx::GlslResourceBindingContext::emitResourceBindings); + .def("emitDirectives", &mx::GlslResourceBindingContext::emitDirectives, "") + .def("emitResourceBindings", &mx::GlslResourceBindingContext::emitResourceBindings, ""); } // Essl shader generator bindings void bindPyEsslShaderGenerator(py::module& mod) { - py::class_(mod, "EsslShaderGenerator") - .def_static("create", &EsslShaderGenerator_create) - .def("generate", &mx::EsslShaderGenerator::generate) - .def("getTarget", &mx::EsslShaderGenerator::getTarget) - .def("getVersion", &mx::EsslShaderGenerator::getVersion); + py::class_(mod, "EsslShaderGenerator", "An ESSL (OpenGL ES Shading Language) shader generator.") + .def_static("create", &EsslShaderGenerator_create, "Creator function.\n\nIf a TypeSystem is not provided it will be created internally. Optionally pass in an externally created TypeSystem here, if you want to keep type descriptions alive after the lifetime of the shader generator.") + .def("generate", &mx::EsslShaderGenerator::generate, "Generate a shader starting from the given element, translating the element and all dependencies upstream into shader code.") + .def("getTarget", &mx::EsslShaderGenerator::getTarget, "Return a unique identifier for the target this generator is for.") + .def("getVersion", &mx::EsslShaderGenerator::getVersion, "Return the version string for the ESSL version this generator is for."); } // Glsl Vulkan shader generator bindings void bindPyVkShaderGenerator(py::module& mod) { - py::class_(mod, "VkShaderGenerator") - .def_static("create", &VkShaderGenerator_create) - .def("generate", &mx::VkShaderGenerator::generate) - .def("getTarget", &mx::VkShaderGenerator::getTarget) - .def("getVersion", &mx::VkShaderGenerator::getVersion); + py::class_(mod, "VkShaderGenerator", "A Vulkan GLSL shader generator.") + .def_static("create", &VkShaderGenerator_create, "Creator function.\n\nIf a TypeSystem is not provided it will be created internally. Optionally pass in an externally created TypeSystem here, if you want to keep type descriptions alive after the lifetime of the shader generator.") + .def("generate", &mx::VkShaderGenerator::generate, "Generate a shader starting from the given element, translating the element and all dependencies upstream into shader code.") + .def("getTarget", &mx::VkShaderGenerator::getTarget, "Return a unique identifier for the target this generator is for.") + .def("getVersion", &mx::VkShaderGenerator::getVersion, "Return the version string for the GLSL version this generator is for."); } // Glsl Wgsl shader generator bindings void bindPyWgslShaderGenerator(py::module& mod) { - py::class_(mod, "WgslShaderGenerator") - .def_static("create", &WgslShaderGenerator_create) - .def("generate", &mx::WgslShaderGenerator::generate) - .def("getTarget", &mx::WgslShaderGenerator::getTarget) - .def("getVersion", &mx::WgslShaderGenerator::getVersion); + py::class_(mod, "WgslShaderGenerator", "WGSL Flavor of Vulkan GLSL shader generator.") + .def_static("create", &WgslShaderGenerator_create, "Creator function.\n\nIf a TypeSystem is not provided it will be created internally. Optionally pass in an externally created TypeSystem here, if you want to keep type descriptions alive after the lifetime of the shader generator.") + .def("generate", &mx::WgslShaderGenerator::generate, "Generate a shader starting from the given element, translating the element and all dependencies upstream into shader code.") + .def("getTarget", &mx::WgslShaderGenerator::getTarget, "Return a unique identifier for the target this generator is for.") + .def("getVersion", &mx::WgslShaderGenerator::getVersion, "Return the version string for the GLSL version this generator is for."); } diff --git a/source/PyMaterialX/PyMaterialXGenMdl/PyMdlShaderGenerator.cpp b/source/PyMaterialX/PyMaterialXGenMdl/PyMdlShaderGenerator.cpp index b62a84a65f..5173e7f655 100644 --- a/source/PyMaterialX/PyMaterialXGenMdl/PyMdlShaderGenerator.cpp +++ b/source/PyMaterialX/PyMaterialXGenMdl/PyMdlShaderGenerator.cpp @@ -22,7 +22,7 @@ namespace void bindPyMdlShaderGenerator(py::module& mod) { - py::class_(mod, "MdlShaderGenerator") - .def_static("create", &MdlShaderGenerator_create) - .def("getTarget", &mx::MdlShaderGenerator::getTarget); + py::class_(mod, "MdlShaderGenerator", "Shader generator for MDL (Material Definition Language).") + .def_static("create", &MdlShaderGenerator_create, "Creator function.\n\nIf a TypeSystem is not provided it will be created internally. Optionally pass in an externally created TypeSystem here, if you want to keep type descriptions alive after the lifetime of the shader generator.") + .def("getTarget", &mx::MdlShaderGenerator::getTarget, "Return a unique identifier for the target this generator is for."); } diff --git a/source/PyMaterialX/PyMaterialXGenMsl/PyMslShaderGenerator.cpp b/source/PyMaterialX/PyMaterialXGenMsl/PyMslShaderGenerator.cpp index e11cedf4d9..84081b2c39 100644 --- a/source/PyMaterialX/PyMaterialXGenMsl/PyMslShaderGenerator.cpp +++ b/source/PyMaterialX/PyMaterialXGenMsl/PyMslShaderGenerator.cpp @@ -28,16 +28,16 @@ namespace void bindPyMslShaderGenerator(py::module& mod) { py::class_(mod, "MslShaderGenerator") - .def_static("create", &MslShaderGenerator_create) - .def("generate", &mx::MslShaderGenerator::generate) - .def("getTarget", &mx::MslShaderGenerator::getTarget) - .def("getVersion", &mx::MslShaderGenerator::getVersion); + .def_static("create", &MslShaderGenerator_create, "Creator function.\n\nIf a TypeSystem is not provided it will be created internally. Optionally pass in an externally created TypeSystem here, if you want to keep type descriptions alive after the lifetime of the shader generator.") + .def("generate", &mx::MslShaderGenerator::generate, "Generate a shader starting from the given element, translating the element and all dependencies upstream into shader code.") + .def("getTarget", &mx::MslShaderGenerator::getTarget, "Return a unique identifier for the target this generator is for.") + .def("getVersion", &mx::MslShaderGenerator::getVersion, "Return the version string for the GLSL version this generator is for."); } void bindPyMslResourceBindingContext(py::module &mod) { py::class_(mod, "MslResourceBindingContext") - .def_static("create", &mx::MslResourceBindingContext::create) + .def_static("create", &mx::MslResourceBindingContext::create, "Creator function.\n\nIf a TypeSystem is not provided it will be created internally. Optionally pass in an externally created TypeSystem here, if you want to keep type descriptions alive after the lifetime of the shader generator.") .def(py::init()) .def("emitDirectives", &mx::MslResourceBindingContext::emitDirectives) .def("emitResourceBindings", &mx::MslResourceBindingContext::emitResourceBindings); diff --git a/source/PyMaterialX/PyMaterialXGenOsl/PyOslShaderGenerator.cpp b/source/PyMaterialX/PyMaterialXGenOsl/PyOslShaderGenerator.cpp index 6e457411b9..473b598c8c 100644 --- a/source/PyMaterialX/PyMaterialXGenOsl/PyOslShaderGenerator.cpp +++ b/source/PyMaterialX/PyMaterialXGenOsl/PyOslShaderGenerator.cpp @@ -27,8 +27,8 @@ void bindPyOslShaderGenerator(py::module& mod) mod.attr("OSL_INPUTS") = mx::OSL::INPUTS; mod.attr("OSL_OUTPUTS") = mx::OSL::OUTPUTS; - py::class_(mod, "OslShaderGenerator") - .def_static("create", &OslShaderGenerator_create) - .def("getTarget", &mx::OslShaderGenerator::getTarget) - .def("generate", &mx::OslShaderGenerator::generate); + py::class_(mod, "OslShaderGenerator", "Base class for OSL (Open Shading Language) shader generators.\n\nA generator for a specific OSL target should be derived from this class.") + .def_static("create", &OslShaderGenerator_create, "Creator function.\n\nIf a TypeSystem is not provided it will be created internally. Optionally pass in an externally created TypeSystem here, if you want to keep type descriptions alive after the lifetime of the shader generator.") + .def("getTarget", &mx::OslShaderGenerator::getTarget, "Return a unique identifier for the target this generator is for.") + .def("generate", &mx::OslShaderGenerator::generate, "Generate a shader starting from the given element, translating the element and all dependencies upstream into shader code."); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyColorManagement.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyColorManagement.cpp index 8229294c7e..672c25b377 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyColorManagement.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyColorManagement.cpp @@ -47,27 +47,27 @@ class PyColorManagementSystem : public mx::ColorManagementSystem void bindPyColorManagement(py::module& mod) { - py::class_(mod, "ColorSpaceTransform") + py::class_(mod, "ColorSpaceTransform", "Structure that represents color space transform information.") .def(py::init()) .def_readwrite("sourceSpace", &mx::ColorSpaceTransform::sourceSpace) .def_readwrite("targetSpace", &mx::ColorSpaceTransform::targetSpace) .def_readwrite("type", &mx::ColorSpaceTransform::type); - py::class_(mod, "ColorManagementSystem") + py::class_(mod, "ColorManagementSystem", "Abstract base class for color management systems.") .def(py::init<>()) - .def("getName", &mx::ColorManagementSystem::getName) - .def("loadLibrary", &mx::ColorManagementSystem::loadLibrary) - .def("supportsTransform", &mx::ColorManagementSystem::supportsTransform); + .def("getName", &mx::ColorManagementSystem::getName, "Return the ColorManagementSystem name.") + .def("loadLibrary", &mx::ColorManagementSystem::loadLibrary, "Load a library of implementations from the provided document, replacing any previously loaded content.") + .def("supportsTransform", &mx::ColorManagementSystem::supportsTransform, "Returns whether this color management system supports a provided transform."); - py::class_(mod, "DefaultColorManagementSystem") - .def_static("create", &mx::DefaultColorManagementSystem::create) - .def("getName", &mx::DefaultColorManagementSystem::getName); + py::class_(mod, "DefaultColorManagementSystem", "Class for a default color management system.") + .def_static("create", &mx::DefaultColorManagementSystem::create, "Create a new DefaultColorManagementSystem.") + .def("getName", &mx::DefaultColorManagementSystem::getName, "Return the DefaultColorManagementSystem name."); #ifdef MATERIALX_BUILD_OCIO py::class_(mod, "OcioColorManagementSystem") .def_static("createFromEnv", &mx::OcioColorManagementSystem::createFromEnv) .def_static("createFromFile", &mx::OcioColorManagementSystem::createFromFile) .def_static("createFromBuiltinConfig", &mx::OcioColorManagementSystem::createFromBuiltinConfig) - .def("getName", &mx::OcioColorManagementSystem::getName); + .def("getName", &mx::OcioColorManagementSystem::getName, "Return the name of this port."); #endif } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyGenContext.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyGenContext.cpp index e73090e964..4aebed25a9 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyGenContext.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyGenContext.cpp @@ -14,21 +14,21 @@ void bindPyGenContext(py::module& mod) { py::class_(mod, "ApplicationVariableHandler"); - py::class_(mod, "GenContext") + py::class_(mod, "GenContext", "A context class for shader generation.\n\nUsed for thread local storage of data needed during shader generation.") .def(py::init()) - .def("getShaderGenerator", &mx::GenContext::getShaderGenerator) - .def("getOptions", static_cast(&mx::GenContext::getOptions), py::return_value_policy::reference) - .def("getTypeDesc", &mx::GenContext::getTypeDesc) - .def("registerSourceCodeSearchPath", static_cast(&mx::GenContext::registerSourceCodeSearchPath)) - .def("registerSourceCodeSearchPath", static_cast(&mx::GenContext::registerSourceCodeSearchPath)) - .def("resolveSourceFile", &mx::GenContext::resolveSourceFile) - .def("pushUserData", &mx::GenContext::pushUserData) - .def("setApplicationVariableHandler", &mx::GenContext::setApplicationVariableHandler) - .def("getApplicationVariableHandler", &mx::GenContext::getApplicationVariableHandler); + .def("getShaderGenerator", &mx::GenContext::getShaderGenerator, "Return shader generatior.") + .def("getOptions", static_cast(&mx::GenContext::getOptions), py::return_value_policy::reference, "Return shader generation options.") + .def("getTypeDesc", &mx::GenContext::getTypeDesc, "Return a TypeDesc for the given type name.") + .def("registerSourceCodeSearchPath", static_cast(&mx::GenContext::registerSourceCodeSearchPath), "Register a user search path for finding source code during code generation.") + .def("registerSourceCodeSearchPath", static_cast(&mx::GenContext::registerSourceCodeSearchPath), "Register a user search path for finding source code during code generation.") + .def("resolveSourceFile", &mx::GenContext::resolveSourceFile, "Resolve a source code filename, first checking the given local path then checking any file paths registered by the user.") + .def("pushUserData", &mx::GenContext::pushUserData, "Add user data to the context to make it available during shader generator.") + .def("setApplicationVariableHandler", &mx::GenContext::setApplicationVariableHandler, "Set handler for application variables.") + .def("getApplicationVariableHandler", &mx::GenContext::getApplicationVariableHandler, "Get handler for application variables."); } void bindPyGenUserData(py::module& mod) { - py::class_(mod, "GenUserData") - .def("getSelf", static_cast(&mx::GenUserData::getSelf)); + py::class_(mod, "GenUserData", "Base class for custom user data needed during shader generation.") + .def("getSelf", static_cast(&mx::GenUserData::getSelf), "Return a shared pointer for this object."); } \ No newline at end of file diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyGenOptions.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyGenOptions.cpp index 3949d50b8d..698484df6b 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyGenOptions.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyGenOptions.cpp @@ -23,7 +23,7 @@ void bindPyGenOptions(py::module& mod) .value("SPECULAR_ENVIRONMENT_NONE", mx::HwSpecularEnvironmentMethod::SPECULAR_ENVIRONMENT_NONE) .export_values(); - py::class_(mod, "GenOptions") + py::class_(mod, "GenOptions", "Class holding options to configure shader generation.") .def_readwrite("shaderInterfaceType", &mx::GenOptions::shaderInterfaceType) .def_readwrite("fileTextureVerticalFlip", &mx::GenOptions::fileTextureVerticalFlip) .def_readwrite("targetColorSpaceOverride", &mx::GenOptions::targetColorSpaceOverride) diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyShader.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyShader.cpp index 581964aeac..b9293292ed 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyShader.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyShader.cpp @@ -17,16 +17,16 @@ void bindPyShader(py::module& mod) // Note: py::return_value_policy::reference was needed because getStage returns a // ShaderStage& and without this parameter it would return a copy and not a // reference - py::class_(mod, "Shader") + py::class_(mod, "Shader", "Class containing all data needed during shader generation.\n\nAfter generation is completed it will contain the resulting source code emitted by shader generators.\n\nThe class contains a default implementation using a single shader stage. Derived shaders can override this, as well as overriding all methods that add code to the shader.") .def(py::init()) - .def("getName", &mx::Shader::getName) - .def("hasStage", &mx::Shader::hasStage) - .def("numStages", &mx::Shader::numStages) - .def("getStage", static_cast(&mx::Shader::getStage), py::return_value_policy::reference) - .def("getStage", static_cast(&mx::Shader::getStage), py::return_value_policy::reference) - .def("getSourceCode", &mx::Shader::getSourceCode) - .def("hasAttribute", &mx::Shader::hasAttribute) - .def("getAttribute", &mx::Shader::getAttribute) - .def("setAttribute", static_cast(&mx::Shader::setAttribute)) - .def("setAttribute", static_cast(&mx::Shader::setAttribute)); + .def("getName", &mx::Shader::getName, "Return the shader name.") + .def("hasStage", &mx::Shader::hasStage, "Return if stage exists.") + .def("numStages", &mx::Shader::numStages, "Return the number of shader stages for this shader.") + .def("getStage", static_cast(&mx::Shader::getStage), py::return_value_policy::reference, "Return a stage by name.") + .def("getStage", static_cast(&mx::Shader::getStage), py::return_value_policy::reference, "Return a stage by name.") + .def("getSourceCode", &mx::Shader::getSourceCode, "Return the shader source code for a given shader stage.") + .def("hasAttribute", &mx::Shader::hasAttribute, "Return true if the shader has a given named attribute.") + .def("getAttribute", &mx::Shader::getAttribute, "Return the value for a named attribute, or nullptr if no such attribute is found.") + .def("setAttribute", static_cast(&mx::Shader::setAttribute), "Set a flag attribute on the shader.") + .def("setAttribute", static_cast(&mx::Shader::setAttribute), "Set a flag attribute on the shader."); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyShaderGenerator.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyShaderGenerator.cpp index fdf4b94aac..744560ab92 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyShaderGenerator.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyShaderGenerator.cpp @@ -14,14 +14,14 @@ namespace mx = MaterialX; void bindPyShaderGenerator(py::module& mod) { - py::class_(mod, "ShaderGenerator") - .def("getTarget", &mx::ShaderGenerator::getTarget) - .def("generate", &mx::ShaderGenerator::generate) - .def("setColorManagementSystem", &mx::ShaderGenerator::setColorManagementSystem) - .def("getColorManagementSystem", &mx::ShaderGenerator::getColorManagementSystem) - .def("setUnitSystem", &mx::ShaderGenerator::setUnitSystem) - .def("getUnitSystem", &mx::ShaderGenerator::getUnitSystem) - .def("getTokenSubstitutions", &mx::ShaderGenerator::getTokenSubstitutions) - .def("registerTypeDefs", &mx::ShaderGenerator::registerTypeDefs) - .def("registerShaderMetadata", &mx::ShaderGenerator::registerShaderMetadata); + py::class_(mod, "ShaderGenerator", "Base class for shader generators All third-party shader generators should derive from this class.\n\nDerived classes should use DECLARE_SHADER_GENERATOR / DEFINE_SHADER_GENERATOR in their declaration / definition, and register with the Registry class.") + .def("getTarget", &mx::ShaderGenerator::getTarget, "Return the name of the target this generator is for.") + .def("generate", &mx::ShaderGenerator::generate, "Generate a shader starting from the given element, translating the element and all dependencies upstream into shader code.") + .def("setColorManagementSystem", &mx::ShaderGenerator::setColorManagementSystem, "Sets the color management system.") + .def("getColorManagementSystem", &mx::ShaderGenerator::getColorManagementSystem, "Returns the color management system.") + .def("setUnitSystem", &mx::ShaderGenerator::setUnitSystem, "Sets the unit system.") + .def("getUnitSystem", &mx::ShaderGenerator::getUnitSystem, "Returns the unit system.") + .def("getTokenSubstitutions", &mx::ShaderGenerator::getTokenSubstitutions, "Return the map of token substitutions used by the generator.") + .def("registerTypeDefs", &mx::ShaderGenerator::registerTypeDefs, "Register type definitions from the document.") + .def("registerShaderMetadata", &mx::ShaderGenerator::registerShaderMetadata, "Register metadata that should be exported to the generated shaders.\n\nSupported metadata includes standard UI attributes like \"uiname\", \"uifolder\", \"uimin\", \"uimax\", etc. But it is also extendable by defining custom attributes using AttributeDefs. Any AttributeDef in the given document with exportable=\"true\" will be exported as shader metadata when found on nodes during shader generation. Derived shader generators may override this method to change the registration. Applications must explicitly call this method before shader generation to enable export of metadata."); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyShaderPort.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyShaderPort.cpp index 146674c222..e7c62fd8c9 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyShaderPort.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyShaderPort.cpp @@ -12,27 +12,27 @@ namespace mx = MaterialX; void bindPyShaderPort(py::module& mod) { - py::class_(mod, "ShaderPort") - .def("setType", &mx::ShaderPort::setType) - .def("getType", &mx::ShaderPort::getType) - .def("setName", &mx::ShaderPort::setName) - .def("getName", &mx::ShaderPort::getName) - .def("getFullName", &mx::ShaderPort::getFullName) - .def("setVariable", &mx::ShaderPort::setVariable) - .def("getVariable", &mx::ShaderPort::getVariable) - .def("setSemantic", &mx::ShaderPort::setSemantic) - .def("getSemantic", &mx::ShaderPort::getSemantic) - .def("setValue", &mx::ShaderPort::setValue) - .def("getValue", &mx::ShaderPort::getValue) - .def("getValueString", &mx::ShaderPort::getValueString) - .def("setGeomProp", &mx::ShaderPort::setGeomProp) - .def("getGeomProp", &mx::ShaderPort::getGeomProp) - .def("setPath", &mx::ShaderPort::setPath) - .def("getPath", &mx::ShaderPort::getPath) - .def("setUnit", &mx::ShaderPort::setUnit) - .def("getUnit", &mx::ShaderPort::getUnit) - .def("setColorSpace", &mx::ShaderPort::setColorSpace) - .def("getColorSpace", &mx::ShaderPort::getColorSpace) - .def("isUniform", &mx::ShaderPort::isUniform) - .def("isEmitted", &mx::ShaderPort::isEmitted); + py::class_(mod, "ShaderPort", "An input or output port on a ShaderNode.") + .def("setType", &mx::ShaderPort::setType, "Set the data type for this port.") + .def("getType", &mx::ShaderPort::getType, "Return the data type for this port.") + .def("setName", &mx::ShaderPort::setName, "Set the name of this port.") + .def("getName", &mx::ShaderPort::getName, "Return the name of this port.") + .def("getFullName", &mx::ShaderPort::getFullName, "Return the name of this port.") + .def("setVariable", &mx::ShaderPort::setVariable, "Set the variable name of this port.") + .def("getVariable", &mx::ShaderPort::getVariable, "Return the variable name of this port.") + .def("setSemantic", &mx::ShaderPort::setSemantic, "Set the variable semantic of this port.") + .def("getSemantic", &mx::ShaderPort::getSemantic, "Return the variable semantic of this port.") + .def("setValue", &mx::ShaderPort::setValue, "Set a value on this port.") + .def("getValue", &mx::ShaderPort::getValue, "Return the value set on this port.") + .def("getValueString", &mx::ShaderPort::getValueString, "Return the value set on this port as a string, or an empty string if there is no value.") + .def("setGeomProp", &mx::ShaderPort::setGeomProp, "Set geomprop name if the input has a default geomprop to be assigned when it is unconnected.") + .def("getGeomProp", &mx::ShaderPort::getGeomProp, "Get geomprop name.") + .def("setPath", &mx::ShaderPort::setPath, "Set the path to this port.") + .def("getPath", &mx::ShaderPort::getPath, "Return the path to this port.") + .def("setUnit", &mx::ShaderPort::setUnit, "Set a unit type for the value on this port.") + .def("getUnit", &mx::ShaderPort::getUnit, "Return the unit type for the value on this port.") + .def("setColorSpace", &mx::ShaderPort::setColorSpace, "Set a source color space for the value on this port.") + .def("getColorSpace", &mx::ShaderPort::getColorSpace, "Return the source color space for the value on this port.") + .def("isUniform", &mx::ShaderPort::isUniform, "Return the uniform flag on this port.") + .def("isEmitted", &mx::ShaderPort::isEmitted, "Return the emitted state of this port."); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyShaderStage.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyShaderStage.cpp index 62347caa6b..e94b450f8a 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyShaderStage.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyShaderStage.cpp @@ -16,33 +16,33 @@ void bindPyShaderStage(py::module& mod) py::class_(mod, "ShaderPortPredicate"); - py::class_(mod, "VariableBlock") + py::class_(mod, "VariableBlock", "A block of variables in a shader stage.") .def(py::init()) - .def("getName", &mx::VariableBlock::getName) - .def("getInstance", &mx::VariableBlock::getInstance) - .def("empty", &mx::VariableBlock::empty) - .def("size", &mx::VariableBlock::size) - .def("find", static_cast(&mx::VariableBlock::find)) - .def("find", (mx::ShaderPort* (mx::VariableBlock::*)(const mx::ShaderPortPredicate& )) &mx::VariableBlock::find) - .def("__len__", &mx::VariableBlock::size) + .def("getName", &mx::VariableBlock::getName, "Get the name of this block.") + .def("getInstance", &mx::VariableBlock::getInstance, "Get the instance name of this block.") + .def("empty", &mx::VariableBlock::empty, "Return true if the block has no variables.") + .def("size", &mx::VariableBlock::size, "Return the number of variables in this block.") + .def("find", static_cast(&mx::VariableBlock::find), "Find a port based on a predicate.") + .def("find", (mx::ShaderPort* (mx::VariableBlock::*)(const mx::ShaderPortPredicate& )) &mx::VariableBlock::find, "Find a port based on a predicate.") + .def("__len__", &mx::VariableBlock::size, "Return the number of variables in this block.") .def("__getitem__", [](const mx::VariableBlock &vb, size_t i) { if (i >= vb.size()) throw py::index_error(); return vb[i]; - }, py::return_value_policy::reference_internal); + }, py::return_value_policy::reference_internal, "Return the number of variables in this block.", "Return the number of paths in the sequence.", "Return the number of paths in the sequence.", "Return the number of variables in this block.", "Return the number of variables in this block.", "Return the number of variables in this block.", "Return the number of variables in this block.", "Return the number of variables in this block.", "Return the number of variables in this block.", "Return the number of variables in this block.", "Return the number of variables in this block.", "Return the number of variables in this block.", "Return the number of variables in this block.", "Return the number of variables in this block."); - py::class_(mod, "ShaderStage") + py::class_(mod, "ShaderStage", "A shader stage, containing the state and resulting source code for the stage.") .def(py::init()) - .def("getName", &mx::ShaderStage::getName) - .def("getFunctionName", &mx::ShaderStage::getFunctionName) - .def("getSourceCode", &mx::ShaderStage::getSourceCode) - .def("getUniformBlock", static_cast(&mx::ShaderStage::getUniformBlock)) - .def("getInputBlock", static_cast(&mx::ShaderStage::getInputBlock)) - .def("getOutputBlock", static_cast(&mx::ShaderStage::getOutputBlock)) - .def("getConstantBlock", static_cast(&mx::ShaderStage::getConstantBlock)) - .def("getUniformBlocks", &mx::ShaderStage::getUniformBlocks) - .def("getInputBlocks", &mx::ShaderStage::getInputBlocks) - .def("getIncludes", &mx::ShaderStage::getIncludes) - .def("getSourceDependencies", &mx::ShaderStage::getSourceDependencies) - .def("getOutputBlocks", &mx::ShaderStage::getOutputBlocks); + .def("getName", &mx::ShaderStage::getName, "Return the stage name.") + .def("getFunctionName", &mx::ShaderStage::getFunctionName, "Return the stage function name.") + .def("getSourceCode", &mx::ShaderStage::getSourceCode, "Return the stage source code.") + .def("getUniformBlock", static_cast(&mx::ShaderStage::getUniformBlock), "Return the uniform variable block with given name.") + .def("getInputBlock", static_cast(&mx::ShaderStage::getInputBlock), "Return the input variable block with given name.") + .def("getOutputBlock", static_cast(&mx::ShaderStage::getOutputBlock), "Return the output variable block with given name.") + .def("getConstantBlock", static_cast(&mx::ShaderStage::getConstantBlock), "Return the constant variable block.") + .def("getUniformBlocks", &mx::ShaderStage::getUniformBlocks, "Return a map of all uniform blocks.") + .def("getInputBlocks", &mx::ShaderStage::getInputBlocks, "Return a map of all input blocks.") + .def("getIncludes", &mx::ShaderStage::getIncludes, "Return a set of all include files.") + .def("getSourceDependencies", &mx::ShaderStage::getSourceDependencies, "Return a set of all source dependencies.") + .def("getOutputBlocks", &mx::ShaderStage::getOutputBlocks, "Return a map of all output blocks."); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyShaderTranslator.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyShaderTranslator.cpp index c6b3f2bfe9..214dca6e9c 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyShaderTranslator.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyShaderTranslator.cpp @@ -12,8 +12,8 @@ namespace mx = MaterialX; void bindPyShaderTranslator(py::module& mod) { - py::class_(mod, "ShaderTranslator") - .def_static("create", &mx::ShaderTranslator::create) - .def("translateShader", &mx::ShaderTranslator::translateShader) - .def("translateAllMaterials", &mx::ShaderTranslator::translateAllMaterials); + py::class_(mod, "ShaderTranslator", "A helper class for translating content between shading models.") + .def_static("create", &mx::ShaderTranslator::create, "") + .def("translateShader", &mx::ShaderTranslator::translateShader, "Translate a shader node to the destination shading model.") + .def("translateAllMaterials", &mx::ShaderTranslator::translateAllMaterials, "Translate each material in the input document to the destination shading model."); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyTypeDesc.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyTypeDesc.cpp index 66a1821e7b..dd0469e5a3 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyTypeDesc.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyTypeDesc.cpp @@ -15,16 +15,16 @@ void bindPyTypeDesc(py::module& mod) // Set nodelete as destructor on returned TypeDescs since they are owned // by the container they are stored in and should not be destroyed when // garbage collected by the python interpreter - py::class_>(mod, "TypeDesc") - .def("getName", &mx::TypeDesc::getName) - .def("getBaseType", &mx::TypeDesc::getBaseType) - .def("getSemantic", &mx::TypeDesc::getSemantic) - .def("getSize", &mx::TypeDesc::getSize) - .def("isScalar", &mx::TypeDesc::isScalar) - .def("isAggregate", &mx::TypeDesc::isAggregate) - .def("isArray", &mx::TypeDesc::isArray) - .def("isFloat2", &mx::TypeDesc::isFloat2) - .def("isFloat3", &mx::TypeDesc::isFloat3) - .def("isFloat4", &mx::TypeDesc::isFloat4) - .def("isClosure", &mx::TypeDesc::isClosure); + py::class_>(mod, "TypeDesc", "A type descriptor for MaterialX data types.\n\nAll types need to have a type descriptor registered in order for shader generators to know about the type. It can be used for type comparisons as well as getting more information about the type. Type descriptors for all standard library data types are defined by default and can be accessed from the Type namespace, e.g. Type::FLOAT. Custom struct types defined through typedef elements in a data library are loaded in and registered by calling the ShaderGenerator::registerTypeDefs method. The TypeSystem class, see below, is used to manage all type descriptions. It can be used to query the registered types.") + .def("getName", &mx::TypeDesc::getName, "Return the name of the type.") + .def("getBaseType", &mx::TypeDesc::getBaseType, "Return the basetype for the type.") + .def("getSemantic", &mx::TypeDesc::getSemantic, "Return the semantic for the type.") + .def("getSize", &mx::TypeDesc::getSize, "Return the number of elements the type is composed of.\n\nWill return 1 for scalar types and a size greater than 1 for aggregate type. For array types 0 is returned since the number of elements is undefined until an array is instantiated.") + .def("isScalar", &mx::TypeDesc::isScalar, "Return true if the type is a scalar type.") + .def("isAggregate", &mx::TypeDesc::isAggregate, "Return true if the type is an aggregate type.") + .def("isArray", &mx::TypeDesc::isArray, "Return true if the type is an array type.") + .def("isFloat2", &mx::TypeDesc::isFloat2, "Return true if the type is an aggregate of 2 floats.") + .def("isFloat3", &mx::TypeDesc::isFloat3, "Return true if the type is an aggregate of 3 floats.") + .def("isFloat4", &mx::TypeDesc::isFloat4, "Return true if the type is an aggregate of 4 floats.") + .def("isClosure", &mx::TypeDesc::isClosure, "Return true if the type represents a closure."); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyUnitSystem.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyUnitSystem.cpp index e5befc21e6..3e1193e565 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyUnitSystem.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyUnitSystem.cpp @@ -14,18 +14,18 @@ namespace mx = MaterialX; void bindPyUnitSystem(py::module& mod) { - py::class_(mod, "UnitTransform") + py::class_(mod, "UnitTransform", "Structure that represents unit transform information.") .def(py::init()) .def_readwrite("sourceUnit", &mx::UnitTransform::sourceUnit) .def_readwrite("targetUnit", &mx::UnitTransform::targetUnit) .def_readwrite("type", &mx::UnitTransform::type) .def_readwrite("unitType", &mx::UnitTransform::type); - py::class_(mod, "UnitSystem") - .def_static("create", &mx::UnitSystem::create) - .def("getName", &mx::UnitSystem::getName) - .def("loadLibrary", &mx::UnitSystem::loadLibrary) - .def("supportsTransform", &mx::UnitSystem::supportsTransform) - .def("setUnitConverterRegistry", &mx::UnitSystem::setUnitConverterRegistry) - .def("getUnitConverterRegistry", &mx::UnitSystem::getUnitConverterRegistry); + py::class_(mod, "UnitSystem", "Base unit system support.") + .def_static("create", &mx::UnitSystem::create, "Create a new UnitSystem.") + .def("getName", &mx::UnitSystem::getName, "Return the UnitSystem name.") + .def("loadLibrary", &mx::UnitSystem::loadLibrary, "assign document with unit implementations replacing any previously loaded content.") + .def("supportsTransform", &mx::UnitSystem::supportsTransform, "Returns whether this unit system supports a provided transform.") + .def("setUnitConverterRegistry", &mx::UnitSystem::setUnitConverterRegistry, "Assign unit converter registry replacing any previous assignment.") + .def("getUnitConverterRegistry", &mx::UnitSystem::getUnitConverterRegistry, "Returns the currently assigned unit converter registry."); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyUtil.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyUtil.cpp index 6c73c2866f..2d16b46ab9 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyUtil.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyUtil.cpp @@ -24,16 +24,16 @@ std::vector findRenderableElements(mx::ConstDocumentPtr doc void bindPyUtil(py::module& mod) { - mod.def("isTransparentSurface", &mx::isTransparentSurface); - mod.def("mapValueToColor", &mx::mapValueToColor); - mod.def("requiresImplementation", &mx::requiresImplementation); - mod.def("elementRequiresShading", &mx::elementRequiresShading); - mod.def("findRenderableMaterialNodes", &findRenderableMaterialNodes); - mod.def("findRenderableElements", &findRenderableElements, py::arg("doc"), py::arg("includeReferencedGraphs") = false); - mod.def("getNodeDefInput", &mx::getNodeDefInput); - mod.def("tokenSubstitution", &mx::tokenSubstitution); - mod.def("getUdimCoordinates", &mx::getUdimCoordinates); - mod.def("getUdimScaleAndOffset", &mx::getUdimScaleAndOffset); - mod.def("connectsToWorldSpaceNode", &mx::connectsToWorldSpaceNode); - mod.def("hasElementAttributes", &mx::hasElementAttributes); + mod.def("isTransparentSurface", &mx::isTransparentSurface, "Returns true if the given element is a surface shader with the potential of being transparent.\n\nThis can be used by HW shader generators to determine if a shader will require transparency handling.\n\nNote: This function will check some common cases for how a surface shader can be transparent. It is not covering all possible cases for how transparency can be done and target applications might need to do additional checks to track transparency correctly. For example, custom surface shader nodes implemented in source code will not be tracked by this function and transparency for such nodes must be tracked separately by the target application."); + mod.def("mapValueToColor", &mx::mapValueToColor, "Maps a value to a four channel color if it is of the appropriate type.\n\nSupported types include float, Vector2, Vector3, Vector4, and Color4. If not mapping is possible the color value is set to opaque black."); + mod.def("requiresImplementation", &mx::requiresImplementation, "Return whether a nodedef requires an implementation."); + mod.def("elementRequiresShading", &mx::elementRequiresShading, "Determine if a given element requires shading / lighting for rendering."); + mod.def("findRenderableMaterialNodes", &findRenderableMaterialNodes, ""); + mod.def("findRenderableElements", &findRenderableElements, py::arg("doc"), py::arg("includeReferencedGraphs") = false, ""); + mod.def("getNodeDefInput", &mx::getNodeDefInput, "Given a node input, return the corresponding input within its matching nodedef.\n\nThe optional target string can be used to guide the selection of nodedef declarations."); + mod.def("tokenSubstitution", &mx::tokenSubstitution, "Perform token substitutions on the given source string, using the given substitution map.\n\nTokens are required to start with '$' and can only consist of alphanumeric characters. The full token name, including '$' and all following alphanumeric character, will be replaced by the corresponding string in the substitution map, if the token exists in the map."); + mod.def("getUdimCoordinates", &mx::getUdimCoordinates, "Compute the UDIM coordinates for a set of UDIM identifiers.\n\nReturns:\n List of UDIM coordinates"); + mod.def("getUdimScaleAndOffset", &mx::getUdimScaleAndOffset, "Get the UV scale and offset to transform uv coordinates from UDIM uv space to 0..1 space."); + mod.def("connectsToWorldSpaceNode", &mx::connectsToWorldSpaceNode, "Determine whether the given output is directly connected to a node that generates world-space coordinates (e.g.\n\nArgs:\n output: Output to check\n\nReturns:\n Return the node if found."); + mod.def("hasElementAttributes", &mx::hasElementAttributes, "Returns true if there is are any value elements with a given set of attributes either on the starting node or any graph upsstream of that node.\n\nArgs:\n output: Starting node\n attributes: Attributes to test for"); } diff --git a/source/PyMaterialX/PyMaterialXGenSlang/PySlangShaderGenerator.cpp b/source/PyMaterialX/PyMaterialXGenSlang/PySlangShaderGenerator.cpp index bb69578412..391202e5ba 100644 --- a/source/PyMaterialX/PyMaterialXGenSlang/PySlangShaderGenerator.cpp +++ b/source/PyMaterialX/PyMaterialXGenSlang/PySlangShaderGenerator.cpp @@ -25,9 +25,9 @@ namespace void bindPySlangShaderGenerator(py::module& mod) { - py::class_(mod, "SlangShaderGenerator") - .def_static("create", &SlangShaderGenerator_create) - .def("generate", &mx::SlangShaderGenerator::generate) - .def("getTarget", &mx::SlangShaderGenerator::getTarget) - .def("getVersion", &mx::SlangShaderGenerator::getVersion); + py::class_(mod, "SlangShaderGenerator", "Base class for Slang code generation.\n\nA generator for a specific Slang target should be derived from this class.") + .def_static("create", &SlangShaderGenerator_create, "Creator function.\n\nIf a TypeSystem is not provided it will be created internally. Optionally pass in an externally created TypeSystem here, if you want to keep type descriptions alive after the lifetime of the shader generator.") + .def("generate", &mx::SlangShaderGenerator::generate, "Generate a shader starting from the given element, translating the element and all dependencies upstream into shader code.") + .def("getTarget", &mx::SlangShaderGenerator::getTarget, "Return a unique identifier for the target this generator is for.") + .def("getVersion", &mx::SlangShaderGenerator::getVersion, "Return the version string for the Slang version this generator is for."); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyCamera.cpp b/source/PyMaterialX/PyMaterialXRender/PyCamera.cpp index 9c19ceb831..aecce93618 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyCamera.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyCamera.cpp @@ -12,23 +12,23 @@ namespace mx = MaterialX; void bindPyCamera(py::module& mod) { - py::class_(mod, "Camera") - .def_static("create", &mx::Camera::create) - .def("setWorldMatrix", &mx::Camera::setWorldMatrix) - .def("getWorldMatrix", &mx::Camera::getWorldMatrix) - .def("setViewMatrix", &mx::Camera::setViewMatrix) - .def("getViewMatrix", &mx::Camera::getViewMatrix) - .def("setProjectionMatrix", &mx::Camera::setProjectionMatrix) - .def("getProjectionMatrix", &mx::Camera::getProjectionMatrix) - .def("getWorldViewProjMatrix", &mx::Camera::getWorldViewProjMatrix) - .def("getViewPosition", &mx::Camera::getViewPosition) - .def("getViewDirection", &mx::Camera::getViewDirection) - .def("setViewportSize", &mx::Camera::setViewportSize) - .def("getViewportSize", &mx::Camera::getViewportSize) - .def("projectToViewport", &mx::Camera::projectToViewport) - .def("unprojectFromViewport", &mx::Camera::unprojectFromViewport) - .def_static("createViewMatrix", &mx::Camera::createViewMatrix) - .def_static("createPerspectiveMatrix", &mx::Camera::createPerspectiveMatrix) - .def_static("createOrthographicMatrix", &mx::Camera::createOrthographicMatrix) - .def_static("transformPointPerspective", &mx::Camera::transformPointPerspective); + py::class_(mod, "Camera", "A simple camera class, supporting transform matrices and arcball functionality for object-viewing applications.") + .def_static("create", &mx::Camera::create, "Create a new camera.") + .def("setWorldMatrix", &mx::Camera::setWorldMatrix, "Set the world matrix.") + .def("getWorldMatrix", &mx::Camera::getWorldMatrix, "Return the world matrix.") + .def("setViewMatrix", &mx::Camera::setViewMatrix, "Set the view matrix.") + .def("getViewMatrix", &mx::Camera::getViewMatrix, "Return the view matrix.") + .def("setProjectionMatrix", &mx::Camera::setProjectionMatrix, "Set the projection matrix.") + .def("getProjectionMatrix", &mx::Camera::getProjectionMatrix, "Return the projection matrix.") + .def("getWorldViewProjMatrix", &mx::Camera::getWorldViewProjMatrix, "Compute our full model-view-projection matrix.") + .def("getViewPosition", &mx::Camera::getViewPosition, "Derive viewer position from the view matrix.") + .def("getViewDirection", &mx::Camera::getViewDirection, "Derive viewer direction from the view matrix.") + .def("setViewportSize", &mx::Camera::setViewportSize, "Set the size of the viewport window.") + .def("getViewportSize", &mx::Camera::getViewportSize, "Return the size of the viewport window.") + .def("projectToViewport", &mx::Camera::projectToViewport, "Project a position from object to viewport space.") + .def("unprojectFromViewport", &mx::Camera::unprojectFromViewport, "Unproject a position from viewport to object space.") + .def_static("createViewMatrix", &mx::Camera::createViewMatrix, "Create a view matrix given an eye position, a target position and an up vector.") + .def_static("createPerspectiveMatrix", &mx::Camera::createPerspectiveMatrix, "Create a perspective projection matrix given a set of clip planes with [-1,1] projected Z.") + .def_static("createOrthographicMatrix", &mx::Camera::createOrthographicMatrix, "Create an orthographic projection matrix given a set of clip planes with [-1,1] projected Z.") + .def_static("transformPointPerspective", &mx::Camera::transformPointPerspective, "Apply a perspective transform to the given 3D point, performing a homogeneous divide on the transformed result."); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyCgltfLoader.cpp b/source/PyMaterialX/PyMaterialXRender/PyCgltfLoader.cpp index 158e6b4849..d70c6d82b3 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyCgltfLoader.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyCgltfLoader.cpp @@ -11,8 +11,8 @@ namespace mx = MaterialX; void bindPyCgltfLoader(py::module& mod) { - py::class_(mod, "CgltfLoader") - .def_static("create", &mx::CgltfLoader::create) + py::class_(mod, "CgltfLoader", "Wrapper for loader to read in GLTF files using the Cgltf library.") + .def_static("create", &mx::CgltfLoader::create, "Create a new loader.") .def(py::init<>()) - .def("load", &mx::CgltfLoader::load); + .def("load", &mx::CgltfLoader::load, "Load geometry from file path."); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyGeometryHandler.cpp b/source/PyMaterialX/PyMaterialXRender/PyGeometryHandler.cpp index 08560254bc..26192c43cc 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyGeometryHandler.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyGeometryHandler.cpp @@ -33,21 +33,21 @@ class PyGeometryLoader : public mx::GeometryLoader void bindPyGeometryHandler(py::module& mod) { - py::class_(mod, "GeometryLoader") + py::class_(mod, "GeometryLoader", "Base class representing a geometry loader.\n\nA loader can be associated with one or more file extensions.") .def(py::init<>()) - .def("supportedExtensions", &mx::GeometryLoader::supportedExtensions) - .def("load", &mx::GeometryLoader::load); + .def("supportedExtensions", &mx::GeometryLoader::supportedExtensions, "Returns a list of supported extensions.\n\nReturns:\n List of support extensions") + .def("load", &mx::GeometryLoader::load, "Load geometry from disk.\n\nArgs:\n filePath: Path to file to load\n meshList: List of meshes to update\n texcoordVerticalFlip: Flip texture coordinates in V when loading\n\nReturns:\n True if load was successful"); - py::class_(mod, "GeometryHandler") + py::class_(mod, "GeometryHandler", "Class which holds a set of geometry loaders.\n\nEach loader is associated with a given set of file extensions.") .def(py::init<>()) - .def_static("create", &mx::GeometryHandler::create) - .def("addLoader", &mx::GeometryHandler::addLoader) - .def("clearGeometry", &mx::GeometryHandler::clearGeometry) - .def("hasGeometry", &mx::GeometryHandler::hasGeometry) - .def("getGeometry", &mx::GeometryHandler::getGeometry) - .def("loadGeometry", &mx::GeometryHandler::loadGeometry) - .def("getMeshes", &mx::GeometryHandler::getMeshes) - .def("findParentMesh", &mx::GeometryHandler::findParentMesh) - .def("getMinimumBounds", &mx::GeometryHandler::getMinimumBounds) - .def("getMaximumBounds", &mx::GeometryHandler::getMaximumBounds); + .def_static("create", &mx::GeometryHandler::create, "Create a new geometry handler.") + .def("addLoader", &mx::GeometryHandler::addLoader, "Add a geometry loader.\n\nArgs:\n loader: Loader to add to list of available loaders.") + .def("clearGeometry", &mx::GeometryHandler::clearGeometry, "Clear all loaded geometry.") + .def("hasGeometry", &mx::GeometryHandler::hasGeometry, "") + .def("getGeometry", &mx::GeometryHandler::getGeometry, "") + .def("loadGeometry", &mx::GeometryHandler::loadGeometry, "Load geometry from a given location.\n\nArgs:\n filePath: Path to geometry\n texcoordVerticalFlip: Flip texture coordinates in V. Default is to not flip.") + .def("getMeshes", &mx::GeometryHandler::getMeshes, "Get list of meshes.") + .def("findParentMesh", &mx::GeometryHandler::findParentMesh, "Return the first mesh in our list containing the given partition.\n\nIf no matching mesh is found, then nullptr is returned.") + .def("getMinimumBounds", &mx::GeometryHandler::getMinimumBounds, "Return the minimum bounds for all meshes.") + .def("getMaximumBounds", &mx::GeometryHandler::getMaximumBounds, "Return the minimum bounds for all meshes."); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyImage.cpp b/source/PyMaterialX/PyMaterialXRender/PyImage.cpp index 2c9a932340..0b9db9ef3d 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyImage.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyImage.cpp @@ -21,33 +21,33 @@ void bindPyImage(py::module& mod) py::class_(mod, "ImageBufferDeallocator"); - py::class_(mod, "Image") - .def_static("create", &mx::Image::create) - .def("getWidth", &mx::Image::getWidth) - .def("getHeight", &mx::Image::getHeight) - .def("getChannelCount", &mx::Image::getChannelCount) - .def("getBaseType", &mx::Image::getBaseType) - .def("getBaseStride", &mx::Image::getBaseStride) - .def("getMaxMipCount", &mx::Image::getMaxMipCount) - .def("setTexelColor", &mx::Image::setTexelColor) - .def("getTexelColor", &mx::Image::getTexelColor) - .def("isUniformColor", &mx::Image::isUniformColor) - .def("setUniformColor", &mx::Image::setUniformColor) - .def("applyMatrixTransform", &mx::Image::applyMatrixTransform) - .def("applyGammaTransform", &mx::Image::applyGammaTransform) - .def("copy", &mx::Image::copy) - .def("applyBoxBlur", &mx::Image::applyBoxBlur) - .def("applyGaussianBlur", &mx::Image::applyGaussianBlur) - .def("applyBoxDownsample", &mx::Image::applyBoxDownsample) - .def("splitByLuminance", &mx::Image::splitByLuminance) - .def("setResourceBuffer", &mx::Image::setResourceBuffer) - .def("getResourceBuffer", &mx::Image::getResourceBuffer) - .def("createResourceBuffer", &mx::Image::createResourceBuffer) - .def("releaseResourceBuffer", &mx::Image::releaseResourceBuffer) - .def("setResourceBufferDeallocator", &mx::Image::setResourceBufferDeallocator) - .def("getResourceBufferDeallocator", &mx::Image::getResourceBufferDeallocator); + py::class_(mod, "Image", "Class representing an image in system memory.") + .def_static("create", &mx::Image::create, "Create an empty image with the given properties.") + .def("getWidth", &mx::Image::getWidth, "Return the width of the image.") + .def("getHeight", &mx::Image::getHeight, "Return the height of the image.") + .def("getChannelCount", &mx::Image::getChannelCount, "Return the channel count of the image.") + .def("getBaseType", &mx::Image::getBaseType, "Return the base type of the image.") + .def("getBaseStride", &mx::Image::getBaseStride, "Return the stride of our base type in bytes.") + .def("getMaxMipCount", &mx::Image::getMaxMipCount, "Return the maximum number of mipmaps for this image.") + .def("setTexelColor", &mx::Image::setTexelColor, "Set the texel color at the given coordinates.\n\nIf the coordinates or image resource buffer are invalid, then an exception is thrown.") + .def("getTexelColor", &mx::Image::getTexelColor, "Return the texel color at the given coordinates.\n\nIf the coordinates or image resource buffer are invalid, then an exception is thrown.") + .def("isUniformColor", &mx::Image::isUniformColor, "Return true if all texels of this image are identical in color.\n\nArgs:\n uniformColor: Return the uniform color of the image, if any.") + .def("setUniformColor", &mx::Image::setUniformColor, "Set all texels of this image to a uniform color.") + .def("applyMatrixTransform", &mx::Image::applyMatrixTransform, "Apply the given matrix transform to all texels of this image.") + .def("applyGammaTransform", &mx::Image::applyGammaTransform, "Apply the given gamma transform to all texels of this image.") + .def("copy", &mx::Image::copy, "Create a copy of this image with the given channel count and base type.") + .def("applyBoxBlur", &mx::Image::applyBoxBlur, "Apply a 3x3 box blur to this image, returning a new blurred image.") + .def("applyGaussianBlur", &mx::Image::applyGaussianBlur, "Apply a 7x7 Gaussian blur to this image, returning a new blurred image.") + .def("applyBoxDownsample", &mx::Image::applyBoxDownsample, "Downsample this image by an integer factor using a box filter, returning the new reduced image.") + .def("splitByLuminance", &mx::Image::splitByLuminance, "Split this image by the given luminance threshold, returning the resulting underflow and overflow images.") + .def("setResourceBuffer", &mx::Image::setResourceBuffer, "Set the resource buffer for this image.") + .def("getResourceBuffer", &mx::Image::getResourceBuffer, "Return the resource buffer for this image.") + .def("createResourceBuffer", &mx::Image::createResourceBuffer, "Allocate a resource buffer for this image that matches its properties.") + .def("releaseResourceBuffer", &mx::Image::releaseResourceBuffer, "Release the resource buffer for this image.") + .def("setResourceBufferDeallocator", &mx::Image::setResourceBufferDeallocator, "Set the resource buffer deallocator for this image.") + .def("getResourceBufferDeallocator", &mx::Image::getResourceBufferDeallocator, "Return the resource buffer deallocator for this image."); - mod.def("createUniformImage", &mx::createUniformImage); - mod.def("createImageStrip", &mx::createImageStrip); - mod.def("getMaxDimensions", &mx::getMaxDimensions); + mod.def("createUniformImage", &mx::createUniformImage, "Create a uniform-color image with the given properties."); + mod.def("createImageStrip", &mx::createImageStrip, "Create a horizontal image strip from a vector of images with identical resolutions and formats."); + mod.def("getMaxDimensions", &mx::getMaxDimensions, "Compute the maximum width and height of all images in the given vector."); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyImageHandler.cpp b/source/PyMaterialX/PyMaterialXRender/PyImageHandler.cpp index d261bc7306..ae23dfc299 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyImageHandler.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyImageHandler.cpp @@ -12,13 +12,13 @@ namespace mx = MaterialX; void bindPyImageHandler(py::module& mod) { - py::class_(mod, "ImageSamplingProperties") + py::class_(mod, "ImageSamplingProperties", "Interface to describe sampling properties for images.") .def_readwrite("uaddressMode", &mx::ImageSamplingProperties::uaddressMode) .def_readwrite("vaddressMode", &mx::ImageSamplingProperties::vaddressMode) .def_readwrite("filterType", &mx::ImageSamplingProperties::filterType) .def_readwrite("defaultColor", &mx::ImageSamplingProperties::defaultColor); - py::class_(mod, "ImageLoader") + py::class_(mod, "ImageLoader", "Abstract base class for file-system image loaders.") .def_readonly_static("BMP_EXTENSION", &mx::ImageLoader::BMP_EXTENSION) .def_readonly_static("EXR_EXTENSION", &mx::ImageLoader::EXR_EXTENSION) .def_readonly_static("GIF_EXTENSION", &mx::ImageLoader::GIF_EXTENSION) @@ -32,28 +32,25 @@ void bindPyImageHandler(py::module& mod) .def_readonly_static("TIF_EXTENSION", &mx::ImageLoader::TIF_EXTENSION) .def_readonly_static("TIFF_EXTENSION", &mx::ImageLoader::TIFF_EXTENSION) .def_readonly_static("TXT_EXTENSION", &mx::ImageLoader::TXT_EXTENSION) - .def("supportedExtensions", &mx::ImageLoader::supportedExtensions) - .def("saveImage", &mx::ImageLoader::saveImage) - .def("loadImage", &mx::ImageLoader::loadImage); + .def("supportedExtensions", &mx::ImageLoader::supportedExtensions, "Returns a list of supported extensions.\n\nReturns:\n List of support extensions") + .def("saveImage", &mx::ImageLoader::saveImage, "Save an image to the file system.\n\nArgs:\n filePath: File path to be written\n image: The image to be saved\n verticalFlip: Whether the image should be flipped in Y during save\n\nReturns:\n if save succeeded") + .def("loadImage", &mx::ImageLoader::loadImage, "Load an image from the file system.\n\nArgs:\n filePath: The requested image file path.\n\nReturns:\n On success, a shared pointer to the loaded image; otherwise an empty shared pointer."); - py::class_(mod, "ImageHandler") - .def_static("create", &mx::ImageHandler::create) - .def("addLoader", &mx::ImageHandler::addLoader) - .def("saveImage", &mx::ImageHandler::saveImage, - py::arg("filePath"), py::arg("image"), py::arg("verticalFlip") = false) - .def("acquireImage", &mx::ImageHandler::acquireImage, - py::arg("filePath"), py::arg("defaultColor") = mx::Color4(0.0f)) - .def("bindImage", &mx::ImageHandler::bindImage) - .def("unbindImage", &mx::ImageHandler::unbindImage) - .def("unbindImages", &mx::ImageHandler::unbindImages) - .def("setSearchPath", &mx::ImageHandler::setSearchPath) - .def("getSearchPath", &mx::ImageHandler::getSearchPath) - .def("setFilenameResolver", &mx::ImageHandler::setFilenameResolver) - .def("getFilenameResolver", &mx::ImageHandler::getFilenameResolver) - .def("createRenderResources", &mx::ImageHandler::createRenderResources) - .def("releaseRenderResources", &mx::ImageHandler::releaseRenderResources, - py::arg("image") = nullptr) - .def("clearImageCache", &mx::ImageHandler::clearImageCache) - .def("getZeroImage", &mx::ImageHandler::getZeroImage) - .def("getReferencedImages", &mx::ImageHandler::getReferencedImages); + py::class_(mod, "ImageHandler", "Base image handler class.\n\nKeeps track of images which are loaded from disk via supplied ImageLoader. Derived classes are responsible for determining how to perform the logic for \"binding\" of these resources for a given target (such as a given shading language).") + .def_static("create", &mx::ImageHandler::create, "") + .def("addLoader", &mx::ImageHandler::addLoader, "Add another image loader to the handler, which will be invoked if existing loaders cannot load a given image.") + .def("saveImage", &mx::ImageHandler::saveImage, py::arg("filePath"), py::arg("image"), py::arg("verticalFlip") = false, "Save image to disk.\n\nArgs:\n filePath: File path to be written\n image: The image to be saved\n verticalFlip: Whether the image should be flipped in Y during save\n\nReturns:\n if save succeeded") + .def("acquireImage", &mx::ImageHandler::acquireImage, py::arg("filePath"), py::arg("defaultColor") = mx::Color4(0.0f), "Acquire an image from the cache or file system.\n\nArgs:\n filePath: File path of the image.\n defaultColor: Default color to use as a fallback for missing images.\n\nReturns:\n On success, a shared pointer to the acquired image.") + .def("bindImage", &mx::ImageHandler::bindImage, "Bind an image for rendering.\n\nArgs:\n image: The image to bind.\n samplingProperties: Sampling properties for the image.") + .def("unbindImage", &mx::ImageHandler::unbindImage, "Unbind an image, making it no longer active for rendering.\n\nArgs:\n image: The image to unbind.") + .def("unbindImages", &mx::ImageHandler::unbindImages, "Unbind all images that are currently stored in the cache.") + .def("setSearchPath", &mx::ImageHandler::setSearchPath, "Set the search path to be used for finding images on the file system.") + .def("getSearchPath", &mx::ImageHandler::getSearchPath, "Return the image search path.") + .def("setFilenameResolver", &mx::ImageHandler::setFilenameResolver, "Set the filename resolver for images.") + .def("getFilenameResolver", &mx::ImageHandler::getFilenameResolver, "Return the filename resolver for images.") + .def("createRenderResources", &mx::ImageHandler::createRenderResources, "Create rendering resources for the given image.") + .def("releaseRenderResources", &mx::ImageHandler::releaseRenderResources, py::arg("image") = nullptr, "Release rendering resources for the given image, or for all cached images if no image pointer is specified.") + .def("clearImageCache", &mx::ImageHandler::clearImageCache, "Clear the contents of the image cache, first releasing any render resources associated with cached images.") + .def("getZeroImage", &mx::ImageHandler::getZeroImage, "Return a fallback image with zeroes in all channels.") + .def("getReferencedImages", &mx::ImageHandler::getReferencedImages, "Acquire all images referenced by the given document, and return the images in a vector."); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyLightHandler.cpp b/source/PyMaterialX/PyMaterialXRender/PyLightHandler.cpp index 50725a7768..a2043d63ec 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyLightHandler.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyLightHandler.cpp @@ -14,31 +14,31 @@ namespace mx = MaterialX; void bindPyLightHandler(py::module& mod) { - py::class_(mod, "LightHandler") - .def_static("create", &mx::LightHandler::create) + py::class_(mod, "LightHandler", "Utility light handler for creating and providing light data for shader binding.") + .def_static("create", &mx::LightHandler::create, "Create a new light handler.") .def(py::init<>()) - .def("setLightTransform", &mx::LightHandler::setLightTransform) - .def("getLightTransform", &mx::LightHandler::getLightTransform) - .def("setDirectLighting", &mx::LightHandler::setDirectLighting) - .def("getDirectLighting", &mx::LightHandler::getDirectLighting) - .def("setIndirectLighting", &mx::LightHandler::setIndirectLighting) - .def("getIndirectLighting", &mx::LightHandler::getIndirectLighting) - .def("setEnvRadianceMap", &mx::LightHandler::setEnvRadianceMap) - .def("getEnvRadianceMap", &mx::LightHandler::getEnvRadianceMap) - .def("setEnvIrradianceMap", &mx::LightHandler::setEnvIrradianceMap) - .def("getEnvIrradianceMap", &mx::LightHandler::getEnvIrradianceMap) - .def("setAlbedoTable", &mx::LightHandler::setAlbedoTable) - .def("getAlbedoTable", &mx::LightHandler::getAlbedoTable) - .def("setEnvSampleCount", &mx::LightHandler::setEnvSampleCount) - .def("getEnvSampleCount", &mx::LightHandler::getEnvSampleCount) - .def("setRefractionTwoSided", &mx::LightHandler::setRefractionTwoSided) - .def("getRefractionTwoSided", &mx::LightHandler::getRefractionTwoSided) - .def("addLightSource", &mx::LightHandler::addLightSource) - .def("setLightSources", &mx::LightHandler::setLightSources) - .def("getLightSources", &mx::LightHandler::getLightSources) - .def("getFirstLightOfCategory", &mx::LightHandler::getFirstLightOfCategory) - .def("getLightIdMap", &mx::LightHandler::getLightIdMap) - .def("computeLightIdMap", &mx::LightHandler::computeLightIdMap) - .def("findLights", &mx::LightHandler::findLights) - .def("registerLights", &mx::LightHandler::registerLights); + .def("setLightTransform", &mx::LightHandler::setLightTransform, "Set the light transform.") + .def("getLightTransform", &mx::LightHandler::getLightTransform, "Return the light transform.") + .def("setDirectLighting", &mx::LightHandler::setDirectLighting, "Set whether direct lighting is enabled.") + .def("getDirectLighting", &mx::LightHandler::getDirectLighting, "Return whether direct lighting is enabled.") + .def("setIndirectLighting", &mx::LightHandler::setIndirectLighting, "Set whether indirect lighting is enabled.") + .def("getIndirectLighting", &mx::LightHandler::getIndirectLighting, "Return whether indirect lighting is enabled.") + .def("setEnvRadianceMap", &mx::LightHandler::setEnvRadianceMap, "Set the environment radiance map.") + .def("getEnvRadianceMap", &mx::LightHandler::getEnvRadianceMap, "Return the environment radiance map.") + .def("setEnvIrradianceMap", &mx::LightHandler::setEnvIrradianceMap, "Set the environment irradiance map.") + .def("getEnvIrradianceMap", &mx::LightHandler::getEnvIrradianceMap, "Return the environment irradiance map.") + .def("setAlbedoTable", &mx::LightHandler::setAlbedoTable, "Set the directional albedo table.") + .def("getAlbedoTable", &mx::LightHandler::getAlbedoTable, "Return the directional albedo table.") + .def("setEnvSampleCount", &mx::LightHandler::setEnvSampleCount, "Set the environment lighting sample count.") + .def("getEnvSampleCount", &mx::LightHandler::getEnvSampleCount, "Return the environment lighting sample count.") + .def("setRefractionTwoSided", &mx::LightHandler::setRefractionTwoSided, "Set the two-sided refraction property.") + .def("getRefractionTwoSided", &mx::LightHandler::getRefractionTwoSided, "Return the two-sided refraction property.") + .def("addLightSource", &mx::LightHandler::addLightSource, "Add a light source.") + .def("setLightSources", &mx::LightHandler::setLightSources, "Set the vector of light sources.") + .def("getLightSources", &mx::LightHandler::getLightSources, "Return the vector of light sources.") + .def("getFirstLightOfCategory", &mx::LightHandler::getFirstLightOfCategory, "Return the first light source, if any, of the given category.") + .def("getLightIdMap", &mx::LightHandler::getLightIdMap, "Get a list of identifiers associated with a given light nodedef.") + .def("computeLightIdMap", &mx::LightHandler::computeLightIdMap, "From a set of nodes, create a mapping of corresponding nodedef identifiers to numbers.") + .def("findLights", &mx::LightHandler::findLights, "Find lights to use based on an input document.\n\nArgs:\n doc: Document to scan for lights\n lights: List of lights found in document") + .def("registerLights", &mx::LightHandler::registerLights, "Register light node definitions and light count with a given generation context.\n\nArgs:\n doc: Document containing light nodes and definitions\n lights: Lights to register\n context: Context to update"); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyMesh.cpp b/source/PyMaterialX/PyMaterialXRender/PyMesh.cpp index 5c26821436..9af79eefe5 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyMesh.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyMesh.cpp @@ -12,7 +12,7 @@ namespace mx = MaterialX; void bindPyMesh(py::module& mod) { - py::class_(mod, "MeshStream") + py::class_(mod, "MeshStream", "Class to represent a mesh data stream.") .def_readonly_static("POSITION_ATTRIBUTE", &mx::MeshStream::POSITION_ATTRIBUTE) .def_readonly_static("NORMAL_ATTRIBUTE", &mx::MeshStream::NORMAL_ATTRIBUTE) .def_readonly_static("TEXCOORD_ATTRIBUTE", &mx::MeshStream::TEXCOORD_ATTRIBUTE) @@ -20,58 +20,58 @@ void bindPyMesh(py::module& mod) .def_readonly_static("BITANGENT_ATTRIBUTE", &mx::MeshStream::BITANGENT_ATTRIBUTE) .def_readonly_static("COLOR_ATTRIBUTE", &mx::MeshStream::COLOR_ATTRIBUTE) .def_readonly_static("GEOMETRY_PROPERTY_ATTRIBUTE", &mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE) - .def_static("create", &mx::MeshStream::create) + .def_static("create", &mx::MeshStream::create, "Create a new mesh stream.") .def(py::init()) - .def("reserve", &mx::MeshStream::reserve) - .def("resize", &mx::MeshStream::resize) - .def("getName", &mx::MeshStream::getName) - .def("getType", &mx::MeshStream::getType) - .def("getIndex", &mx::MeshStream::getIndex) - .def("getData", static_cast(&mx::MeshStream::getData), py::return_value_policy::reference) - .def("getStride", &mx::MeshStream::getStride) - .def("setStride", &mx::MeshStream::setStride) - .def("getSize", &mx::MeshStream::getSize) - .def("transform", &mx::MeshStream::transform); + .def("reserve", &mx::MeshStream::reserve, "Reserve memory for a given number of elements.") + .def("resize", &mx::MeshStream::resize, "Resize data to an given number of elements.") + .def("getName", &mx::MeshStream::getName, "Get stream name.") + .def("getType", &mx::MeshStream::getType, "Get stream attribute name.") + .def("getIndex", &mx::MeshStream::getIndex, "Get stream index.") + .def("getData", static_cast(&mx::MeshStream::getData), py::return_value_policy::reference, "Return the raw float vector.") + .def("getStride", &mx::MeshStream::getStride, "Get stride between elements.") + .def("setStride", &mx::MeshStream::setStride, "Set stride between elements.") + .def("getSize", &mx::MeshStream::getSize, "Get the number of elements.") + .def("transform", &mx::MeshStream::transform, "Transform elements by a matrix."); - py::class_(mod, "MeshPartition") - .def_static("create", &mx::MeshPartition::create) + py::class_(mod, "MeshPartition", "Class that describes a sub-region of a mesh using vertex indexing.\n\nNote that a face is considered to be a triangle.") + .def_static("create", &mx::MeshPartition::create, "Create a new mesh partition.") .def(py::init<>()) - .def("resize", &mx::MeshPartition::resize) - .def("setName", &mx::MeshPartition::setName) - .def("getName", &mx::MeshPartition::getName) - .def("addSourceName", &mx::MeshPartition::addSourceName) - .def("getSourceNames", &mx::MeshPartition::getSourceNames) - .def("getIndices", static_cast(&mx::MeshPartition::getIndices), py::return_value_policy::reference) - .def("getFaceCount", &mx::MeshPartition::getFaceCount) - .def("setFaceCount", &mx::MeshPartition::setFaceCount); + .def("resize", &mx::MeshPartition::resize, "Resize data to the given number of indices.") + .def("setName", &mx::MeshPartition::setName, "Set the name of this partition.") + .def("getName", &mx::MeshPartition::getName, "Return the name of this partition.") + .def("addSourceName", &mx::MeshPartition::addSourceName, "Add a source name, representing a partition that was processed to generate this one.") + .def("getSourceNames", &mx::MeshPartition::getSourceNames, "Return the vector of source names, representing all partitions that were processed to generate this one.") + .def("getIndices", static_cast(&mx::MeshPartition::getIndices), py::return_value_policy::reference, "Return indexing.") + .def("getFaceCount", &mx::MeshPartition::getFaceCount, "Return number of faces.") + .def("setFaceCount", &mx::MeshPartition::setFaceCount, "Set face count."); - py::class_(mod, "Mesh") - .def_static("create", &mx::Mesh::create) + py::class_(mod, "Mesh", "Container for mesh data.") + .def_static("create", &mx::Mesh::create, "Create a new mesh.") .def(py::init()) - .def("getName", &mx::Mesh::getName) - .def("setSourceUri", &mx::Mesh::setSourceUri) - .def("hasSourceUri", &mx::Mesh::hasSourceUri) - .def("getSourceUri", &mx::Mesh::getSourceUri) - .def("getStream", static_cast(&mx::Mesh::getStream)) - .def("getStream", static_cast (&mx::Mesh::getStream)) - .def("addStream", &mx::Mesh::addStream) - .def("setVertexCount", &mx::Mesh::setVertexCount) - .def("getVertexCount", &mx::Mesh::getVertexCount) - .def("setMinimumBounds", &mx::Mesh::setMinimumBounds) - .def("getMinimumBounds", &mx::Mesh::getMinimumBounds) - .def("setMaximumBounds", &mx::Mesh::setMaximumBounds) - .def("getMaximumBounds", &mx::Mesh::getMaximumBounds) - .def("setSphereCenter", &mx::Mesh::setSphereCenter) - .def("getSphereCenter", &mx::Mesh::getSphereCenter) - .def("setSphereRadius", &mx::Mesh::setSphereRadius) - .def("getSphereRadius", &mx::Mesh::getSphereRadius) - .def("getPartitionCount", &mx::Mesh::getPartitionCount) - .def("addPartition", &mx::Mesh::addPartition) - .def("getPartition", &mx::Mesh::getPartition) - .def("generateTextureCoordinates", &mx::Mesh::generateTextureCoordinates) - .def("generateNormals", &mx::Mesh::generateNormals) - .def("generateTangents", &mx::Mesh::generateTangents) - .def("generateBitangents", &mx::Mesh::generateBitangents) - .def("mergePartitions", &mx::Mesh::mergePartitions) - .def("splitByUdims", &mx::Mesh::splitByUdims); + .def("getName", &mx::Mesh::getName, "Return the name of this mesh.") + .def("setSourceUri", &mx::Mesh::setSourceUri, "Set the mesh's source URI.") + .def("hasSourceUri", &mx::Mesh::hasSourceUri, "Return true if this mesh has a source URI.") + .def("getSourceUri", &mx::Mesh::getSourceUri, "Return the mesh's source URI.") + .def("getStream", static_cast(&mx::Mesh::getStream), "Get a mesh stream by type and index.\n\nArgs:\n type: Type of stream\n index: Index of stream\n\nReturns:\n Reference to a mesh stream if found") + .def("getStream", static_cast (&mx::Mesh::getStream), "Get a mesh stream by type and index.\n\nArgs:\n type: Type of stream\n index: Index of stream\n\nReturns:\n Reference to a mesh stream if found") + .def("addStream", &mx::Mesh::addStream, "Add a mesh stream.") + .def("setVertexCount", &mx::Mesh::setVertexCount, "Set vertex count.") + .def("getVertexCount", &mx::Mesh::getVertexCount, "Get vertex count.") + .def("setMinimumBounds", &mx::Mesh::setMinimumBounds, "Set the minimum bounds for the geometry.") + .def("getMinimumBounds", &mx::Mesh::getMinimumBounds, "Return the minimum bounds for the geometry.") + .def("setMaximumBounds", &mx::Mesh::setMaximumBounds, "Set the minimum bounds for the geometry.") + .def("getMaximumBounds", &mx::Mesh::getMaximumBounds, "Return the minimum bounds for the geometry.") + .def("setSphereCenter", &mx::Mesh::setSphereCenter, "Set center of the bounding sphere.") + .def("getSphereCenter", &mx::Mesh::getSphereCenter, "Return center of the bounding sphere.") + .def("setSphereRadius", &mx::Mesh::setSphereRadius, "Set radius of the bounding sphere.") + .def("getSphereRadius", &mx::Mesh::getSphereRadius, "Return radius of the bounding sphere.") + .def("getPartitionCount", &mx::Mesh::getPartitionCount, "Return the number of mesh partitions.") + .def("addPartition", &mx::Mesh::addPartition, "Add a partition.") + .def("getPartition", &mx::Mesh::getPartition, "Return a reference to a mesh partition.") + .def("generateTextureCoordinates", &mx::Mesh::generateTextureCoordinates, "Create texture coordinates from the given positions.\n\nArgs:\n positionStream: Input position stream\n\nReturns:\n The generated texture coordinate stream") + .def("generateNormals", &mx::Mesh::generateNormals, "Generate face normals from the given positions.\n\nArgs:\n positionStream: Input position stream\n\nReturns:\n The generated normal stream") + .def("generateTangents", &mx::Mesh::generateTangents, "Generate tangents from the given positions, normals, and texture coordinates.\n\nArgs:\n positionStream: Input position stream\n normalStream: Input normal stream\n texcoordStream: Input texcoord stream\n\nReturns:\n The generated tangent stream, on success; otherwise, a null pointer.") + .def("generateBitangents", &mx::Mesh::generateBitangents, "Generate bitangents from the given normals and tangents.\n\nArgs:\n normalStream: Input normal stream\n tangentStream: Input tangent stream\n\nReturns:\n The generated bitangent stream, on success; otherwise, a null pointer.") + .def("mergePartitions", &mx::Mesh::mergePartitions, "Merge all mesh partitions into one.") + .def("splitByUdims", &mx::Mesh::splitByUdims, "Split the mesh into a single partition per UDIM."); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyOiioImageLoader.cpp b/source/PyMaterialX/PyMaterialXRender/PyOiioImageLoader.cpp index 387af2714d..ee18b4bb2e 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyOiioImageLoader.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyOiioImageLoader.cpp @@ -13,8 +13,8 @@ namespace mx = MaterialX; void bindPyOiioImageLoader(py::module& mod) { py::class_(mod, "OiioImageLoader") - .def_static("create", &mx::OiioImageLoader::create) + .def_static("create", &mx::OiioImageLoader::create, "Creator function.\n\nIf a TypeSystem is not provided it will be created internally. Optionally pass in an externally created TypeSystem here, if you want to keep type descriptions alive after the lifetime of the shader generator.") .def(py::init<>()) - .def("saveImage", &mx::OiioImageLoader::saveImage) - .def("loadImage", &mx::OiioImageLoader::loadImage); + .def("saveImage", &mx::OiioImageLoader::saveImage, "Save an image to the file system.\n\nArgs:\n filePath: File path to be written\n image: The image to be saved\n verticalFlip: Whether the image should be flipped in Y during save\n\nReturns:\n if save succeeded") + .def("loadImage", &mx::OiioImageLoader::loadImage, "Load an image from the file system.\n\nArgs:\n filePath: The requested image file path.\n\nReturns:\n On success, a shared pointer to the loaded image; otherwise an empty shared pointer."); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp b/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp index 94c0d49cee..365bbc8907 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp @@ -12,22 +12,22 @@ namespace mx = MaterialX; void bindPyShaderRenderer(py::module& mod) { - py::class_(mod, "ShaderRenderer") - .def("initialize", &mx::ShaderRenderer::initialize, py::arg("renderContextHandle") = nullptr) - .def("setCamera", &mx::ShaderRenderer::setCamera) - .def("getCamera", &mx::ShaderRenderer::getCamera) - .def("setImageHandler", &mx::ShaderRenderer::setImageHandler) - .def("getImageHandler", &mx::ShaderRenderer::getImageHandler) - .def("setLightHandler", &mx::ShaderRenderer::setLightHandler) - .def("getLightHandler", &mx::ShaderRenderer::getLightHandler) - .def("setGeometryHandler", &mx::ShaderRenderer::setGeometryHandler) - .def("getGeometryHandler", &mx::ShaderRenderer::getGeometryHandler) - .def("createProgram", static_cast(&mx::ShaderRenderer::createProgram)) - .def("createProgram", static_cast(&mx::ShaderRenderer::createProgram)) - .def("validateInputs", &mx::ShaderRenderer::validateInputs) - .def("updateUniform", &mx::ShaderRenderer::updateUniform) - .def("setSize", &mx::ShaderRenderer::setSize) - .def("render", &mx::ShaderRenderer::render); + py::class_(mod, "ShaderRenderer", "Base class for renderers that generate shader code to produce images.") + .def("initialize", &mx::ShaderRenderer::initialize, py::arg("renderContextHandle") = nullptr, "Initialize the renderer.") + .def("setCamera", &mx::ShaderRenderer::setCamera, "Set the camera.") + .def("getCamera", &mx::ShaderRenderer::getCamera, "Return the camera.") + .def("setImageHandler", &mx::ShaderRenderer::setImageHandler, "Set the image handler used by this renderer for image I/O.") + .def("getImageHandler", &mx::ShaderRenderer::getImageHandler, "Return the image handler.") + .def("setLightHandler", &mx::ShaderRenderer::setLightHandler, "Set the light handler used by this renderer for light bindings.") + .def("getLightHandler", &mx::ShaderRenderer::getLightHandler, "Return the light handler.") + .def("setGeometryHandler", &mx::ShaderRenderer::setGeometryHandler, "Set the geometry handler.") + .def("getGeometryHandler", &mx::ShaderRenderer::getGeometryHandler, "Return the geometry handler.") + .def("createProgram", static_cast(&mx::ShaderRenderer::createProgram), "Create program based on shader stage source code.\n\nArgs:\n stages: Map of name and source code for the shader stages.") + .def("createProgram", static_cast(&mx::ShaderRenderer::createProgram), "Create program based on shader stage source code.\n\nArgs:\n stages: Map of name and source code for the shader stages.") + .def("validateInputs", &mx::ShaderRenderer::validateInputs, "Validate inputs for the program.") + .def("updateUniform", &mx::ShaderRenderer::updateUniform, "Update the program with value of the uniform.") + .def("setSize", &mx::ShaderRenderer::setSize, "Set the size of the rendered image.") + .def("render", &mx::ShaderRenderer::render, "Render the current program to produce an image."); static py::exception pyExceptionRenderError(mod, "ExceptionRenderError"); diff --git a/source/PyMaterialX/PyMaterialXRender/PyStbImageLoader.cpp b/source/PyMaterialX/PyMaterialXRender/PyStbImageLoader.cpp index 4c710746c1..16e04f8440 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyStbImageLoader.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyStbImageLoader.cpp @@ -12,8 +12,8 @@ namespace mx = MaterialX; void bindPyStbImageLoader(py::module& mod) { - py::class_(mod, "StbImageLoader") - .def_static("create", &mx::StbImageLoader::create) - .def("saveImage", &mx::StbImageLoader::saveImage) - .def("loadImage", &mx::StbImageLoader::loadImage); + py::class_(mod, "StbImageLoader", "Stb image file loader.") + .def_static("create", &mx::StbImageLoader::create, "Create a new stb image loader.") + .def("saveImage", &mx::StbImageLoader::saveImage, "Save an image to the file system.") + .def("loadImage", &mx::StbImageLoader::loadImage, "Load an image from the file system."); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyTinyObjLoader.cpp b/source/PyMaterialX/PyMaterialXRender/PyTinyObjLoader.cpp index 13677453b9..b3e8e26499 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyTinyObjLoader.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyTinyObjLoader.cpp @@ -12,8 +12,8 @@ namespace mx = MaterialX; void bindPyTinyObjLoader(py::module& mod) { - py::class_(mod, "TinyObjLoader") - .def_static("create", &mx::TinyObjLoader::create) + py::class_(mod, "TinyObjLoader", "Wrapper for geometry loader to read in OBJ files using the TinyObj library.") + .def_static("create", &mx::TinyObjLoader::create, "Create a new TinyObjLoader.") .def(py::init<>()) - .def("load", &mx::TinyObjLoader::load); + .def("load", &mx::TinyObjLoader::load, "Load geometry from disk."); } diff --git a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp index 9ebbd93ecb..d7d920a77a 100644 --- a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp +++ b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp @@ -12,11 +12,10 @@ namespace mx = MaterialX; void bindPyGLTextureHandler(py::module& mod) { - py::class_(mod, "GLTextureHandler") - .def_static("create", &mx::GLTextureHandler::create) - .def("bindImage", &mx::GLTextureHandler::bindImage) - .def("unbindImage", &mx::GLTextureHandler::unbindImage) - .def("createRenderResources", &mx::GLTextureHandler::createRenderResources) - .def("releaseRenderResources", &mx::GLTextureHandler::releaseRenderResources, - py::arg("image") = nullptr); + py::class_(mod, "GLTextureHandler", "An OpenGL texture handler class.") + .def_static("create", &mx::GLTextureHandler::create, "") + .def("bindImage", &mx::GLTextureHandler::bindImage, "Bind an image.\n\nThis method will bind the texture to an active texture unit as defined by the corresponding image description. The method will fail if there are not enough available image units to bind to.") + .def("unbindImage", &mx::GLTextureHandler::unbindImage, "Unbind an image.") + .def("createRenderResources", &mx::GLTextureHandler::createRenderResources, "Create rendering resources for the given image.") + .def("releaseRenderResources", &mx::GLTextureHandler::releaseRenderResources, py::arg("image") = nullptr, "Release rendering resources for the given image, or for all cached images if no image pointer is specified."); } diff --git a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslProgram.cpp b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslProgram.cpp index f0b7f59f0f..9c7a842183 100644 --- a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslProgram.cpp +++ b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslProgram.cpp @@ -12,35 +12,34 @@ namespace mx = MaterialX; void bindPyGlslProgram(py::module& mod) { - py::class_(mod, "GlslProgram") + py::class_(mod, "GlslProgram", "A class representing an executable GLSL program.\n\nThere are two main interfaces which can be used. One which takes in a HwShader and one which allows for explicit setting of shader stage code.") .def_readwrite_static("UNDEFINED_OPENGL_RESOURCE_ID", &mx::GlslProgram::UNDEFINED_OPENGL_RESOURCE_ID) .def_readwrite_static("UNDEFINED_OPENGL_PROGRAM_LOCATION", &mx::GlslProgram::UNDEFINED_OPENGL_PROGRAM_LOCATION) - .def_static("create", &mx::GlslProgram::create) - .def("setStages", &mx::GlslProgram::setStages) - .def("addStage", &mx::GlslProgram::addStage) - .def("getStageSourceCode", &mx::GlslProgram::getStageSourceCode) - .def("getShader", &mx::GlslProgram::getShader) - .def("build", &mx::GlslProgram::build) - .def("hasBuiltData", &mx::GlslProgram::hasBuiltData) - .def("clearBuiltData", &mx::GlslProgram::clearBuiltData) - .def("getUniformsList", &mx::GlslProgram::getUniformsList) - .def("getAttributesList", &mx::GlslProgram::getAttributesList) - .def("findInputs", &mx::GlslProgram::findInputs) - .def("bind", &mx::GlslProgram::bind) - .def("hasActiveAttributes", &mx::GlslProgram::hasActiveAttributes) - .def("bindUniform", &mx::GlslProgram::bindUniform) - .def("bindAttribute", &mx::GlslProgram::bindAttribute) - .def("bindPartition", &mx::GlslProgram::bindPartition) - .def("bindMesh", &mx::GlslProgram::bindMesh) - .def("unbindGeometry", &mx::GlslProgram::unbindGeometry) - .def("bindTextures", &mx::GlslProgram::bindTextures) - .def("bindLighting", &mx::GlslProgram::bindLighting) - .def("bindViewInformation", &mx::GlslProgram::bindViewInformation) - .def("bindTimeAndFrame", &mx::GlslProgram::bindTimeAndFrame, - py::arg("time") = 0.0f, py::arg("frame") = 1.0f) - .def("unbind", &mx::GlslProgram::unbind); + .def_static("create", &mx::GlslProgram::create, "Create a GLSL program instance.") + .def("setStages", &mx::GlslProgram::setStages, "Set up code stages to validate based on an input hardware shader.\n\nArgs:\n shader: Hardware shader to use") + .def("addStage", &mx::GlslProgram::addStage, "Set the code stages based on a list of stage strings.\n\nArgs:\n stage: Name of the shader stage.\n sourceCode: Source code of the shader stage.") + .def("getStageSourceCode", &mx::GlslProgram::getStageSourceCode, "Get source code string for a given stage.\n\nReturns:\n Shader stage string. String is empty if not found.") + .def("getShader", &mx::GlslProgram::getShader, "Return the shader, if any, used to generate this program.") + .def("build", &mx::GlslProgram::build, "Build shader program data from the source code set for each shader stage.\n\nAn exception is thrown if the program cannot be built. The exception will contain a list of compilation errors.") + .def("hasBuiltData", &mx::GlslProgram::hasBuiltData, "Return true if built shader program data is present.") + .def("clearBuiltData", &mx::GlslProgram::clearBuiltData, "") + .def("getUniformsList", &mx::GlslProgram::getUniformsList, "Get list of program input uniforms.\n\nReturns:\n Program uniforms list.") + .def("getAttributesList", &mx::GlslProgram::getAttributesList, "Get list of program input attributes.\n\nReturns:\n Program attributes list.") + .def("findInputs", &mx::GlslProgram::findInputs, "Find the locations in the program which starts with a given variable name.\n\nArgs:\n variable: Variable to search for\n variableList: List of program inputs to search\n foundList: Returned list of found program inputs. Empty if none found.\n exactMatch: Search for exact variable name match.") + .def("bind", &mx::GlslProgram::bind, "Bind the program.\n\nReturns:\n False if failed") + .def("hasActiveAttributes", &mx::GlslProgram::hasActiveAttributes, "Return true if the program has active attributes.") + .def("bindUniform", &mx::GlslProgram::bindUniform, "Bind a value to the uniform with the given name.") + .def("bindAttribute", &mx::GlslProgram::bindAttribute, "Bind attribute buffers to attribute inputs.\n\nArgs:\n inputs: Attribute inputs to bind to\n mesh: Mesh containing streams to bind") + .def("bindPartition", &mx::GlslProgram::bindPartition, "Bind input geometry partition (indexing).") + .def("bindMesh", &mx::GlslProgram::bindMesh, "Bind input geometry streams.") + .def("unbindGeometry", &mx::GlslProgram::unbindGeometry, "Unbind any bound geometry.") + .def("bindTextures", &mx::GlslProgram::bindTextures, "Bind any input textures.") + .def("bindLighting", &mx::GlslProgram::bindLighting, "Bind lighting.") + .def("bindViewInformation", &mx::GlslProgram::bindViewInformation, "Bind view information.") + .def("bindTimeAndFrame", &mx::GlslProgram::bindTimeAndFrame, py::arg("time") = 0.0f, py::arg("frame") = 1.0f, "Bind time and frame.") + .def("unbind", &mx::GlslProgram::unbind, "Unbind the program. Equivalent to binding no program."); - py::class_(mod, "Input") + py::class_(mod, "Input", "An input element within a Node or NodeDef.\n\nAn Input holds either a uniform value or a connection to a spatially-varying Output, either of which may be modified within the scope of a Material.") .def_readwrite_static("INVALID_OPENGL_TYPE", &mx::GlslProgram::Input::INVALID_OPENGL_TYPE) .def_readwrite("location", &mx::GlslProgram::Input::location) .def_readwrite("gltype", &mx::GlslProgram::Input::gltype) diff --git a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslRenderer.cpp b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslRenderer.cpp index 354ddf1a28..ac2b7d7431 100644 --- a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslRenderer.cpp +++ b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslRenderer.cpp @@ -12,14 +12,14 @@ namespace mx = MaterialX; void bindPyGlslRenderer(py::module& mod) { - py::class_(mod, "GlslRenderer") - .def_static("create", &mx::GlslRenderer::create) - .def("initialize", &mx::GlslRenderer::initialize, py::arg("renderContextHandle") = nullptr) - .def("createProgram", static_cast(&mx::GlslRenderer::createProgram)) - .def("createProgram", static_cast(&mx::GlslRenderer::createProgram)) - .def("validateInputs", &mx::GlslRenderer::validateInputs) - .def("render", &mx::GlslRenderer::render) - .def("renderTextureSpace", &mx::GlslRenderer::renderTextureSpace) - .def("captureImage", &mx::GlslRenderer::captureImage) - .def("getProgram", &mx::GlslRenderer::getProgram); + py::class_(mod, "GlslRenderer", "Helper class for rendering generated GLSL code to produce images.\n\nThere are two main interfaces which can be used. One which takes in a HwShader and one which allows for explicit setting of shader stage code.\n\nThe main services provided are: Validation: All shader stages are compiled and atteched to a GLSL shader program. Introspection: The compiled shader program is examined for uniforms and attributes. Binding: Uniforms and attributes which match the predefined variables generated the GLSL code generator will have values assigned to this. This includes matrices, attribute streams, and textures. Rendering: The program with bound inputs will be used to drawing geometry to an offscreen buffer. An interface is provided to save this offscreen buffer to disk using an externally defined image handler.") + .def_static("create", &mx::GlslRenderer::create, "Create a GLSL renderer instance.") + .def("initialize", &mx::GlslRenderer::initialize, py::arg("renderContextHandle") = nullptr, "Internal initialization of stages and OpenGL constructs required for program validation and rendering.\n\nArgs:\n renderContextHandle: allows initializing the GlslRenderer with a Shared OpenGL Context") + .def("createProgram", static_cast(&mx::GlslRenderer::createProgram), "Create GLSL program based on shader stage source code.\n\nArgs:\n stages: Map of name and source code for the shader stages.") + .def("createProgram", static_cast(&mx::GlslRenderer::createProgram), "Create GLSL program based on shader stage source code.\n\nArgs:\n stages: Map of name and source code for the shader stages.") + .def("validateInputs", &mx::GlslRenderer::validateInputs, "Validate inputs for the program.") + .def("render", &mx::GlslRenderer::render, "Render the current program to an offscreen buffer.") + .def("renderTextureSpace", &mx::GlslRenderer::renderTextureSpace, "Render the current program in texture space to an off-screen buffer.") + .def("captureImage", &mx::GlslRenderer::captureImage, "Capture the current contents of the off-screen hardware buffer as an image.") + .def("getProgram", &mx::GlslRenderer::getProgram, "Return the GLSL program."); } diff --git a/source/PyMaterialX/PyMaterialXRenderGlsl/PyTextureBaker.cpp b/source/PyMaterialX/PyMaterialXRenderGlsl/PyTextureBaker.cpp index 800ddf590a..fedf6a2547 100644 --- a/source/PyMaterialX/PyMaterialXRenderGlsl/PyTextureBaker.cpp +++ b/source/PyMaterialX/PyMaterialXRenderGlsl/PyTextureBaker.cpp @@ -13,35 +13,35 @@ namespace mx = MaterialX; void bindPyTextureBaker(py::module& mod) { - py::class_(mod, "TextureBaker") - .def_static("create", &mx::TextureBakerGlsl::create) - .def("setExtension", &mx::TextureBakerGlsl::setExtension) - .def("getExtension", &mx::TextureBakerGlsl::getExtension) - .def("setColorSpace", &mx::TextureBakerGlsl::setColorSpace) - .def("getColorSpace", &mx::TextureBakerGlsl::getColorSpace) - .def("setDistanceUnit", &mx::TextureBakerGlsl::setDistanceUnit) - .def("getDistanceUnit", &mx::TextureBakerGlsl::getDistanceUnit) - .def("setAverageImages", &mx::TextureBakerGlsl::setAverageImages) - .def("getAverageImages", &mx::TextureBakerGlsl::getAverageImages) - .def("setOptimizeConstants", &mx::TextureBakerGlsl::setOptimizeConstants) - .def("getOptimizeConstants", &mx::TextureBakerGlsl::getOptimizeConstants) - .def("setOutputImagePath", &mx::TextureBakerGlsl::setOutputImagePath) - .def("getOutputImagePath", &mx::TextureBakerGlsl::getOutputImagePath) - .def("setBakedGraphName", &mx::TextureBakerGlsl::setBakedGraphName) - .def("getBakedGraphName", &mx::TextureBakerGlsl::getBakedGraphName) - .def("setBakedGeomInfoName", &mx::TextureBakerGlsl::setBakedGeomInfoName) - .def("getBakedGeomInfoName", &mx::TextureBakerGlsl::getBakedGeomInfoName) - .def("setTextureFilenameTemplate", &mx::TextureBakerGlsl::setTextureFilenameTemplate) - .def("getTextureFilenameTemplate", &mx::TextureBakerGlsl::getTextureFilenameTemplate) - .def("setFilenameTemplateVarOverride", &mx::TextureBakerGlsl::setFilenameTemplateVarOverride) - .def("setHashImageNames", &mx::TextureBakerGlsl::setHashImageNames) - .def("getHashImageNames", &mx::TextureBakerGlsl::getHashImageNames) - .def("setTextureSpaceMin", &mx::TextureBakerGlsl::setTextureSpaceMin) - .def("getTextureSpaceMin", &mx::TextureBakerGlsl::getTextureSpaceMin) - .def("setTextureSpaceMax", &mx::TextureBakerGlsl::setTextureSpaceMax) - .def("getTextureSpaceMax", &mx::TextureBakerGlsl::getTextureSpaceMax) - .def("setupUnitSystem", &mx::TextureBakerGlsl::setupUnitSystem) - .def("bakeMaterialToDoc", &mx::TextureBakerGlsl::bakeMaterialToDoc) - .def("bakeAllMaterials", &mx::TextureBakerGlsl::bakeAllMaterials) - .def("writeDocumentPerMaterial", &mx::TextureBakerGlsl::writeDocumentPerMaterial); + py::class_(mod, "TextureBaker", "A helper class for baking procedural material content to textures.\n\nTODO: Add support for graphs containing geometric nodes such as position and normal.") + .def_static("create", &mx::TextureBakerGlsl::create, "") + .def("setExtension", &mx::TextureBakerGlsl::setExtension, "Set the file extension for baked textures.") + .def("getExtension", &mx::TextureBakerGlsl::getExtension, "Return the file extension for baked textures.") + .def("setColorSpace", &mx::TextureBakerGlsl::setColorSpace, "Set the color space in which color textures are encoded.") + .def("getColorSpace", &mx::TextureBakerGlsl::getColorSpace, "Return the color space in which color textures are encoded.") + .def("setDistanceUnit", &mx::TextureBakerGlsl::setDistanceUnit, "Set the distance unit to which textures are baked. Defaults to meters.") + .def("getDistanceUnit", &mx::TextureBakerGlsl::getDistanceUnit, "Return the distance unit to which textures are baked.") + .def("setAverageImages", &mx::TextureBakerGlsl::setAverageImages, "Set whether images should be averaged to generate constants. Defaults to false.") + .def("getAverageImages", &mx::TextureBakerGlsl::getAverageImages, "Return whether images should be averaged to generate constants.") + .def("setOptimizeConstants", &mx::TextureBakerGlsl::setOptimizeConstants, "Set whether uniform textures should be stored as constants. Defaults to true.") + .def("getOptimizeConstants", &mx::TextureBakerGlsl::getOptimizeConstants, "Return whether uniform textures should be stored as constants.") + .def("setOutputImagePath", &mx::TextureBakerGlsl::setOutputImagePath, "Set the output location for baked texture images.\n\nDefaults to the root folder of the destination material.") + .def("getOutputImagePath", &mx::TextureBakerGlsl::getOutputImagePath, "Get the current output location for baked texture images.") + .def("setBakedGraphName", &mx::TextureBakerGlsl::setBakedGraphName, "Set the name of the baked graph element.") + .def("getBakedGraphName", &mx::TextureBakerGlsl::getBakedGraphName, "Return the name of the baked graph element.") + .def("setBakedGeomInfoName", &mx::TextureBakerGlsl::setBakedGeomInfoName, "Set the name of the baked geometry info element.") + .def("getBakedGeomInfoName", &mx::TextureBakerGlsl::getBakedGeomInfoName, "Return the name of the baked geometry info element.") + .def("setTextureFilenameTemplate", &mx::TextureBakerGlsl::setTextureFilenameTemplate, "Set the texture filename template.") + .def("getTextureFilenameTemplate", &mx::TextureBakerGlsl::getTextureFilenameTemplate, "Get the texture filename template.") + .def("setFilenameTemplateVarOverride", &mx::TextureBakerGlsl::setFilenameTemplateVarOverride, "Set texFilenameOverrides if template variable exists.") + .def("setHashImageNames", &mx::TextureBakerGlsl::setHashImageNames, "Set whether to create a short name for baked images by hashing the baked image filenames This is useful for file systems which may have a maximum limit on filename size.\n\nBy default names are not hashed.") + .def("getHashImageNames", &mx::TextureBakerGlsl::getHashImageNames, "Return whether automatic baked texture resolution is set.") + .def("setTextureSpaceMin", &mx::TextureBakerGlsl::setTextureSpaceMin, "Set the minimum texcoords used in texture baking. Defaults to 0, 0.") + .def("getTextureSpaceMin", &mx::TextureBakerGlsl::getTextureSpaceMin, "Return the minimum texcoords used in texture baking.") + .def("setTextureSpaceMax", &mx::TextureBakerGlsl::setTextureSpaceMax, "Set the maximum texcoords used in texture baking. Defaults to 1, 1.") + .def("getTextureSpaceMax", &mx::TextureBakerGlsl::getTextureSpaceMax, "Return the maximum texcoords used in texture baking.") + .def("setupUnitSystem", &mx::TextureBakerGlsl::setupUnitSystem, "Set up the unit definitions to be used in baking.") + .def("bakeMaterialToDoc", &mx::TextureBakerGlsl::bakeMaterialToDoc, "Bake material to document in memory and write baked textures to disk.") + .def("bakeAllMaterials", &mx::TextureBakerGlsl::bakeAllMaterials, "Bake materials in the given document and write them to disk.\n\nIf multiple documents are written, then the given output filename will be used as a template.") + .def("writeDocumentPerMaterial", &mx::TextureBakerGlsl::writeDocumentPerMaterial, "Set whether to write a separate document per material when calling bakeAllMaterials.\n\nBy default separate documents are written."); } diff --git a/source/PyMaterialX/PyMaterialXRenderOsl/PyOslRenderer.cpp b/source/PyMaterialX/PyMaterialXRenderOsl/PyOslRenderer.cpp index 9ae042cced..11290538fb 100644 --- a/source/PyMaterialX/PyMaterialXRenderOsl/PyOslRenderer.cpp +++ b/source/PyMaterialX/PyMaterialXRenderOsl/PyOslRenderer.cpp @@ -12,25 +12,25 @@ namespace mx = MaterialX; void bindPyOslRenderer(py::module& mod) { - py::class_(mod, "OslRenderer") - .def_static("create", &mx::OslRenderer::create) + py::class_(mod, "OslRenderer", "Helper class for rendering generated OSL code to produce images.\n\nThe main services provided are: Source code validation: Use of \"oslc\" to compile and test output results Introspection check: None at this time. Binding: None at this time. Render validation: Use of \"testrender\" to output rendered images. Assumes source compilation was success as it depends on the existence of corresponding .oso files.") + .def_static("create", &mx::OslRenderer::create, "Create an OSL renderer instance.") .def_readwrite_static("OSL_CLOSURE_COLOR_STRING", &mx::OslRenderer::OSL_CLOSURE_COLOR_STRING) - .def("initialize", &mx::OslRenderer::initialize, py::arg("renderContextHandle") = nullptr) - .def("createProgram", static_cast(&mx::OslRenderer::createProgram)) - .def("createProgram", static_cast(&mx::OslRenderer::createProgram)) - .def("validateInputs", &mx::OslRenderer::validateInputs) - .def("render", &mx::OslRenderer::render) - .def("captureImage", &mx::OslRenderer::captureImage) - .def("setOslCompilerExecutable", &mx::OslRenderer::setOslCompilerExecutable) - .def("setOslIncludePath", &mx::OslRenderer::setOslIncludePath) - .def("setOslOutputFilePath", &mx::OslRenderer::setOslOutputFilePath) - .def("setShaderParameterOverrides", &mx::OslRenderer::setShaderParameterOverrides) - .def("setOslShaderOutput", &mx::OslRenderer::setOslShaderOutput) - .def("setOslTestShadeExecutable", &mx::OslRenderer::setOslTestShadeExecutable) - .def("setOslTestRenderExecutable", &mx::OslRenderer::setOslTestRenderExecutable) - .def("setOslTestRenderSceneTemplateFile", &mx::OslRenderer::setOslTestRenderSceneTemplateFile) - .def("setOslShaderName", &mx::OslRenderer::setOslShaderName) - .def("setOslUtilityOSOPath", &mx::OslRenderer::setOslUtilityOSOPath) - .def("useTestRender", &mx::OslRenderer::useTestRender) - .def("compileOSL", &mx::OslRenderer::compileOSL); + .def("initialize", &mx::OslRenderer::initialize, py::arg("renderContextHandle") = nullptr, "Internal initialization required for program validation and rendering.\n\nAn exception is thrown on failure. The exception will contain a list of initialization errors.") + .def("createProgram", static_cast(&mx::OslRenderer::createProgram), "Create OSL program based on shader stage source code.\n\nArgs:\n stages: Map of name and source code for the shader stages.") + .def("createProgram", static_cast(&mx::OslRenderer::createProgram), "Create OSL program based on shader stage source code.\n\nArgs:\n stages: Map of name and source code for the shader stages.") + .def("validateInputs", &mx::OslRenderer::validateInputs, "Validate inputs for the compiled OSL program.\n\nNote: Currently no validation has been implemented.") + .def("render", &mx::OslRenderer::render, "Render OSL program to disk.\n\nThis is done by using either \"testshade\" or \"testrender\". Currently only \"testshade\" is supported.\n\nUsage of both executables requires compiled source (.oso) files as input. A shader output must be set before running this test via the setOslOutputName() method to ensure that the appropriate .oso files can be located.") + .def("captureImage", &mx::OslRenderer::captureImage, "Capture the current rendered output as an image.") + .def("setOslCompilerExecutable", &mx::OslRenderer::setOslCompilerExecutable, "Set the path to the OSL executable.\n\nArgs:\n executableFilePath: Path to OSL compiler executable") + .def("setOslIncludePath", &mx::OslRenderer::setOslIncludePath, "Set the search locations for OSL include files.\n\nArgs:\n dirPath: Include path(s) for the OSL compiler. This should include the path to stdosl.h.") + .def("setOslOutputFilePath", &mx::OslRenderer::setOslOutputFilePath, "Set the location where compiled OSL files will reside.\n\nArgs:\n dirPath: Path to output location") + .def("setShaderParameterOverrides", &mx::OslRenderer::setShaderParameterOverrides, "Set shader parameter strings to be added to the scene XML file.\n\nThese strings will set parameter overrides for the shader.") + .def("setOslShaderOutput", &mx::OslRenderer::setOslShaderOutput, "Set the OSL shader output.\n\nArgs:\n outputName: Name of shader output\n outputType: The MaterialX type of the output") + .def("setOslTestShadeExecutable", &mx::OslRenderer::setOslTestShadeExecutable, "Set the path to the OSL shading tester.\n\nArgs:\n executableFilePath: Path to OSL \"testshade\" executable") + .def("setOslTestRenderExecutable", &mx::OslRenderer::setOslTestRenderExecutable, "Set the path to the OSL rendering tester.\n\nArgs:\n executableFilePath: Path to OSL \"testrender\" executable") + .def("setOslTestRenderSceneTemplateFile", &mx::OslRenderer::setOslTestRenderSceneTemplateFile, "Set the XML scene file to use for testrender.\n\nThis is a template file with the following tokens for replacement: shader% : which will be replaced with the name of the shader to use shader_output% : which will be replace with the name of the shader output to use templateFilePath Scene file name\n\nArgs:\n templateFilePath: Scene file name") + .def("setOslShaderName", &mx::OslRenderer::setOslShaderName, "Set the name of the shader to be used for the input XML scene file.\n\nArgs:\n shaderName: Name of shader") + .def("setOslUtilityOSOPath", &mx::OslRenderer::setOslUtilityOSOPath, "Set the search path for dependent shaders (.oso files) which are used when rendering with testrender.\n\nArgs:\n dirPath: Path to location containing .oso files.") + .def("useTestRender", &mx::OslRenderer::useTestRender, "Used to toggle to either use testrender or testshade during render validation By default testshade is used.\n\nArgs:\n useTestRender: Indicate whether to use testrender.") + .def("compileOSL", &mx::OslRenderer::compileOSL, "Compile OSL code stored in a file.\n\nArgs:\n oslFilePath: OSL file path."); }