From 4d190856367e94e2be85331544f0dafb5da4c697 Mon Sep 17 00:00:00 2001 From: Bernard Kwok Date: Tue, 4 Nov 2025 14:58:56 -0500 Subject: [PATCH 1/9] Doxygen docs insertion into pybind cpp code. --- documents/Doxyfile.in | 3 + python/Scripts/pybind_docs.py | 729 ++++++++++++++++++ .../PyMaterialXCore/PyDefinition.cpp | 108 +-- .../PyMaterialXCore/PyDocument.cpp | 158 ++-- .../PyMaterialX/PyMaterialXCore/PyElement.cpp | 222 +++--- source/PyMaterialX/PyMaterialXCore/PyGeom.cpp | 96 +-- .../PyMaterialXCore/PyInterface.cpp | 134 ++-- source/PyMaterialX/PyMaterialXCore/PyLook.cpp | 102 +-- source/PyMaterialX/PyMaterialXCore/PyNode.cpp | 94 +-- .../PyMaterialXCore/PyProperty.cpp | 48 +- .../PyMaterialXCore/PyTraversal.cpp | 38 +- .../PyMaterialX/PyMaterialXCore/PyTypes.cpp | 62 +- .../PyMaterialXCore/PyUnitConverter.cpp | 40 +- source/PyMaterialX/PyMaterialXCore/PyUtil.cpp | 4 +- .../PyMaterialX/PyMaterialXCore/PyValue.cpp | 10 +- .../PyMaterialX/PyMaterialXCore/PyVariant.cpp | 26 +- .../PyMaterialX/PyMaterialXFormat/PyFile.cpp | 50 +- .../PyMaterialX/PyMaterialXFormat/PyUtil.cpp | 2 +- .../PyMaterialX/PyMaterialXFormat/PyXmlIo.cpp | 4 +- .../PyGlslShaderGenerator.cpp | 34 +- .../PyMdlShaderGenerator.cpp | 4 +- .../PyMslShaderGenerator.cpp | 6 +- .../PyOslShaderGenerator.cpp | 6 +- .../PyColorManagement.cpp | 16 +- .../PyMaterialXGenShader/PyGenContext.cpp | 24 +- .../PyMaterialXGenShader/PyGenOptions.cpp | 2 +- .../PyMaterialXGenShader/PyShader.cpp | 22 +- .../PyShaderGenerator.cpp | 20 +- .../PyMaterialXGenShader/PyShaderPort.cpp | 46 +- .../PyMaterialXGenShader/PyShaderStage.cpp | 42 +- .../PyShaderTranslator.cpp | 6 +- .../PyMaterialXGenShader/PyTypeDesc.cpp | 24 +- .../PyMaterialXGenShader/PyUnitSystem.cpp | 14 +- .../PyMaterialXRender/PyCamera.cpp | 28 +- .../PyMaterialXRender/PyCgltfLoader.cpp | 4 +- .../PyMaterialXRender/PyGeometryHandler.cpp | 22 +- .../PyMaterialX/PyMaterialXRender/PyImage.cpp | 48 +- .../PyMaterialXRender/PyImageHandler.cpp | 40 +- .../PyMaterialXRender/PyLightHandler.cpp | 50 +- .../PyMaterialX/PyMaterialXRender/PyMesh.cpp | 94 +-- .../PyMaterialXRender/PyOiioImageLoader.cpp | 2 +- .../PyMaterialXRender/PyShaderRenderer.cpp | 32 +- .../PyMaterialXRender/PyStbImageLoader.cpp | 4 +- .../PyMaterialXRender/PyTinyObjLoader.cpp | 4 +- .../PyGLTextureHandler.cpp | 10 +- .../PyMaterialXRenderGlsl/PyGlslProgram.cpp | 46 +- .../PyMaterialXRenderGlsl/PyGlslRenderer.cpp | 18 +- .../PyMaterialXRenderGlsl/PyTextureBaker.cpp | 60 +- .../PyMaterialXRenderOsl/PyOslRenderer.cpp | 38 +- 49 files changed, 1714 insertions(+), 982 deletions(-) create mode 100644 python/Scripts/pybind_docs.py 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/python/Scripts/pybind_docs.py b/python/Scripts/pybind_docs.py new file mode 100644 index 0000000000..00f30db6da --- /dev/null +++ b/python/Scripts/pybind_docs.py @@ -0,0 +1,729 @@ +import argparse +import re +import json +import xml.etree.ElementTree as ET +from pathlib import Path + +# Adjust these paths as needed +DOXYGEN_XML_DIR = Path("build/documents/doxygen_xml") +PYBIND_DIR = Path("source/PyMaterialX") + +# Output debug dump of extracted docs +DEBUG_JSON = Path("doc_map.json") + +class pybind_doc_builder: + def __init__(self, doxygen_xml_dir: Path, pybind_dir: Path): + self.doxygen_xml_dir = doxygen_xml_dir + self.pybind_dir = pybind_dir + self.class_docs = {} + self.func_docs = {} + +# Extraction maps +class_docs = {} # key: "MaterialX::Document" -> docstring +func_docs = {} # key: "MaterialX::Document::addNodeDefFromGraph" -> dict {brief, detail, params:{name:desc}, returns, args_sig} + +def _text_of(elem): + """Concatenate element text and children text, normalized whitespace.""" + if elem is None: + return "" + text = "".join(elem.itertext()) + text = re.sub(r'\s+', ' ', text).strip() + return text + +def _param_desc_tuple(param_item): + """Return (name, description) for a element.""" + name = _text_of(param_item.find("parameternamelist/parametername")) + desc_elem = param_item.find("parameterdescription") + desc = _text_of(desc_elem) + return name, desc + +def extract_docs_from_xml(): + """ + Populate class_docs and func_docs. + """ + if not DOXYGEN_XML_DIR.exists(): + raise FileNotFoundError(f"Doxygen XML directory not found: {DOXYGEN_XML_DIR}") + + # Iterate over xml files + for xml_file in DOXYGEN_XML_DIR.glob("*.xml"): + tree = ET.parse(xml_file) + root = tree.getroot() + + # Extract class/struct compound docs + for compound in root.findall(".//compounddef[@kind='class']") + root.findall(".//compounddef[@kind='struct']"): + compoundname = _text_of(compound.find("compoundname")) + brief = _text_of(compound.find("briefdescription/para")) + # collect detailed paragraphs excluding parameterlist/simplesect which are typically for members + detail_parts = [] + for para in compound.findall("detaileddescription/para"): + if para.find("parameterlist") is None and para.find("simplesect") is None: + detail_parts.append(_text_of(para)) + detail = "\n\n".join(p for p in detail_parts if p) + doc_parts = [] + if brief: + doc_parts.append(brief) + if detail: + doc_parts.append(detail) + full = "\n\n".join(doc_parts).strip() + if full: + # normalize name to MaterialX::... if not already + key = compoundname + if key and not key.startswith("MaterialX::"): + key = "MaterialX::" + key + class_docs[key] = full + + # Extract member functions + for member in root.findall(".//memberdef[@kind='function']"): + name = _text_of(member.find("name")) + qualified = _text_of(member.find("qualifiedname")) + + # try to build qualified if missing using parent compoundname + if not qualified and name: + compound = member.find("../../compoundname") + compound_name = _text_of(compound) if compound is not None else "" + if compound_name: + qualified = compound_name + "::" + name + + # brief + detailed + brief = _text_of(member.find("briefdescription/para")) + detail_parts = [] + for para in member.findall("detaileddescription/para"): + # skip param lists and return sections here + if para.find("parameterlist") is not None or para.find("simplesect") is not None: + continue + detail_parts.append(_text_of(para)) + detail = "\n\n".join(p for p in detail_parts if p) + + # params + params = {} + for param_item in member.findall(".//parameterlist[@kind='param']/parameteritem"): + pname, pdesc = _param_desc_tuple(param_item) + if pname: + params[pname] = pdesc + + # returns + ret_text = "" + retsect = member.find(".//simplesect[@kind='return']") + if retsect is not None: + ret_text = _text_of(retsect) + + # argsstring for overload disambiguation + argsstring = _text_of(member.find("argsstring")) # e.g. "(NodeGraphPtr nodeGraph, const string &nodeDefName, ...)" + # canonicalize argsstring: remove repeated spaces and normalize commas + args_sig = "" + if argsstring: + args_sig = re.sub(r'\s*,\s*', ',', argsstring) + args_sig = re.sub(r'\s+', ' ', args_sig).strip() + + # normalize qualified name to MaterialX:: prefix variants + q1 = qualified + if q1 and not q1.startswith("MaterialX::"): + q_mat = "MaterialX::" + q1 + else: + q_mat = q1 + + # choose a primary key for func_docs as the fully qualified name (prefer MaterialX::... form) + primary_key = q_mat if q_mat else q1 + if not primary_key: + continue + + func_docs[primary_key] = { + "brief": brief, + "detail": detail, + "params": params, + "returns": ret_text, + "args_sig": args_sig + } + + print(f"Extracted {len(class_docs)} classes and {len(func_docs)} functions.") + +# ---------------------------- +# Insertion code +# ---------------------------- +def escape_docstring_for_cpp(s: str) -> str: + # escape double quotes and backslashes, then represent newlines as \n in a C++ string literal + # keep it as a single-line C++ string literal with explicit \n sequences + if s is None: + return "" + s = s.replace("\\", "\\\\").replace('"', '\\"') + s = s.replace("\r\n", "\n").replace("\r", "\n") + s = s.replace("\n", "\\n") + return s + +def build_method_docstring(func_entry): + """Build a readable docstring for a method from func_entry dict.""" + parts = [] + if func_entry.get("brief"): + parts.append(func_entry["brief"]) + if func_entry.get("detail"): + parts.append(func_entry["detail"]) + # Args block + params = func_entry.get("params", {}) + if params: + param_lines = [] + for pname, pdesc in params.items(): + if pdesc: + param_lines.append(f" {pname}: {pdesc}") + else: + param_lines.append(f" {pname}:") + parts.append("Args:\n" + "\n".join(param_lines)) + # Returns + if func_entry.get("returns"): + parts.append("Returns:\n " + func_entry["returns"]) + return "\n\n".join(parts).strip() + +def insert_docs_into_bindings_old(): + """ + Modify C++ pybind files in place: + - Insert class docstrings into the py::class_<...>(mod, "Name", "doc") + - Insert method docstrings into .def("name", &mx::Class::name, "doc") + """ + cpp_files = list(PYBIND_DIR.rglob("*.cpp")) + + # Regex patterns + # py::class_<...>(mod, "Document") + class_pattern = re.compile(r'(py::class_<[^>]+>\(\s*([A-Za-z0-9_:]+)\s*,\s*"([^"]+)"\s*)(\))') + # Then a variant where there are spaces/newline before closing paren: handle simpler by searching for 'py::class_<...' up to the first ')' on the same line. + + # .def("name", &mx::Document::name, ...) + def_pattern = re.compile(r'\.def\(\s*"([^"]+)"\s*,\s*&([A-Za-z0-9_:]+)\s*([,)]?)') + + for cpp in cpp_files: + text = cpp.read_text(encoding="utf-8") + orig_text = text + + lines = text.splitlines() + new_lines = [] + changed = False + + for i, line in enumerate(lines): + # First: try class insertion when a py::class_ is declared + # We look for the pattern: py::class_<...>(mod, "Name") + class_match = re.search(r'py::class_<[^>]+>\(\s*([A-Za-z0-9_:]+)\s*,\s*"([^"]+)"\s*\)', line) + if class_match: + cpp_class_type = class_match.group(1) # maybe mx::Document, etc + class_name = class_match.group(2) # "Document" + # Normalize to MaterialX::ClassName + # We try keys: MaterialX::Document, Document, mx::Document + keys = [] + if cpp_class_type: + # if cpp_class_type includes 'mx::', remove and use just the class name + short = class_name + keys.append(f"MaterialX::{short}") + keys.append(short) + if cpp_class_type.startswith("mx::"): + keys.append(cpp_class_type.replace("mx::", "MaterialX::")) + else: + keys.append(cpp_class_type) + else: + keys.append(class_name) + keys.append(f"MaterialX::{class_name}") + + class_doc = None + for k in keys: + if k in class_docs: + class_doc = class_docs[k] + break + + if class_doc: + # If the line already has a third argument (docstring) skip + # Simple check: count commas inside the parentheses - but safer to check presence of a string literal after the class name + if re.search(r'py::class_<[^>]+>\(\s*[A-Za-z0-9_:]+\s*,\s*"' + re.escape(class_name) + r'"\s*,', line): + # already has doc - do nothing + pass + else: + escaped = escape_docstring_for_cpp(class_doc) + # Insert as third argument before the closing paren + new_line = re.sub(r'\)\s*$', f', "{escaped}")', line) + new_lines.append(new_line) + changed = True + continue # skip default append at end + # Next: method .def insertion + m = def_pattern.search(line) + if m: + py_name = m.group(1) # "createInput" + cpp_ref = m.group(2) # mx::Document::addNodeDefFromGraph or Document::... + # Normalize cpp_ref to MaterialX::... form to lookup func_docs + lookup_keys = [] + if cpp_ref.startswith("mx::"): + lookup_keys.append(cpp_ref.replace("mx::", "MaterialX::")) + lookup_keys.append(cpp_ref) + # Also try without namespace + if "::" in cpp_ref: + parts = cpp_ref.split("::") + lookup_keys.append("::".join(parts[-2:])) # Class::method + lookup_keys.append(parts[-1]) # method only + + func_entry = None + for k in lookup_keys: + if k in func_docs: + func_entry = func_docs[k] + break + + # If not found, try suffix match (sometimes args_sig stored) + if not func_entry: + for k, v in func_docs.items(): + # match just by 'Class::method' ending part + if k.endswith("::" + py_name): + func_entry = v + break + + if func_entry: + # Check if this .def already has a docstring (a string literal after the callable) + # We'll consider the remainder of the line after the callable + rest = line[m.end():].strip() + already_has_doc = False + # crude check: does a string literal appear before the closing ')' + if '"' in rest or "R\"" in rest: + already_has_doc = True + + if not already_has_doc: + docstring = build_method_docstring(func_entry) + if docstring: + escaped = escape_docstring_for_cpp(docstring) + # We must be careful to not break existing trailing arguments (py::arg(...)) + # We'll try to insert the docstring after the callable reference and before other args. + # If line ends with ')' we can simply replace the trailing ')' with , "doc") + if line.rstrip().endswith(")"): + new_line = line.rstrip() + # but avoid adding doc into lines that are multi-line function chains; this is a best-effort inline insertion + new_line = new_line.rstrip(")") + new_line = new_line + f', "{escaped}")' + new_lines.append(new_line) + changed = True + continue + else: + # line doesn't end with ')', likely args continue on following lines; just add doc at line end for now + new_line = line + f', "{escaped}"' + new_lines.append(new_line) + changed = True + continue + + # default: copy original line + new_lines.append(line) + + # if changed, write back + if changed: + new_text = "\n".join(new_lines) + cpp.write_text(new_text, encoding="utf-8") + #print("new text", new_text) + print(f"Patched: {cpp}") + + print("Insertion complete.") + +# Replace the previous insert_docs_into_bindings() with this more robust version. + +def _find_matching_paren(s: str, start_idx: int) -> int: + """Find the index of the matching ')' for s[start_idx] == '('. + Handles nested parentheses and string literals roughly (so quotes inside strings are ignored).""" + i = start_idx + depth = 0 + in_single = False + in_double = False + in_raw = False + raw_delim = None + L = len(s) + while i < L: + c = s[i] + # handle entering/exiting raw string literal R"delim(... )delim" + if not in_single and not in_double: + if s.startswith('R"', i): + # find raw delim start: R"delim( + # grab delim between R" and ( + m = re.match(r'R"([^\(\s]*)\(', s[i:]) + if m: + in_raw = True + raw_delim = m.group(1) + i += 2 + len(raw_delim) # position at '(' after delim + depth += 1 + i += 1 + continue + + if in_raw: + # raw literal ends with )delim" + # check for )raw_delim" + end_token = ')' + (raw_delim or '') + '"' + if s.startswith(end_token, i): + in_raw = False + i += len(end_token) + continue + else: + i += 1 + continue + + if c == '"' and not in_single: + # toggle double quotes (but ignore escaped quotes) + # ensure not escaped + prev = s[i-1] if i > 0 else '' + if prev != '\\': + in_double = not in_double + i += 1 + continue + if c == "'" and not in_double: + prev = s[i-1] if i > 0 else '' + if prev != '\\': + in_single = not in_single + i += 1 + continue + + if in_single or in_double: + i += 1 + continue + + if c == '(': + depth += 1 + elif c == ')': + depth -= 1 + if depth == 0: + return i + i += 1 + return -1 + + +def _split_top_level_args(arglist: str): + """Split a string of arguments (content inside parentheses) into a list of top-level args. + Returns list of (arg_text, start_index, end_index) relative to the arglist string. + Handles nested parentheses and string literals so commas inside those are ignored. + """ + args = [] + start = 0 + i = 0 + depth = 0 + in_single = False + in_double = False + in_raw = False + raw_delim = None + L = len(arglist) + while i < L: + c = arglist[i] + # raw string handling + if not in_single and not in_double: + if arglist.startswith('R"', i): + m = re.match(r'R"([^\(\s]*)\(', arglist[i:]) + if m: + in_raw = True + raw_delim = m.group(1) + i += 2 + len(raw_delim) # move to '(' + depth += 1 + i += 1 + continue + + if in_raw: + end_token = ')' + (raw_delim or '') + '"' + if arglist.startswith(end_token, i): + in_raw = False + i += len(end_token) + continue + else: + i += 1 + continue + + if c == '"' and not in_single: + prev = arglist[i-1] if i > 0 else '' + if prev != '\\': + in_double = not in_double + i += 1 + continue + if c == "'" and not in_double: + prev = arglist[i-1] if i > 0 else '' + if prev != '\\': + in_single = not in_single + i += 1 + continue + + if in_single or in_double: + i += 1 + continue + + if c == '(': + depth += 1 + elif c == ')': + if depth > 0: + depth -= 1 + elif c == ',' and depth == 0: + # top-level comma + args.append((arglist[start:i].strip(), start, i)) + start = i + 1 + i += 1 + + # append last arg + if start < L: + args.append((arglist[start:].strip(), start, L)) + return args + +def _has_docstring(args): + """ + True if there is a string literal among top-level args that is not a py::arg(...) + """ + for a, _, _ in args[1:]: # skip first arg (Python name) + # strip whitespace + a_stripped = a.strip() + # skip py::arg(...) calls + if a_stripped.startswith("py::arg"): + continue + # crude string literal check + if re.match(r'^R?".*"$', a_stripped): + return True + return False + + +def insert_docs_into_bindings(): + """ + Robust insertion: + 1. Insert class docstrings into py::class_<...>(mod, "Name") -> py::class_<...>(mod, "Name", "doc") + 2. Parse each .def(...) call, split top-level args, and insert docstring + after the second argument (callable) if no docstring is present. + """ + cpp_files = list(PYBIND_DIR.rglob("*.cpp")) + def_start_re = re.compile(r'\.def\s*\(') + class_start_re = re.compile(r'py::class_<') + + for cpp in cpp_files: + text = cpp.read_text(encoding="utf-8") + + # First pass: insert class documentation + # Look for py::class_<...>(mod, "Name") and add docstring as third parameter + class_changed = False + class_matches = [] + for m in class_start_re.finditer(text): + start = m.start() + # Find the opening paren after py::class_<...> + # First find the closing > of the template + template_start = start + len('py::class_<') + depth = 1 + i = template_start + while i < len(text) and depth > 0: + if text[i] == '<': + depth += 1 + elif text[i] == '>': + depth -= 1 + i += 1 + # Now find the opening paren + paren_open = text.find('(', i) + if paren_open == -1: + continue + paren_close = _find_matching_paren(text, paren_open) + if paren_close == -1: + continue + + # Extract arguments + arglist = text[paren_open+1:paren_close] + args = _split_top_level_args(arglist) + + # We expect at least 2 args: (mod, "ClassName") + if len(args) < 2: + continue + + # Check if there's already a third argument (docstring) + if len(args) >= 3: + # Already has docstring + continue + + # Extract class name from second argument + class_name = args[1][0].strip().strip('"') + + # Try to find documentation + # Build lookup keys + lookup_keys = [ + f"MaterialX::{class_name}", + class_name + ] + + class_doc = None + for k in lookup_keys: + if k in class_docs: + class_doc = class_docs[k] + break + + if class_doc: + escaped = escape_docstring_for_cpp(class_doc) + class_matches.append((paren_close, f', "{escaped}"')) + class_changed = True + + # Apply class documentation insertions in reverse order to preserve positions + if class_changed: + for pos, insertion in reversed(class_matches): + text = text[:pos] + insertion + text[pos:] + + # Second pass: insert method documentation + new_text_parts = [] + idx = 0 + changed = False + while True: + m = def_start_re.search(text, idx) + if not m: + # append rest and break + new_text_parts.append(text[idx:]) + break + + start = m.start() + new_text_parts.append(text[idx:start]) # content up to .def( + paren_open = text.find('(', start) + if paren_open == -1: + # shouldn't happen; append rest and break + new_text_parts.append(text[start:]) + break + + paren_close = _find_matching_paren(text, paren_open) + if paren_close == -1: + # unmatched, append rest + new_text_parts.append(text[start:]) + break + + # argument list content (without outer parentheses) + arglist = text[paren_open+1:paren_close] + args = _split_top_level_args(arglist) + + # if we have less than 2 args, we can't determine callable; just copy as-is + if len(args) < 2: + new_text_parts.append(text[start:paren_close+1]) + idx = paren_close + 1 + continue + + # first arg is typically the python name string literal (e.g. "addNode") + # second arg is the callable, e.g. &mx::Document::addNodeGraph + first_arg_text = args[0][0] + second_arg_text = args[1][0] + + # determine if any existing argument is a string literal (treat raw string R"..." too) + has_docstring = _has_docstring(args) + # A simpler check: presence of an unqualified string literal among top-level args other than the first (name) arg + if not has_docstring: + # build lookup keys using the second arg (callable) + cpp_ref = second_arg_text + # strip & and possible std::function wrappers, templates, whitespace + cpp_ref_clean = cpp_ref.strip() + + # Check if this is a lambda function (starts with '[') + is_lambda = cpp_ref_clean.startswith('[') + + if is_lambda: + # For lambda functions, try to infer the method name from the Python method name + # and look for it being called within the lambda + py_method_name = first_arg_text.strip().strip('"') + lookup_keys = [] + + # Try to find method calls within the lambda body + # Look for patterns like elem.method( or obj->method( + method_call_pattern = re.search(r'[\.\->](\w+)\s*\(', cpp_ref_clean) + if method_call_pattern: + called_method = method_call_pattern.group(1) + # Try to match with various namespace prefixes + lookup_keys.append(f"MaterialX::Element::{called_method}") + lookup_keys.append(f"MaterialX::{called_method}") + lookup_keys.append(called_method) + + # Also try using the Python method name directly + lookup_keys.append(f"MaterialX::Element::{py_method_name}") + lookup_keys.append(f"MaterialX::{py_method_name}") + lookup_keys.append(py_method_name) + + possible = py_method_name + else: + # remove address-of operator and potential casts like (py::cpp_function) &foo — try to extract last token with ::method + # crude heuristic: find last token containing :: and use everything from that token to end (remove trailing spaces) + tokens = re.split(r'\s+', cpp_ref_clean) + possible = None + for t in reversed(tokens): + if '::' in t: + possible = t + break + if not possible: + possible = tokens[-1] + + # strip trailing commas/parens etc + possible = possible.rstrip(',').strip() + + # normalize to lookups used earlier + lookup_keys = [] + if possible.startswith("mx::"): + lookup_keys.append(possible.replace("mx::", "MaterialX::")) + lookup_keys.append(possible) + if "::" in possible: + parts = possible.split("::") + lookup_keys.append("::".join(parts[-2:])) # Class::method + lookup_keys.append(parts[-1]) # method only + + func_entry = None + for k in lookup_keys: + if k in func_docs: + func_entry = func_docs[k] + break + if not func_entry: + # fallback: suffix match + for k, v in func_docs.items(): + if k.endswith("::" + args[0][0].strip().strip('"')) or k.endswith("::" + possible.split("::")[-1]): + func_entry = v + break + + if func_entry: + docstring = build_method_docstring(func_entry) + if docstring: + escaped = escape_docstring_for_cpp(docstring) + # Simple approach: insert ", "docstring"" right before the closing ) + # This preserves all the original formatting and structure + new_def_text = text[start:paren_close] + f', "{escaped}")' + new_text_parts.append(new_def_text) + idx = paren_close + 1 + changed = True + continue + + # no insertion performed — copy original .def(...) exactly + new_text_parts.append(text[start:paren_close+1]) + idx = paren_close + 1 + + if changed or class_changed: + new_text = "".join(new_text_parts) + cpp.write_text(new_text, encoding="utf-8") + print(f"- Patched: {cpp}") + else: + # no changes; nothing to write + print('- No changes needed for:', cpp) + pass + + print("Code insertion complete.") + + +def main(): + parser = argparse.ArgumentParser(description="Extract Doxygen XML docs and insert into pybind11 bindings.") + 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 C++ bindings directory.") + parser.add_argument("-j", "--write_json", action='store_true', help="Write extracted docs to JSON file.") + + args = parser.parse_args() + doxygen_xml_dir = args.doxygen_xml_dir + pybind_dir = args.pybind_dir + if not doxygen_xml_dir.exists(): + print(f"Error: Doxygen XML directory does not exist: {doxygen-xml-dir}") + return + if not pybind_dir.exists(): + print(f"Error: Pybind directory does not exist: {pybind-dir}") + return + + DOXYGEN_XML_DIR = doxygen_xml_dir + PYBIND_DIR = pybind_dir + + # Build documentation maps + extract_docs_from_xml() + + # Update CPP files + insert_docs_into_bindings() + + # Write extracted documentation to JSON files. + write_json = args.write_json if args.write_json else False + if write_json: + json_path = Path("class_docs.json") + with json_path.open("w", encoding="utf-8") as f: + print("Writing class docs to", json_path) + json.dump(class_docs, f, indent=2) + json_path = Path("func_docs.json") + with json_path.open("w", encoding="utf-8") as f: + print("Writing function docs to", json_path) + json.dump(func_docs, f, indent=2) + + print("Done.") + +if __name__ == "__main__": + main() diff --git a/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp b/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp index 7990387f96..1bb16bba05 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp @@ -14,17 +14,17 @@ 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) + 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 Implementation, if any, with the given name.") .def("getImplementation", &mx::NodeDef::getImplementation, - py::arg("target") = mx::EMPTY_STRING) - .def("isVersionCompatible", &mx::NodeDef::isVersionCompatible) + py::arg("target") = mx::EMPTY_STRING, "Return the Implementation, if any, with the given name.") + .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 +36,70 @@ 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, "Returns a nodedef for a given transform.") + .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, if any, with the given name.") .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) + py::class_(mod, "TypeDef", "A type definition element within a Document.") + .def("setSemantic", &mx::TypeDef::setSemantic, "Set the variable semantic of this port.") + .def("hasSemantic", &mx::TypeDef::hasSemantic, "Return true if the given TypeDef has a semantic string.") + .def("getSemantic", &mx::TypeDef::getSemantic, "Return the variable semantic of this port.") + .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) - .def("getMember", &mx::TypeDef::getMember) + 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) - .def("removeMember", &mx::TypeDef::removeMember) + .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 unit 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 type for the value on this port.") + .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, "Return a vector of all Member elements in the TypeDef.") .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, "Return value string.") + .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..b40ad86b10 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp @@ -24,100 +24,100 @@ 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) + 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 with the given implementation element.\n\nInitialization must set the name and hash for the implementation, as well as any other data needed to emit code for the node.") + .def("copy", &mx::Document::copy, "Create a deep copy of the value.") + .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) - .def("getNodeGraph", &mx::Document::getNodeGraph) - .def("getNodeGraphs", &mx::Document::getNodeGraphs) - .def("removeNodeGraph", &mx::Document::removeNodeGraph) - .def("getMatchingPorts", &mx::Document::getMatchingPorts) + 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) - .def("getGeomInfo", &mx::Document::getGeomInfo) - .def("getGeomInfos", &mx::Document::getGeomInfos) - .def("removeGeomInfo", &mx::Document::removeGeomInfo) + 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) - .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) + 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) - .def("getLook", &mx::Document::getLook) - .def("getLooks", &mx::Document::getLooks) - .def("removeLook", &mx::Document::removeLook) + 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) - .def("getLookGroup", &mx::Document::getLookGroup) - .def("getLookGroups", &mx::Document::getLookGroups) - .def("removeLookGroup", &mx::Document::removeLookGroup) + 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) - .def("getCollection", &mx::Document::getCollection) - .def("getCollections", &mx::Document::getCollections) - .def("removeCollection", &mx::Document::removeCollection) + 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) - .def("getTypeDef", &mx::Document::getTypeDef) - .def("getTypeDefs", &mx::Document::getTypeDefs) - .def("removeTypeDef", &mx::Document::removeTypeDef) + 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) + 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("getNodeDef", &mx::Document::getNodeDef, "Returns a nodedef for a given transform.") + .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) - .def("getPropertySet", &mx::Document::getPropertySet) - .def("getPropertySets", &mx::Document::getPropertySets) - .def("removePropertySet", &mx::Document::removePropertySet) + 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) - .def("getVariantSet", &mx::Document::getVariantSet) - .def("getVariantSets", &mx::Document::getVariantSets) - .def("removeVariantSet", &mx::Document::removeVariantSet) + 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) - .def("getImplementation", &mx::Document::getImplementation) - .def("getImplementations", &mx::Document::getImplementations) - .def("removeImplementation", &mx::Document::removeImplementation) - .def("getMatchingImplementations", &mx::Document::getMatchingImplementations) + 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) - .def("getUnitDefs", &mx::Document::getUnitDefs) - .def("removeUnitDef", &mx::Document::removeUnitDef) + .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) - .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("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..861c972fea 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,81 @@ 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) + }, "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 ColorManagementSystem name.") .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) + 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) - .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) + 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 our self pointer.") + .def("getParent", static_cast(&mx::Element::getParent), "Return our parent element.") + .def("getRoot", static_cast(&mx::Element::getRoot), "Return the root element of our tree.") + .def("getDocument", static_cast(&mx::Element::getDocument), "Return the root document of our tree.") + .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) - .def("getUpstreamEdgeCount", &mx::Element::getUpstreamEdgeCount) + 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) - .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) + py::arg("index") = 0, "Return the upstream element of the edge.") + .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) + }, "Validate that the given element tree, including all descendants, is consistent with the MaterialX specification.") + .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) - .def("asString", &mx::Element::asString) - .def("__str__", &mx::Element::asString) + 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 +135,38 @@ 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 data type for this port.") + .def("hasType", &mx::TypedElement::hasType, "Return true if the given element has a type string.") + .def("getType", &mx::TypedElement::getType, "Get stream attribute name.") + .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, if any, with the given name.") .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) + 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, "Return value string.") .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) + 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, "Returns a value formatted according to this type syntax.\n\nThe value is constructed from the given value object.") .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) + .def("setUnit", &mx::ValueElement::setUnit, "Set a unit type for the value on this port.") + .def("hasUnit", &mx::ValueElement::hasUnit, "Return true if the given element has a unit string.") + .def("getUnit", &mx::ValueElement::getUnit, "Return the unit type for the value on this port.") + .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 element's unittype string.") + .def("hasUnitType", &mx::ValueElement::hasUnitType, "Return true if the given element has a unittype string.") + .def("getUnitType", &mx::ValueElement::getUnitType, "Return the unit type string.") + .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,37 +199,37 @@ 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 element's file prefix string.") + .def("getFilePrefix", &mx::StringResolver::getFilePrefix, "Return the element's file prefix string.") + .def("setGeomPrefix", &mx::StringResolver::setGeomPrefix, "Set the element's geom prefix string.") + .def("getGeomPrefix", &mx::StringResolver::getGeomPrefix, "Return the element's geom prefix string.") + .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"); diff --git a/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp b/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp index 712851fac2..5f0ae10c40 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp @@ -15,27 +15,27 @@ 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, if any, with the given name."); - py::class_(mod, "GeomInfo") - .def("addGeomProp", &mx::GeomInfo::addGeomProp) - .def("getGeomProp", &mx::GeomInfo::getGeomProp) - .def("getGeomProps", &mx::GeomInfo::getGeomProps) - .def("removeGeomProp", &mx::GeomInfo::removeGeomProp) + 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) - .def("getToken", &mx::GeomInfo::getToken) - .def("getTokens", &mx::GeomInfo::getTokens) - .def("removeToken", &mx::GeomInfo::removeToken) - .def("setTokenValue", &mx::GeomInfo::setTokenValue) + 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,39 +53,39 @@ 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 GeomProp, if any, with the given name.") + .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 GeomProp, if any, with the given name.") .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); diff --git a/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp b/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp index 42e978d282..4e8fbc258b 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp @@ -17,84 +17,84 @@ 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 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::PortElement::getConnectedNode, "Return the node, if any, to which this input is connected.") + .def("setConnectedOutput", &mx::PortElement::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::PortElement::getConnectedOutput, "Return the output connected to the given input.\n\nIf the given input is not present, then an empty OutputPtr is returned."); - 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) + 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) - .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) + 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) - .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) + 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) - .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) + 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::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 a unique identifier for the target this generator is for.") + .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) - .def("clearContent", &mx::InterfaceElement::clearContent) + py::arg("target") = mx::EMPTY_STRING, "Return the first declaration of this interface, optionally filtered by the given target name.") + .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) + 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..7104f8c302 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyLook.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyLook.cpp @@ -15,70 +15,70 @@ namespace mx = MaterialX; void bindPyLook(py::module& mod) { - py::class_(mod, "Look") + 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) - .def("getMaterialAssign", &mx::Look::getMaterialAssign) - .def("getMaterialAssigns", &mx::Look::getMaterialAssigns) - .def("getActiveMaterialAssigns", &mx::Look::getActiveMaterialAssigns) - .def("removeMaterialAssign", &mx::Look::removeMaterialAssign) + 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) - .def("getPropertyAssign", &mx::Look::getPropertyAssign) - .def("getPropertyAssigns", &mx::Look::getPropertyAssigns) - .def("getActivePropertyAssigns", &mx::Look::getActivePropertyAssigns) - .def("removePropertyAssign", &mx::Look::removePropertyAssign) + 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) - .def("getPropertySetAssign", &mx::Look::getPropertySetAssign) - .def("getPropertySetAssigns", &mx::Look::getPropertySetAssigns) - .def("getActivePropertySetAssigns", &mx::Look::getActivePropertySetAssigns) - .def("removePropertySetAssign", &mx::Look::removePropertySetAssign) + 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) - .def("getVariantAssign", &mx::Look::getVariantAssign) - .def("getVariantAssigns", &mx::Look::getVariantAssigns) - .def("getActiveVariantAssigns", &mx::Look::getActiveVariantAssigns) - .def("removeVariantAssign", &mx::Look::removeVariantAssign) + 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) - .def("getVisibility", &mx::Look::getVisibility) - .def("getVisibilities", &mx::Look::getVisibilities) - .def("getActiveVisibilities", &mx::Look::getActiveVisibilities) - .def("removeVisibility", &mx::Look::removeVisibility) + 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, "Return a vector of all Look elements in the document.") + .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 material-type outputs for all nodegraphs in the document.") + .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, diff --git a/source/PyMaterialX/PyMaterialXCore/PyNode.cpp b/source/PyMaterialX/PyMaterialXCore/PyNode.cpp index 55448dee80..f97ece1b8d 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyNode.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyNode.cpp @@ -14,66 +14,66 @@ 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) + 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, if any, to which this input is connected.") + .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) + py::arg("target") = mx::EMPTY_STRING, py::arg("allowRoughMatch") = false, "Returns a nodedef for a given transform.") .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::arg("target") = mx::EMPTY_STRING, "Return the Implementation, if any, with the given name.") + .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") + 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) + 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) - .def("getNode", &mx::GraphElement::getNode) + 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) - .def("removeNode", &mx::GraphElement::removeNode) + 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) - .def("getMaterialNodes", &mx::GraphElement::getMaterialNodes) + 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) - .def("getBackdrop", &mx::GraphElement::getBackdrop) - .def("getBackdrops", &mx::GraphElement::getBackdrops) - .def("removeBackdrop", &mx::GraphElement::removeBackdrop) + 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) - .def("topologicalSort", &mx::GraphElement::topologicalSort) - .def("addGeomNode", &mx::GraphElement::addGeomNode) - .def("asStringDot", &mx::GraphElement::asStringDot); + 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 material-type outputs for all nodegraphs in the document.") + .def("setNodeDef", &mx::NodeGraph::setNodeDef, "Set the NodeDef element referenced by the Implementation.") + .def("getNodeDef", &mx::NodeGraph::getNodeDef, "Returns a nodedef for a given transform.") + .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 node, 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..477d39a1f0 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, if any, with the given name.") .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 PropertySet, if any, with the given name.") .def_readonly_static("CATEGORY", &mx::PropertySetAssign::CATEGORY); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyTraversal.cpp b/source/PyMaterialX/PyMaterialXCore/PyTraversal.cpp index f83200b680..877f592944 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyTraversal.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyTraversal.cpp @@ -14,17 +14,17 @@ 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 ColorManagementSystem name."); - py::class_(mod, "TreeIterator") + py::class_(mod, "TreeIterator", "An iterator object representing the state of a tree traversal.") .def("getElement", &mx::TreeIterator::getElement) - .def("getElementDepth", &mx::TreeIterator::getElementDepth) - .def("setPruneSubtree", &mx::TreeIterator::setPruneSubtree) - .def("getPruneSubtree", &mx::TreeIterator::getPruneSubtree) + .def("getElementDepth", &mx::TreeIterator::getElementDepth, "Return the element depth of the current traversal, where a single edge between two elements represents a depth of one.") + .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); @@ -36,15 +36,15 @@ void bindPyTraversal(py::module& mod) return *it; }); - 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 edge.") + .def("getConnectingElement", &mx::GraphIterator::getConnectingElement, "Return the connecting element of the edge, if any.") + .def("getUpstreamElement", &mx::GraphIterator::getUpstreamElement, "Return the upstream element of the 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); @@ -56,7 +56,7 @@ void bindPyTraversal(py::module& mod) return *it; }); - 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); diff --git a/source/PyMaterialX/PyMaterialXCore/PyTypes.cpp b/source/PyMaterialX/PyMaterialXCore/PyTypes.cpp index fec7867b3a..a5644257de 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 value.") \ .def_static("__len__", &V::numElements) #define BIND_MATRIX_SUBCLASS(M, N) \ @@ -74,12 +74,12 @@ 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("copy", [](const M& m) { return M(m); }, "Create a deep copy of the value.") \ +.def("isEquivalent", &M::isEquivalent, "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("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) \ .def_static("createTranslation", &M::createTranslation) \ .def_static("numRows", &M::numRows) \ @@ -88,60 +88,60 @@ using IndexPair = std::pair; 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("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) .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("multiply", &mx::Matrix44::multiply, "Return the product of this matrix and a 3D vector.") + .def("transformPoint", &mx::Matrix44::transformPoint, "Transform the given 2D point.") + .def("transformVector", &mx::Matrix44::transformVector, "Transform the given 2D direction vector.") + .def("transformNormal", &mx::Matrix44::transformNormal, "Transform the given 3D normal vector.") .def_static("createRotationX", &mx::Matrix44::createRotationX) .def_static("createRotationY", &mx::Matrix44::createRotationY) .def_static("createRotationZ", &mx::Matrix44::createRotationZ) diff --git a/source/PyMaterialX/PyMaterialXCore/PyUnitConverter.cpp b/source/PyMaterialX/PyMaterialXCore/PyUnitConverter.cpp index bf3bcbf0f9..6d3a41df1e 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.\n\nReturns -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.\n\nReturns Empty string if not found"); - py::class_(mod, "LinearUnitConverter") + py::class_(mod, "LinearUnitConverter", "A converter class for linear units that require only a scalar multiplication.") .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); + .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") + py::class_(mod, "UnitConverterRegistry", "A registry for unit converters.") .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); + .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..4a544af4f4 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyUtil.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyUtil.cpp @@ -15,8 +15,8 @@ namespace mx = MaterialX; void bindPyUtil(py::module& mod) { - mod.def("getVersionString", &mx::getVersionString); - mod.def("getVersionIntegers", &mx::getVersionIntegers); + mod.def("getVersionString", &mx::getVersionString, "Return the version string of this interface."); + mod.def("getVersionIntegers", &mx::getVersionIntegers, "Return the major and minor versions as an integer pair."); mod.def("createValidName", &mx::createValidName, py::arg("name"), py::arg("replaceChar") = '_'); mod.def("isValidName", &mx::isValidName); mod.def("incrementName", &mx::incrementName); diff --git a/source/PyMaterialX/PyMaterialXCore/PyValue.cpp b/source/PyMaterialX/PyMaterialXCore/PyValue.cpp index c36bd6c288..ada14bf6b9 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 value string.") \ .def_static("createValue", &mx::Value::createValue) \ .def_readonly_static("TYPE", &mx::TypedValue::TYPE); @@ -20,9 +20,9 @@ namespace mx = MaterialX; void bindPyValue(py::module& mod) { - py::class_(mod, "Value") - .def("getValueString", &mx::Value::getValueString) - .def("getTypeString", &mx::Value::getTypeString) + py::class_(mod, "Value", "A generic, discriminated value, whose type may be queried dynamically.") + .def("getValueString", &mx::Value::getValueString, "Return value string.") + .def("getTypeString", &mx::Value::getTypeString, "Return type string.") .def_static("createValueFromStrings", &mx::Value::createValueFromStrings, py::arg("value"), py::arg("type"), diff --git a/source/PyMaterialX/PyMaterialXCore/PyVariant.cpp b/source/PyMaterialX/PyMaterialXCore/PyVariant.cpp index 13c7fa572c..bf087f140c 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyVariant.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyVariant.cpp @@ -12,23 +12,23 @@ 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") + py::class_(mod, "VariantSet", "A variant set element within a Document.") .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::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..48d34cad22 100644 --- a/source/PyMaterialX/PyMaterialXFormat/PyFile.cpp +++ b/source/PyMaterialX/PyMaterialXFormat/PyFile.cpp @@ -24,44 +24,44 @@ 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) + py::arg("format") = mx::FilePath::Format::FormatNative, "Return a single-line description of this element, including its category, name, and attributes.") + .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.") + .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) .def_static("getModulePath", &mx::FilePath::getModulePath); - 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); + py::arg("sep") = mx::PATH_LIST_SEPARATOR, "Return a single-line description of this element, including its category, name, and attributes.") + .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 strings in the path.") + .def("isEmpty", &mx::FileSearchPath::isEmpty, "Return true if the given 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..6cc4ab4c90 100644 --- a/source/PyMaterialX/PyMaterialXFormat/PyUtil.cpp +++ b/source/PyMaterialX/PyMaterialXFormat/PyUtil.cpp @@ -20,7 +20,7 @@ void bindPyUtil(py::module& mod) 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); + py::arg("file"), py::arg("doc"), py::arg("searchPath") = mx::FileSearchPath(), py::arg("readOptions") = (mx::XmlReadOptions*) nullptr, "Load a library of implementations from the provided document, replacing any previously loaded content."); 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, diff --git a/source/PyMaterialX/PyMaterialXFormat/PyXmlIo.cpp b/source/PyMaterialX/PyMaterialXFormat/PyXmlIo.cpp index 9b4b32cf6a..0f933f6f39 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,7 +21,7 @@ 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); diff --git a/source/PyMaterialX/PyMaterialXGenGlsl/PyGlslShaderGenerator.cpp b/source/PyMaterialX/PyMaterialXGenGlsl/PyGlslShaderGenerator.cpp index 87726352ff..496caec5d5 100644 --- a/source/PyMaterialX/PyMaterialXGenGlsl/PyGlslShaderGenerator.cpp +++ b/source/PyMaterialX/PyMaterialXGenGlsl/PyGlslShaderGenerator.cpp @@ -41,16 +41,16 @@ namespace void bindPyGlslShaderGenerator(py::module& mod) { - py::class_(mod, "GlslShaderGenerator") + 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) - .def("generate", &mx::GlslShaderGenerator::generate) - .def("getTarget", &mx::GlslShaderGenerator::getTarget) - .def("getVersion", &mx::GlslShaderGenerator::getVersion); + .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 ESSL version this generator is for."); } void bindPyGlslResourceBindingContext(py::module &mod) { - py::class_(mod, "GlslResourceBindingContext") + 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) @@ -61,31 +61,31 @@ void bindPyGlslResourceBindingContext(py::module &mod) void bindPyEsslShaderGenerator(py::module& mod) { - py::class_(mod, "EsslShaderGenerator") + py::class_(mod, "EsslShaderGenerator", "An ESSL (OpenGL ES Shading Language) shader generator.") .def_static("create", &EsslShaderGenerator_create) - .def("generate", &mx::EsslShaderGenerator::generate) - .def("getTarget", &mx::EsslShaderGenerator::getTarget) - .def("getVersion", &mx::EsslShaderGenerator::getVersion); + .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") + py::class_(mod, "VkShaderGenerator", "A Vulkan GLSL shader generator.") .def_static("create", &VkShaderGenerator_create) - .def("generate", &mx::VkShaderGenerator::generate) - .def("getTarget", &mx::VkShaderGenerator::getTarget) - .def("getVersion", &mx::VkShaderGenerator::getVersion); + .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 ESSL version this generator is for."); } // Glsl Wgsl shader generator bindings void bindPyWgslShaderGenerator(py::module& mod) { - py::class_(mod, "WgslShaderGenerator") + py::class_(mod, "WgslShaderGenerator", "WGSL Flavor of Vulkan GLSL shader generator") .def_static("create", &WgslShaderGenerator_create) - .def("generate", &mx::WgslShaderGenerator::generate) - .def("getTarget", &mx::WgslShaderGenerator::getTarget) - .def("getVersion", &mx::WgslShaderGenerator::getVersion); + .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 ESSL version this generator is for."); } diff --git a/source/PyMaterialX/PyMaterialXGenMdl/PyMdlShaderGenerator.cpp b/source/PyMaterialX/PyMaterialXGenMdl/PyMdlShaderGenerator.cpp index b62a84a65f..ef2387c9d9 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") + py::class_(mod, "MdlShaderGenerator", "Shader generator for MDL (Material Definition Language).") .def_static("create", &MdlShaderGenerator_create) - .def("getTarget", &mx::MdlShaderGenerator::getTarget); + .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..2aa00492ae 100644 --- a/source/PyMaterialX/PyMaterialXGenMsl/PyMslShaderGenerator.cpp +++ b/source/PyMaterialX/PyMaterialXGenMsl/PyMslShaderGenerator.cpp @@ -29,9 +29,9 @@ 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("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 ESSL version this generator is for."); } void bindPyMslResourceBindingContext(py::module &mod) diff --git a/source/PyMaterialX/PyMaterialXGenOsl/PyOslShaderGenerator.cpp b/source/PyMaterialX/PyMaterialXGenOsl/PyOslShaderGenerator.cpp index 6e457411b9..44ae989877 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") + 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) - .def("getTarget", &mx::OslShaderGenerator::getTarget) - .def("generate", &mx::OslShaderGenerator::generate); + .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..e3d0a3e645 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") + py::class_(mod, "DefaultColorManagementSystem", "Class for a default color management system.") .def_static("create", &mx::DefaultColorManagementSystem::create) - .def("getName", &mx::DefaultColorManagementSystem::getName); + .def("getName", &mx::DefaultColorManagementSystem::getName, "Return the ColorManagementSystem 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 ColorManagementSystem name."); #endif } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyGenContext.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyGenContext.cpp index e73090e964..8df02a49a2 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 our self pointer."); } \ 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..b51a46fba9 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 ColorManagementSystem 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 given attribute is present.") + .def("getAttribute", &mx::Shader::getAttribute, "Return the value string of the given attribute.\n\nIf the given attribute is not present, then an empty string is returned.") + .def("setAttribute", static_cast(&mx::Shader::setAttribute), "Set the value string of the given attribute.") + .def("setAttribute", static_cast(&mx::Shader::setAttribute), "Set the value string of the given attribute."); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyShaderGenerator.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyShaderGenerator.cpp index fdf4b94aac..dd8c568392 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 a unique identifier for 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, "Set the color management system string.") + .def("getColorManagementSystem", &mx::ShaderGenerator::getColorManagementSystem, "Return the color management system string.") + .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."); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyShaderPort.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyShaderPort.cpp index 146674c222..0b8855935e 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, "Get stream attribute name.") + .def("setName", &mx::ShaderPort::setName, "Set the element's name string.") + .def("getName", &mx::ShaderPort::getName, "Return the ColorManagementSystem name.") + .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 the typed value of an element from a C-style string.") + .def("getValue", &mx::ShaderPort::getValue, "Returns a value formatted according to this type syntax.\n\nThe value is constructed from the given value object.") + .def("getValueString", &mx::ShaderPort::getValueString, "Return value string.") + .def("setGeomProp", &mx::ShaderPort::setGeomProp, "Set the geometric property string of this element.") + .def("getGeomProp", &mx::ShaderPort::getGeomProp, "Return the GeomProp, if any, with the given 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 the element's color space string.") + .def("getColorSpace", &mx::ShaderPort::getColorSpace, "Return the element's color space string.") + .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..f05a6d7680 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, "Return the ColorManagementSystem name.") + .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 strings in the path.") + .def("find", static_cast(&mx::VariableBlock::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.") + .def("find", (mx::ShaderPort* (mx::VariableBlock::*)(const mx::ShaderPortPredicate& )) &mx::VariableBlock::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.") + .def("__len__", &mx::VariableBlock::size, "Return the number of strings in the path.") .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::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 ColorManagementSystem name.") + .def("getFunctionName", &mx::ShaderStage::getFunctionName, "Return the stage function name.") + .def("getSourceCode", &mx::ShaderStage::getSourceCode, "Return the shader source code for a given shader stage.") + .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..e26cb2e51e 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") + py::class_(mod, "ShaderTranslator", "A helper class for translating content between shading models.") .def_static("create", &mx::ShaderTranslator::create) - .def("translateShader", &mx::ShaderTranslator::translateShader) - .def("translateAllMaterials", &mx::ShaderTranslator::translateAllMaterials); + .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..c5ef35df57 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 ColorManagementSystem name.") + .def("getBaseType", &mx::TypeDesc::getBaseType, "Return the base type of the image.") + .def("getSemantic", &mx::TypeDesc::getSemantic, "Return the variable semantic of this port.") + .def("getSize", &mx::TypeDesc::getSize, "Get the number of elements.") + .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..59f30a5953 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") + py::class_(mod, "UnitSystem", "Base unit system support.") .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); + .def("getName", &mx::UnitSystem::getName, "Return the ColorManagementSystem name.") + .def("loadLibrary", &mx::UnitSystem::loadLibrary, "Load a library of implementations from the provided document, replacing any previously loaded content.") + .def("supportsTransform", &mx::UnitSystem::supportsTransform, "Returns whether this color management 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/PyMaterialXRender/PyCamera.cpp b/source/PyMaterialX/PyMaterialXRender/PyCamera.cpp index 9c19ceb831..d0c751be0e 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyCamera.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyCamera.cpp @@ -12,21 +12,21 @@ namespace mx = MaterialX; void bindPyCamera(py::module& mod) { - py::class_(mod, "Camera") + py::class_(mod, "Camera", "A simple camera class, supporting transform matrices and arcball functionality for object-viewing applications.") .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("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) .def_static("createPerspectiveMatrix", &mx::Camera::createPerspectiveMatrix) .def_static("createOrthographicMatrix", &mx::Camera::createOrthographicMatrix) diff --git a/source/PyMaterialX/PyMaterialXRender/PyCgltfLoader.cpp b/source/PyMaterialX/PyMaterialXRender/PyCgltfLoader.cpp index 158e6b4849..405b69fc87 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") + py::class_(mod, "CgltfLoader", "Wrapper for loader to read in GLTF files using the Cgltf library.") .def_static("create", &mx::CgltfLoader::create) .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..dcf5d80a64 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, "Get a list of extensions supported by the handler.") + .def("load", &mx::GeometryLoader::load, "Load geometry from file path."); - 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("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) - .def("getMeshes", &mx::GeometryHandler::getMeshes) - .def("findParentMesh", &mx::GeometryHandler::findParentMesh) - .def("getMinimumBounds", &mx::GeometryHandler::getMinimumBounds) - .def("getMaximumBounds", &mx::GeometryHandler::getMaximumBounds); + .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..9e0578e40a 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyImage.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyImage.cpp @@ -21,31 +21,31 @@ void bindPyImage(py::module& mod) py::class_(mod, "ImageBufferDeallocator"); - py::class_(mod, "Image") + py::class_(mod, "Image", "Class representing an image in system memory.") .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); + .def("getWidth", &mx::Image::getWidth, "Return the width attribute of the backdrop.") + .def("getHeight", &mx::Image::getHeight, "Return the height attribute of the backdrop.") + .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 deep copy of the value.") + .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); diff --git a/source/PyMaterialX/PyMaterialXRender/PyImageHandler.cpp b/source/PyMaterialX/PyMaterialXRender/PyImageHandler.cpp index d261bc7306..2dfc0a5d1f 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,28 @@ 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("supportedExtensions", &mx::ImageLoader::supportedExtensions, "Get a list of extensions supported by the handler.") + .def("saveImage", &mx::ImageLoader::saveImage, "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("loadImage", &mx::ImageLoader::loadImage); - py::class_(mod, "ImageHandler") + 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) + .def("addLoader", &mx::ImageHandler::addLoader, "Add a geometry loader.\n\nArgs:\n loader: Loader to add to list of available loaders.") .def("saveImage", &mx::ImageHandler::saveImage, - py::arg("filePath"), py::arg("image"), py::arg("verticalFlip") = false) + 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)) - .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) + 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 a single image.") + .def("unbindImage", &mx::ImageHandler::unbindImage, "Unbind an image.") + .def("unbindImages", &mx::ImageHandler::unbindImages, "Unbbind all images for this material.") + .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) - .def("clearImageCache", &mx::ImageHandler::clearImageCache) - .def("getZeroImage", &mx::ImageHandler::getZeroImage) - .def("getReferencedImages", &mx::ImageHandler::getReferencedImages); + 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..d3da769ba7 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") + py::class_(mod, "LightHandler", "Utility light handler for creating and providing light data for shader binding.") .def_static("create", &mx::LightHandler::create) .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..38be0568fd 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) @@ -22,56 +22,56 @@ void bindPyMesh(py::module& mod) .def_readonly_static("GEOMETRY_PROPERTY_ATTRIBUTE", &mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE) .def_static("create", &mx::MeshStream::create) .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 the given number of indices.") + .def("getName", &mx::MeshStream::getName, "Return the ColorManagementSystem name.") + .def("getType", &mx::MeshStream::getType, "Get stream attribute name.") + .def("getIndex", &mx::MeshStream::getIndex, "Return the index string of this element.") + .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") + 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) .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 element's name string.") + .def("getName", &mx::MeshPartition::getName, "Return the ColorManagementSystem name.") + .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") + py::class_(mod, "Mesh", "Container for mesh data.") .def_static("create", &mx::Mesh::create) .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 ColorManagementSystem name.") + .def("setSourceUri", &mx::Mesh::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::Mesh::hasSourceUri, "Return true if this element has a source URI.") + .def("getSourceUri", &mx::Mesh::getSourceUri, "Return the element'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 all meshes.") + .def("setMaximumBounds", &mx::Mesh::setMaximumBounds, "Set the minimum bounds for the geometry.") + .def("getMaximumBounds", &mx::Mesh::getMaximumBounds, "Return the minimum bounds for all meshes.") + .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..d7feb8e59b 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyOiioImageLoader.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyOiioImageLoader.cpp @@ -15,6 +15,6 @@ void bindPyOiioImageLoader(py::module& mod) py::class_(mod, "OiioImageLoader") .def_static("create", &mx::OiioImageLoader::create) .def(py::init<>()) - .def("saveImage", &mx::OiioImageLoader::saveImage) + .def("saveImage", &mx::OiioImageLoader::saveImage, "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("loadImage", &mx::OiioImageLoader::loadImage); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp b/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp index 94c0d49cee..6352ffc3d1 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 with the given implementation element.\n\nInitialization must set the name and hash for the implementation, as well as any other data needed to emit code for the node.") + .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 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::ShaderRenderer::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::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 an offscreen buffer."); static py::exception pyExceptionRenderError(mod, "ExceptionRenderError"); diff --git a/source/PyMaterialX/PyMaterialXRender/PyStbImageLoader.cpp b/source/PyMaterialX/PyMaterialXRender/PyStbImageLoader.cpp index 4c710746c1..110e28d145 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") + py::class_(mod, "StbImageLoader", "Stb image file loader.") .def_static("create", &mx::StbImageLoader::create) - .def("saveImage", &mx::StbImageLoader::saveImage) + .def("saveImage", &mx::StbImageLoader::saveImage, "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("loadImage", &mx::StbImageLoader::loadImage); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyTinyObjLoader.cpp b/source/PyMaterialX/PyMaterialXRender/PyTinyObjLoader.cpp index 13677453b9..cb967d729a 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") + py::class_(mod, "TinyObjLoader", "Wrapper for geometry loader to read in OBJ files using the TinyObj library.") .def_static("create", &mx::TinyObjLoader::create) .def(py::init<>()) - .def("load", &mx::TinyObjLoader::load); + .def("load", &mx::TinyObjLoader::load, "Load geometry from file path."); } diff --git a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp index 9ebbd93ecb..3f62d83de9 100644 --- a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp +++ b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp @@ -12,11 +12,11 @@ namespace mx = MaterialX; void bindPyGLTextureHandler(py::module& mod) { - py::class_(mod, "GLTextureHandler") + py::class_(mod, "GLTextureHandler", "An OpenGL texture handler class.") .def_static("create", &mx::GLTextureHandler::create) - .def("bindImage", &mx::GLTextureHandler::bindImage) - .def("unbindImage", &mx::GLTextureHandler::unbindImage) - .def("createRenderResources", &mx::GLTextureHandler::createRenderResources) + .def("bindImage", &mx::GLTextureHandler::bindImage, "Bind a single image.") + .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); + 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..8c84521ac9 100644 --- a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslProgram.cpp +++ b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslProgram.cpp @@ -12,35 +12,35 @@ 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("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) - .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("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 a mesh partition to this material.") + .def("bindMesh", &mx::GlslProgram::bindMesh, "Bind the given mesh to this material.") + .def("unbindGeometry", &mx::GlslProgram::unbindGeometry, "Unbind all geometry from this material.") + .def("bindTextures", &mx::GlslProgram::bindTextures, "Bind any input textures.") + .def("bindLighting", &mx::GlslProgram::bindLighting, "Bind lights to shader.") + .def("bindViewInformation", &mx::GlslProgram::bindViewInformation, "Bind viewing information for this material.") .def("bindTimeAndFrame", &mx::GlslProgram::bindTimeAndFrame, - py::arg("time") = 0.0f, py::arg("frame") = 1.0f) - .def("unbind", &mx::GlslProgram::unbind); + 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..07027650b6 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") + 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) - .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); + .def("initialize", &mx::GlslRenderer::initialize, py::arg("renderContextHandle") = nullptr, "Initialize with the given implementation element.\n\nInitialization must set the name and hash for the implementation, as well as any other data needed to emit code for the node.") + .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 underlying GLSL program."); } diff --git a/source/PyMaterialX/PyMaterialXRenderGlsl/PyTextureBaker.cpp b/source/PyMaterialX/PyMaterialXRenderGlsl/PyTextureBaker.cpp index 800ddf590a..18637b69a6 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") + 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) - .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); + .def("setExtension", &mx::TextureBakerGlsl::setExtension, "Set the file extension for baked textures.") + .def("getExtension", &mx::TextureBakerGlsl::getExtension, "Return the file extension of the given path.") + .def("setColorSpace", &mx::TextureBakerGlsl::setColorSpace, "Set the element's color space string.") + .def("getColorSpace", &mx::TextureBakerGlsl::getColorSpace, "Return the element's color space string.") + .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..cb74359c27 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") + 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) .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, "Initialize with the given implementation element.\n\nInitialization must set the name and hash for the implementation, as well as any other data needed to emit code for the node.") + .def("createProgram", static_cast(&mx::OslRenderer::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::OslRenderer::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::OslRenderer::validateInputs, "Validate inputs for the program.") + .def("render", &mx::OslRenderer::render, "Render the current program to an offscreen buffer.") + .def("captureImage", &mx::OslRenderer::captureImage, "Capture the current contents of the off-screen hardware buffer 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."); } From 0c3dc8532ce8ce0a31b8a9f7e96b44fd1c6cb1af Mon Sep 17 00:00:00 2001 From: Bernard Kwok Date: Tue, 4 Nov 2025 15:34:48 -0500 Subject: [PATCH 2/9] Simplify and add in "force" replacement of docs. --- python/Scripts/pybind_docs.py | 679 +++++++----------- .../PyMaterialXGenShader/PyShaderStage.cpp | 2 +- 2 files changed, 269 insertions(+), 412 deletions(-) diff --git a/python/Scripts/pybind_docs.py b/python/Scripts/pybind_docs.py index 00f30db6da..db26698d40 100644 --- a/python/Scripts/pybind_docs.py +++ b/python/Scripts/pybind_docs.py @@ -8,20 +8,61 @@ DOXYGEN_XML_DIR = Path("build/documents/doxygen_xml") PYBIND_DIR = Path("source/PyMaterialX") -# Output debug dump of extracted docs -DEBUG_JSON = Path("doc_map.json") - -class pybind_doc_builder: - def __init__(self, doxygen_xml_dir: Path, pybind_dir: Path): - self.doxygen_xml_dir = doxygen_xml_dir - self.pybind_dir = pybind_dir - self.class_docs = {} - self.func_docs = {} - # Extraction maps class_docs = {} # key: "MaterialX::Document" -> docstring func_docs = {} # key: "MaterialX::Document::addNodeDefFromGraph" -> dict {brief, detail, params:{name:desc}, returns, args_sig} +# ---------------------------- +# Helper functions +# ---------------------------- +def normalize_name(name: str) -> str: + """Ensure name has MaterialX:: prefix if not already present.""" + if not name: + return name + return name if name.startswith("MaterialX::") else f"MaterialX::{name}" + +def generate_lookup_keys(name: str, include_variants: bool = True) -> list: + """Generate all possible lookup key variants for a given name.""" + if not name: + return [] + + keys = [] + # Add MaterialX:: variant + normalized = normalize_name(name) + if normalized != name: + keys.append(normalized) + keys.append(name) + + # Add mx:: to MaterialX:: conversion + if name.startswith("mx::"): + keys.insert(0, name.replace("mx::", "MaterialX::")) + + # Add class::method and method-only variants + if include_variants and "::" in name: + parts = name.split("::") + if len(parts) >= 2: + keys.append("::".join(parts[-2:])) # Class::method + keys.append(parts[-1]) # method only + + return keys + +def extract_detail_text(parent_elem, exclude_tags={'parameterlist', 'simplesect'}): + """Extract detail paragraphs from XML element, excluding specified child tags.""" + parts = [] + for para in parent_elem.findall("para"): + if not any(para.find(tag) is not None for tag in exclude_tags): + parts.append(_text_of(para)) + return "\n\n".join(p for p in parts if p) + +def find_doc_by_suffix(func_docs: dict, *suffixes) -> dict: + """Find function doc by suffix match. Returns first match or None.""" + for suffix in suffixes: + if suffix: + for key, value in func_docs.items(): + if key.endswith(f"::{suffix}"): + return value + return None + def _text_of(elem): """Concatenate element text and children text, normalized whitespace.""" if elem is None: @@ -53,24 +94,12 @@ def extract_docs_from_xml(): for compound in root.findall(".//compounddef[@kind='class']") + root.findall(".//compounddef[@kind='struct']"): compoundname = _text_of(compound.find("compoundname")) brief = _text_of(compound.find("briefdescription/para")) - # collect detailed paragraphs excluding parameterlist/simplesect which are typically for members - detail_parts = [] - for para in compound.findall("detaileddescription/para"): - if para.find("parameterlist") is None and para.find("simplesect") is None: - detail_parts.append(_text_of(para)) - detail = "\n\n".join(p for p in detail_parts if p) - doc_parts = [] - if brief: - doc_parts.append(brief) - if detail: - doc_parts.append(detail) + detail = extract_detail_text(compound.find("detaileddescription")) + + doc_parts = [p for p in [brief, detail] if p] full = "\n\n".join(doc_parts).strip() if full: - # normalize name to MaterialX::... if not already - key = compoundname - if key and not key.startswith("MaterialX::"): - key = "MaterialX::" + key - class_docs[key] = full + class_docs[normalize_name(compoundname)] = full # Extract member functions for member in root.findall(".//memberdef[@kind='function']"): @@ -86,13 +115,7 @@ def extract_docs_from_xml(): # brief + detailed brief = _text_of(member.find("briefdescription/para")) - detail_parts = [] - for para in member.findall("detaileddescription/para"): - # skip param lists and return sections here - if para.find("parameterlist") is not None or para.find("simplesect") is not None: - continue - detail_parts.append(_text_of(para)) - detail = "\n\n".join(p for p in detail_parts if p) + detail = extract_detail_text(member.find("detaileddescription")) # params params = {} @@ -108,32 +131,23 @@ def extract_docs_from_xml(): ret_text = _text_of(retsect) # argsstring for overload disambiguation - argsstring = _text_of(member.find("argsstring")) # e.g. "(NodeGraphPtr nodeGraph, const string &nodeDefName, ...)" - # canonicalize argsstring: remove repeated spaces and normalize commas + argsstring = _text_of(member.find("argsstring")) args_sig = "" if argsstring: + # Normalize argsstring args_sig = re.sub(r'\s*,\s*', ',', argsstring) args_sig = re.sub(r'\s+', ' ', args_sig).strip() - # normalize qualified name to MaterialX:: prefix variants - q1 = qualified - if q1 and not q1.startswith("MaterialX::"): - q_mat = "MaterialX::" + q1 - else: - q_mat = q1 - - # choose a primary key for func_docs as the fully qualified name (prefer MaterialX::... form) - primary_key = q_mat if q_mat else q1 - if not primary_key: - continue - - func_docs[primary_key] = { - "brief": brief, - "detail": detail, - "params": params, - "returns": ret_text, - "args_sig": args_sig - } + # Store with normalized qualified name + if qualified: + primary_key = normalize_name(qualified) + func_docs[primary_key] = { + "brief": brief, + "detail": detail, + "params": params, + "returns": ret_text, + "args_sig": args_sig + } print(f"Extracted {len(class_docs)} classes and {len(func_docs)} functions.") @@ -172,308 +186,118 @@ def build_method_docstring(func_entry): parts.append("Returns:\n " + func_entry["returns"]) return "\n\n".join(parts).strip() -def insert_docs_into_bindings_old(): - """ - Modify C++ pybind files in place: - - Insert class docstrings into the py::class_<...>(mod, "Name", "doc") - - Insert method docstrings into .def("name", &mx::Class::name, "doc") - """ - cpp_files = list(PYBIND_DIR.rglob("*.cpp")) - - # Regex patterns - # py::class_<...>(mod, "Document") - class_pattern = re.compile(r'(py::class_<[^>]+>\(\s*([A-Za-z0-9_:]+)\s*,\s*"([^"]+)"\s*)(\))') - # Then a variant where there are spaces/newline before closing paren: handle simpler by searching for 'py::class_<...' up to the first ')' on the same line. - - # .def("name", &mx::Document::name, ...) - def_pattern = re.compile(r'\.def\(\s*"([^"]+)"\s*,\s*&([A-Za-z0-9_:]+)\s*([,)]?)') - - for cpp in cpp_files: - text = cpp.read_text(encoding="utf-8") - orig_text = text - - lines = text.splitlines() - new_lines = [] - changed = False - - for i, line in enumerate(lines): - # First: try class insertion when a py::class_ is declared - # We look for the pattern: py::class_<...>(mod, "Name") - class_match = re.search(r'py::class_<[^>]+>\(\s*([A-Za-z0-9_:]+)\s*,\s*"([^"]+)"\s*\)', line) - if class_match: - cpp_class_type = class_match.group(1) # maybe mx::Document, etc - class_name = class_match.group(2) # "Document" - # Normalize to MaterialX::ClassName - # We try keys: MaterialX::Document, Document, mx::Document - keys = [] - if cpp_class_type: - # if cpp_class_type includes 'mx::', remove and use just the class name - short = class_name - keys.append(f"MaterialX::{short}") - keys.append(short) - if cpp_class_type.startswith("mx::"): - keys.append(cpp_class_type.replace("mx::", "MaterialX::")) - else: - keys.append(cpp_class_type) - else: - keys.append(class_name) - keys.append(f"MaterialX::{class_name}") - - class_doc = None - for k in keys: - if k in class_docs: - class_doc = class_docs[k] - break - - if class_doc: - # If the line already has a third argument (docstring) skip - # Simple check: count commas inside the parentheses - but safer to check presence of a string literal after the class name - if re.search(r'py::class_<[^>]+>\(\s*[A-Za-z0-9_:]+\s*,\s*"' + re.escape(class_name) + r'"\s*,', line): - # already has doc - do nothing - pass - else: - escaped = escape_docstring_for_cpp(class_doc) - # Insert as third argument before the closing paren - new_line = re.sub(r'\)\s*$', f', "{escaped}")', line) - new_lines.append(new_line) - changed = True - continue # skip default append at end - # Next: method .def insertion - m = def_pattern.search(line) - if m: - py_name = m.group(1) # "createInput" - cpp_ref = m.group(2) # mx::Document::addNodeDefFromGraph or Document::... - # Normalize cpp_ref to MaterialX::... form to lookup func_docs - lookup_keys = [] - if cpp_ref.startswith("mx::"): - lookup_keys.append(cpp_ref.replace("mx::", "MaterialX::")) - lookup_keys.append(cpp_ref) - # Also try without namespace - if "::" in cpp_ref: - parts = cpp_ref.split("::") - lookup_keys.append("::".join(parts[-2:])) # Class::method - lookup_keys.append(parts[-1]) # method only - - func_entry = None - for k in lookup_keys: - if k in func_docs: - func_entry = func_docs[k] - break - - # If not found, try suffix match (sometimes args_sig stored) - if not func_entry: - for k, v in func_docs.items(): - # match just by 'Class::method' ending part - if k.endswith("::" + py_name): - func_entry = v - break - - if func_entry: - # Check if this .def already has a docstring (a string literal after the callable) - # We'll consider the remainder of the line after the callable - rest = line[m.end():].strip() - already_has_doc = False - # crude check: does a string literal appear before the closing ')' - if '"' in rest or "R\"" in rest: - already_has_doc = True - - if not already_has_doc: - docstring = build_method_docstring(func_entry) - if docstring: - escaped = escape_docstring_for_cpp(docstring) - # We must be careful to not break existing trailing arguments (py::arg(...)) - # We'll try to insert the docstring after the callable reference and before other args. - # If line ends with ')' we can simply replace the trailing ')' with , "doc") - if line.rstrip().endswith(")"): - new_line = line.rstrip() - # but avoid adding doc into lines that are multi-line function chains; this is a best-effort inline insertion - new_line = new_line.rstrip(")") - new_line = new_line + f', "{escaped}")' - new_lines.append(new_line) - changed = True - continue - else: - # line doesn't end with ')', likely args continue on following lines; just add doc at line end for now - new_line = line + f', "{escaped}"' - new_lines.append(new_line) - changed = True - continue - - # default: copy original line - new_lines.append(line) - - # if changed, write back - if changed: - new_text = "\n".join(new_lines) - cpp.write_text(new_text, encoding="utf-8") - #print("new text", new_text) - print(f"Patched: {cpp}") - - print("Insertion complete.") - -# Replace the previous insert_docs_into_bindings() with this more robust version. +def _has_docstring(args): + """Check if any arg (after first) is a string literal, excluding py::arg calls.""" + for arg_text, _, _ in args[1:]: + a = arg_text.strip() + if a.startswith("py::arg"): + continue + if re.match(r'^".*"$', a): + return True + return False def _find_matching_paren(s: str, start_idx: int) -> int: - """Find the index of the matching ')' for s[start_idx] == '('. - Handles nested parentheses and string literals roughly (so quotes inside strings are ignored).""" - i = start_idx + """Find the index of the matching ')' for '(' at start_idx. + Handles nested parentheses and string literals.""" depth = 0 - in_single = False - in_double = False - in_raw = False - raw_delim = None - L = len(s) - while i < L: + in_string = False + escape_next = False + i = start_idx + + while i < len(s): c = s[i] - # handle entering/exiting raw string literal R"delim(... )delim" - if not in_single and not in_double: - if s.startswith('R"', i): - # find raw delim start: R"delim( - # grab delim between R" and ( - m = re.match(r'R"([^\(\s]*)\(', s[i:]) - if m: - in_raw = True - raw_delim = m.group(1) - i += 2 + len(raw_delim) # position at '(' after delim - depth += 1 - i += 1 - continue - - if in_raw: - # raw literal ends with )delim" - # check for )raw_delim" - end_token = ')' + (raw_delim or '') + '"' - if s.startswith(end_token, i): - in_raw = False - i += len(end_token) - continue - else: - i += 1 - continue - - if c == '"' and not in_single: - # toggle double quotes (but ignore escaped quotes) - # ensure not escaped - prev = s[i-1] if i > 0 else '' - if prev != '\\': - in_double = not in_double + + if escape_next: + escape_next = False i += 1 continue - if c == "'" and not in_double: - prev = s[i-1] if i > 0 else '' - if prev != '\\': - in_single = not in_single + + if c == '\\': + escape_next = True i += 1 continue - - if in_single or in_double: + + if c == '"': + in_string = not in_string i += 1 continue - - if c == '(': - depth += 1 - elif c == ')': - depth -= 1 - if depth == 0: - return i + + if not in_string: + if c == '(': + depth += 1 + elif c == ')': + depth -= 1 + if depth == 0: + return i + i += 1 + return -1 def _split_top_level_args(arglist: str): - """Split a string of arguments (content inside parentheses) into a list of top-level args. - Returns list of (arg_text, start_index, end_index) relative to the arglist string. - Handles nested parentheses and string literals so commas inside those are ignored. - """ + """Split arguments at top-level commas. + Returns list of (arg_text, start_index, end_index) tuples. + Handles nested parentheses and string literals.""" args = [] start = 0 i = 0 depth = 0 - in_single = False - in_double = False - in_raw = False - raw_delim = None - L = len(arglist) - while i < L: + in_string = False + escape_next = False + + while i < len(arglist): c = arglist[i] - # raw string handling - if not in_single and not in_double: - if arglist.startswith('R"', i): - m = re.match(r'R"([^\(\s]*)\(', arglist[i:]) - if m: - in_raw = True - raw_delim = m.group(1) - i += 2 + len(raw_delim) # move to '(' - depth += 1 - i += 1 - continue - - if in_raw: - end_token = ')' + (raw_delim or '') + '"' - if arglist.startswith(end_token, i): - in_raw = False - i += len(end_token) - continue - else: - i += 1 - continue - - if c == '"' and not in_single: - prev = arglist[i-1] if i > 0 else '' - if prev != '\\': - in_double = not in_double + + if escape_next: + escape_next = False i += 1 continue - if c == "'" and not in_double: - prev = arglist[i-1] if i > 0 else '' - if prev != '\\': - in_single = not in_single + + if c == '\\': + escape_next = True i += 1 continue - - if in_single or in_double: + + if c == '"': + in_string = not in_string i += 1 continue - - if c == '(': - depth += 1 - elif c == ')': - if depth > 0: + + if not in_string: + if c == '(': + depth += 1 + elif c == ')': depth -= 1 - elif c == ',' and depth == 0: - # top-level comma - args.append((arglist[start:i].strip(), start, i)) - start = i + 1 + elif c == ',' and depth == 0: + # Top-level comma found + args.append((arglist[start:i].strip(), start, i)) + start = i + 1 + i += 1 - - # append last arg - if start < L: - args.append((arglist[start:].strip(), start, L)) + + # Append last argument + if start < len(arglist): + args.append((arglist[start:].strip(), start, len(arglist))) + return args -def _has_docstring(args): - """ - True if there is a string literal among top-level args that is not a py::arg(...) - """ - for a, _, _ in args[1:]: # skip first arg (Python name) - # strip whitespace - a_stripped = a.strip() - # skip py::arg(...) calls - if a_stripped.startswith("py::arg"): - continue - # crude string literal check - if re.match(r'^R?".*"$', a_stripped): - return True - return False - -def insert_docs_into_bindings(): +def insert_docs_into_bindings(force_replace=False): """ Robust insertion: 1. Insert class docstrings into py::class_<...>(mod, "Name") -> py::class_<...>(mod, "Name", "doc") 2. Parse each .def(...) call, split top-level args, and insert docstring after the second argument (callable) if no docstring is present. + + Args: + force_replace: If True, replace existing docstrings. If False, skip entries that already have docs. """ + if force_replace: + print("Force replace mode: Will update existing docstrings") + else: + print("Normal mode: Will skip entries with existing docstrings") + cpp_files = list(PYBIND_DIR.rglob("*.cpp")) def_start_re = re.compile(r'\.def\s*\(') class_start_re = re.compile(r'py::class_<') @@ -516,34 +340,58 @@ def insert_docs_into_bindings(): # Check if there's already a third argument (docstring) if len(args) >= 3: - # Already has docstring - continue + if not force_replace: + # Already has docstring, skip unless force_replace + continue + else: + # Force replace: we'll need to replace the third arg + # Store as (start, end, new_doc) for replacement + pass # Handle below # Extract class name from second argument class_name = args[1][0].strip().strip('"') - # Try to find documentation - # Build lookup keys - lookup_keys = [ - f"MaterialX::{class_name}", - class_name - ] - + # Find documentation using helper class_doc = None - for k in lookup_keys: + for k in generate_lookup_keys(class_name, include_variants=False): if k in class_docs: class_doc = class_docs[k] break if class_doc: escaped = escape_docstring_for_cpp(class_doc) - class_matches.append((paren_close, f', "{escaped}"')) + if len(args) >= 3 and force_replace: + # Replace existing docstring (third argument) - just replace the quoted content + # Find the position of the third argument in the original text + arg3_start = paren_open + 1 + args[2][1] + arg3_end = paren_open + 1 + args[2][2] + original_arg = text[arg3_start:arg3_end] + + # Find the opening and closing quotes + first_quote = original_arg.find('"') + last_quote = original_arg.rfind('"') + + if first_quote != -1 and last_quote != -1 and first_quote < last_quote: + # Replace only the content between quotes, preserving everything else + new_arg = original_arg[:first_quote+1] + escaped + original_arg[last_quote:] + class_matches.append((arg3_start, arg3_end, new_arg, True)) + else: + # Fallback: replace entire argument + class_matches.append((arg3_start, arg3_end, f'"{escaped}"', True)) + else: + # Insert new docstring (with space after comma to match original style) + class_matches.append((paren_close, f', "{escaped}"', False)) # False = insert class_changed = True - # Apply class documentation insertions in reverse order to preserve positions + # Apply class documentation changes in reverse order to preserve positions if class_changed: - for pos, insertion in reversed(class_matches): - text = text[:pos] + insertion + text[pos:] + for match in reversed(class_matches): + if len(match) == 4: # Replace mode: (start, end, new_text, is_replace) + start, end, new_text, _ = match + text = text[:start] + new_text + text[end:] + else: # Insert mode: (pos, insertion, is_replace) + pos, insertion, _ = match + text = text[:pos] + insertion + text[pos:] # Second pass: insert method documentation new_text_parts = [] @@ -587,81 +435,85 @@ def insert_docs_into_bindings(): # determine if any existing argument is a string literal (treat raw string R"..." too) has_docstring = _has_docstring(args) - # A simpler check: presence of an unqualified string literal among top-level args other than the first (name) arg - if not has_docstring: - # build lookup keys using the second arg (callable) - cpp_ref = second_arg_text - # strip & and possible std::function wrappers, templates, whitespace - cpp_ref_clean = cpp_ref.strip() + + if not has_docstring or force_replace: + # Extract Python method name and C++ callable reference + py_method_name = first_arg_text.strip().strip('"') + cpp_ref_clean = second_arg_text.strip() - # Check if this is a lambda function (starts with '[') - is_lambda = cpp_ref_clean.startswith('[') + # Extract the callable name (works for both regular functions and lambdas) + # For lambdas: try to find method calls like elem.method( or obj->method( + # For regular callables: extract the qualified name like &mx::Class::method + callable_name = None - if is_lambda: - # For lambda functions, try to infer the method name from the Python method name - # and look for it being called within the lambda - py_method_name = first_arg_text.strip().strip('"') - lookup_keys = [] - - # Try to find method calls within the lambda body - # Look for patterns like elem.method( or obj->method( - method_call_pattern = re.search(r'[\.\->](\w+)\s*\(', cpp_ref_clean) - if method_call_pattern: - called_method = method_call_pattern.group(1) - # Try to match with various namespace prefixes - lookup_keys.append(f"MaterialX::Element::{called_method}") - lookup_keys.append(f"MaterialX::{called_method}") - lookup_keys.append(called_method) - - # Also try using the Python method name directly - lookup_keys.append(f"MaterialX::Element::{py_method_name}") - lookup_keys.append(f"MaterialX::{py_method_name}") - lookup_keys.append(py_method_name) - - possible = py_method_name + # Check for method call pattern (handles lambdas and some edge cases) + method_call = re.search(r'[\.\->](\w+)\s*\(', cpp_ref_clean) + if method_call: + callable_name = method_call.group(1) else: - # remove address-of operator and potential casts like (py::cpp_function) &foo — try to extract last token with ::method - # crude heuristic: find last token containing :: and use everything from that token to end (remove trailing spaces) + # Extract qualified name from regular callable reference + # Find last token containing :: tokens = re.split(r'\s+', cpp_ref_clean) - possible = None - for t in reversed(tokens): - if '::' in t: - possible = t + for token in reversed(tokens): + if '::' in token: + callable_name = token.rstrip(',').strip() break - if not possible: - possible = tokens[-1] - - # strip trailing commas/parens etc - possible = possible.rstrip(',').strip() - - # normalize to lookups used earlier - lookup_keys = [] - if possible.startswith("mx::"): - lookup_keys.append(possible.replace("mx::", "MaterialX::")) - lookup_keys.append(possible) - if "::" in possible: - parts = possible.split("::") - lookup_keys.append("::".join(parts[-2:])) # Class::method - lookup_keys.append(parts[-1]) # method only + if not callable_name and tokens: + callable_name = tokens[-1].rstrip(',').strip() + + # Generate lookup keys + lookup_keys = generate_lookup_keys(callable_name if callable_name else py_method_name) + # Also try with Python method name if different + if callable_name != py_method_name: + lookup_keys.extend(generate_lookup_keys(py_method_name)) + # Find documentation func_entry = None for k in lookup_keys: if k in func_docs: func_entry = func_docs[k] break + + # Fallback: suffix match if not func_entry: - # fallback: suffix match - for k, v in func_docs.items(): - if k.endswith("::" + args[0][0].strip().strip('"')) or k.endswith("::" + possible.split("::")[-1]): - func_entry = v - break + func_entry = find_doc_by_suffix(func_docs, callable_name, py_method_name) if func_entry: docstring = build_method_docstring(func_entry) if docstring: escaped = escape_docstring_for_cpp(docstring) - # Simple approach: insert ", "docstring"" right before the closing ) - # This preserves all the original formatting and structure + + if has_docstring and force_replace: + # Find and replace the existing docstring argument + # Find which argument is the docstring (first string literal after callable) + doc_arg_idx = None + for i, (arg_text, _, _) in enumerate(args[2:], start=2): # Start from 3rd arg + a = arg_text.strip() + if not a.startswith("py::arg") and re.match(r'^".*"$', a): + doc_arg_idx = i + break + + if doc_arg_idx is not None: + # Replace only the quoted content, preserving all formatting + arg_start = paren_open + 1 + args[doc_arg_idx][1] + arg_end = paren_open + 1 + args[doc_arg_idx][2] + original_arg = text[arg_start:arg_end] + + # Find the opening and closing quotes + first_quote = original_arg.find('"') + last_quote = original_arg.rfind('"') + + if first_quote != -1 and last_quote != -1 and first_quote < last_quote: + # Replace only content between quotes, preserving everything else + new_arg = original_arg[:first_quote+1] + escaped + original_arg[last_quote:] + # Copy everything before the docstring, insert new docstring, copy everything after + new_def_text = text[start:arg_start] + new_arg + text[arg_end:paren_close+1] + new_text_parts.append(new_def_text) + idx = paren_close + 1 + changed = True + continue + + # Insert new docstring at the end new_def_text = text[start:paren_close] + f', "{escaped}")' new_text_parts.append(new_def_text) idx = paren_close + 1 @@ -690,37 +542,42 @@ def main(): help="Path to Doxygen XML output directory.") parser.add_argument("-p", "--pybind_dir", type=Path, default=Path("source/PyMaterialX"), help="Path to pybind11 C++ bindings directory.") - parser.add_argument("-j", "--write_json", action='store_true', help="Write extracted docs to JSON file.") + parser.add_argument("-j", "--write_json", action='store_true', + help="Write extracted docs to JSON file.") + parser.add_argument("-f", "--force", action='store_true', + help="Force replace existing docstrings. By default, existing docstrings are preserved.") args = parser.parse_args() - doxygen_xml_dir = args.doxygen_xml_dir - pybind_dir = args.pybind_dir - if not doxygen_xml_dir.exists(): - print(f"Error: Doxygen XML directory does not exist: {doxygen-xml-dir}") + + # Validate paths + if not args.doxygen_xml_dir.exists(): + print(f"Error: Doxygen XML directory does not exist: {args.doxygen_xml_dir}") return - if not pybind_dir.exists(): - print(f"Error: Pybind directory does not exist: {pybind-dir}") + if not args.pybind_dir.exists(): + print(f"Error: Pybind directory does not exist: {args.pybind_dir}") return - DOXYGEN_XML_DIR = doxygen_xml_dir - PYBIND_DIR = pybind_dir + # Set global paths (needed by extraction/insertion functions) + global DOXYGEN_XML_DIR, PYBIND_DIR + DOXYGEN_XML_DIR = args.doxygen_xml_dir + PYBIND_DIR = args.pybind_dir # Build documentation maps extract_docs_from_xml() # Update CPP files - insert_docs_into_bindings() - - # Write extracted documentation to JSON files. - write_json = args.write_json if args.write_json else False - if write_json: - json_path = Path("class_docs.json") - with json_path.open("w", encoding="utf-8") as f: - print("Writing class docs to", json_path) + insert_docs_into_bindings(force_replace=args.force) + + # Write extracted documentation to JSON files + if args.write_json: + class_json = Path("class_docs.json") + with class_json.open("w", encoding="utf-8") as f: + print(f"Writing class docs to {class_json}") json.dump(class_docs, f, indent=2) - json_path = Path("func_docs.json") - with json_path.open("w", encoding="utf-8") as f: - print("Writing function docs to", json_path) + + func_json = Path("func_docs.json") + with func_json.open("w", encoding="utf-8") as f: + print(f"Writing function docs to {func_json}") json.dump(func_docs, f, indent=2) print("Done.") diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyShaderStage.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyShaderStage.cpp index f05a6d7680..c07f5a9624 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyShaderStage.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyShaderStage.cpp @@ -29,7 +29,7 @@ void bindPyShaderStage(py::module& mod) { 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 strings in the path."); py::class_(mod, "ShaderStage", "A shader stage, containing the state and resulting source code for the stage.") .def(py::init()) From 57b928a38c9ff42686cb2ee263e4000dd56dddb1 Mon Sep 17 00:00:00 2001 From: Bernard Kwok Date: Mon, 17 Nov 2025 09:05:27 -0500 Subject: [PATCH 3/9] Automate docs insertion for PyMaterialX by adding in a docs dependency. Add a "force" replacement flag to rebuild all docs as desired.. Picked up some new docs changes since last sync. --- CMakeLists.txt | 10 ++++--- source/PyMaterialX/CMakeLists.txt | 27 +++++++++++++++++++ .../PyMaterialX/PyMaterialXFormat/PyFile.cpp | 2 +- .../PySlangShaderGenerator.cpp | 8 +++--- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a4bba653b7..f2e051ccee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,6 +123,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').") @@ -197,6 +198,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) @@ -535,16 +537,16 @@ if(MATERIALX_BUILD_TESTS) add_subdirectory(source/MaterialXTest) endif() +if (MATERIALX_BUILD_DOCS OR MATERIALX_BUILD_PYTHON) + 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/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/PyMaterialXFormat/PyFile.cpp b/source/PyMaterialX/PyMaterialXFormat/PyFile.cpp index 48d34cad22..690760dfad 100644 --- a/source/PyMaterialX/PyMaterialXFormat/PyFile.cpp +++ b/source/PyMaterialX/PyMaterialXFormat/PyFile.cpp @@ -43,7 +43,7 @@ void bindPyFile(py::module& mod) .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.") + .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) diff --git a/source/PyMaterialX/PyMaterialXGenSlang/PySlangShaderGenerator.cpp b/source/PyMaterialX/PyMaterialXGenSlang/PySlangShaderGenerator.cpp index bb69578412..f378746eed 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") + 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) - .def("generate", &mx::SlangShaderGenerator::generate) - .def("getTarget", &mx::SlangShaderGenerator::getTarget) - .def("getVersion", &mx::SlangShaderGenerator::getVersion); + .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 ESSL version this generator is for."); } From 0ff87e152bf79c0d095459f952672bf5b7819eb1 Mon Sep 17 00:00:00 2001 From: Bernard Kwok Date: Mon, 17 Nov 2025 09:36:27 -0500 Subject: [PATCH 4/9] Fix docs build logic in main.yml. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f2e051ccee..46f8626b15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -537,7 +537,7 @@ if(MATERIALX_BUILD_TESTS) add_subdirectory(source/MaterialXTest) endif() -if (MATERIALX_BUILD_DOCS OR MATERIALX_BUILD_PYTHON) +if (MATERIALX_BUILD_DOCS) add_subdirectory(documents) endif() From 54c9a5afe33327892210f000112d8e8343686c0e Mon Sep 17 00:00:00 2001 From: Bernard Kwok Date: Mon, 17 Nov 2025 09:51:01 -0500 Subject: [PATCH 5/9] Make wheels build (re)generate docs. --- .github/workflows/main.yml | 4 +++- pyproject.toml | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0c0a301cf2..7cdef28b32 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -458,7 +458,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/pyproject.toml b/pyproject.toml index 2f5f1cbe17..b221bb9d8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,6 +81,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' From 1fce313d3fa092821ce2a0d992b120c29b1be1d8 Mon Sep 17 00:00:00 2001 From: Bernard Kwok Date: Mon, 17 Nov 2025 10:02:54 -0500 Subject: [PATCH 6/9] Needs documents while building. Exclude from the wheel. --- pyproject.toml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b221bb9d8d..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" From 1b75c9110561bc83ba223ef7fa08457c7abdd55b Mon Sep 17 00:00:00 2001 From: kwokcb Date: Mon, 17 Nov 2025 23:46:12 -0500 Subject: [PATCH 7/9] Cleanup logic - Cleanup extraction to build better key lookups - Fix insertion to handle statics and globals and use better key lookups. --- python/Scripts/pybind_docs.py | 1026 ++++++++--------- .../PyMaterialXCore/PyDefinition.cpp | 26 +- .../PyMaterialXCore/PyDocument.cpp | 43 +- .../PyMaterialX/PyMaterialXCore/PyElement.cpp | 62 +- source/PyMaterialX/PyMaterialXCore/PyGeom.cpp | 11 +- .../PyMaterialXCore/PyInterface.cpp | 25 +- source/PyMaterialX/PyMaterialXCore/PyLook.cpp | 22 +- .../PyMaterialXCore/PyMaterial.cpp | 5 +- source/PyMaterialX/PyMaterialXCore/PyNode.cpp | 34 +- .../PyMaterialXCore/PyProperty.cpp | 4 +- .../PyMaterialXCore/PyTraversal.cpp | 24 +- .../PyMaterialX/PyMaterialXCore/PyTypes.cpp | 28 +- .../PyMaterialXCore/PyUnitConverter.cpp | 8 +- source/PyMaterialX/PyMaterialXCore/PyUtil.cpp | 26 +- .../PyMaterialX/PyMaterialXCore/PyValue.cpp | 11 +- .../PyMaterialX/PyMaterialXCore/PyVariant.cpp | 3 +- .../PyMaterialX/PyMaterialXFormat/PyFile.cpp | 14 +- .../PyMaterialX/PyMaterialXFormat/PyUtil.cpp | 19 +- .../PyMaterialX/PyMaterialXFormat/PyXmlIo.cpp | 9 +- .../PyGlslShaderGenerator.cpp | 22 +- .../PyMdlShaderGenerator.cpp | 2 +- .../PyMslShaderGenerator.cpp | 6 +- .../PyOslShaderGenerator.cpp | 2 +- .../PyColorManagement.cpp | 6 +- .../PyMaterialXGenShader/PyGenContext.cpp | 2 +- .../PyMaterialXGenShader/PyShader.cpp | 10 +- .../PyShaderGenerator.cpp | 8 +- .../PyMaterialXGenShader/PyShaderPort.cpp | 20 +- .../PyMaterialXGenShader/PyShaderStage.cpp | 16 +- .../PyShaderTranslator.cpp | 2 +- .../PyMaterialXGenShader/PyTypeDesc.cpp | 8 +- .../PyMaterialXGenShader/PyUnitSystem.cpp | 8 +- .../PyMaterialXGenShader/PyUtil.cpp | 24 +- .../PySlangShaderGenerator.cpp | 4 +- .../PyMaterialXRender/PyCamera.cpp | 10 +- .../PyMaterialXRender/PyCgltfLoader.cpp | 2 +- .../PyMaterialXRender/PyGeometryHandler.cpp | 10 +- .../PyMaterialX/PyMaterialXRender/PyImage.cpp | 14 +- .../PyMaterialXRender/PyImageHandler.cpp | 25 +- .../PyMaterialXRender/PyLightHandler.cpp | 2 +- .../PyMaterialX/PyMaterialXRender/PyMesh.cpp | 28 +- .../PyMaterialXRender/PyOiioImageLoader.cpp | 6 +- .../PyMaterialXRender/PyShaderRenderer.cpp | 8 +- .../PyMaterialXRender/PyStbImageLoader.cpp | 6 +- .../PyMaterialXRender/PyTinyObjLoader.cpp | 4 +- .../PyGLTextureHandler.cpp | 7 +- .../PyMaterialXRenderGlsl/PyGlslProgram.cpp | 17 +- .../PyMaterialXRenderGlsl/PyGlslRenderer.cpp | 6 +- .../PyMaterialXRenderGlsl/PyTextureBaker.cpp | 8 +- .../PyMaterialXRenderOsl/PyOslRenderer.cpp | 14 +- 50 files changed, 788 insertions(+), 919 deletions(-) diff --git a/python/Scripts/pybind_docs.py b/python/Scripts/pybind_docs.py index db26698d40..c0281aa80c 100644 --- a/python/Scripts/pybind_docs.py +++ b/python/Scripts/pybind_docs.py @@ -1,586 +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 -# Adjust these paths as needed +# Defaults (can be overridden by CLI) DOXYGEN_XML_DIR = Path("build/documents/doxygen_xml") PYBIND_DIR = Path("source/PyMaterialX") -# Extraction maps -class_docs = {} # key: "MaterialX::Document" -> docstring -func_docs = {} # key: "MaterialX::Document::addNodeDefFromGraph" -> dict {brief, detail, params:{name:desc}, returns, args_sig} - -# ---------------------------- -# Helper functions -# ---------------------------- -def normalize_name(name: str) -> str: - """Ensure name has MaterialX:: prefix if not already present.""" - if not name: - return name - return name if name.startswith("MaterialX::") else f"MaterialX::{name}" - -def generate_lookup_keys(name: str, include_variants: bool = True) -> list: - """Generate all possible lookup key variants for a given name.""" - if not name: - return [] - - keys = [] - # Add MaterialX:: variant - normalized = normalize_name(name) - if normalized != name: - keys.append(normalized) - keys.append(name) - - # Add mx:: to MaterialX:: conversion - if name.startswith("mx::"): - keys.insert(0, name.replace("mx::", "MaterialX::")) - - # Add class::method and method-only variants - if include_variants and "::" in name: - parts = name.split("::") - if len(parts) >= 2: - keys.append("::".join(parts[-2:])) # Class::method - keys.append(parts[-1]) # method only - - return keys - -def extract_detail_text(parent_elem, exclude_tags={'parameterlist', 'simplesect'}): - """Extract detail paragraphs from XML element, excluding specified child tags.""" - parts = [] - for para in parent_elem.findall("para"): - if not any(para.find(tag) is not None for tag in exclude_tags): - parts.append(_text_of(para)) - return "\n\n".join(p for p in parts if p) - -def find_doc_by_suffix(func_docs: dict, *suffixes) -> dict: - """Find function doc by suffix match. Returns first match or None.""" - for suffix in suffixes: - if suffix: - for key, value in func_docs.items(): - if key.endswith(f"::{suffix}"): - return value - return None - -def _text_of(elem): - """Concatenate element text and children text, normalized whitespace.""" - if elem is None: - return "" - text = "".join(elem.itertext()) - text = re.sub(r'\s+', ' ', text).strip() - return text - -def _param_desc_tuple(param_item): - """Return (name, description) for a element.""" - name = _text_of(param_item.find("parameternamelist/parametername")) - desc_elem = param_item.find("parameterdescription") - desc = _text_of(desc_elem) - return name, desc - -def extract_docs_from_xml(): - """ - Populate class_docs and func_docs. - """ - if not DOXYGEN_XML_DIR.exists(): - raise FileNotFoundError(f"Doxygen XML directory not found: {DOXYGEN_XML_DIR}") - - # Iterate over xml files - for xml_file in DOXYGEN_XML_DIR.glob("*.xml"): + +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() - # Extract class/struct compound docs + # Class / struct documentation for compound in root.findall(".//compounddef[@kind='class']") + root.findall(".//compounddef[@kind='struct']"): - compoundname = _text_of(compound.find("compoundname")) - brief = _text_of(compound.find("briefdescription/para")) - detail = extract_detail_text(compound.find("detaileddescription")) - - doc_parts = [p for p in [brief, detail] if p] - full = "\n\n".join(doc_parts).strip() - if full: - class_docs[normalize_name(compoundname)] = full - - # Extract member functions + self._extract_class_doc(compound) + + # Function documentation for member in root.findall(".//memberdef[@kind='function']"): - name = _text_of(member.find("name")) - qualified = _text_of(member.find("qualifiedname")) - - # try to build qualified if missing using parent compoundname - if not qualified and name: - compound = member.find("../../compoundname") - compound_name = _text_of(compound) if compound is not None else "" - if compound_name: - qualified = compound_name + "::" + name - - # brief + detailed - brief = _text_of(member.find("briefdescription/para")) - detail = extract_detail_text(member.find("detaileddescription")) - - # params - params = {} - for param_item in member.findall(".//parameterlist[@kind='param']/parameteritem"): - pname, pdesc = _param_desc_tuple(param_item) - if pname: - params[pname] = pdesc - - # returns - ret_text = "" - retsect = member.find(".//simplesect[@kind='return']") - if retsect is not None: - ret_text = _text_of(retsect) - - # argsstring for overload disambiguation - argsstring = _text_of(member.find("argsstring")) - args_sig = "" - if argsstring: - # Normalize argsstring - args_sig = re.sub(r'\s*,\s*', ',', argsstring) - args_sig = re.sub(r'\s+', ' ', args_sig).strip() - - # Store with normalized qualified name - if qualified: - primary_key = normalize_name(qualified) - func_docs[primary_key] = { - "brief": brief, - "detail": detail, - "params": params, - "returns": ret_text, - "args_sig": args_sig - } - - print(f"Extracted {len(class_docs)} classes and {len(func_docs)} functions.") - -# ---------------------------- -# Insertion code -# ---------------------------- -def escape_docstring_for_cpp(s: str) -> str: - # escape double quotes and backslashes, then represent newlines as \n in a C++ string literal - # keep it as a single-line C++ string literal with explicit \n sequences - if s is None: - return "" - s = s.replace("\\", "\\\\").replace('"', '\\"') - s = s.replace("\r\n", "\n").replace("\r", "\n") - s = s.replace("\n", "\\n") - return s - -def build_method_docstring(func_entry): - """Build a readable docstring for a method from func_entry dict.""" - parts = [] - if func_entry.get("brief"): - parts.append(func_entry["brief"]) - if func_entry.get("detail"): - parts.append(func_entry["detail"]) - # Args block - params = func_entry.get("params", {}) - if params: - param_lines = [] - for pname, pdesc in params.items(): - if pdesc: - param_lines.append(f" {pname}: {pdesc}") - else: - param_lines.append(f" {pname}:") - parts.append("Args:\n" + "\n".join(param_lines)) - # Returns - if func_entry.get("returns"): - parts.append("Returns:\n " + func_entry["returns"]) - return "\n\n".join(parts).strip() - -def _has_docstring(args): - """Check if any arg (after first) is a string literal, excluding py::arg calls.""" - for arg_text, _, _ in args[1:]: - a = arg_text.strip() - if a.startswith("py::arg"): - continue - if re.match(r'^".*"$', a): + 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 - return False - -def _find_matching_paren(s: str, start_idx: int) -> int: - """Find the index of the matching ')' for '(' at start_idx. - Handles nested parentheses and string literals.""" - depth = 0 - in_string = False - escape_next = False - i = start_idx - - while i < len(s): - c = s[i] - - if escape_next: - escape_next = False - i += 1 - continue - - if c == '\\': - escape_next = True - i += 1 - continue - - if c == '"': - in_string = not in_string - i += 1 - continue - - if not in_string: - if c == '(': - depth += 1 - elif c == ')': - depth -= 1 - if depth == 0: - return i - - i += 1 - - return -1 - - -def _split_top_level_args(arglist: str): - """Split arguments at top-level commas. - Returns list of (arg_text, start_index, end_index) tuples. - Handles nested parentheses and string literals.""" - args = [] - start = 0 - i = 0 - depth = 0 - in_string = False - escape_next = False - - while i < len(arglist): - c = arglist[i] - - if escape_next: - escape_next = False - i += 1 - continue - - if c == '\\': - escape_next = True - i += 1 - continue - - if c == '"': - in_string = not in_string - i += 1 - continue - - if not in_string: - if c == '(': - depth += 1 - elif c == ')': - depth -= 1 - elif c == ',' and depth == 0: - # Top-level comma found - args.append((arglist[start:i].strip(), start, i)) - start = i + 1 - - i += 1 - - # Append last argument - if start < len(arglist): - args.append((arglist[start:].strip(), start, len(arglist))) - - return args - - -def insert_docs_into_bindings(force_replace=False): - """ - Robust insertion: - 1. Insert class docstrings into py::class_<...>(mod, "Name") -> py::class_<...>(mod, "Name", "doc") - 2. Parse each .def(...) call, split top-level args, and insert docstring - after the second argument (callable) if no docstring is present. - - Args: - force_replace: If True, replace existing docstrings. If False, skip entries that already have docs. - """ - if force_replace: - print("Force replace mode: Will update existing docstrings") - else: - print("Normal mode: Will skip entries with existing docstrings") - - cpp_files = list(PYBIND_DIR.rglob("*.cpp")) - def_start_re = re.compile(r'\.def\s*\(') - class_start_re = re.compile(r'py::class_<') - - for cpp in cpp_files: - text = cpp.read_text(encoding="utf-8") - - # First pass: insert class documentation - # Look for py::class_<...>(mod, "Name") and add docstring as third parameter - class_changed = False - class_matches = [] - for m in class_start_re.finditer(text): - start = m.start() - # Find the opening paren after py::class_<...> - # First find the closing > of the template - template_start = start + len('py::class_<') - depth = 1 - i = template_start - while i < len(text) and depth > 0: - if text[i] == '<': - depth += 1 - elif text[i] == '>': - depth -= 1 - i += 1 - # Now find the opening paren - paren_open = text.find('(', i) - if paren_open == -1: + 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_close = _find_matching_paren(text, paren_open) - if paren_close == -1: + + paren_start = content.find('(', template_end) + if paren_start == -1: + result.append(content[start:match.end()]) + pos = match.end() continue - - # Extract arguments - arglist = text[paren_open+1:paren_close] - args = _split_top_level_args(arglist) - - # We expect at least 2 args: (mod, "ClassName") - if len(args) < 2: + + paren_end = self._find_matching_paren(content, paren_start) + if paren_end == -1: + result.append(content[start:match.end()]) + pos = match.end() continue - - # Check if there's already a third argument (docstring) - if len(args) >= 3: - if not force_replace: - # Already has docstring, skip unless force_replace - continue - else: - # Force replace: we'll need to replace the third arg - # Store as (start, end, new_doc) for replacement - pass # Handle below - - # Extract class name from second argument - class_name = args[1][0].strip().strip('"') - - # Find documentation using helper - class_doc = None - for k in generate_lookup_keys(class_name, include_variants=False): - if k in class_docs: - class_doc = class_docs[k] - break - - if class_doc: - escaped = escape_docstring_for_cpp(class_doc) - if len(args) >= 3 and force_replace: - # Replace existing docstring (third argument) - just replace the quoted content - # Find the position of the third argument in the original text - arg3_start = paren_open + 1 + args[2][1] - arg3_end = paren_open + 1 + args[2][2] - original_arg = text[arg3_start:arg3_end] - - # Find the opening and closing quotes - first_quote = original_arg.find('"') - last_quote = original_arg.rfind('"') - - if first_quote != -1 and last_quote != -1 and first_quote < last_quote: - # Replace only the content between quotes, preserving everything else - new_arg = original_arg[:first_quote+1] + escaped + original_arg[last_quote:] - class_matches.append((arg3_start, arg3_end, new_arg, True)) + + 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: - # Fallback: replace entire argument - class_matches.append((arg3_start, arg3_end, f'"{escaped}"', True)) - else: - # Insert new docstring (with space after comma to match original style) - class_matches.append((paren_close, f', "{escaped}"', False)) # False = insert - class_changed = True - - # Apply class documentation changes in reverse order to preserve positions - if class_changed: - for match in reversed(class_matches): - if len(match) == 4: # Replace mode: (start, end, new_text, is_replace) - start, end, new_text, _ = match - text = text[:start] + new_text + text[end:] - else: # Insert mode: (pos, insertion, is_replace) - pos, insertion, _ = match - text = text[:pos] + insertion + text[pos:] - - # Second pass: insert method documentation - new_text_parts = [] - idx = 0 - changed = False - while True: - m = def_start_re.search(text, idx) - if not m: - # append rest and break - new_text_parts.append(text[idx:]) - break - - start = m.start() - new_text_parts.append(text[idx:start]) # content up to .def( - paren_open = text.find('(', start) - if paren_open == -1: - # shouldn't happen; append rest and break - new_text_parts.append(text[start:]) - break - - paren_close = _find_matching_paren(text, paren_open) - if paren_close == -1: - # unmatched, append rest - new_text_parts.append(text[start:]) - break - - # argument list content (without outer parentheses) - arglist = text[paren_open+1:paren_close] - args = _split_top_level_args(arglist) - - # if we have less than 2 args, we can't determine callable; just copy as-is + 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: - new_text_parts.append(text[start:paren_close+1]) - idx = paren_close + 1 + 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 - # first arg is typically the python name string literal (e.g. "addNode") - # second arg is the callable, e.g. &mx::Document::addNodeGraph - first_arg_text = args[0][0] - second_arg_text = args[1][0] - - # determine if any existing argument is a string literal (treat raw string R"..." too) - has_docstring = _has_docstring(args) - - if not has_docstring or force_replace: - # Extract Python method name and C++ callable reference - py_method_name = first_arg_text.strip().strip('"') - cpp_ref_clean = second_arg_text.strip() - - # Extract the callable name (works for both regular functions and lambdas) - # For lambdas: try to find method calls like elem.method( or obj->method( - # For regular callables: extract the qualified name like &mx::Class::method - callable_name = None - - # Check for method call pattern (handles lambdas and some edge cases) - method_call = re.search(r'[\.\->](\w+)\s*\(', cpp_ref_clean) - if method_call: - callable_name = method_call.group(1) - else: - # Extract qualified name from regular callable reference - # Find last token containing :: - tokens = re.split(r'\s+', cpp_ref_clean) - for token in reversed(tokens): - if '::' in token: - callable_name = token.rstrip(',').strip() - break - if not callable_name and tokens: - callable_name = tokens[-1].rstrip(',').strip() - - # Generate lookup keys - lookup_keys = generate_lookup_keys(callable_name if callable_name else py_method_name) - # Also try with Python method name if different - if callable_name != py_method_name: - lookup_keys.extend(generate_lookup_keys(py_method_name)) - - # Find documentation - func_entry = None - for k in lookup_keys: - if k in func_docs: - func_entry = func_docs[k] - break - - # Fallback: suffix match - if not func_entry: - func_entry = find_doc_by_suffix(func_docs, callable_name, py_method_name) - - if func_entry: - docstring = build_method_docstring(func_entry) - if docstring: - escaped = escape_docstring_for_cpp(docstring) - - if has_docstring and force_replace: - # Find and replace the existing docstring argument - # Find which argument is the docstring (first string literal after callable) - doc_arg_idx = None - for i, (arg_text, _, _) in enumerate(args[2:], start=2): # Start from 3rd arg - a = arg_text.strip() - if not a.startswith("py::arg") and re.match(r'^".*"$', a): - doc_arg_idx = i - break - - if doc_arg_idx is not None: - # Replace only the quoted content, preserving all formatting - arg_start = paren_open + 1 + args[doc_arg_idx][1] - arg_end = paren_open + 1 + args[doc_arg_idx][2] - original_arg = text[arg_start:arg_end] - - # Find the opening and closing quotes - first_quote = original_arg.find('"') - last_quote = original_arg.rfind('"') - - if first_quote != -1 and last_quote != -1 and first_quote < last_quote: - # Replace only content between quotes, preserving everything else - new_arg = original_arg[:first_quote+1] + escaped + original_arg[last_quote:] - # Copy everything before the docstring, insert new docstring, copy everything after - new_def_text = text[start:arg_start] + new_arg + text[arg_end:paren_close+1] - new_text_parts.append(new_def_text) - idx = paren_close + 1 - changed = True - continue - - # Insert new docstring at the end - new_def_text = text[start:paren_close] + f', "{escaped}")' - new_text_parts.append(new_def_text) - idx = paren_close + 1 - changed = True + 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 - # no insertion performed — copy original .def(...) exactly - new_text_parts.append(text[start:paren_close+1]) - idx = paren_close + 1 + result.append(content[start:paren_end]) + result.append(f', "{escaped}")') + pos = paren_end + 1 + continue - if changed or class_changed: - new_text = "".join(new_text_parts) - cpp.write_text(new_text, encoding="utf-8") - print(f"- Patched: {cpp}") - else: - # no changes; nothing to write - print('- No changes needed for:', cpp) - pass + result.append(content[start:paren_end + 1]) + pos = paren_end + 1 + + result.append(content[pos:]) + return "".join(result) - print("Code insertion complete.") + 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 XML docs and insert into pybind11 bindings.") - 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 C++ bindings directory.") - parser.add_argument("-j", "--write_json", action='store_true', - help="Write extracted docs to JSON file.") - parser.add_argument("-f", "--force", action='store_true', - help="Force replace existing docstrings. By default, existing docstrings are preserved.") - + 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() - - # Validate paths + if not args.doxygen_xml_dir.exists(): - print(f"Error: Doxygen XML directory does not exist: {args.doxygen_xml_dir}") - return + 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 does not exist: {args.pybind_dir}") - return - - # Set global paths (needed by extraction/insertion functions) - global DOXYGEN_XML_DIR, PYBIND_DIR - DOXYGEN_XML_DIR = args.doxygen_xml_dir - PYBIND_DIR = args.pybind_dir + print(f"Error: Pybind directory not found: {args.pybind_dir}") + return 1 - # Build documentation maps - extract_docs_from_xml() + print("Extracting documentation from Doxygen XML...") + extractor = DocExtractor(args.doxygen_xml_dir) + extractor.extract() - # Update CPP files - insert_docs_into_bindings(force_replace=args.force) - - # Write extracted documentation to JSON files if args.write_json: - class_json = Path("class_docs.json") - with class_json.open("w", encoding="utf-8") as f: - print(f"Writing class docs to {class_json}") - json.dump(class_docs, f, indent=2) - - func_json = Path("func_docs.json") - with func_json.open("w", encoding="utf-8") as f: - print(f"Writing function docs to {func_json}") - json.dump(func_docs, f, indent=2) - - print("Done.") + 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__": - main() + exit(main()) + diff --git a/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp b/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp index 1bb16bba05..160effe781 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp @@ -21,9 +21,8 @@ void bindPyDefinition(py::module& mod) .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 Implementation, if any, with the given name.") - .def("getImplementation", &mx::NodeDef::getImplementation, - py::arg("target") = mx::EMPTY_STRING, "Return the Implementation, if any, with the given name.") + .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) @@ -44,25 +43,24 @@ void bindPyDefinition(py::module& mod) .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, "Returns a nodedef for a given transform.") + .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, if any, with the given name.") + .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", "A type definition element within a Document.") - .def("setSemantic", &mx::TypeDef::setSemantic, "Set the variable semantic of this port.") + .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 variable semantic of this port.") + .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("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) + .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) @@ -77,15 +75,15 @@ void bindPyDefinition(py::module& mod) 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 unit type 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 type for the value on this port.") + .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", "A unit type definition element within a Document.") - .def("getUnitDefs", &mx::UnitTypeDef::getUnitDefs, "Return a vector of all Member elements in the TypeDef.") + .def("getUnitDefs", &mx::UnitTypeDef::getUnitDefs, "Find all UnitDefs for the UnitTypeDef.") .def_readonly_static("CATEGORY", &mx::UnitTypeDef::CATEGORY); py::class_(mod, "AttributeDef", "An attribute definition element within a Document.") @@ -94,7 +92,7 @@ void bindPyDefinition(py::module& mod) .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, "Return 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); diff --git a/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp b/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp index b40ad86b10..d76b03425a 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp @@ -27,57 +27,49 @@ void bindPyDocument(py::module& mod) mod.def("createDocument", &mx::createDocument, "Create a new document of the given subclass.\n\nCreate a new Document."); 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 with the given implementation element.\n\nInitialization must set the name and hash for the implementation, as well as any other data needed to emit code for the node.") - .def("copy", &mx::Document::copy, "Create a deep copy of the value.") + .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("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("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("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("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("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("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("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("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, "Returns a nodedef for a given transform.") + .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.") @@ -89,27 +81,24 @@ void bindPyDocument(py::module& mod) .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("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("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("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("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("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.") diff --git a/source/PyMaterialX/PyMaterialXCore/PyElement.cpp b/source/PyMaterialX/PyMaterialXCore/PyElement.cpp index 861c972fea..f297feb90a 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyElement.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyElement.cpp @@ -38,9 +38,8 @@ void bindPyElement(py::module& mod) .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 ColorManagementSystem name.") - .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("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.") @@ -67,8 +66,7 @@ void bindPyElement(py::module& mod) .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("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.") @@ -80,17 +78,15 @@ void bindPyElement(py::module& mod) .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 our self pointer.") - .def("getParent", static_cast(&mx::Element::getParent), "Return our parent element.") + .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 root document 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("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 upstream element of the edge.") + .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.") @@ -101,12 +97,11 @@ void bindPyElement(py::module& mod) std::string message; bool res = elem.validate(&message); return std::pair(res, message); - }, "Validate that the given element tree, including all descendants, is consistent with the MaterialX specification.") + }, "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("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) @@ -136,35 +131,34 @@ void bindPyElement(py::module& mod) BIND_ELEMENT_FUNC_INSTANCE(Visibility); py::class_(mod, "TypedElement", "The base class for typed elements.") - .def("setType", &mx::TypedElement::setType, "Set the data type for this port.") + .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, "Get stream attribute name.") + .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, if any, with the given name.") + .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", "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, "Return value string.") - .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("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, "Returns a value formatted according to this type syntax.\n\nThe value is constructed from the given value object.") - .def("_getDefaultValue", &mx::ValueElement::getDefaultValue) - .def("setUnit", &mx::ValueElement::setUnit, "Set a unit type for the value on this port.") + .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 type for the value on this port.") + .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 element's unittype string.") - .def("hasUnitType", &mx::ValueElement::hasUnitType, "Return true if the given element has a unittype string.") - .def("getUnitType", &mx::ValueElement::getUnitType, "Return the unit type string.") + .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) @@ -219,10 +213,10 @@ void bindPyElement(py::module& mod) .def(py::init<>()); 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 element's file prefix string.") - .def("getFilePrefix", &mx::StringResolver::getFilePrefix, "Return the element's file prefix string.") - .def("setGeomPrefix", &mx::StringResolver::setGeomPrefix, "Set the element's geom prefix string.") - .def("getGeomPrefix", &mx::StringResolver::getGeomPrefix, "Return the element's geom prefix string.") + .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.") @@ -235,6 +229,6 @@ void bindPyElement(py::module& mod) 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 5f0ae10c40..4f3ea67ace 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp @@ -23,15 +23,14 @@ void bindPyGeom(py::module& mod) .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, if any, with the given name."); + .def("getCollection", &mx::GeomElement::getCollection, "Return the Collection that is assigned to this element."); 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("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.") @@ -59,7 +58,7 @@ void bindPyGeom(py::module& mod) 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 GeomProp, if any, with the given name.") + .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.") @@ -68,7 +67,7 @@ void bindPyGeom(py::module& mod) .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 GeomProp, if any, with the given name.") + .def("getGeomProp", &mx::GeomPropDef::getGeomProp, "Return the geometric property string of this element.") .def_readonly_static("CATEGORY", &mx::GeomPropDef::CATEGORY); py::class_(mod, "Collection", "A collection element within a Document.") @@ -88,7 +87,7 @@ void bindPyGeom(py::module& mod) .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 4e8fbc258b..42c7851089 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp @@ -26,10 +26,10 @@ void bindPyInterface(py::module& mod) .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 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::PortElement::getConnectedNode, "Return the node, if any, to which this input is connected.") - .def("setConnectedOutput", &mx::PortElement::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::PortElement::getConnectedOutput, "Return the output connected to the given input.\n\nIf the given input is not present, then an empty OutputPtr is returned."); + .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", "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.") @@ -50,16 +50,14 @@ void bindPyInterface(py::module& mod) .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("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("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.") @@ -68,8 +66,7 @@ void bindPyInterface(py::module& mod) .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 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("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.") @@ -82,7 +79,7 @@ void bindPyInterface(py::module& mod) .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 a unique identifier for the target this generator is for.") + .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.") @@ -90,11 +87,9 @@ void bindPyInterface(py::module& mod) .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.") + .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.") + .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 7104f8c302..f3acac53b6 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyLook.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyLook.cpp @@ -16,32 +16,27 @@ namespace mx = MaterialX; void bindPyLook(py::module& mod) { 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("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("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("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("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("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.") @@ -49,7 +44,7 @@ void bindPyLook(py::module& mod) .def_readonly_static("CATEGORY", &mx::Look::CATEGORY); py::class_(mod, "LookGroup", "A look group element within a Document.") - .def("getLooks", &mx::LookGroup::getLooks, "Return a vector of all Look elements in the 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.") @@ -61,7 +56,7 @@ void bindPyLook(py::module& mod) .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 material-type outputs for all nodegraphs in the document.") + .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.") @@ -81,6 +76,5 @@ void bindPyLook(py::module& mod) .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 f97ece1b8d..8fabf495c2 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyNode.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyNode.cpp @@ -16,50 +16,42 @@ void bindPyNode(py::module& mod) 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, if any, to which this input is connected.") + .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, "Returns a nodedef for a given transform.") - .def("getImplementation", &mx::Node::getImplementation, - py::arg("target") = mx::EMPTY_STRING, "Return the Implementation, if any, with the given name.") + .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", "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("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("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("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("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("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", "A node graph element within a Document.") - .def("getMaterialOutputs", &mx::NodeGraph::getMaterialOutputs, "Return material-type outputs for all nodegraphs in the document.") - .def("setNodeDef", &mx::NodeGraph::setNodeDef, "Set the NodeDef element referenced by the Implementation.") - .def("getNodeDef", &mx::NodeGraph::getNodeDef, "Returns a nodedef for a given transform.") + .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 node, ordered by the names of the port elements.") + .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", "A layout element used to contain, group and document nodes within a graph.") diff --git a/source/PyMaterialX/PyMaterialXCore/PyProperty.cpp b/source/PyMaterialX/PyMaterialXCore/PyProperty.cpp index 477d39a1f0..3018c50d68 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyProperty.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyProperty.cpp @@ -29,7 +29,7 @@ void bindPyProperty(py::module& mod) .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, if any, with the given name.") + .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", "A property set element within a Document.") @@ -59,6 +59,6 @@ void bindPyProperty(py::module& mod) .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 PropertySet, if any, with the given name.") + .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 877f592944..0a9bc16206 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyTraversal.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyTraversal.cpp @@ -18,28 +18,28 @@ void bindPyTraversal(py::module& mod) .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 ColorManagementSystem name."); + .def("getName", &mx::Edge::getName, "Return the name of this edge, if any."); py::class_(mod, "TreeIterator", "An iterator object representing the state of a tree traversal.") - .def("getElement", &mx::TreeIterator::getElement) - .def("getElementDepth", &mx::TreeIterator::getElementDepth, "Return the element depth of the current traversal, where a single edge between two elements represents a depth of one.") + .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", "An iterator object representing the state of an upstream graph traversal.") - .def("getDownstreamElement", &mx::GraphIterator::getDownstreamElement, "Return the downstream element of the edge.") - .def("getConnectingElement", &mx::GraphIterator::getConnectingElement, "Return the connecting element of the edge, if any.") - .def("getUpstreamElement", &mx::GraphIterator::getUpstreamElement, "Return the upstream element of the edge.") + .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.") @@ -48,25 +48,25 @@ void bindPyTraversal(py::module& mod) .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", "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 a5644257de..973abd9f03 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyTypes.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyTypes.cpp @@ -46,7 +46,7 @@ using IndexPair = std::pair; { v[i] = f; } ) \ .def("__str__", [](const V& v) \ { return mx::toValueString(v); }) \ -.def("copy", [](const V& v) { return V(v); }, "Create a deep copy of the value.") \ +.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,16 +74,16 @@ 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); }, "Create a deep copy of the value.") \ -.def("isEquivalent", &M::isEquivalent, "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("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) \ -.def_static("createTranslation", &M::createTranslation) \ -.def_static("numRows", &M::numRows) \ -.def_static("numColumns", &M::numColumns) \ +.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) @@ -129,7 +129,7 @@ void bindPyTypes(py::module& mod) .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) + .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", "A 4x4 matrix of floating-point values.\n\nVector transformation methods follow the row-vector convention, with matrix-vector multiplication computed as v' = vM.") @@ -138,13 +138,13 @@ void bindPyTypes(py::module& mod) float, float, float, float, float, float, float, float, float, float, float, float>()) - .def("multiply", &mx::Matrix44::multiply, "Return the product of this matrix and a 3D vector.") - .def("transformPoint", &mx::Matrix44::transformPoint, "Transform the given 2D point.") - .def("transformVector", &mx::Matrix44::transformVector, "Transform the given 2D direction vector.") + .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) - .def_static("createRotationY", &mx::Matrix44::createRotationY) - .def_static("createRotationZ", &mx::Matrix44::createRotationZ) + .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 6d3a41df1e..d4f749bf4e 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyUnitConverter.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyUnitConverter.cpp @@ -71,11 +71,11 @@ void bindPyUnitConverters(py::module& mod) .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.\n\nReturns -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.\n\nReturns Empty string if not found"); + .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", "A converter class for linear units that require only a scalar multiplication.") - .def_static("create", &mx::LinearUnitConverter::create) + .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") @@ -85,7 +85,7 @@ void bindPyUnitConverters(py::module& mod) .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", "A registry for unit converters.") - .def_static("create", &mx::UnitConverterRegistry::create) + .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.") diff --git a/source/PyMaterialX/PyMaterialXCore/PyUtil.cpp b/source/PyMaterialX/PyMaterialXCore/PyUtil.cpp index 4a544af4f4..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, "Return the version string of this interface."); - mod.def("getVersionIntegers", &mx::getVersionIntegers, "Return the major and minor versions as an integer pair."); - 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 ada14bf6b9..05fcd44278 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyValue.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyValue.cpp @@ -11,7 +11,7 @@ #define BIND_TYPE_INSTANCE(NAME, T) \ py::class_, std::shared_ptr< mx::TypedValue >, mx::Value>(mod, "TypedValue_" #NAME) \ .def("getData", &mx::TypedValue::getData, "Return the raw float vector.") \ - .def("getValueString", &mx::TypedValue::getValueString, "Return value string.") \ + .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); @@ -21,12 +21,9 @@ namespace mx = MaterialX; void bindPyValue(py::module& mod) { py::class_(mod, "Value", "A generic, discriminated value, whose type may be queried dynamically.") - .def("getValueString", &mx::Value::getValueString, "Return value string.") - .def("getTypeString", &mx::Value::getTypeString, "Return type string.") - .def_static("createValueFromStrings", &mx::Value::createValueFromStrings, - py::arg("value"), - py::arg("type"), - py::arg("typeDefPtr") = nullptr); + .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 bf087f140c..03942ae93c 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyVariant.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyVariant.cpp @@ -16,8 +16,7 @@ void bindPyVariant(py::module& mod) .def_readonly_static("CATEGORY", &mx::Variant::CATEGORY); 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("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.") diff --git a/source/PyMaterialX/PyMaterialXFormat/PyFile.cpp b/source/PyMaterialX/PyMaterialXFormat/PyFile.cpp index 690760dfad..f4c0f489d7 100644 --- a/source/PyMaterialX/PyMaterialXFormat/PyFile.cpp +++ b/source/PyMaterialX/PyMaterialXFormat/PyFile.cpp @@ -30,8 +30,7 @@ void bindPyFile(py::module& mod) .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, "Return a single-line description of this element, including its category, name, and attributes.") + .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.") @@ -46,21 +45,20 @@ void bindPyFile(py::module& mod) .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) - .def_static("getModulePath", &mx::FilePath::getModulePath); + .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", "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, "Return a single-line description of this element, including its category, name, and attributes.") + .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 strings in the path.") - .def("isEmpty", &mx::FileSearchPath::isEmpty, "Return true if the given path is empty.") + .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(); diff --git a/source/PyMaterialX/PyMaterialXFormat/PyUtil.cpp b/source/PyMaterialX/PyMaterialXFormat/PyUtil.cpp index 6cc4ab4c90..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, "Load a library of implementations from the provided document, replacing any previously loaded content."); - 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 0f933f6f39..c08875b3f4 100644 --- a/source/PyMaterialX/PyMaterialXFormat/PyXmlIo.cpp +++ b/source/PyMaterialX/PyMaterialXFormat/PyXmlIo.cpp @@ -26,18 +26,15 @@ void bindPyXmlIo(py::module& mod) .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 496caec5d5..fd95bc5522 100644 --- a/source/PyMaterialX/PyMaterialXGenGlsl/PyGlslShaderGenerator.cpp +++ b/source/PyMaterialX/PyMaterialXGenGlsl/PyGlslShaderGenerator.cpp @@ -42,19 +42,19 @@ namespace void bindPyGlslShaderGenerator(py::module& mod) { 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) + .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 ESSL version 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", "Class representing a resource binding for Glsl shader resources.") - .def_static("create", &mx::GlslResourceBindingContext::create) + .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 @@ -62,7 +62,7 @@ void bindPyGlslResourceBindingContext(py::module &mod) void bindPyEsslShaderGenerator(py::module& mod) { py::class_(mod, "EsslShaderGenerator", "An ESSL (OpenGL ES Shading Language) shader generator.") - .def_static("create", &EsslShaderGenerator_create) + .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."); @@ -73,19 +73,19 @@ void bindPyEsslShaderGenerator(py::module& mod) void bindPyVkShaderGenerator(py::module& mod) { py::class_(mod, "VkShaderGenerator", "A Vulkan GLSL shader generator.") - .def_static("create", &VkShaderGenerator_create) + .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 ESSL version 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", "WGSL Flavor of Vulkan GLSL shader generator") - .def_static("create", &WgslShaderGenerator_create) + 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 ESSL version 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 ef2387c9d9..5173e7f655 100644 --- a/source/PyMaterialX/PyMaterialXGenMdl/PyMdlShaderGenerator.cpp +++ b/source/PyMaterialX/PyMaterialXGenMdl/PyMdlShaderGenerator.cpp @@ -23,6 +23,6 @@ namespace void bindPyMdlShaderGenerator(py::module& mod) { py::class_(mod, "MdlShaderGenerator", "Shader generator for MDL (Material Definition Language).") - .def_static("create", &MdlShaderGenerator_create) + .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 2aa00492ae..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_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 ESSL version 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 44ae989877..473b598c8c 100644 --- a/source/PyMaterialX/PyMaterialXGenOsl/PyOslShaderGenerator.cpp +++ b/source/PyMaterialX/PyMaterialXGenOsl/PyOslShaderGenerator.cpp @@ -28,7 +28,7 @@ void bindPyOslShaderGenerator(py::module& mod) mod.attr("OSL_OUTPUTS") = mx::OSL::OUTPUTS; 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) + .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 e3d0a3e645..672c25b377 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyColorManagement.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyColorManagement.cpp @@ -60,14 +60,14 @@ void bindPyColorManagement(py::module& mod) .def("supportsTransform", &mx::ColorManagementSystem::supportsTransform, "Returns whether this color management system supports a provided transform."); py::class_(mod, "DefaultColorManagementSystem", "Class for a default color management system.") - .def_static("create", &mx::DefaultColorManagementSystem::create) - .def("getName", &mx::DefaultColorManagementSystem::getName, "Return the ColorManagementSystem name."); + .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, "Return the ColorManagementSystem name."); + .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 8df02a49a2..4aebed25a9 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyGenContext.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyGenContext.cpp @@ -30,5 +30,5 @@ void bindPyGenContext(py::module& mod) void bindPyGenUserData(py::module& mod) { py::class_(mod, "GenUserData", "Base class for custom user data needed during shader generation.") - .def("getSelf", static_cast(&mx::GenUserData::getSelf), "Return our self pointer."); + .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/PyShader.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyShader.cpp index b51a46fba9..b9293292ed 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyShader.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyShader.cpp @@ -19,14 +19,14 @@ void bindPyShader(py::module& mod) // reference 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, "Return the ColorManagementSystem name.") + .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 given attribute is present.") - .def("getAttribute", &mx::Shader::getAttribute, "Return the value string of the given attribute.\n\nIf the given attribute is not present, then an empty string is returned.") - .def("setAttribute", static_cast(&mx::Shader::setAttribute), "Set the value string of the given attribute.") - .def("setAttribute", static_cast(&mx::Shader::setAttribute), "Set the value string of the given attribute."); + .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 dd8c568392..744560ab92 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyShaderGenerator.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyShaderGenerator.cpp @@ -15,13 +15,13 @@ namespace mx = MaterialX; void bindPyShaderGenerator(py::module& mod) { 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 a unique identifier for the target this generator is for.") + .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, "Set the color management system string.") - .def("getColorManagementSystem", &mx::ShaderGenerator::getColorManagementSystem, "Return the color management system string.") + .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."); + .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 0b8855935e..e7c62fd8c9 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyShaderPort.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyShaderPort.cpp @@ -14,25 +14,25 @@ void bindPyShaderPort(py::module& mod) { 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, "Get stream attribute name.") - .def("setName", &mx::ShaderPort::setName, "Set the element's name string.") - .def("getName", &mx::ShaderPort::getName, "Return the ColorManagementSystem name.") + .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 the typed value of an element from a C-style string.") - .def("getValue", &mx::ShaderPort::getValue, "Returns a value formatted according to this type syntax.\n\nThe value is constructed from the given value object.") - .def("getValueString", &mx::ShaderPort::getValueString, "Return value string.") - .def("setGeomProp", &mx::ShaderPort::setGeomProp, "Set the geometric property string of this element.") - .def("getGeomProp", &mx::ShaderPort::getGeomProp, "Return the GeomProp, if any, with the given name.") + .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 the element's color space string.") - .def("getColorSpace", &mx::ShaderPort::getColorSpace, "Return the element's color space string.") + .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 c07f5a9624..e94b450f8a 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyShaderStage.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyShaderStage.cpp @@ -18,24 +18,24 @@ void bindPyShaderStage(py::module& mod) py::class_(mod, "VariableBlock", "A block of variables in a shader stage.") .def(py::init()) - .def("getName", &mx::VariableBlock::getName, "Return the ColorManagementSystem name.") + .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 strings in the path.") - .def("find", static_cast(&mx::VariableBlock::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.") - .def("find", (mx::ShaderPort* (mx::VariableBlock::*)(const mx::ShaderPortPredicate& )) &mx::VariableBlock::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.") - .def("__len__", &mx::VariableBlock::size, "Return the number of strings in the path.") + .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, "Return the number of strings in the path."); + }, 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", "A shader stage, containing the state and resulting source code for the stage.") .def(py::init()) - .def("getName", &mx::ShaderStage::getName, "Return the ColorManagementSystem name.") + .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 shader source code for a given shader stage.") + .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.") diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyShaderTranslator.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyShaderTranslator.cpp index e26cb2e51e..214dca6e9c 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyShaderTranslator.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyShaderTranslator.cpp @@ -13,7 +13,7 @@ namespace mx = MaterialX; void bindPyShaderTranslator(py::module& mod) { py::class_(mod, "ShaderTranslator", "A helper class for translating content between shading models.") - .def_static("create", &mx::ShaderTranslator::create) + .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 c5ef35df57..dd0469e5a3 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyTypeDesc.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyTypeDesc.cpp @@ -16,10 +16,10 @@ void bindPyTypeDesc(py::module& mod) // by the container they are stored in and should not be destroyed when // garbage collected by the python interpreter 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 ColorManagementSystem name.") - .def("getBaseType", &mx::TypeDesc::getBaseType, "Return the base type of the image.") - .def("getSemantic", &mx::TypeDesc::getSemantic, "Return the variable semantic of this port.") - .def("getSize", &mx::TypeDesc::getSize, "Get the number of elements.") + .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.") diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyUnitSystem.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyUnitSystem.cpp index 59f30a5953..3e1193e565 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyUnitSystem.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyUnitSystem.cpp @@ -22,10 +22,10 @@ void bindPyUnitSystem(py::module& mod) .def_readwrite("unitType", &mx::UnitTransform::type); py::class_(mod, "UnitSystem", "Base unit system support.") - .def_static("create", &mx::UnitSystem::create) - .def("getName", &mx::UnitSystem::getName, "Return the ColorManagementSystem name.") - .def("loadLibrary", &mx::UnitSystem::loadLibrary, "Load a library of implementations from the provided document, replacing any previously loaded content.") - .def("supportsTransform", &mx::UnitSystem::supportsTransform, "Returns whether this color management system supports a provided transform.") + .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 f378746eed..391202e5ba 100644 --- a/source/PyMaterialX/PyMaterialXGenSlang/PySlangShaderGenerator.cpp +++ b/source/PyMaterialX/PyMaterialXGenSlang/PySlangShaderGenerator.cpp @@ -26,8 +26,8 @@ namespace void bindPySlangShaderGenerator(py::module& mod) { 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) + .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 ESSL version 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 d0c751be0e..aecce93618 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyCamera.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyCamera.cpp @@ -13,7 +13,7 @@ namespace mx = MaterialX; void bindPyCamera(py::module& mod) { py::class_(mod, "Camera", "A simple camera class, supporting transform matrices and arcball functionality for object-viewing applications.") - .def_static("create", &mx::Camera::create) + .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.") @@ -27,8 +27,8 @@ void bindPyCamera(py::module& mod) .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) - .def_static("createPerspectiveMatrix", &mx::Camera::createPerspectiveMatrix) - .def_static("createOrthographicMatrix", &mx::Camera::createOrthographicMatrix) - .def_static("transformPointPerspective", &mx::Camera::transformPointPerspective); + .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 405b69fc87..d70c6d82b3 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyCgltfLoader.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyCgltfLoader.cpp @@ -12,7 +12,7 @@ namespace mx = MaterialX; void bindPyCgltfLoader(py::module& mod) { py::class_(mod, "CgltfLoader", "Wrapper for loader to read in GLTF files using the Cgltf library.") - .def_static("create", &mx::CgltfLoader::create) + .def_static("create", &mx::CgltfLoader::create, "Create a new loader.") .def(py::init<>()) .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 dcf5d80a64..26192c43cc 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyGeometryHandler.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyGeometryHandler.cpp @@ -35,16 +35,16 @@ void bindPyGeometryHandler(py::module& mod) { 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, "Get a list of extensions supported by the handler.") - .def("load", &mx::GeometryLoader::load, "Load geometry from file path."); + .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", "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_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("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.") diff --git a/source/PyMaterialX/PyMaterialXRender/PyImage.cpp b/source/PyMaterialX/PyMaterialXRender/PyImage.cpp index 9e0578e40a..0b9db9ef3d 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyImage.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyImage.cpp @@ -22,9 +22,9 @@ void bindPyImage(py::module& mod) py::class_(mod, "ImageBufferDeallocator"); py::class_(mod, "Image", "Class representing an image in system memory.") - .def_static("create", &mx::Image::create) - .def("getWidth", &mx::Image::getWidth, "Return the width attribute of the backdrop.") - .def("getHeight", &mx::Image::getHeight, "Return the height attribute of the backdrop.") + .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.") @@ -35,7 +35,7 @@ void bindPyImage(py::module& mod) .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 deep copy of the value.") + .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.") @@ -47,7 +47,7 @@ void bindPyImage(py::module& mod) .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 2dfc0a5d1f..ae23dfc299 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyImageHandler.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyImageHandler.cpp @@ -32,27 +32,24 @@ 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, "Get a list of extensions supported by the handler.") - .def("saveImage", &mx::ImageLoader::saveImage, "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("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", "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 a geometry loader.\n\nArgs:\n loader: Loader to add to list of available loaders.") - .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 a single image.") - .def("unbindImage", &mx::ImageHandler::unbindImage, "Unbind an image.") - .def("unbindImages", &mx::ImageHandler::unbindImages, "Unbbind all images for this material.") + .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("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 d3da769ba7..a2043d63ec 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyLightHandler.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyLightHandler.cpp @@ -15,7 +15,7 @@ namespace mx = MaterialX; void bindPyLightHandler(py::module& mod) { py::class_(mod, "LightHandler", "Utility light handler for creating and providing light data for shader binding.") - .def_static("create", &mx::LightHandler::create) + .def_static("create", &mx::LightHandler::create, "Create a new light handler.") .def(py::init<>()) .def("setLightTransform", &mx::LightHandler::setLightTransform, "Set the light transform.") .def("getLightTransform", &mx::LightHandler::getLightTransform, "Return the light transform.") diff --git a/source/PyMaterialX/PyMaterialXRender/PyMesh.cpp b/source/PyMaterialX/PyMaterialXRender/PyMesh.cpp index 38be0568fd..9af79eefe5 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyMesh.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyMesh.cpp @@ -20,13 +20,13 @@ 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, "Reserve memory for a given number of elements.") - .def("resize", &mx::MeshStream::resize, "Resize data to the given number of indices.") - .def("getName", &mx::MeshStream::getName, "Return the ColorManagementSystem name.") + .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, "Return the index string of this element.") + .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.") @@ -34,11 +34,11 @@ void bindPyMesh(py::module& mod) .def("transform", &mx::MeshStream::transform, "Transform elements by a matrix."); 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) + .def_static("create", &mx::MeshPartition::create, "Create a new mesh partition.") .def(py::init<>()) .def("resize", &mx::MeshPartition::resize, "Resize data to the given number of indices.") - .def("setName", &mx::MeshPartition::setName, "Set the element's name string.") - .def("getName", &mx::MeshPartition::getName, "Return the ColorManagementSystem name.") + .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.") @@ -46,21 +46,21 @@ void bindPyMesh(py::module& mod) .def("setFaceCount", &mx::MeshPartition::setFaceCount, "Set face count."); py::class_(mod, "Mesh", "Container for mesh data.") - .def_static("create", &mx::Mesh::create) + .def_static("create", &mx::Mesh::create, "Create a new mesh.") .def(py::init()) - .def("getName", &mx::Mesh::getName, "Return the ColorManagementSystem name.") - .def("setSourceUri", &mx::Mesh::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::Mesh::hasSourceUri, "Return true if this element has a source URI.") - .def("getSourceUri", &mx::Mesh::getSourceUri, "Return the element's source URI.") + .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 all meshes.") + .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 all meshes.") + .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.") diff --git a/source/PyMaterialX/PyMaterialXRender/PyOiioImageLoader.cpp b/source/PyMaterialX/PyMaterialXRender/PyOiioImageLoader.cpp index d7feb8e59b..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, "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("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 6352ffc3d1..365bbc8907 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp @@ -13,7 +13,7 @@ namespace mx = MaterialX; void bindPyShaderRenderer(py::module& mod) { 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 with the given implementation element.\n\nInitialization must set the name and hash for the implementation, as well as any other data needed to emit code for the node.") + .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.") @@ -22,12 +22,12 @@ void bindPyShaderRenderer(py::module& mod) .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 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::ShaderRenderer::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::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 an offscreen buffer."); + .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 110e28d145..16e04f8440 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyStbImageLoader.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyStbImageLoader.cpp @@ -13,7 +13,7 @@ namespace mx = MaterialX; void bindPyStbImageLoader(py::module& mod) { py::class_(mod, "StbImageLoader", "Stb image file loader.") - .def_static("create", &mx::StbImageLoader::create) - .def("saveImage", &mx::StbImageLoader::saveImage, "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("loadImage", &mx::StbImageLoader::loadImage); + .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 cb967d729a..b3e8e26499 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyTinyObjLoader.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyTinyObjLoader.cpp @@ -13,7 +13,7 @@ namespace mx = MaterialX; void bindPyTinyObjLoader(py::module& mod) { py::class_(mod, "TinyObjLoader", "Wrapper for geometry loader to read in OBJ files using the TinyObj library.") - .def_static("create", &mx::TinyObjLoader::create) + .def_static("create", &mx::TinyObjLoader::create, "Create a new TinyObjLoader.") .def(py::init<>()) - .def("load", &mx::TinyObjLoader::load, "Load geometry from file path."); + .def("load", &mx::TinyObjLoader::load, "Load geometry from disk."); } diff --git a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp index 3f62d83de9..d7d920a77a 100644 --- a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp +++ b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp @@ -13,10 +13,9 @@ namespace mx = MaterialX; void bindPyGLTextureHandler(py::module& mod) { py::class_(mod, "GLTextureHandler", "An OpenGL texture handler class.") - .def_static("create", &mx::GLTextureHandler::create) - .def("bindImage", &mx::GLTextureHandler::bindImage, "Bind a single image.") + .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."); + .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 8c84521ac9..9c7a842183 100644 --- a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslProgram.cpp +++ b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslProgram.cpp @@ -15,14 +15,14 @@ void bindPyGlslProgram(py::module& mod) 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_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("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.") @@ -30,14 +30,13 @@ void bindPyGlslProgram(py::module& mod) .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 a mesh partition to this material.") - .def("bindMesh", &mx::GlslProgram::bindMesh, "Bind the given mesh to this material.") - .def("unbindGeometry", &mx::GlslProgram::unbindGeometry, "Unbind all geometry from this material.") + .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 lights to shader.") - .def("bindViewInformation", &mx::GlslProgram::bindViewInformation, "Bind viewing information for this material.") - .def("bindTimeAndFrame", &mx::GlslProgram::bindTimeAndFrame, - py::arg("time") = 0.0f, py::arg("frame") = 1.0f, "Bind time and frame.") + .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", "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.") diff --git a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslRenderer.cpp b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslRenderer.cpp index 07027650b6..ac2b7d7431 100644 --- a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslRenderer.cpp +++ b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslRenderer.cpp @@ -13,13 +13,13 @@ namespace mx = MaterialX; void bindPyGlslRenderer(py::module& mod) { 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) - .def("initialize", &mx::GlslRenderer::initialize, py::arg("renderContextHandle") = nullptr, "Initialize with the given implementation element.\n\nInitialization must set the name and hash for the implementation, as well as any other data needed to emit code for the node.") + .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 underlying GLSL program."); + .def("getProgram", &mx::GlslRenderer::getProgram, "Return the GLSL program."); } diff --git a/source/PyMaterialX/PyMaterialXRenderGlsl/PyTextureBaker.cpp b/source/PyMaterialX/PyMaterialXRenderGlsl/PyTextureBaker.cpp index 18637b69a6..fedf6a2547 100644 --- a/source/PyMaterialX/PyMaterialXRenderGlsl/PyTextureBaker.cpp +++ b/source/PyMaterialX/PyMaterialXRenderGlsl/PyTextureBaker.cpp @@ -14,11 +14,11 @@ namespace mx = MaterialX; void bindPyTextureBaker(py::module& mod) { 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_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 of the given path.") - .def("setColorSpace", &mx::TextureBakerGlsl::setColorSpace, "Set the element's color space string.") - .def("getColorSpace", &mx::TextureBakerGlsl::getColorSpace, "Return the element's color space string.") + .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.") diff --git a/source/PyMaterialX/PyMaterialXRenderOsl/PyOslRenderer.cpp b/source/PyMaterialX/PyMaterialXRenderOsl/PyOslRenderer.cpp index cb74359c27..11290538fb 100644 --- a/source/PyMaterialX/PyMaterialXRenderOsl/PyOslRenderer.cpp +++ b/source/PyMaterialX/PyMaterialXRenderOsl/PyOslRenderer.cpp @@ -13,14 +13,14 @@ namespace mx = MaterialX; void bindPyOslRenderer(py::module& mod) { 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) + .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, "Initialize with the given implementation element.\n\nInitialization must set the name and hash for the implementation, as well as any other data needed to emit code for the node.") - .def("createProgram", static_cast(&mx::OslRenderer::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::OslRenderer::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::OslRenderer::validateInputs, "Validate inputs for the program.") - .def("render", &mx::OslRenderer::render, "Render the current program to an offscreen buffer.") - .def("captureImage", &mx::OslRenderer::captureImage, "Capture the current contents of the off-screen hardware buffer as an image.") + .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") From fabfe2aef9ed72a66b75a060cfaf9a9f7aeab707 Mon Sep 17 00:00:00 2001 From: kwokcb Date: Tue, 18 Nov 2025 00:00:01 -0500 Subject: [PATCH 8/9] Remove non-ansi characters. --- python/Scripts/pybind_docs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/Scripts/pybind_docs.py b/python/Scripts/pybind_docs.py index c0281aa80c..207fbfe843 100644 --- a/python/Scripts/pybind_docs.py +++ b/python/Scripts/pybind_docs.py @@ -177,7 +177,7 @@ def _process_file(self, cpp_file: Path) -> bool: if content != original: cpp_file.write_text(content, encoding="utf-8") - print(f" ✓ {cpp_file.relative_to(self.pybind_dir.parent)}") + print(f" - {cpp_file.relative_to(self.pybind_dir.parent)}") return True else: print(f" - {cpp_file.relative_to(self.pybind_dir.parent)}") @@ -498,8 +498,8 @@ def main(): 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(" - 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) From d4ac99b3e9210111e865d22d33412d7bf6565c32 Mon Sep 17 00:00:00 2001 From: kwokcb Date: Wed, 10 Dec 2025 12:30:33 -0500 Subject: [PATCH 9/9] Revert so source does not have embedded pybind docs. --- .../PyMaterialXCore/PyDefinition.cpp | 112 +++++---- .../PyMaterialXCore/PyDocument.cpp | 173 +++++++------ .../PyMaterialX/PyMaterialXCore/PyElement.cpp | 234 +++++++++--------- source/PyMaterialX/PyMaterialXCore/PyGeom.cpp | 99 ++++---- .../PyMaterialXCore/PyInterface.cpp | 139 ++++++----- source/PyMaterialX/PyMaterialXCore/PyLook.cpp | 110 ++++---- .../PyMaterialXCore/PyMaterial.cpp | 5 +- source/PyMaterialX/PyMaterialXCore/PyNode.cpp | 102 ++++---- .../PyMaterialXCore/PyProperty.cpp | 48 ++-- .../PyMaterialXCore/PyTraversal.cpp | 52 ++-- .../PyMaterialX/PyMaterialXCore/PyTypes.cpp | 78 +++--- .../PyMaterialXCore/PyUnitConverter.cpp | 44 ++-- source/PyMaterialX/PyMaterialXCore/PyUtil.cpp | 26 +- .../PyMaterialX/PyMaterialXCore/PyValue.cpp | 15 +- .../PyMaterialX/PyMaterialXCore/PyVariant.cpp | 27 +- .../PyMaterialX/PyMaterialXFormat/PyFile.cpp | 56 +++-- .../PyMaterialX/PyMaterialXFormat/PyUtil.cpp | 19 +- .../PyMaterialX/PyMaterialXFormat/PyXmlIo.cpp | 13 +- .../PyGlslShaderGenerator.cpp | 48 ++-- .../PyMdlShaderGenerator.cpp | 6 +- .../PyMslShaderGenerator.cpp | 10 +- .../PyOslShaderGenerator.cpp | 8 +- .../PyColorManagement.cpp | 18 +- .../PyMaterialXGenShader/PyGenContext.cpp | 24 +- .../PyMaterialXGenShader/PyGenOptions.cpp | 2 +- .../PyMaterialXGenShader/PyShader.cpp | 22 +- .../PyShaderGenerator.cpp | 20 +- .../PyMaterialXGenShader/PyShaderPort.cpp | 46 ++-- .../PyMaterialXGenShader/PyShaderStage.cpp | 44 ++-- .../PyShaderTranslator.cpp | 8 +- .../PyMaterialXGenShader/PyTypeDesc.cpp | 24 +- .../PyMaterialXGenShader/PyUnitSystem.cpp | 16 +- .../PyMaterialXGenShader/PyUtil.cpp | 24 +- .../PySlangShaderGenerator.cpp | 10 +- .../PyMaterialXRender/PyCamera.cpp | 38 +-- .../PyMaterialXRender/PyCgltfLoader.cpp | 6 +- .../PyMaterialXRender/PyGeometryHandler.cpp | 28 +-- .../PyMaterialXRender/PyLightHandler.cpp | 52 ++-- .../PyMaterialX/PyMaterialXRender/PyMesh.cpp | 100 ++++---- .../PyMaterialXRender/PyOiioImageLoader.cpp | 6 +- .../PyMaterialXRender/PyShaderRenderer.cpp | 32 +-- .../PyMaterialXRender/PyStbImageLoader.cpp | 8 +- .../PyMaterialXRender/PyTinyObjLoader.cpp | 6 +- .../PyGLTextureHandler.cpp | 13 +- .../PyMaterialXRenderGlsl/PyGlslProgram.cpp | 51 ++-- .../PyMaterialXRenderGlsl/PyGlslRenderer.cpp | 20 +- .../PyMaterialXRenderGlsl/PyTextureBaker.cpp | 62 ++--- .../PyMaterialXRenderOsl/PyOslRenderer.cpp | 40 +-- 48 files changed, 1100 insertions(+), 1044 deletions(-) diff --git a/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp b/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp index 160effe781..7990387f96 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp @@ -14,16 +14,17 @@ namespace mx = MaterialX; void bindPyDefinition(py::module& mod) { - 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.") + 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) .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) @@ -35,69 +36,70 @@ 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", "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.") + 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) .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", "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.") + 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) .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", "A member element within a TypeDef.") + py::class_(mod, "Member") .def_readonly_static("CATEGORY", &mx::TypeDef::CATEGORY); - py::class_(mod, "Unit", "A unit declaration within a UnitDef.") + py::class_(mod, "Unit") .def_readonly_static("CATEGORY", &mx::Unit::CATEGORY); - 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.") + 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) .def_readonly_static("CATEGORY", &mx::UnitDef::CATEGORY) .def_readonly_static("UNITTYPE_ATTRIBUTE", &mx::UnitDef::UNITTYPE_ATTRIBUTE); - py::class_(mod, "UnitTypeDef", "A unit type definition element within a Document.") - .def("getUnitDefs", &mx::UnitTypeDef::getUnitDefs, "Find all UnitDefs for the UnitTypeDef.") + py::class_(mod, "UnitTypeDef") + .def("getUnitDefs", &mx::UnitTypeDef::getUnitDefs) .def_readonly_static("CATEGORY", &mx::UnitTypeDef::CATEGORY); - 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.") + 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) .def_readonly_static("CATEGORY", &mx::AttributeDef::CATEGORY); - 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.") + py::class_(mod, "TargetDef") + .def("getMatchingTargets", &mx::TargetDef::getMatchingTargets) .def_readonly_static("CATEGORY", &mx::TargetDef::CATEGORY); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp b/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp index d76b03425a..e021bc92de 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp @@ -24,89 +24,100 @@ class PyBindDocument : public mx::Document void bindPyDocument(py::module& mod) { - mod.def("createDocument", &mx::createDocument, "Create a new document of the given subclass.\n\nCreate a new Document."); + mod.def("createDocument", &mx::createDocument); - 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.") + 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) .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, "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."); + .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); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyElement.cpp b/source/PyMaterialX/PyMaterialXCore/PyElement.cpp index f297feb90a..05f858d4c3 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", "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.") + py::class_(mod, "Element") .def(py::self == py::self) .def(py::self != py::self) .def("isEquivalent", [](const mx::Element& elem, mx::ConstElementPtr& rhs, const mx::ElementEquivalenceOptions& options) @@ -34,76 +34,81 @@ void bindPyElement(py::module& mod) std::string message; bool res = elem.isEquivalent(rhs, options, &message); return std::pair(res, message); - }, "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("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) .def("validate", [](const mx::Element& elem) { std::string message; bool res = elem.validate(&message); return std::pair(res, message); - }, "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("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) .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) @@ -130,37 +135,38 @@ void bindPyElement(py::module& mod) BIND_ELEMENT_FUNC_INSTANCE(TypeDef) BIND_ELEMENT_FUNC_INSTANCE(Visibility); - 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.") + 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) .def_readonly_static("TYPE_ATTRIBUTE", &mx::TypedElement::TYPE_ATTRIBUTE); - 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.") + 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) .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) @@ -193,42 +199,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", "A token element representing a string value.\n\nToken elements are used to define input and output values for string substitutions in image filenames.") + py::class_(mod, "Token") .def_readonly_static("CATEGORY", &mx::Token::CATEGORY); - 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.") + py::class_(mod, "CommentElement") .def_readonly_static("CATEGORY", &mx::CommentElement::CATEGORY); - py::class_(mod, "NewlineElement", "An element representing a newline within a document.") + py::class_(mod, "NewlineElement") .def_readonly_static("CATEGORY", &mx::NewlineElement::CATEGORY); - py::class_(mod, "GenericElement", "A generic element subclass, for instantiating elements with unrecognized categories.") + py::class_(mod, "GenericElement") .def_readonly_static("CATEGORY", &mx::GenericElement::CATEGORY); - py::class_(mod, "ElementEquivalenceOptions", "A set of options for comparing the functional equivalence of elements.") + py::class_(mod, "ElementEquivalenceOptions") .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", "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, "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, "ElementPredicate"); py::register_exception(mod, "ExceptionOrphanedElement"); - 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."); + mod.def("targetStringsMatch", &mx::targetStringsMatch); + mod.def("prettyPrint", &mx::prettyPrint); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp b/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp index 4f3ea67ace..712851fac2 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyGeom.cpp @@ -15,26 +15,27 @@ namespace mx = MaterialX; void bindPyGeom(py::module& mod) { - 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, "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, "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.") + 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) BIND_GEOMINFO_FUNC_INSTANCE(integer, int) BIND_GEOMINFO_FUNC_INSTANCE(boolean, bool) BIND_GEOMINFO_FUNC_INSTANCE(float, float) @@ -52,42 +53,42 @@ void bindPyGeom(py::module& mod) BIND_GEOMINFO_FUNC_INSTANCE(stringarray, mx::StringVec) .def_readonly_static("CATEGORY", &mx::GeomInfo::CATEGORY); - py::class_(mod, "GeomProp", "A geometric property element within a GeomInfo.") + py::class_(mod, "GeomProp") .def_readonly_static("CATEGORY", &mx::GeomProp::CATEGORY); - 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.") + 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) .def_readonly_static("CATEGORY", &mx::GeomPropDef::CATEGORY); - 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.") + 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) .def_readonly_static("CATEGORY", &mx::Collection::CATEGORY); - 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.def("geomStringsMatch", &mx::geomStringsMatch); 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 42c7851089..42e978d282 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp @@ -17,79 +17,84 @@ namespace mx = MaterialX; void bindPyInterface(py::module& mod) { - 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, "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, "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.") + 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) .def_readonly_static("CATEGORY", &mx::Input::CATEGORY); - 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.") + py::class_(mod, "Output") + .def("hasUpstreamCycle", &mx::Output::hasUpstreamCycle) .def_readonly_static("CATEGORY", &mx::Output::CATEGORY) .def_readonly_static("DEFAULT_INPUT_ATTRIBUTE", &mx::Output::DEFAULT_INPUT_ATTRIBUTE); - 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.") + 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) 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 f3acac53b6..5d1e1e8af3 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyLook.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyLook.cpp @@ -15,66 +15,72 @@ namespace mx = MaterialX; void bindPyLook(py::module& mod) { - 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.") + 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) .def_readonly_static("CATEGORY", &mx::Look::CATEGORY); - 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.") + py::class_(mod, "LookGroup") + .def("getLooks", &mx::LookGroup::getLooks) + .def("setLooks", &mx::LookGroup::setLooks) + .def("getActiveLook", &mx::LookGroup::getActiveLook) + .def("setActiveLook", &mx::LookGroup::setActiveLook) .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", "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.") + 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) .def_readonly_static("CATEGORY", &mx::MaterialAssign::CATEGORY); - 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.") + 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) .def_readonly_static("CATEGORY", &mx::Visibility::CATEGORY); - 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"); + mod.def("getGeometryBindings", &mx::getGeometryBindings, + py::arg("materialNode") , py::arg("geom") = mx::UNIVERSAL_GEOM_NAME); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyMaterial.cpp b/source/PyMaterialX/PyMaterialXCore/PyMaterial.cpp index 3a4500e0f5..31656c67f9 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyMaterial.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyMaterial.cpp @@ -13,6 +13,7 @@ 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, "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."); + 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); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyNode.cpp b/source/PyMaterialX/PyMaterialXCore/PyNode.cpp index 8fabf495c2..55448dee80 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyNode.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyNode.cpp @@ -14,58 +14,66 @@ void bindPyNode(py::module& mod) { py::class_(mod, "NodePredicate"); - 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.") + 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) .def_readonly_static("CATEGORY", &mx::Node::CATEGORY); - 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, "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, "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.") + 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) .def_readonly_static("CATEGORY", &mx::NodeGraph::CATEGORY); - 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.") + 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) .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 3018c50d68..a9b213a19b 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", "A property element within a PropertySet.") + py::class_(mod, "Property") .def_readonly_static("CATEGORY", &mx::Property::CATEGORY); - 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.") + 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) .def_readonly_static("CATEGORY", &mx::PropertyAssign::CATEGORY); - 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.") + py::class_(mod, "PropertySet") + .def("addProperty", &mx::PropertySet::addProperty) + .def("getProperties", &mx::PropertySet::getProperties) + .def("removeProperty", &mx::PropertySet::removeProperty) + .def("_getPropertyValue", &mx::PropertySet::getPropertyValue) 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", "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.") + 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) .def_readonly_static("CATEGORY", &mx::PropertySetAssign::CATEGORY); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyTraversal.cpp b/source/PyMaterialX/PyMaterialXCore/PyTraversal.cpp index 0a9bc16206..f83200b680 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", "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, "Edge") + .def("getDownstreamElement", &mx::Edge::getDownstreamElement) + .def("getConnectingElement", &mx::Edge::getConnectingElement) + .def("getUpstreamElement", &mx::Edge::getUpstreamElement) + .def("getName", &mx::Edge::getName); - 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.") + py::class_(mod, "TreeIterator") + .def("getElement", &mx::TreeIterator::getElement) + .def("getElementDepth", &mx::TreeIterator::getElementDepth) + .def("setPruneSubtree", &mx::TreeIterator::setPruneSubtree) + .def("getPruneSubtree", &mx::TreeIterator::getPruneSubtree) .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", "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.") + 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) .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", "An iterator object representing the current state of an inheritance traversal.") + py::class_(mod, "InheritanceIterator") .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 973abd9f03..fec7867b3a 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, "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("getMagnitude", &V::getMagnitude) \ +.def("getNormalized", &V::getNormalized) \ +.def("dot", &V::dot) \ .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); }, "Create a deep copy of the document.") \ +.def("copy", [](const V& v) { return V(v); }) \ .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); }, "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("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_static("__len__", &M::numRows) void bindPyTypes(py::module& mod) { - 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, "VectorBase"); + py::class_(mod, "MatrixBase"); - py::class_(mod, "Vector2", "A vector of two floating-point values.") + py::class_(mod, "Vector2") BIND_VECTOR_SUBCLASS(mx::Vector2, 2) .def(py::init()) - .def("cross", &mx::Vector2::cross, "Return the cross product of two vectors.") + .def("cross", &mx::Vector2::cross) .def("asTuple", [](const mx::Vector2& v) { return std::make_tuple(v[0], v[1]); }); - py::class_(mod, "Vector3", "A vector of three floating-point values.") + py::class_(mod, "Vector3") BIND_VECTOR_SUBCLASS(mx::Vector3, 3) .def(py::init()) - .def("cross", &mx::Vector3::cross, "Return the cross product of two vectors.") + .def("cross", &mx::Vector3::cross) .def("asTuple", [](const mx::Vector3& v) { return std::make_tuple(v[0], v[1], v[2]); }); - py::class_(mod, "Vector4", "A vector of four floating-point values.") + py::class_(mod, "Vector4") 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", "A three-component color value.") + py::class_(mod, "Color3") BIND_VECTOR_SUBCLASS(mx::Color3, 3) .def(py::init()) - .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("linearToSrgb", &mx::Color3::linearToSrgb) + .def("srgbToLinear", &mx::Color3::srgbToLinear) .def("asTuple", [](const mx::Color3& v) { return std::make_tuple(v[0], v[1], v[2]); }); - py::class_(mod, "Color4", "A four-component color value.") + py::class_(mod, "Color4") 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", "A 3x3 matrix of floating-point values.\n\nVector transformation methods follow the row-vector convention, with matrix-vector multiplication computed as v' = vM.") + py::class_(mod, "Matrix33") BIND_MATRIX_SUBCLASS(mx::Matrix33, 3) .def(py::init()) - .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("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_readonly_static("IDENTITY", &mx::Matrix33::IDENTITY); - 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.") + py::class_(mod, "Matrix44") BIND_MATRIX_SUBCLASS(mx::Matrix44, 4) .def(py::init()) - .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("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_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 d4f749bf4e..bf3bcbf0f9 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", "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, "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, "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, "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, "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."); + 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); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyUtil.cpp b/source/PyMaterialX/PyMaterialXCore/PyUtil.cpp index 85ad22eac2..826c5adf84 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, "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."); + 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); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyValue.cpp b/source/PyMaterialX/PyMaterialXCore/PyValue.cpp index 05fcd44278..c36bd6c288 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, "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("getData", &mx::TypedValue::getData) \ + .def("getValueString", &mx::TypedValue::getValueString) \ .def_static("createValue", &mx::Value::createValue) \ .def_readonly_static("TYPE", &mx::TypedValue::TYPE); @@ -20,10 +20,13 @@ namespace mx = MaterialX; void bindPyValue(py::module& mod) { - 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."); + 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); 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 03942ae93c..13c7fa572c 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyVariant.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyVariant.cpp @@ -12,22 +12,23 @@ namespace mx = MaterialX; void bindPyVariant(py::module& mod) { - py::class_(mod, "Variant", "A variant element within a VariantSet.") + py::class_(mod, "Variant") .def_readonly_static("CATEGORY", &mx::Variant::CATEGORY); - 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.") + 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) .def_readonly_static("CATEGORY", &mx::VariantSet::CATEGORY); - 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.") + 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) .def_readonly_static("CATEGORY", &mx::VariantAssign::CATEGORY); } diff --git a/source/PyMaterialX/PyMaterialXFormat/PyFile.cpp b/source/PyMaterialX/PyMaterialXFormat/PyFile.cpp index f4c0f489d7..c451037566 100644 --- a/source/PyMaterialX/PyMaterialXFormat/PyFile.cpp +++ b/source/PyMaterialX/PyMaterialXFormat/PyFile.cpp @@ -24,42 +24,44 @@ void bindPyFile(py::module& mod) .value("FormatNative", mx::FilePath::Format::FormatNative) .export_values(); - py::class_(mod, "FilePath", "A generic file path, supporting both syntactic and file system operations.") + py::class_(mod, "FilePath") .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, "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."); + .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); - 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.") + py::class_(mod, "FileSearchPath") .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, "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."); + .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); py::implicitly_convertible(); py::implicitly_convertible(); diff --git a/source/PyMaterialX/PyMaterialXFormat/PyUtil.cpp b/source/PyMaterialX/PyMaterialXFormat/PyUtil.cpp index 7668da1221..dcd78bbcb5 100644 --- a/source/PyMaterialX/PyMaterialXFormat/PyUtil.cpp +++ b/source/PyMaterialX/PyMaterialXFormat/PyUtil.cpp @@ -14,11 +14,16 @@ namespace mx = MaterialX; void bindPyUtil(py::module& mod) { - 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."); + 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); } diff --git a/source/PyMaterialX/PyMaterialXFormat/PyXmlIo.cpp b/source/PyMaterialX/PyMaterialXFormat/PyXmlIo.cpp index c08875b3f4..9b4b32cf6a 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", "A set of options for controlling the behavior of XML read functions.") + py::class_(mod, "XmlReadOptions") .def(py::init()) .def_readwrite("readXIncludeFunction", &mx::XmlReadOptions::readXIncludeFunction) .def_readwrite("readComments", &mx::XmlReadOptions::readComments) @@ -21,20 +21,23 @@ void bindPyXmlIo(py::module& mod) .def_readwrite("upgradeVersion", &mx::XmlReadOptions::upgradeVersion) .def_readwrite("parentXIncludes", &mx::XmlReadOptions::parentXIncludes); - py::class_(mod, "XmlWriteOptions", "A set of options for controlling the behavior of XML write functions.") + py::class_(mod, "XmlWriteOptions") .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, "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("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("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, "Return a FileSearchPath object from search path environment variable."); + mod.def("getEnvironmentPath", &mx::getEnvironmentPath, + py::arg("sep") = mx::PATH_LIST_SEPARATOR); 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 fd95bc5522..87726352ff 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", "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."); + py::class_(mod, "GlslShaderGenerator") + .def_static("create", &GlslShaderGenerator_create) + .def("generate", &mx::GlslShaderGenerator::generate) + .def("getTarget", &mx::GlslShaderGenerator::getTarget) + .def("getVersion", &mx::GlslShaderGenerator::getVersion); } void bindPyGlslResourceBindingContext(py::module &mod) { - py::class_(mod, "GlslResourceBindingContext", "Class representing a resource binding for Glsl shader resources.") - .def_static("create", &mx::GlslResourceBindingContext::create, "") + py::class_(mod, "GlslResourceBindingContext") + .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", "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."); + py::class_(mod, "EsslShaderGenerator") + .def_static("create", &EsslShaderGenerator_create) + .def("generate", &mx::EsslShaderGenerator::generate) + .def("getTarget", &mx::EsslShaderGenerator::getTarget) + .def("getVersion", &mx::EsslShaderGenerator::getVersion); } // Glsl Vulkan shader generator bindings void bindPyVkShaderGenerator(py::module& mod) { - 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."); + py::class_(mod, "VkShaderGenerator") + .def_static("create", &VkShaderGenerator_create) + .def("generate", &mx::VkShaderGenerator::generate) + .def("getTarget", &mx::VkShaderGenerator::getTarget) + .def("getVersion", &mx::VkShaderGenerator::getVersion); } // Glsl Wgsl shader generator bindings void bindPyWgslShaderGenerator(py::module& mod) { - 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."); + py::class_(mod, "WgslShaderGenerator") + .def_static("create", &WgslShaderGenerator_create) + .def("generate", &mx::WgslShaderGenerator::generate) + .def("getTarget", &mx::WgslShaderGenerator::getTarget) + .def("getVersion", &mx::WgslShaderGenerator::getVersion); } diff --git a/source/PyMaterialX/PyMaterialXGenMdl/PyMdlShaderGenerator.cpp b/source/PyMaterialX/PyMaterialXGenMdl/PyMdlShaderGenerator.cpp index 5173e7f655..b62a84a65f 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", "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."); + py::class_(mod, "MdlShaderGenerator") + .def_static("create", &MdlShaderGenerator_create) + .def("getTarget", &mx::MdlShaderGenerator::getTarget); } diff --git a/source/PyMaterialX/PyMaterialXGenMsl/PyMslShaderGenerator.cpp b/source/PyMaterialX/PyMaterialXGenMsl/PyMslShaderGenerator.cpp index 84081b2c39..e11cedf4d9 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, "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."); + .def_static("create", &MslShaderGenerator_create) + .def("generate", &mx::MslShaderGenerator::generate) + .def("getTarget", &mx::MslShaderGenerator::getTarget) + .def("getVersion", &mx::MslShaderGenerator::getVersion); } void bindPyMslResourceBindingContext(py::module &mod) { py::class_(mod, "MslResourceBindingContext") - .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_static("create", &mx::MslResourceBindingContext::create) .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 473b598c8c..6e457411b9 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", "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."); + py::class_(mod, "OslShaderGenerator") + .def_static("create", &OslShaderGenerator_create) + .def("getTarget", &mx::OslShaderGenerator::getTarget) + .def("generate", &mx::OslShaderGenerator::generate); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyColorManagement.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyColorManagement.cpp index 672c25b377..8229294c7e 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", "Structure that represents color space transform information.") + py::class_(mod, "ColorSpaceTransform") .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", "Abstract base class for color management systems.") + py::class_(mod, "ColorManagementSystem") .def(py::init<>()) - .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."); + .def("getName", &mx::ColorManagementSystem::getName) + .def("loadLibrary", &mx::ColorManagementSystem::loadLibrary) + .def("supportsTransform", &mx::ColorManagementSystem::supportsTransform); - 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."); + py::class_(mod, "DefaultColorManagementSystem") + .def_static("create", &mx::DefaultColorManagementSystem::create) + .def("getName", &mx::DefaultColorManagementSystem::getName); #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, "Return the name of this port."); + .def("getName", &mx::OcioColorManagementSystem::getName); #endif } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyGenContext.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyGenContext.cpp index 4aebed25a9..e73090e964 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", "A context class for shader generation.\n\nUsed for thread local storage of data needed during shader generation.") + py::class_(mod, "GenContext") .def(py::init()) - .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."); + .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); } void bindPyGenUserData(py::module& mod) { - 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."); + py::class_(mod, "GenUserData") + .def("getSelf", static_cast(&mx::GenUserData::getSelf)); } \ No newline at end of file diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyGenOptions.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyGenOptions.cpp index 698484df6b..3949d50b8d 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", "Class holding options to configure shader generation.") + py::class_(mod, "GenOptions") .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 b9293292ed..581964aeac 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", "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.") + py::class_(mod, "Shader") .def(py::init()) - .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."); + .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)); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyShaderGenerator.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyShaderGenerator.cpp index 744560ab92..fdf4b94aac 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", "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."); + 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); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyShaderPort.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyShaderPort.cpp index e7c62fd8c9..146674c222 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", "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."); + 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); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyShaderStage.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyShaderStage.cpp index e94b450f8a..62347caa6b 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", "A block of variables in a shader stage.") + py::class_(mod, "VariableBlock") .def(py::init()) - .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("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("__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, "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::return_value_policy::reference_internal); - py::class_(mod, "ShaderStage", "A shader stage, containing the state and resulting source code for the stage.") + py::class_(mod, "ShaderStage") .def(py::init()) - .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."); + .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); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyShaderTranslator.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyShaderTranslator.cpp index 214dca6e9c..c6b3f2bfe9 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", "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."); + py::class_(mod, "ShaderTranslator") + .def_static("create", &mx::ShaderTranslator::create) + .def("translateShader", &mx::ShaderTranslator::translateShader) + .def("translateAllMaterials", &mx::ShaderTranslator::translateAllMaterials); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyTypeDesc.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyTypeDesc.cpp index dd0469e5a3..66a1821e7b 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", "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."); + 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); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyUnitSystem.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyUnitSystem.cpp index 3e1193e565..e5befc21e6 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", "Structure that represents unit transform information.") + py::class_(mod, "UnitTransform") .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", "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."); + 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); } diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyUtil.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyUtil.cpp index 2d16b46ab9..6c73c2866f 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, "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"); + 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); } diff --git a/source/PyMaterialX/PyMaterialXGenSlang/PySlangShaderGenerator.cpp b/source/PyMaterialX/PyMaterialXGenSlang/PySlangShaderGenerator.cpp index 391202e5ba..bb69578412 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", "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."); + py::class_(mod, "SlangShaderGenerator") + .def_static("create", &SlangShaderGenerator_create) + .def("generate", &mx::SlangShaderGenerator::generate) + .def("getTarget", &mx::SlangShaderGenerator::getTarget) + .def("getVersion", &mx::SlangShaderGenerator::getVersion); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyCamera.cpp b/source/PyMaterialX/PyMaterialXRender/PyCamera.cpp index aecce93618..9c19ceb831 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", "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."); + 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); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyCgltfLoader.cpp b/source/PyMaterialX/PyMaterialXRender/PyCgltfLoader.cpp index d70c6d82b3..158e6b4849 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", "Wrapper for loader to read in GLTF files using the Cgltf library.") - .def_static("create", &mx::CgltfLoader::create, "Create a new loader.") + py::class_(mod, "CgltfLoader") + .def_static("create", &mx::CgltfLoader::create) .def(py::init<>()) - .def("load", &mx::CgltfLoader::load, "Load geometry from file path."); + .def("load", &mx::CgltfLoader::load); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyGeometryHandler.cpp b/source/PyMaterialX/PyMaterialXRender/PyGeometryHandler.cpp index 26192c43cc..08560254bc 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", "Base class representing a geometry loader.\n\nA loader can be associated with one or more file extensions.") + py::class_(mod, "GeometryLoader") .def(py::init<>()) - .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"); + .def("supportedExtensions", &mx::GeometryLoader::supportedExtensions) + .def("load", &mx::GeometryLoader::load); - py::class_(mod, "GeometryHandler", "Class which holds a set of geometry loaders.\n\nEach loader is associated with a given set of file extensions.") + py::class_(mod, "GeometryHandler") .def(py::init<>()) - .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."); + .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); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyLightHandler.cpp b/source/PyMaterialX/PyMaterialXRender/PyLightHandler.cpp index a2043d63ec..50725a7768 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", "Utility light handler for creating and providing light data for shader binding.") - .def_static("create", &mx::LightHandler::create, "Create a new light handler.") + py::class_(mod, "LightHandler") + .def_static("create", &mx::LightHandler::create) .def(py::init<>()) - .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"); + .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); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyMesh.cpp b/source/PyMaterialX/PyMaterialXRender/PyMesh.cpp index 9af79eefe5..5c26821436 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", "Class to represent a mesh data stream.") + py::class_(mod, "MeshStream") .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, "Create a new mesh stream.") + .def_static("create", &mx::MeshStream::create) .def(py::init()) - .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."); + .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); - 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.") + py::class_(mod, "MeshPartition") + .def_static("create", &mx::MeshPartition::create) .def(py::init<>()) - .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."); + .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); - py::class_(mod, "Mesh", "Container for mesh data.") - .def_static("create", &mx::Mesh::create, "Create a new mesh.") + py::class_(mod, "Mesh") + .def_static("create", &mx::Mesh::create) .def(py::init()) - .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."); + .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); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyOiioImageLoader.cpp b/source/PyMaterialX/PyMaterialXRender/PyOiioImageLoader.cpp index ee18b4bb2e..387af2714d 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, "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_static("create", &mx::OiioImageLoader::create) .def(py::init<>()) - .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."); + .def("saveImage", &mx::OiioImageLoader::saveImage) + .def("loadImage", &mx::OiioImageLoader::loadImage); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp b/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp index 365bbc8907..94c0d49cee 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", "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."); + 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); static py::exception pyExceptionRenderError(mod, "ExceptionRenderError"); diff --git a/source/PyMaterialX/PyMaterialXRender/PyStbImageLoader.cpp b/source/PyMaterialX/PyMaterialXRender/PyStbImageLoader.cpp index 16e04f8440..4c710746c1 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", "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."); + py::class_(mod, "StbImageLoader") + .def_static("create", &mx::StbImageLoader::create) + .def("saveImage", &mx::StbImageLoader::saveImage) + .def("loadImage", &mx::StbImageLoader::loadImage); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyTinyObjLoader.cpp b/source/PyMaterialX/PyMaterialXRender/PyTinyObjLoader.cpp index b3e8e26499..13677453b9 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", "Wrapper for geometry loader to read in OBJ files using the TinyObj library.") - .def_static("create", &mx::TinyObjLoader::create, "Create a new TinyObjLoader.") + py::class_(mod, "TinyObjLoader") + .def_static("create", &mx::TinyObjLoader::create) .def(py::init<>()) - .def("load", &mx::TinyObjLoader::load, "Load geometry from disk."); + .def("load", &mx::TinyObjLoader::load); } diff --git a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp index d7d920a77a..9ebbd93ecb 100644 --- a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp +++ b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp @@ -12,10 +12,11 @@ namespace mx = MaterialX; void bindPyGLTextureHandler(py::module& mod) { - 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."); + 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); } diff --git a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslProgram.cpp b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslProgram.cpp index 9c7a842183..f0b7f59f0f 100644 --- a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslProgram.cpp +++ b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslProgram.cpp @@ -12,34 +12,35 @@ namespace mx = MaterialX; void bindPyGlslProgram(py::module& mod) { - 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.") + py::class_(mod, "GlslProgram") .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, "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."); + .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); - 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.") + py::class_(mod, "Input") .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 ac2b7d7431..354ddf1a28 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", "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."); + 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); } diff --git a/source/PyMaterialX/PyMaterialXRenderGlsl/PyTextureBaker.cpp b/source/PyMaterialX/PyMaterialXRenderGlsl/PyTextureBaker.cpp index fedf6a2547..800ddf590a 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", "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."); + 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); } diff --git a/source/PyMaterialX/PyMaterialXRenderOsl/PyOslRenderer.cpp b/source/PyMaterialX/PyMaterialXRenderOsl/PyOslRenderer.cpp index 11290538fb..9ae042cced 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", "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.") + py::class_(mod, "OslRenderer") + .def_static("create", &mx::OslRenderer::create) .def_readwrite_static("OSL_CLOSURE_COLOR_STRING", &mx::OslRenderer::OSL_CLOSURE_COLOR_STRING) - .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."); + .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); }