QuiltiX Plugins 0.0.1
Custom Plugins for QuiltiX
Loading...
Searching...
No Matches
materialxgltf_pt/plugin.py
Go to the documentation of this file.
1"""
2Copyright (c) 2025 NanMu Consulting
3Author: Bernard Kwok (kwokcb@gmail.com)
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16"""
17
18'''
19@file materialxgltf_pt/plugin.py
20@brief MaterialX glTF Texture Procedurals plugin for QuiltiX
21@details This plugin provides serialization of MaterialX graphs and procedural texture extension for glTF.
22'''
23import logging
24import os
25
26# Optional syntax highlighting if pygments is installed
27have_highliting = True
28try:
29 from pygments import highlight
30 from pygments.lexers import JsonLexer
31 from pygments.formatters import HtmlFormatter
32except ImportError:
33 have_highliting = False
34
35from typing import TYPE_CHECKING
36
37def load_qt_modules():
38 global QtCore, QAction, QTextEdit
39 try:
40 from qtpy import QtCore # type: ignore
41 from qtpy.QtWidgets import ( # type: ignore
42 QAction,
43 QTextEdit,
44 )
45 logger.info("qtpy modules loaded successfully")
46 return True
47 except ImportError as e:
48 logger.error(f"Failed to import qtpy modules: {e}")
49 return False
50
51from QuiltiX import constants, qx_plugin
52
53logger = logging.getLogger(__name__)
54have_module = True
55
56try:
57 from gltf_materialx_converter import converter as MxGLTFPT
58 from gltf_materialx_converter import utilities as MxGLTFPTUtil
59except ImportError:
60 have_module = False
61 logger.error("gltf_materialx_converter module not found")
62
63if TYPE_CHECKING:
64 from QuiltiX import quiltix
65
66
68 def __init__(self, font_size="14px"):
69 self.lexer = JsonLexer()
70 self.formatter = HtmlFormatter(linenos=False, style='github-dark')
71 self.font_size = font_size
72
73 def highlight(self, text):
74 highlighted_html = highlight(text, self.lexer, self.formatter)
75 styles = (
76 f"<style>"
77 f"{self.formatter.get_style_defs('.highlight')}"
78 f"pre {{ line-height: 1.0; margin: 0; font-size: {self.font_size}; }}"
79 f"</style>"
80 )
81 full_html = f"<html><head>{styles}</head><body>{highlighted_html}</body></html>"
82 return full_html
83
85 def __init__(self, editor, root):
86 '''
87 Initialize the JSON serializer.
88 '''
89 self.editor = editor
90 self.root = root
91 self.indent = 4
92
93 stdlib, libFiles = MxGLTFPTUtil.load_standard_libraries()
94 self.stdlib = stdlib
95
96 # Add JSON menu to the file menu
97 # ----------------------------------------
98 editor.file_menu.addSeparator()
99 mymenu = editor.file_menu.addMenu("glTF Texture Procedurals")
100
101 # Export procedurals item
102 export_json = QAction("Save procedurals...", editor)
103 export_json.triggered.connect(self.export_json_triggeredexport_json_triggered)
104 mymenu.addAction(export_json)
105
106 # Import procedurals item
107 import_json = QAction("Load procedurals...", editor)
108 import_json.triggered.connect(self.import_gltf_triggeredimport_gltf_triggered)
109 mymenu.addAction(import_json)
110
111 # Show procedurals text. Does most of export, except does not write to file
112 show_json_text = QAction("Show procedurals...", editor)
113 show_json_text.triggered.connect(self.show_gltf_triggeredshow_gltf_triggered)
114 mymenu.addAction(show_json_text)
115
116 def set_indent(self, indent):
117 '''
118 Set the indent for the JSON output.
119 '''
120 self.indent = indent
121
123 '''
124 Get the JSON for the given MaterialX document.
125 '''
126 doc = self.editor.qx_node_graph.get_current_mx_graph_doc()
127 if doc:
128 logger.debug("Convert graph to glTF procedural")
129 doc.importLibrary(self.stdlib)
130 converter = MxGLTFPT.glTFMaterialXConverter()
131 json_result, status = converter.materialX_to_glTF(doc)
132 #logger.debug("glTF conversion status: " + status + " JSON: " + json_result)
133 if (len(json_result) == 0):
134 logger.warning("Empty result for conversion to glTF")
135 return None
136 return json_result
137 return None
138
140 '''
141 Show the JSON for the current MaterialX document.
142 '''
143 logger.debug("Show glTF triggered")
144 json_result = self.get_gltf_from_graph()
145
146 # Write JSON UI text box
147 if json_result:
148 self.show_text_box(json_result, "glTF Texture Procedurals")
149 else:
150 logger.error(f"Failed to get glTF from graph")
151
152 def export_json_triggered(self, editor):
153 '''
154 Export the current graph to a JSON file.
155 '''
156 start_path = self.editor.mx_selection_path
157 if not start_path:
158 start_path = self.editor.geometry_selection_path
159
160 if not start_path:
161 start_path = os.path.join(self.root, "resources", "materials")
162
163 path = self.editor.request_filepath(
164 title="Save procedural file",
165 start_path=start_path,
166 file_filter="glTF files (*.gltf)",
167 mode="save",
168 )
169
170 if not path:
171 return
172
173 json_result = self.get_gltf_from_graph()
174
175 # Write JSON to file
176 if json_result:
177 with open(path, "w") as f:
178 f.write(json_result)
179 logger.info(f"Wrote glTF file: {path}")
180
181 self.editor.set_current_filepath(path)
182
183 def load_gltf_file(self, inputFile):
184 if os.path.exists(inputFile):
185 json_string = MxGLTFPTUtil.load_json_file(inputFile)
186 return json_string
187
188 def import_gltf_triggered(self, editor):
189 '''
190 Import a glTF procedural file into the current graph.
191 '''
192 start_path = self.editor.mx_selection_path
193 if not start_path:
194 start_path = self.editor.geometry_selection_path
195
196 if not start_path:
197 start_path = os.path.join(self.root, "resources", "materials")
198
199 path = self.editor.request_filepath(
200 title="Load glTF file",
201 start_path=start_path,
202 file_filter="glTF files (*.gltf)",
203 mode="open",
204 )
205 if not path:
206 return
207
208 if not os.path.exists(path):
209 logger.error("Cannot find input file: " + path)
210 return
211
212 converter = MxGLTFPT.glTFMaterialXConverter()
213 json_string = self.load_gltf_file(path)
214 logger.info("Loaded glTF: " + json_string)
215 if (len(json_string) == 0):
216 logger.error("glTF file is empty: " + path)
217 return
218
219 doc = converter.gltf_string_to_materialX(json_string, self.stdlib)
220 if doc:
221 logger.info("Loaded glTF file: " + path)
222 self.editor.mx_selection_path = path
223 self.editor.qx_node_graph.load_graph_from_mx_doc(doc)
224 self.editor.qx_node_graph.mx_file_loaded.emit(path)
225
226 # Helper functions
227 def show_text_box(self, text, title=""):
228 '''
229 Show a text box with the given text.
230 '''
231 te_text = QTextEdit()
232 te_text.setReadOnly(True)
233 te_text.setParent(self.editor, QtCore.Qt.Window)
234 te_text.setWindowTitle(title)
235 te_text.resize(1000, 800)
236
237 if have_highliting:
238 jsonHighlighter = JsonHighlighter()
239 highlighted_html = jsonHighlighter.highlight(text)
240 te_text.setHtml(highlighted_html)
241 else:
242 te_text.setPlainText(text)
243
244 te_text.show()
245
246
247@qx_plugin.hookimpl
248def after_ui_init(editor: "quiltix.QuiltiXWindow"):
249 logger.debug("> Checking if glTF Procedurals plugin is valid" + str(have_module))
250 if load_qt_modules():
251 editor.gltfPT_serializer = QuiltiX_GLTFPT_serializer(editor, constants.ROOT)
252 logger.debug(f"> QuiltiX GLTFPT plugin loaded {editor.gltfPT_serializer != None}")
253
254def plugin_name() -> str:
255 return "MaterialX GLTF Procedurals"
256
257def is_valid() -> bool:
258 return have_module
__init__(self, font_size="14px")
show_text_box(self, text, title="")
Show a text box with the given text.
__init__(self, editor, root)
Initialize the JSON serializer.
import_gltf_triggered(self, editor)
Import a glTF procedural file into the current graph.
get_gltf_from_graph(self)
Get the JSON for the given MaterialX document.
set_indent(self, indent)
Set the indent for the JSON output.
export_json_triggered(self, editor)
Export the current graph to a JSON file.
show_gltf_triggered(self)
Show the JSON for the current MaterialX document.
after_ui_init("quiltix.QuiltiXWindow" editor)
After UI initialization, add the MaterialX glTF serializer to the editor.
bool is_valid()
Check if the plugin is valid.
str plugin_name()
Get the name of the plugin.
load_qt_modules()
Function to delay loading of Qt modules until after the QuiltiX UI is initialized.