Browse Source

Centralize the definitions for standard Plasma APIs.

This helps make Korman more maintainable by reducing potential
duplication of Python attribute definitions. Further, it simplifies the
code to create a Python File Node for a standard API file.
pull/414/head
Adam Johnson 5 months ago
parent
commit
778caadf11
Signed by: Hoikas
GPG Key ID: 0B6515D6FF6F271E
  1. 118
      korman/plasma_api.py
  2. 12
      korman/properties/modifiers/avatar.py
  3. 5
      korman/properties/modifiers/base.py
  4. 87
      korman/properties/modifiers/gui.py
  5. 28
      korman/properties/modifiers/logic.py
  6. 26
      korman/properties/modifiers/render.py

118
korman/plasma_api.py

@ -0,0 +1,118 @@
# This file is part of Korman.
#
# Korman is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Korman is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Korman. If not, see <http://www.gnu.org/licenses/>.
python_files = {
# Provided by all variants of Uru and Myst V
"xDialogToggle.py": (
{ "id": 1, "type": "ptAttribActivator", "name": "Activate" },
{ "id": 4, "type": "ptAttribString", "name": "Vignette" },
),
# Provided by CWE or OfflineKI
"xDynTextLoc.py": (
{ "id": 1, "type": "ptAttribDynamicMap", "name": "dynTextMap", },
{ "id": 2, "type": "ptAttribString", "name": "locPath" },
{ "id": 3, "type": "ptAttribString", "name": "fontFace" },
{ "id": 4, "type": "ptAttribInt", "name": "fontSize" },
{ "id": 5, "type": "ptAttribFloat", "name": "fontColorR" },
{ "id": 6, "type": "ptAttribFloat", "name": "fontColorG" },
{ "id": 7, "type": "ptAttribFloat", "name": "fontColorB" },
{ "id": 8, "type": "ptAttribFloat", "name": "fontColorA" },
{ "id": 9, "type": "ptAttribInt", "name": "marginTop" },
{ "id": 10, "type": "ptAttribInt", "name": "marginLeft" },
{ "id": 11, "type": "ptAttribInt", "name": "marginBottom" },
{ "id": 12, "type": "ptAttribInt", "name": "marginRight" },
{ "id": 13, "type": "ptAttribInt", "name": "lineSpacing" },
# Yes, it"s really a ptAttribDropDownList, but those are only for use in
# artist generated node trees.
{ "id": 14, "type": "ptAttribString", "name": "justify" },
{ "id": 15, "type": "ptAttribFloat", "name": "clearColorR" },
{ "id": 16, "type": "ptAttribFloat", "name": "clearColorG" },
{ "id": 17, "type": "ptAttribFloat", "name": "clearColorB" },
{ "id": 18, "type": "ptAttribFloat", "name": "clearColorA" },
{ "id": 19, "type": "ptAttribBoolean", "name": "blockRGB" },
),
# Provided by CWE and OfflineKI
"xEntryCam.py": (
{ "id": 1, "type": "ptAttribActivator", "name": "actRegionSensor" },
{ "id": 2, "type": "ptAttribSceneobject", "name": "camera" },
{ "id": 3, "type": "ptAttribBoolean", "name": "undoFirstPerson" },
),
# Provided by CWE
"xJournalBookGUIPopup.py": (
{ "id": 1, "type": "ptAttribActivator", "name": "actClickableBook" },
{ "id": 10, "type": "ptAttribBoolean", "name": "StartOpen" },
{ "id": 11, "type": "ptAttribFloat", "name": "BookWidth" },
{ "id": 12, "type": "ptAttribFloat", "name": "BookHeight" },
{ "id": 13, "type": "ptAttribString", "name": "LocPath" },
{ "id": 14, "type": "ptAttribString", "name": "GUIType" },
),
# Provided by all variants of Uru and Myst V
"xLinkingBookGUIPopup.py": (
{ "id": 1, "type": "ptAttribActivator", "name": "actClickableBook" },
{ "id": 2, "type": "ptAttribBehavior", "name": "SeekBehavior" },
{ "id": 3, "type": "ptAttribResponder", "name": "respLinkResponder" },
{ "id": 4, "type": "ptAttribString", "name": "TargetAge" },
{ "id": 5, "type": "ptAttribActivator", "name": "actBookshelf" },
{ "id": 6, "type": "ptAttribActivator", "name": "shareRegion" },
{ "id": 7, "type": "ptAttribBehavior", "name": "shareBookSeek" },
{ "id": 10, "type": "ptAttribBoolean", "name": "IsDRCStamped" },
{ "id": 11, "type": "ptAttribBoolean", "name": "ForceThirdPerson" },
),
# Supplied by the OfflineKI script:
# https://gitlab.com/diafero/offline-ki/blob/master/offlineki/xSimpleJournal.py
"xSimpleJournal.py": (
{ "id": 1, "type": "ptAttribActivator", "name": "bookClickable" },
{ "id": 2, "type": "ptAttribString", "name": "journalFileName" },
{ "id": 3, "type": "ptAttribBoolean", "name": "isNotebook" },
{ "id": 4, "type": "ptAttribFloat", "name": "BookWidth" },
{ "id": 5, "type": "ptAttribFloat", "name": "BookHeight" },
),
# Supplied by the OfflineKI script:
# https://gitlab.com/diafero/offline-ki/blob/master/offlineki/xSimpleLinkingBook.py
"xSimpleLinkingBook.py": (
{ "id": 1, "type": "ptAttribActivator", "name": "bookClickable" },
{ "id": 2, "type": "ptAttribString", "name": "destinationAge" },
{ "id": 3, "type": "ptAttribString", "name": "spawnPoint" },
{ "id": 4, "type": "ptAttribString", "name": "linkPanel" },
{ "id": 5, "type": "ptAttribString", "name": "bookCover" },
{ "id": 6, "type": "ptAttribString", "name": "stampTexture" },
{ "id": 7, "type": "ptAttribFloat", "name": "stampX" },
{ "id": 8, "type": "ptAttribFloat", "name": "stampY" },
{ "id": 9, "type": "ptAttribFloat", "name": "bookWidth" },
{ "id": 10, "type": "ptAttribFloat", "name": "BookHeight" },
{ "id": 11, "type": "ptAttribBehavior", "name": "msbSeekBeforeUI" },
{ "id": 12, "type": "ptAttribResponder", "name": "respOneShot" },
),
# Provided by CWE or OfflineKI
"xSitCam.py": (
{ "id": 1, "type": "ptAttribActivator", "name": "sitAct" },
{ "id": 2, "type": "ptAttribSceneobject", "name": "sitCam" },
),
# Provided by all variants of Uru and Myst V
"xTelescope.py": (
{ "id": 1, "type": "ptAttribActivator", "name": "Activate" },
{ "id": 2, "type": "ptAttribSceneobject", "name": "Camera" },
{ "id": 3, "type": "ptAttribBehavior", "name": "Behavior" },
{ "id": 4, "type": "ptAttribString", "name": "Vignette" },
)
}

12
korman/properties/modifiers/avatar.py

@ -78,16 +78,6 @@ class PlasmaLadderModifier(PlasmaModifierProperties):
return True return True
# Use xSitCam.py for when we want a camera to pop up
sitting_pfm = {
"filename": "xSitCam.py",
"attribs": (
{ 'id': 1, 'type': "ptAttribActivator", 'name': "sitAct" },
{ 'id': 2, 'type': "ptAttribSceneobject", 'name': "sitCam" },
)
}
sitting_approach_flags = [("kApproachFront", "Front", "Approach from the font"), sitting_approach_flags = [("kApproachFront", "Front", "Approach from the font"),
("kApproachLeft", "Left", "Approach from the left"), ("kApproachLeft", "Left", "Approach from the left"),
("kApproachRight", "Right", "Approach from the right"), ("kApproachRight", "Right", "Approach from the right"),
@ -140,7 +130,7 @@ class PlasmaSittingBehavior(idprops.IDPropObjectMixin, PlasmaModifierProperties,
sittingmod.name = "SittingBeh" sittingmod.name = "SittingBeh"
# xSitCam.py PythonFileMod # xSitCam.py PythonFileMod
if self.sitting_camera is not None: if self.sitting_camera is not None:
sittingpynode = self._create_python_file_node(tree, sitting_pfm["filename"], sitting_pfm["attribs"]) sittingpynode = self._create_python_standard_file_node(tree, "xSitCam.py")
sittingmod.link_output(sittingpynode, "satisfies", "sitAct") sittingmod.link_output(sittingpynode, "satisfies", "sitAct")
# Camera Object # Camera Object

5
korman/properties/modifiers/base.py

@ -26,6 +26,7 @@ if TYPE_CHECKING:
from ...nodes.node_python import * from ...nodes.node_python import *
from ... import helpers from ... import helpers
from ... import plasma_api
class PlasmaModifierProperties(bpy.types.PropertyGroup): class PlasmaModifierProperties(bpy.types.PropertyGroup):
@property @property
@ -207,6 +208,10 @@ class PlasmaModifierLogicWiz:
pfm_node.update() pfm_node.update()
return pfm_node return pfm_node
def _create_standard_python_file_node(self, tree, filename: str) -> bpy.types.Node:
"""Create a Python File Node for a standard Plasma Python API file (e.g. xAgeSDLBoolShowHide.py)"""
return self._create_python_file_node(tree, filename, plasma_api.python_files[filename])
def _create_python_attribute(self, pfm_node: PlasmaPythonFileNode, attribute_name: str, **kwargs): def _create_python_attribute(self, pfm_node: PlasmaPythonFileNode, attribute_name: str, **kwargs):
"""Creates and links a Python Attribute Node to the Python File Node given by `pfm_node`. """Creates and links a Python Attribute Node to the Python File Node given by `pfm_node`.
For attribute nodes that require multiple values, the `value` may be set to None and For attribute nodes that require multiple values, the `value` may be set to None and

87
korman/properties/modifiers/gui.py

@ -35,33 +35,6 @@ if TYPE_CHECKING:
from ...exporter import Exporter from ...exporter import Exporter
from .game_gui import PlasmaGameGuiDialogModifier from .game_gui import PlasmaGameGuiDialogModifier
journal_pfms = {
pvPots : {
# Supplied by the OfflineKI script:
# https://gitlab.com/diafero/offline-ki/blob/master/offlineki/xSimpleJournal.py
"filename": "xSimpleJournal.py",
"attribs": (
{ 'id': 1, 'type': "ptAttribActivator", "name": "bookClickable" },
{ 'id': 2, 'type': "ptAttribString", "name": "journalFileName" },
{ 'id': 3, 'type': "ptAttribBoolean", "name": "isNotebook" },
{ 'id': 4, 'type': "ptAttribFloat", "name": "BookWidth" },
{ 'id': 5, 'type': "ptAttribFloat", "name": "BookHeight" },
)
},
pvMoul : {
"filename": "xJournalBookGUIPopup.py",
"attribs": (
{ 'id': 1, 'type': "ptAttribActivator", 'name': "actClickableBook" },
{ 'id': 10, 'type': "ptAttribBoolean", 'name': "StartOpen" },
{ 'id': 11, 'type': "ptAttribFloat", 'name': "BookWidth" },
{ 'id': 12, 'type': "ptAttribFloat", 'name': "BookHeight" },
{ 'id': 13, 'type': "ptAttribString", 'name': "LocPath" },
{ 'id': 14, 'type': "ptAttribString", 'name': "GUIType" },
)
},
}
# Do not change the numeric IDs. They allow the list to be rearranged. # Do not change the numeric IDs. They allow the list to be rearranged.
_languages = [("Dutch", "Nederlands", "Dutch", 0), _languages = [("Dutch", "Nederlands", "Dutch", 0),
("English", "English", "", 1), ("English", "English", "", 1),
@ -246,11 +219,11 @@ class PlasmaJournalBookModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz
def logicwiz(self, bo, tree, age_name, version): def logicwiz(self, bo, tree, age_name, version):
# Assign journal script based on target version # Assign journal script based on target version
journal_pfm = journal_pfms[version]
journalnode = self._create_python_file_node(tree, journal_pfm["filename"], journal_pfm["attribs"])
if version <= pvPots: if version <= pvPots:
journalnode = self._create_standard_python_file_node(tree, "xSimpleJournal.py")
self._create_pots_nodes(bo, tree.nodes, journalnode, age_name) self._create_pots_nodes(bo, tree.nodes, journalnode, age_name)
else: else:
journalnode = self._create_standard_python_file_node(tree, "xJournalBookGUIPopup.py")
self._create_moul_nodes(bo, tree.nodes, journalnode, age_name) self._create_moul_nodes(bo, tree.nodes, journalnode, age_name)
def _create_pots_nodes(self, clickable_object, nodes, journalnode, age_name): def _create_pots_nodes(self, clickable_object, nodes, journalnode, age_name):
@ -324,43 +297,6 @@ class PlasmaJournalBookModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz
return self.journal_translations return self.journal_translations
linking_pfms = {
pvPots : {
# Supplied by the OfflineKI script:
# https://gitlab.com/diafero/offline-ki/blob/master/offlineki/xSimpleLinkingBook.py
"filename": "xSimpleLinkingBook.py",
"attribs": (
{ 'id': 1, 'type': "ptAttribActivator", "name": "bookClickable" },
{ 'id': 2, 'type': "ptAttribString", "name": "destinationAge" },
{ 'id': 3, 'type': "ptAttribString", "name": "spawnPoint" },
{ 'id': 4, 'type': "ptAttribString", "name": "linkPanel" },
{ 'id': 5, 'type': "ptAttribString", "name": "bookCover" },
{ 'id': 6, 'type': "ptAttribString", "name": "stampTexture" },
{ 'id': 7, 'type': "ptAttribFloat", "name": "stampX" },
{ 'id': 8, 'type': "ptAttribFloat", "name": "stampY" },
{ 'id': 9, 'type': "ptAttribFloat", "name": "bookWidth" },
{ 'id': 10, 'type': "ptAttribFloat", "name": "BookHeight" },
{ 'id': 11, 'type': "ptAttribBehavior", "name": "msbSeekBeforeUI" },
{ 'id': 12, 'type': "ptAttribResponder", "name": "respOneShot" },
)
},
pvMoul : {
"filename": "xLinkingBookGUIPopup.py",
"attribs": (
{ 'id': 1, 'type': "ptAttribActivator", 'name': "actClickableBook" },
{ 'id': 2, 'type': "ptAttribBehavior", 'name': "SeekBehavior" },
{ 'id': 3, 'type': "ptAttribResponder", 'name': "respLinkResponder" },
{ 'id': 4, 'type': "ptAttribString", 'name': "TargetAge" },
{ 'id': 5, 'type': "ptAttribActivator", 'name': "actBookshelf" },
{ 'id': 6, 'type': "ptAttribActivator", 'name': "shareRegion" },
{ 'id': 7, 'type': "ptAttribBehavior", 'name': "shareBookSeek" },
{ 'id': 10, 'type': "ptAttribBoolean", 'name': "IsDRCStamped" },
{ 'id': 11, 'type': "ptAttribBoolean", 'name': "ForceThirdPerson" },
)
},
}
class PlasmaLinkingBookModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz): class PlasmaLinkingBookModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz):
pl_id = "linkingbookmod" pl_id = "linkingbookmod"
@ -495,12 +431,11 @@ class PlasmaLinkingBookModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz
yield self.seek_point.name yield self.seek_point.name
def logicwiz(self, bo, tree, age_name, version): def logicwiz(self, bo, tree, age_name, version):
# Assign linking book script based on target version
linking_pfm = linking_pfms[version]
linkingnode = self._create_python_file_node(tree, linking_pfm["filename"], linking_pfm["attribs"])
if version <= pvPots: if version <= pvPots:
linkingnode = self._create_standard_python_file_node(tree, "xSimpleLinkingBook.py")
self._create_pots_nodes(bo, tree.nodes, linkingnode, age_name) self._create_pots_nodes(bo, tree.nodes, linkingnode, age_name)
else: else:
linkingnode = self._create_standard_python_file_node(tree, "xLinkingBookGUIPopup.py")
self._create_moul_nodes(bo, tree.nodes, linkingnode, age_name) self._create_moul_nodes(bo, tree.nodes, linkingnode, age_name)
def _create_pots_nodes(self, clickable_object, nodes, linkingnode, age_name): def _create_pots_nodes(self, clickable_object, nodes, linkingnode, age_name):
@ -667,14 +602,6 @@ class PlasmaLinkingBookModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz
raise ExportError("{}: Linking Book modifier requires a seek point!", self.id_data.name) raise ExportError("{}: Linking Book modifier requires a seek point!", self.id_data.name)
dialog_toggle = {
"filename": "xDialogToggle.py",
"attribs": (
{ 'id': 1, 'type': "ptAttribActivator", 'name': "Activate" },
{ 'id': 4, 'type': "ptAttribString", 'name': "Vignette" },
)
}
class PlasmaNotePopupModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz): class PlasmaNotePopupModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz):
pl_id = "note_popup" pl_id = "note_popup"
@ -755,11 +682,7 @@ class PlasmaNotePopupModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz):
nodes = tree.nodes nodes = tree.nodes
# xDialogToggle.py PythonFile Node # xDialogToggle.py PythonFile Node
dialog_node = self._create_python_file_node( dialog_node = self._create_standard_python_file_node(tree, "xDialogToggle.py")
tree,
dialog_toggle["filename"],
dialog_toggle["attribs"]
)
self._create_python_attribute(dialog_node, "Vignette", value=self.gui_page) self._create_python_attribute(dialog_node, "Vignette", value=self.gui_page)
# Clickable # Clickable

28
korman/properties/modifiers/logic.py

@ -34,15 +34,6 @@ from ...exporter import ExportError, utils
from ... import idprops from ... import idprops
from .physics import bounds_type_index, bounds_type_str, bounds_types from .physics import bounds_type_index, bounds_type_str, bounds_types
entry_cam_pfm = {
"filename": "xEntryCam.py",
"attribs": (
{ 'id': 1, 'type': "ptAttribActivator", 'name': "actRegionSensor" },
{ 'id': 2, 'type': "ptAttribSceneobject", 'name': "camera" },
{ 'id': 3, 'type': "ptAttribBoolean", 'name': "undoFirstPerson" },
)
}
class PlasmaVersionedNodeTree(idprops.IDPropMixin, bpy.types.PropertyGroup): class PlasmaVersionedNodeTree(idprops.IDPropMixin, bpy.types.PropertyGroup):
version = EnumProperty(name="Version", version = EnumProperty(name="Version",
description="Plasma versions this node tree exports under", description="Plasma versions this node tree exports under",
@ -148,11 +139,7 @@ class PlasmaSpawnPoint(PlasmaModifierProperties, PlasmaModifierLogicWiz):
yield self.convert_logic(bo) yield self.convert_logic(bo)
def logicwiz(self, bo, tree): def logicwiz(self, bo, tree):
pfm_node = self._create_python_file_node( pfm_node = self._create_standard_python_file_node(tree, "xEntryCam.py")
tree,
entry_cam_pfm["filename"],
entry_cam_pfm["attribs"]
)
volume_sensor: PlasmaVolumeSensorNode = tree.nodes.new("PlasmaVolumeSensorNode") volume_sensor: PlasmaVolumeSensorNode = tree.nodes.new("PlasmaVolumeSensorNode")
volume_sensor.find_input_socket("enter").allow = True volume_sensor.find_input_socket("enter").allow = True
@ -204,17 +191,6 @@ class PlasmaMaintainersMarker(PlasmaModifierProperties):
return True return True
telescope_pfm = {
"filename": "xTelescope.py",
"attribs": (
{ 'id': 1, 'type': "ptAttribActivator", 'name': "Activate" },
{ 'id': 2, 'type': "ptAttribSceneobject", 'name': "Camera" },
{ 'id': 3, 'type': "ptAttribBehavior", 'name': "Behavior" },
{ 'id': 4, 'type': "ptAttribString", 'name': "Vignette" },
)
}
class PlasmaTelescope(PlasmaModifierProperties, PlasmaModifierLogicWiz): class PlasmaTelescope(PlasmaModifierProperties, PlasmaModifierLogicWiz):
pl_id="telescope" pl_id="telescope"
@ -255,7 +231,7 @@ class PlasmaTelescope(PlasmaModifierProperties, PlasmaModifierLogicWiz):
nodes = tree.nodes nodes = tree.nodes
# Create Python Node # Create Python Node
telescopepynode = self._create_python_file_node(tree, telescope_pfm["filename"], telescope_pfm["attribs"]) telescopepynode = self._create_standard_python_file_node(tree, "xTelescope.py")
# Clickable # Clickable
telescopeclick = nodes.new("PlasmaClickableNode") telescopeclick = nodes.new("PlasmaClickableNode")

26
korman/properties/modifiers/render.py

@ -624,30 +624,6 @@ class PlasmaLightingMod(PlasmaModifierProperties):
return False return False
_LOCALIZED_TEXT_PFM = (
{ 'id': 1, 'type': "ptAttribDynamicMap", 'name': "dynTextMap", },
{ 'id': 2, 'type': "ptAttribString", 'name': "locPath" },
{ 'id': 3, 'type': "ptAttribString", 'name': "fontFace" },
{ 'id': 4, 'type': "ptAttribInt", 'name': "fontSize" },
{ 'id': 5, 'type': "ptAttribFloat", 'name': "fontColorR" },
{ 'id': 6, 'type': "ptAttribFloat", 'name': "fontColorG" },
{ 'id': 7, 'type': "ptAttribFloat", 'name': "fontColorB" },
{ 'id': 8, 'type': "ptAttribFloat", 'name': "fontColorA" },
{ 'id': 9, 'type': "ptAttribInt", 'name': "marginTop" },
{ 'id': 10, 'type': "ptAttribInt", 'name': "marginLeft" },
{ 'id': 11, 'type': "ptAttribInt", 'name': "marginBottom" },
{ 'id': 12, 'type': "ptAttribInt", 'name': "marginRight" },
{ 'id': 13, 'type': "ptAttribInt", 'name': "lineSpacing" },
# Yes, it's really a ptAttribDropDownList, but those are only for use in
# artist generated node trees.
{ 'id': 14, 'type': "ptAttribString", 'name': "justify" },
{ 'id': 15, 'type': "ptAttribFloat", 'name': "clearColorR" },
{ 'id': 16, 'type': "ptAttribFloat", 'name': "clearColorG" },
{ 'id': 17, 'type': "ptAttribFloat", 'name': "clearColorB" },
{ 'id': 18, 'type': "ptAttribFloat", 'name': "clearColorA" },
{ 'id': 19, 'type': "ptAttribBoolean", 'name': "blockRGB" },
)
class PlasmaLocalizedTextModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz, TranslationMixin): class PlasmaLocalizedTextModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz, TranslationMixin):
pl_id = "dynatext" pl_id = "dynatext"
pl_page_types = {"gui", "room"} pl_page_types = {"gui", "room"}
@ -738,7 +714,7 @@ class PlasmaLocalizedTextModifier(PlasmaModifierProperties, PlasmaModifierLogicW
self._create_nodes(bo, tree, age_name=age_name, version=version) self._create_nodes(bo, tree, age_name=age_name, version=version)
def _create_nodes(self, bo, tree, *, age_name, version, material=None, clear_color=None): def _create_nodes(self, bo, tree, *, age_name, version, material=None, clear_color=None):
pfm_node = self._create_python_file_node(tree, "xDynTextLoc.py", _LOCALIZED_TEXT_PFM) pfm_node = self._create_standard_python_file_node(tree, "xDynTextLoc.py")
loc_path = self.key_name if version <= pvPots else "{}.{}.{}".format(age_name, self.localization_set, self.key_name) loc_path = self.key_name if version <= pvPots else "{}.{}.{}".format(age_name, self.localization_set, self.key_name)
self._create_python_attribute(pfm_node, "dynTextMap", "ptAttribDynamicMap", self._create_python_attribute(pfm_node, "dynTextMap", "ptAttribDynamicMap",

Loading…
Cancel
Save