Blend Shape Anim (python)
from functools import partial import Atoms import AtomsCore import os from Atoms.ui.utils.qthandler import QtWidgets, QtCore, QtGui from Atoms.images import get_image_path from Atoms.ui.utils.qt import build_layout, ORIENTATION from Atoms.ui.components.collapsiblebox import CollapsibleBox from Atoms.ui.components.modules import BaseModuleWidget from Atoms.ui.factory import MODULE_WIDGETS_FACTORY ADD_ICON = QtGui.QIcon(get_image_path('add.png')) CLEAR_ICON = QtGui.QIcon(get_image_path('clear.png')) REMOVE_ICON = QtGui.QIcon(get_image_path('remove.png')) class BlendShapesAnimBoxWidget(CollapsibleBox): def __init__(self, host_bridge, module_name, parent): super(BlendShapesAnimBoxWidget, self, ).__init__(parent) self._module_name = module_name self._host_bridge = host_bridge self._layout = build_layout(ORIENTATION.VERTICAL, margins=4, alignment=QtCore.Qt.AlignTop) self.set_content_layout(self._layout) self.set_collapsed(True) self._build_content_widgets() self._build_content_signals() self._update_tree() def _build_content_widgets(self): h_layout = build_layout(ORIENTATION.HORIZONTAL, margins=0) self._add_button = QtWidgets.QPushButton(ADD_ICON, "Add", self) self._clear_button = QtWidgets.QPushButton(CLEAR_ICON, "Clear", self) self._clear_button.setFixedWidth(65) h_layout.addWidget(self._add_button) h_layout.addWidget(self._clear_button) self._layout.addLayout(h_layout) self._tree = QtWidgets.QTreeWidget() style = """QTreeView::item {height:20; border: 1px solid #2B2B2B; border-right-color:transparent; border-left: 0px solid #2B2B2B;}""" self._tree.setStyleSheet(style) self._tree.setUniformRowHeights(True) self._tree.setIndentation(0) self._tree.setColumnCount(7) self._tree.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) self._tree.setFocusPolicy(QtCore.Qt.NoFocus) #self._tree.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self._tree.setHeaderLabels(["","Agent Type", "Geo", "Metadata", "Anim file", "Anim type",""]) header = self._tree.header() header.setStretchLastSection(False) header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed) header.resizeSection(0, 20) header.resizeSection(6, 20) self._layout.addWidget(self._tree) def _build_content_signals(self): self._add_button.clicked.connect(self._add_button_clicked) self._clear_button.clicked.connect(self._clear_button_clicked) def _add_button_clicked(self): count = self._tree.topLevelItemCount() index = 0 if count > 0: index = int(self._tree.topLevelItem(count - 1).text(0)) + 1 self._host_bridge.add_index_to_array_metadata(self._module_name, "animData", index, "") self._add_item(index) def _add_item(self, index, values=[]): twi = QtWidgets.QTreeWidgetItem(self._tree) twi.setText(0, str(index)) self._tree.addTopLevelItem(twi) for i in range(5): vn = QtWidgets.QLineEdit(self._tree) if i < len(values): vn.setText(values[i]) vn.editingFinished.connect(partial(self._value_changed, twi)) self._tree.setItemWidget(twi, i + 1, vn) b = QtWidgets.QPushButton(REMOVE_ICON, '', self._tree) b.clicked.connect(partial(self._remove_item, twi)) b.setFixedSize(QtCore.QSize(20, 20)) self._tree.setItemWidget(twi, 6, b) return twi def _value_changed(self, item): host_index = int(item.text(0)) value = [] for i in range(5): value.append(str(self._tree.itemWidget(item, i + 1).text())) type_str = AtomsCore.StringMetadata.staticTypeStr() value_str = ','.join(value) self._host_bridge.set_metadata_value(self._module_name, "animData", type_str, value_str, index=host_index) def _clear_button_clicked(self): self._clear_button.setFocus() self._tree.clear() indices = self._host_bridge.get_array_metadata_indices( self._module_name, "animData") for index in indices: self._host_bridge.remove_metadata_at_index(self._module_name, "animData", index) def _remove_item(self, item): mindex = self._tree.indexFromItem(item) host_index = int(item.text(0)) for i in range(1, 4): w = self._tree.itemWidget(item, i) w.setParent(None) self._tree.takeTopLevelItem(mindex.row()) self._host_bridge.remove_metadata_at_index(self._module_name, "animData", host_index) def _update_tree(self): self._tree.clear() type_str = AtomsCore.StringMetadata.staticTypeStr() type_double = AtomsCore.DoubleMetadata.staticTypeStr() indices = self._host_bridge.get_array_metadata_indices( self._module_name, "animData") for index in indices: ln = self._host_bridge.get_metadata_value(self._module_name, "animData", type_str, index=index) self._add_item(index, values=ln.split(',')) class BlendShapesAnimModuleWidget(BaseModuleWidget): def __init__(self, host_bridge, module_name, attributes_map, parent): super(BlendShapesAnimModuleWidget, self).__init__(host_bridge, module_name, attributes_map, parent) self._anim_widgets = BlendShapesAnimBoxWidget(host_bridge, module_name, self) self.add_widget("anim data", self._anim_widgets) @staticmethod def excluded_metadatas_from_automatic_build(): return ["animData"] class BlendShapesAnimModule(Atoms.BehaviourModule): def __init__(self): self.anim_table = {} Atoms.BehaviourModule.__init__(self) self.addAttribute("enable", AtomsCore.BoolMetadata(True), True) self.addAttribute("animData", AtomsCore.StringArrayMetadata()) def initSimulation(self, agentGroup): #Called at the first frame of the simulation animData = self.attributes()["animData"].value() self.anim_table.clear() for clip in animData: data = clip.replace(" ","").split(',') if len(data) < 4: continue agent_type = data[0] mesh_name = data[1] metadata_trigger = data[2] anim_file = data[3] property = "loop" if len(data) > 4: property = data[4] #load the anim clip if not os.path.exists(anim_file): continue ark = AtomsCore.Archive() if not ark.readFromFile(anim_file): continue if not agent_type in self.anim_table: self.anim_table[agent_type] = {} if not mesh_name in self.anim_table[agent_type]: self.anim_table[agent_type][mesh_name] = {} curves_data = AtomsCore.MapMetadata() if not curves_data.deserialise(ark): continue weights = [] for i, target in enumerate(curves_data["targets"].value()): weights.append(curves_data["weights"][target].value()) data_dict = {"property": property, "metadata": metadata_trigger, "range": (curves_data["startFrame"].value(), curves_data["endFrame"].value()), "weights": weights} self.anim_table[agent_type][mesh_name] = data_dict def initFrame(self, agents, agentGroup): #Called at the begin of each frame after agentsCreated is_enabled = self.attributes()["enable"].value() enable_override = self.attributes()["enable_override"] for agent in agents: enabled = is_enabled agent_id = agent.metadata()["groupId"].value() a_id_str = str(agent_id) if a_id_str in enable_override: enabled = self.attributes()["enable_override"][a_id_str].value() if not enabled: continue agent_type_name = agent.agentType().name() if not agent_type_name in self.anim_table: continue for mesh in self.anim_table[agent_type_name]: mesh_table = self.anim_table[agent_type_name][mesh] trigger_meta_name = mesh_table["metadata"] if not trigger_meta_name in agent.metadata(): continue trigger_value = agent.metadata()[trigger_meta_name].value() mesh_anim = mesh + "_anim" if not mesh_anim in agent.metadata(): agent.metadata()[mesh_anim] = AtomsCore.IntMetadata(-1) anim_frame = agent.metadata()[mesh_anim].value() if trigger_value < 0.00001: if anim_frame == -1: # the animation is not started and the trigger is at 0 # so we can skip continue else: # we need to reset the animation agent.metadata()[mesh_anim].set(-1) for i in range(len(mesh_table["weights"])): weight_meta_name = "%s_%s_%d" % (agent_type_name, mesh, i) if weight_meta_name in agent.metadata(): agent.metadata()[weight_meta_name].set(0.0) else: anim_frame += 1 ws = mesh_table["weights"] for i in range(len(ws)): weight_meta_name = "%s_%s_%d" % (agent_type_name, mesh, i) if not weight_meta_name in agent.metadata(): dm = AtomsCore.DoubleMetadata(0.0) agent.metadata()[weight_meta_name] = dm start = mesh_table["range"][0] end = mesh_table["range"][1] if mesh_table["property"] == "loop": anim_frame = anim_frame % (end - start) else: if anim_frame > (end - start): anim_frame = end - start agent.metadata()[weight_meta_name].set(trigger_value * ws[i][anim_frame]) agent.metadata()[mesh_anim].set(anim_frame) def register(): Atoms.BehaviourModules.instance().registerBehaviourModule( "PyBlendShapesAnim", BlendShapesAnimModule, True) MODULE_WIDGETS_FACTORY.register('PyBlendShapesAnim', BlendShapesAnimModuleWidget)
Copyright © 2017, Toolchefs LTD.