diff --git a/Common/DzRuntimePluginAction.cpp b/Common/DzRuntimePluginAction.cpp index e865195..e0479e5 100644 --- a/Common/DzRuntimePluginAction.cpp +++ b/Common/DzRuntimePluginAction.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +88,9 @@ void DzRuntimePluginAction::Export() } } + //Unlock the transform controls so the node can be moved to root + UnlockTranform(Node); + // Disconnect the asset being sent from everything else QList AttachmentList; DisconnectNode(Node, AttachmentList); @@ -297,7 +301,11 @@ void DzRuntimePluginAction::ExportNode(DzNode* Node) } ExportOptions.setBoolValue("doLights", false); ExportOptions.setBoolValue("doCameras", false); - ExportOptions.setBoolValue("doAnims", true); + ExportOptions.setBoolValue("doAnims", false); + if (AssetType == "Animation") + { + ExportOptions.setBoolValue("doAnims", true); + } if ((AssetType == "Animation" || AssetType == "SkeletalMesh") && ExportMorphs && MorphString != "") { ExportOptions.setBoolValue("doMorphs", true); @@ -341,9 +349,29 @@ void DzRuntimePluginAction::ExportNode(DzNode* Node) dir.mkpath(CharacterFolder); SetExportOptions(ExportOptions); - Exporter->writeFile(CharacterFBX, &ExportOptions); - WriteConfiguration(); + if (ExportSubdivisions) + { + if (ExportBaseMesh) + { + QString CharacterBaseFBX = CharacterFBX; + CharacterBaseFBX.replace(".fbx", "_base.fbx"); + Exporter->writeFile(CharacterBaseFBX, &ExportOptions); + } + else + { + QString CharacterHDFBX = CharacterFBX; + CharacterHDFBX.replace(".fbx", "_HD.fbx"); + Exporter->writeFile(CharacterHDFBX, &ExportOptions); + + WriteConfiguration(); + } + } + else + { + Exporter->writeFile(CharacterFBX, &ExportOptions); + WriteConfiguration(); + } // Change back material names UndoRenameDuplicateMaterials(Parent, MaterialNames, OriginalMaterialNames); @@ -406,7 +434,8 @@ void DzRuntimePluginAction::GetScenePropList(DzNode* Node, QMapgetAssetUri().getId(); + QString Name = AssetID.remove(QRegExp("[^A-Za-z0-9_]")); if (Figure) { @@ -592,4 +621,29 @@ bool DzRuntimePluginAction::CheckIfPoseExportIsDestructive() return false; } +void DzRuntimePluginAction::UnlockTranform(DzNode* NodeToUnlock) +{ + DzFloatProperty* Property = nullptr; + Property = NodeToUnlock->getXPosControl(); + Property->lock(false); + Property = NodeToUnlock->getYPosControl(); + Property->lock(false); + Property = NodeToUnlock->getZPosControl(); + Property->lock(false); + + Property = NodeToUnlock->getXRotControl(); + Property->lock(false); + Property = NodeToUnlock->getYRotControl(); + Property->lock(false); + Property = NodeToUnlock->getZRotControl(); + Property->lock(false); + + Property = NodeToUnlock->getXScaleControl(); + Property->lock(false); + Property = NodeToUnlock->getYScaleControl(); + Property->lock(false); + Property = NodeToUnlock->getZScaleControl(); + Property->lock(false); +} + #include "moc_DzRuntimePluginAction.cpp" diff --git a/Common/DzRuntimePluginAction.h b/Common/DzRuntimePluginAction.h index 8245808..6109f8b 100644 --- a/Common/DzRuntimePluginAction.h +++ b/Common/DzRuntimePluginAction.h @@ -25,6 +25,8 @@ class DzRuntimePluginAction : public DzAction { QString ImportFolder; QString CharacterFolder; QString CharacterFBX; + QString CharacterBaseFBX; + QString CharacterHDFBX; QString AssetType; QString MorphString; QString FBXVersion; @@ -33,6 +35,7 @@ class DzRuntimePluginAction : public DzAction { bool ExportMorphs; bool ExportSubdivisions; + bool ExportBaseMesh; bool ShowFbxDialog; bool ExportMaterialPropertiesCSV; DzNode* Selection; @@ -64,4 +67,7 @@ class DzRuntimePluginAction : public DzAction { // For Pose exports check if writing to the timeline will alter existing keys bool CheckIfPoseExportIsDestructive(); + + // Need to be able to move asset instances to origin during environment export + void UnlockTranform(DzNode* NodeToUnlock); }; \ No newline at end of file diff --git a/Unreal/DazStudioPlugin/DzUnrealAction.cpp b/Unreal/DazStudioPlugin/DzUnrealAction.cpp index 9abd1fb..7a7e89c 100644 --- a/Unreal/DazStudioPlugin/DzUnrealAction.cpp +++ b/Unreal/DazStudioPlugin/DzUnrealAction.cpp @@ -18,12 +18,15 @@ #include #include #include +#include +#include //#include #include "idzsceneasset.h" #include "dzuri.h" #include "DzUnrealDialog.h" #include "DzUnrealAction.h" +#include "DzUnrealMorphSelectionDialog.h" DzUnrealAction::DzUnrealAction() : DzRuntimePluginAction(tr("&Daz to Unreal"), tr("Send the selected node to Unreal.")) @@ -69,24 +72,36 @@ void DzUnrealAction::executeAction() // If the Accept button was pressed, start the export if (dlg->exec() == QDialog::Accepted) { - // Collect the values from the dialog fields - CharacterName = dlg->assetNameEdit->text(); - ImportFolder = dlg->intermediateFolderEdit->text(); - CharacterFolder = ImportFolder + "\\" + CharacterName + "\\"; - CharacterFBX = CharacterFolder + CharacterName + ".fbx"; - AssetType = dlg->assetTypeCombo->currentText().replace(" ", ""); - MorphString = dlg->GetMorphString(); - Port = dlg->portEdit->text().toInt(); - ExportMorphs = dlg->morphsEnabledCheckBox->isChecked(); - ExportSubdivisions = dlg->subdivisionEnabledCheckBox->isChecked(); - MorphMapping = dlg->GetMorphMapping(); - ShowFbxDialog = dlg->showFbxDialogCheckBox->isChecked(); - ExportMaterialPropertiesCSV = dlg->exportMaterialPropertyCSVCheckBox->isChecked(); - SubdivisionDialog = DzUnrealSubdivisionDialog::Get(dlg); - SubdivisionDialog->LockSubdivisionProperties(ExportSubdivisions); - FBXVersion = dlg->fbxVersionCombo->currentText(); - - Export(); + // Collect the values from the dialog fields + CharacterName = dlg->assetNameEdit->text(); + ImportFolder = dlg->intermediateFolderEdit->text(); + CharacterFolder = ImportFolder + "\\" + CharacterName + "\\"; + CharacterFBX = CharacterFolder + CharacterName + ".fbx"; + CharacterBaseFBX = CharacterFolder + CharacterName + "_base.fbx"; + CharacterHDFBX = CharacterFolder + CharacterName + "_HD.fbx"; + AssetType = dlg->assetTypeCombo->currentText().replace(" ", ""); + MorphString = dlg->GetMorphString(); + Port = dlg->portEdit->text().toInt(); + ExportMorphs = dlg->morphsEnabledCheckBox->isChecked(); + ExportSubdivisions = dlg->subdivisionEnabledCheckBox->isChecked(); + MorphMapping = dlg->GetMorphMapping(); + ShowFbxDialog = dlg->showFbxDialogCheckBox->isChecked(); + ExportMaterialPropertiesCSV = dlg->exportMaterialPropertyCSVCheckBox->isChecked(); + SubdivisionDialog = DzUnrealSubdivisionDialog::Get(dlg); + FBXVersion = dlg->fbxVersionCombo->currentText(); + + if (AssetType == "SkeletalMesh" && ExportSubdivisions) + { + // export base mesh + ExportBaseMesh = true; + SubdivisionDialog->LockSubdivisionProperties(false); + Export(); + } + + ExportBaseMesh = false; + SubdivisionDialog->LockSubdivisionProperties(ExportSubdivisions); + Export(); + } } @@ -100,6 +115,8 @@ void DzUnrealAction::WriteConfiguration() writer.addMember("Asset Name", CharacterName); writer.addMember("Asset Type", AssetType); writer.addMember("FBX File", CharacterFBX); + writer.addMember("Base FBX File", CharacterBaseFBX); + writer.addMember("HD FBX File", CharacterHDFBX); writer.addMember("Import Folder", CharacterFolder); if (AssetType != "Environment") @@ -138,10 +155,42 @@ void DzUnrealAction::WriteConfiguration() writer.finishObject(); } } - writer.finishArray(); - + if (ExportMorphs) + { + DzMainWindow* mw = dzApp->getInterface(); + DzUnrealMorphSelectionDialog* morphDialog = DzUnrealMorphSelectionDialog::Get(mw); + if (morphDialog->IsAutoJCMEnabled()) + { + writer.startMemberArray("JointLinks", true); + QList JointLinks = morphDialog->GetActiveJointControlledMorphs(Selection); + foreach(JointLinkInfo linkInfo, JointLinks) + { + writer.startObject(true); + writer.addMember("Bone", linkInfo.Bone); + writer.addMember("Axis", linkInfo.Axis); + writer.addMember("Morph", linkInfo.Morph); + writer.addMember("Scalar", linkInfo.Scalar); + writer.addMember("Alpha", linkInfo.Alpha); + if (linkInfo.Keys.count() > 0) + { + writer.startMemberArray("Keys", true); + foreach(JointLinkKey key, linkInfo.Keys) + { + writer.startObject(true); + writer.addMember("Angle", key.Angle); + writer.addMember("Value", key.Value); + writer.finishObject(); + } + writer.finishArray(); + } + writer.finishObject(); + } + writer.finishArray(); + } + } + writer.startMemberArray("Subdivisions", true); if (ExportSubdivisions) SubdivisionDialog->WriteSubdivisions(writer); @@ -178,6 +227,14 @@ void DzUnrealAction::WriteConfiguration() DTUfile.close(); + if (AssetType != "Environment" && ExportSubdivisions) + { + QString CMD = "ImportFBXScene " + DTUfilename; + QByteArray array = CMD.toLocal8Bit(); + char* cmd = array.data(); + int res = system(cmd); + } + // Send a message to Unreal telling it to start an import QUdpSocket* sendSocket = new QUdpSocket(this); QHostAddress* sendAddress = new QHostAddress("127.0.0.1"); @@ -330,6 +387,8 @@ void DzUnrealAction::WriteInstances(DzNode* Node, DzJsonWriter& Writer, QMapgetCurrentShape() : NULL; DzGeometry* Geometry = Shape ? Shape->getGeometry() : NULL; DzBone* Bone = qobject_cast(Node); + DzGroupNode* GroupNode = qobject_cast(Node); + DzInstanceNode* InstanceNode = qobject_cast(Node); if (Bone == nullptr && Geometry) { @@ -337,6 +396,16 @@ void DzUnrealAction::WriteInstances(DzNode* Node, DzJsonWriter& Writer, QMapgetNumNodeChildren(); ChildIndex++) { DzNode* ChildNode = Node->getNodeChild(ChildIndex); @@ -348,12 +417,26 @@ QUuid DzUnrealAction::WriteInstance(DzNode* Node, DzJsonWriter& Writer, QUuid Pa { QString Path = Node->getAssetFileInfo().getUri().getFilePath(); QFile File(Path); - QString FileName = File.fileName(); - QStringList Items = FileName.split("/"); - QStringList Parts = Items[Items.count() - 1].split("."); - QString Name = Parts[0].remove(QRegExp("[^A-Za-z0-9_]")); + QString AssetID = Node->getAssetUri().getId(); + QString Name = AssetID.remove(QRegExp("[^A-Za-z0-9_]")); QUuid Uid = QUuid::createUuid(); + // Group Node needs an empty InstanceAsset + DzGroupNode* GroupNode = qobject_cast(Node); + if (GroupNode) + { + Name = ""; + } + + // Group Node needs an empty InstanceAsset + DzInstanceNode* InstanceNode = qobject_cast(Node); + if (InstanceNode) + { + AssetID = InstanceNode->getTarget()->getAssetUri().getId(); + Name = AssetID.remove(QRegExp("[^A-Za-z0-9_]")); + } + + Writer.startObject(true); Writer.addMember("Version", 1); Writer.addMember("InstanceLabel", Node->getLabel()); diff --git a/Unreal/DazStudioPlugin/DzUnrealMorphSelectionDialog.cpp b/Unreal/DazStudioPlugin/DzUnrealMorphSelectionDialog.cpp index 82bd093..f41177f 100644 --- a/Unreal/DazStudioPlugin/DzUnrealMorphSelectionDialog.cpp +++ b/Unreal/DazStudioPlugin/DzUnrealMorphSelectionDialog.cpp @@ -33,6 +33,10 @@ #include "dzproperty.h" #include "dzsettings.h" #include "dzmorph.h" +#include "dzcontroller.h" +#include "dznumericnodeproperty.h" +#include "dzerclink.h" +#include "dzbone.h" #include "DzUnrealMorphSelectionDialog.h" #include "QtGui/qlayout.h" @@ -65,6 +69,8 @@ class SortingListItem : public QListWidgetItem { DzUnrealMorphSelectionDialog::DzUnrealMorphSelectionDialog(QWidget *parent) : DzBasicDialog(parent, DAZ_TO_UNREAL_PLUGIN_NAME) { + settings = new QSettings("Daz 3D", "DazToUnreal"); + morphListWidget = NULL; morphExportListWidget = NULL; morphTreeWidget = NULL; @@ -132,6 +138,7 @@ DzUnrealMorphSelectionDialog::DzUnrealMorphSelectionDialog(QWidget *parent) : QPushButton* TorsoJCMButton = new QPushButton("Torso"); QPushButton* ARKit81Button = new QPushButton("ARKit (Genesis8.1)"); QPushButton* FaceFX8Button = new QPushButton("FaceFX (Genesis8)"); + autoJCMCheckBox = new QCheckBox("Auto JCM"); ((QGridLayout*)JCMGroupBox->layout())->addWidget(ArmsJCMButton, 0, 0); ((QGridLayout*)JCMGroupBox->layout())->addWidget(LegsJCMButton, 0, 1); ((QGridLayout*)JCMGroupBox->layout())->addWidget(TorsoJCMButton, 0, 2); @@ -139,12 +146,19 @@ DzUnrealMorphSelectionDialog::DzUnrealMorphSelectionDialog(QWidget *parent) : ((QGridLayout*)FaceGroupBox->layout())->addWidget(FaceFX8Button, 0, 2); MorphGroupBox->layout()->addWidget(JCMGroupBox); MorphGroupBox->layout()->addWidget(FaceGroupBox); + MorphGroupBox->layout()->addWidget(autoJCMCheckBox); + + if (!settings->value("AutoJCMEnabled").isNull()) + { + autoJCMCheckBox->setChecked(settings->value("AutoJCMEnabled").toBool()); + } connect(ArmsJCMButton, SIGNAL(released()), this, SLOT(HandleArmJCMMorphsButton())); connect(LegsJCMButton, SIGNAL(released()), this, SLOT(HandleLegJCMMorphsButton())); connect(TorsoJCMButton, SIGNAL(released()), this, SLOT(HandleTorsoJCMMorphsButton())); connect(ARKit81Button, SIGNAL(released()), this, SLOT(HandleARKitGenesis81MorphsButton())); connect(FaceFX8Button, SIGNAL(released()), this, SLOT(HandleFaceFXGenesis8Button())); + connect(autoJCMCheckBox, SIGNAL(clicked(bool)), this, SLOT(HandleAutoJCMCheckBoxChange(bool))); treeLayout->addWidget(MorphGroupBox); morphsLayout->addLayout(treeLayout); @@ -223,6 +237,9 @@ void DzUnrealMorphSelectionDialog::PrepareDialog() DzNode* ChildNode = Selection->getNodeChild(ChildIndex); morphList.append(GetAvailableMorphs(ChildNode)); } + + //GetActiveJointControlledMorphs(Selection); + UpdateMorphsTree(); HandlePresetChanged("LastUsed.csv"); } @@ -337,6 +354,170 @@ QStringList DzUnrealMorphSelectionDialog::GetAvailableMorphs(DzNode* Node) return newMorphList; } +// Recursive function for finding all active JCM morphs for a node +QList DzUnrealMorphSelectionDialog::GetActiveJointControlledMorphs(DzNode* Node) +{ + QList returnMorphs; + if (autoJCMCheckBox->isChecked()) + { + if (Node == nullptr) + { + Node = dzScene->getPrimarySelection(); + + // For items like clothing, create the morph list from the character + DzNode* ParentFigureNode = Node; + while (ParentFigureNode->getNodeParent()) + { + ParentFigureNode = ParentFigureNode->getNodeParent(); + if (DzSkeleton* Skeleton = ParentFigureNode->getSkeleton()) + { + if (DzFigure* Figure = qobject_cast(Skeleton)) + { + Node = ParentFigureNode; + break; + } + } + } + } + + + DzObject* Object = Node->getObject(); + DzShape* Shape = Object ? Object->getCurrentShape() : NULL; + + for (int index = 0; index < Node->getNumProperties(); index++) + { + DzProperty* property = Node->getProperty(index); + returnMorphs.append(GetJointControlledMorphInfo(property)); + } + + if (Object) + { + for (int index = 0; index < Object->getNumModifiers(); index++) + { + DzModifier* modifier = Object->getModifier(index); + QString modName = modifier->getName(); + QString modLabel = modifier->getLabel(); + DzMorph* mod = qobject_cast(modifier); + if (mod) + { + for (int propindex = 0; propindex < modifier->getNumProperties(); propindex++) + { + DzProperty* property = modifier->getProperty(propindex); + returnMorphs.append(GetJointControlledMorphInfo(property)); + } + + } + + } + } + + } + + return returnMorphs; +} + +QList DzUnrealMorphSelectionDialog::GetJointControlledMorphInfo(DzProperty* property) +{ + QList returnMorphs; + + QString propName = property->getName(); + QString propLabel = property->getLabel(); + DzPresentation* presentation = property->getPresentation(); + if (presentation && presentation->getType() == "Modifier/Corrective") + { + QString linkLabel; + QString linkDescription; + QString linkBone; + QString linkAxis; + QString linkBodyType; + double bodyStrength = 0.0f; + double currentBodyScalar = 0.0f; + double linkScalar = 0.0f; + bool isJCM = false; + QList keys; + QList keysValues; + QList linkKeys; + + for (int ControllerIndex = 0; ControllerIndex < property->getNumControllers(); ControllerIndex++) + { + DzController* controller = property->getController(ControllerIndex); + + DzERCLink* link = qobject_cast(controller); + if (link) + { + double value = link->getScalar(); + QString linkProperty = link->getProperty()->getName(); + QString linkObject = link->getProperty()->getOwner()->getName(); + double currentValue = link->getProperty()->getDoubleValue(); + + DzBone* bone = qobject_cast(link->getProperty()->getOwner()); + if (bone) + { + linkLabel = propLabel; + linkDescription = controller->description(); + linkBone = linkObject; + linkAxis = linkProperty; + linkScalar = value; + isJCM = true; + + if (link->getType() == 6) + { + for (int keyIndex = 0; keyIndex < link->getNumKeyValues(); keyIndex++) + { + JointLinkKey newKey; + newKey.Angle = link->getKey(keyIndex); + newKey.Value = link->getKeyValue(keyIndex); + linkKeys.append(newKey); + keys.append(link->getKey(keyIndex)); + keysValues.append(link->getKeyValue(keyIndex)); + } + } + } + else + { + linkBodyType = linkObject; + bodyStrength = value; + currentBodyScalar = currentValue; + } + } + } + + if (isJCM && currentBodyScalar > 0.0f) + { + JointLinkInfo linkInfo; + linkInfo.Bone = linkBone; + linkInfo.Axis = linkAxis; + linkInfo.Morph = linkLabel; + linkInfo.Scalar = linkScalar; + linkInfo.Alpha = currentBodyScalar; + linkInfo.Keys = linkKeys; + qDebug() << "Label " << linkLabel << " Description " << linkDescription << " Bone " << linkBone << " Axis " << linkAxis << " Alpha " << currentBodyScalar << " Scalar " << linkScalar; + if (!keys.isEmpty()) + { + foreach(double key, keys) + { + qDebug() << key; + } + + foreach(double key, keysValues) + { + qDebug() << key; + } + + } + + if (morphs.contains(linkLabel) && !morphsToExport.contains(morphs[linkLabel])) + { + morphsToExport.append(morphs[linkLabel]); + } + + returnMorphs.append(linkInfo); + + } + } + return returnMorphs; +} + // Build out the left tree void DzUnrealMorphSelectionDialog::UpdateMorphsTree() { @@ -633,6 +814,16 @@ void DzUnrealMorphSelectionDialog::HandleARKitGenesis81MorphsButton() MorphsToAdd.append("facs_jnt_EyeBlinkRight"); MorphsToAdd.append("facs_bs_EyeSquintLeft_div2"); MorphsToAdd.append("facs_bs_EyeSquintRight_div2"); + MorphsToAdd.append("facs_ctrl_EyeLookUpRight"); + MorphsToAdd.append("facs_ctrl_EyeLookUpLeft"); + MorphsToAdd.append("facs_ctrl_EyeLookOutRight"); + MorphsToAdd.append("facs_ctrl_EyeLookOutLeft"); + MorphsToAdd.append("facs_ctrl_EyeLookInRight"); + MorphsToAdd.append("facs_ctrl_EyeLookInLeft"); + MorphsToAdd.append("facs_ctrl_EyeLookDownRight"); + MorphsToAdd.append("facs_ctrl_EyeLookDownLeft"); + MorphsToAdd.append("facs_bs_NoseSneerRight_div2"); + MorphsToAdd.append("facs_bs_NoseSneerLeft_div2"); MorphsToAdd.append("facs_jnt_JawForward"); MorphsToAdd.append("facs_jnt_JawLeft"); MorphsToAdd.append("facs_jnt_JawRight"); @@ -708,6 +899,11 @@ void DzUnrealMorphSelectionDialog::HandleFaceFXGenesis8Button() RefreshExportMorphList(); } +void DzUnrealMorphSelectionDialog::HandleAutoJCMCheckBoxChange(bool checked) +{ + settings->setValue("AutoJCMEnabled", checked); +} + // Refresh the Right export list void DzUnrealMorphSelectionDialog::RefreshExportMorphList() { @@ -770,12 +966,15 @@ void DzUnrealMorphSelectionDialog::HandlePresetChanged(const QString& presetName } RefreshExportMorphList(); + GetActiveJointControlledMorphs(); file.close(); } // Get the morph string in the format for the Daz FBX Export QString DzUnrealMorphSelectionDialog::GetMorphString() { + GetActiveJointControlledMorphs(); + if (morphsToExport.length() == 0) { return ""; diff --git a/Unreal/DazStudioPlugin/DzUnrealMorphSelectionDialog.h b/Unreal/DazStudioPlugin/DzUnrealMorphSelectionDialog.h index 11aeb1e..c851c93 100644 --- a/Unreal/DazStudioPlugin/DzUnrealMorphSelectionDialog.h +++ b/Unreal/DazStudioPlugin/DzUnrealMorphSelectionDialog.h @@ -11,6 +11,7 @@ class QTreeWidget; class QTreeWidgetItem; class QLineEdit; class QComboBox; +class QCheckBox; struct MorphInfo { QString Name; @@ -36,6 +37,22 @@ struct MorphInfo { } }; +struct JointLinkKey +{ + int Angle; + int Value; +}; + +struct JointLinkInfo +{ + QString Bone; + QString Axis; + QString Morph; + double Scalar; + double Alpha; + QList Keys; +}; + class DzUnrealMorphSelectionDialog : public DzBasicDialog { Q_OBJECT public: @@ -67,6 +84,10 @@ class DzUnrealMorphSelectionDialog : public DzBasicDialog { // Used to rename them to the friendly name in Unreal QMap GetMorphRenaming(); + bool IsAutoJCMEnabled() { return autoJCMCheckBox->isChecked(); } + + // Recursive function for finding all active JCM morphs for a node + QList GetActiveJointControlledMorphs(DzNode* Node = nullptr); public slots: void FilterChanged(const QString& filter); @@ -80,6 +101,7 @@ public slots: void HandleTorsoJCMMorphsButton(); void HandleARKitGenesis81MorphsButton(); void HandleFaceFXGenesis8Button(); + void HandleAutoJCMCheckBoxChange(bool checked); private: @@ -89,6 +111,9 @@ public slots: // Recursive function for finding all morphs for a node QStringList GetAvailableMorphs(DzNode* Node); + // + QList GetJointControlledMorphInfo(DzProperty* property); + void UpdateMorphsTree(); // Returns the tree node for the morph name (with path) @@ -141,4 +166,7 @@ public slots: QTreeWidgetItem* fullBodyMorphTreeItem; QTreeWidgetItem* charactersTreeItem; + QCheckBox* autoJCMCheckBox; + + QSettings* settings; }; diff --git a/Unreal/DazStudioPlugin/DzUnrealSubdivisionDialog.cpp b/Unreal/DazStudioPlugin/DzUnrealSubdivisionDialog.cpp index ea687f4..3cb63b1 100644 --- a/Unreal/DazStudioPlugin/DzUnrealSubdivisionDialog.cpp +++ b/Unreal/DazStudioPlugin/DzUnrealSubdivisionDialog.cpp @@ -149,6 +149,9 @@ void DzUnrealSubdivisionDialog::CreateList(DzNode* Node) subdivisionLevelCombo->setProperty("Object", QVariant(Node->getName())); subdivisionLevelCombo->addItem("0"); subdivisionLevelCombo->addItem("1"); + subdivisionLevelCombo->addItem("2"); + subdivisionLevelCombo->addItem("3"); + subdivisionLevelCombo->addItem("4"); SubdivisionCombos.append(subdivisionLevelCombo); subdivisionItemsGrid->addWidget(subdivisionLevelCombo, row, 1); if (SubdivisionLevels.contains(Node->getName())) diff --git a/Unreal/UnrealPlugin/DazToUnreal/Content/Animation/DazBlendShapes.uasset b/Unreal/UnrealPlugin/DazToUnreal/Content/Animation/DazBlendShapes.uasset new file mode 100644 index 0000000..592e31b Binary files /dev/null and b/Unreal/UnrealPlugin/DazToUnreal/Content/Animation/DazBlendShapes.uasset differ diff --git a/Unreal/UnrealPlugin/DazToUnreal/Content/Animation/Genesis81ARKitMapping.uasset b/Unreal/UnrealPlugin/DazToUnreal/Content/Animation/Genesis81ARKitMapping.uasset index e8d82a4..7fa8ea5 100644 Binary files a/Unreal/UnrealPlugin/DazToUnreal/Content/Animation/Genesis81ARKitMapping.uasset and b/Unreal/UnrealPlugin/DazToUnreal/Content/Animation/Genesis81ARKitMapping.uasset differ diff --git a/Unreal/UnrealPlugin/DazToUnreal/Content/Animation/Genesis81ArkitExample.uasset b/Unreal/UnrealPlugin/DazToUnreal/Content/Animation/Genesis81ArkitExample.uasset index d02bdf5..d4f5f4d 100644 Binary files a/Unreal/UnrealPlugin/DazToUnreal/Content/Animation/Genesis81ArkitExample.uasset and b/Unreal/UnrealPlugin/DazToUnreal/Content/Animation/Genesis81ArkitExample.uasset differ diff --git a/Unreal/UnrealPlugin/DazToUnreal/Content/Animation/Genesis8ControlRig.uasset b/Unreal/UnrealPlugin/DazToUnreal/Content/Animation/Genesis8ControlRig.uasset new file mode 100644 index 0000000..48bf381 Binary files /dev/null and b/Unreal/UnrealPlugin/DazToUnreal/Content/Animation/Genesis8ControlRig.uasset differ diff --git a/Unreal/UnrealPlugin/DazToUnreal/Content/Blueprints/Genesis8Character.uasset b/Unreal/UnrealPlugin/DazToUnreal/Content/Blueprints/Genesis8Character.uasset index 1d87b85..e29b554 100644 Binary files a/Unreal/UnrealPlugin/DazToUnreal/Content/Blueprints/Genesis8Character.uasset and b/Unreal/UnrealPlugin/DazToUnreal/Content/Blueprints/Genesis8Character.uasset differ diff --git a/Unreal/UnrealPlugin/DazToUnreal/Content/Retarget/CR_G8_BasicFootIK.uasset b/Unreal/UnrealPlugin/DazToUnreal/Content/Retarget/CR_G8_BasicFootIK.uasset new file mode 100644 index 0000000..f7093c3 Binary files /dev/null and b/Unreal/UnrealPlugin/DazToUnreal/Content/Retarget/CR_G8_BasicFootIK.uasset differ diff --git a/Unreal/UnrealPlugin/DazToUnreal/Content/Retarget/G81FixRotZeroRootIKRetargeter.uasset b/Unreal/UnrealPlugin/DazToUnreal/Content/Retarget/G81FixRotZeroRootIKRetargeter.uasset new file mode 100644 index 0000000..bc4a5ae Binary files /dev/null and b/Unreal/UnrealPlugin/DazToUnreal/Content/Retarget/G81FixRotZeroRootIKRetargeter.uasset differ diff --git a/Unreal/UnrealPlugin/DazToUnreal/Content/Retarget/G8IKRig.uasset b/Unreal/UnrealPlugin/DazToUnreal/Content/Retarget/G8IKRig.uasset new file mode 100644 index 0000000..f508301 Binary files /dev/null and b/Unreal/UnrealPlugin/DazToUnreal/Content/Retarget/G8IKRig.uasset differ diff --git a/Unreal/UnrealPlugin/DazToUnreal/DazToUnreal.uplugin b/Unreal/UnrealPlugin/DazToUnreal/DazToUnreal.uplugin index fcc4d6d..a63834f 100644 --- a/Unreal/UnrealPlugin/DazToUnreal/DazToUnreal.uplugin +++ b/Unreal/UnrealPlugin/DazToUnreal/DazToUnreal.uplugin @@ -1,18 +1,18 @@ { "FileVersion": 3, - "Version": 4, - "VersionName": "4.2.0", + "Version": 5, + "VersionName": "5.0.0", "FriendlyName": "DazToUnreal", "Description": "", - "Category": "Importers", - "CreatedBy": "Daz 3D", - "CreatedByURL": "http://www.daz3d.com/daz-to-unreal-bridge", - "DocsURL": "http://docs.daz3d.com/doku.php/public/read_me/index/72003/start", - "MarketplaceURL": "", - "SupportURL": "https://www.daz3d.com/help/", + "Category": "Other", + "CreatedBy": "David Vodhanel", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/070a312bc04c48bcb1a4d193aaddf73e", + "SupportURL": "", + "EnabledByDefault": true, "CanContainContent": true, "IsBetaVersion": false, - "IsExperimentalVersion": false, "Installed": true, "Modules": [ { diff --git a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/DazToUnreal.Build.cs b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/DazToUnreal.Build.cs index c15fbc7..cf41b1c 100644 --- a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/DazToUnreal.Build.cs +++ b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/DazToUnreal.Build.cs @@ -24,6 +24,8 @@ public DazToUnreal(ReadOnlyTargetRules Target) : base(Target) PrivateDependencyModuleNames.AddRange( new string[] { + "AnimGraph", + "BlueprintGraph", "Projects", "InputCore", "UnrealEd", @@ -33,6 +35,8 @@ public DazToUnreal(ReadOnlyTargetRules Target) : base(Target) "Slate", "SlateCore", "EditorScriptingUtilities", + "SkeletalMeshUtilitiesCommon", + "DazToUnrealRuntime", // ... add private dependencies that you statically link with here ... } ); @@ -48,8 +52,7 @@ public DazToUnreal(ReadOnlyTargetRules Target) : base(Target) } ); - AddEngineThirdPartyPrivateStaticDependencies(Target, - "FBX" - ); - } + AddEngineThirdPartyPrivateStaticDependencies(Target, "FBX"); + AddEngineThirdPartyPrivateStaticDependencies(Target, new string[] { "MikkTSpace", "OpenSubdiv" }); + } } diff --git a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnreal.cpp b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnreal.cpp index ab88afc..750b01b 100644 --- a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnreal.cpp +++ b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnreal.cpp @@ -7,6 +7,9 @@ #include "DazToUnrealFbx.h" #include "DazToUnrealEnvironment.h" #include "DazToUnrealPoses.h" +#include "DazToUnrealSubdivision.h" +#include "DazToUnrealMorphs.h" +#include "DazJointControlledMorphAnimInstance.h" #include "LevelEditor.h" #include "Widgets/Docking/SDockTab.h" @@ -252,7 +255,11 @@ void FDazToUnrealModule::StartupUDPListener() .BoundToEndpoint(Endpoint); TickDelegate = FTickerDelegate::CreateRaw(this, &FDazToUnrealModule::Tick); +#if ENGINE_MAJOR_VERSION > 4 + TickDelegateHandle = FTSTicker::GetCoreTicker().AddTicker(TickDelegate, 1.0f); +#else TickDelegateHandle = FTicker::GetCoreTicker().AddTicker(TickDelegate, 1.0f); +#endif } void FDazToUnrealModule::ShutdownUDPListener() { @@ -317,6 +324,8 @@ UObject* FDazToUnrealModule::ImportFromDaz(TSharedPtr JsonObject) TMap> MaterialProperties; FString FBXPath = JsonObject->GetStringField(TEXT("FBX File")); + FString BaseFBXPath = JsonObject->GetStringField(TEXT("Base FBX File")); + FString HDFBXPath = JsonObject->GetStringField(TEXT("HD FBX File")); FString AssetName = FDazToUnrealUtils::SanitizeName(JsonObject->GetStringField(TEXT("Asset Name"))); FString ImportFolder = JsonObject->GetStringField(TEXT("Import Folder")); DazAssetType AssetType = DazAssetType::StaticMesh; @@ -340,6 +349,8 @@ UObject* FDazToUnrealModule::ImportFromDaz(TSharedPtr JsonObject) FString ImportCharacterTexturesFolder = FPaths::GetPath(FBXPath) / TEXT("Textures"); FString ImportCharacterMaterialFolder = FPaths::GetPath(FBXPath) / TEXT("Materials"); FString FBXFile = FBXPath; + FString BaseFBXFile = BaseFBXPath; + FString HDFBXFile = HDFBXPath; const UDazToUnrealSettings* CachedSettings = GetDefault(); @@ -363,11 +374,11 @@ UObject* FDazToUnrealModule::ImportFromDaz(TSharedPtr JsonObject) if (!FDazToUnrealUtils::MakeDirectoryAndCheck(ImportDirectory)) return false; if (!FDazToUnrealUtils::MakeDirectoryAndCheck(ImportCharacterFolder)) return false; if (!FDazToUnrealUtils::MakeDirectoryAndCheck(ImportCharacterTexturesFolder)) return false; - if (!FDazToUnrealUtils::MakeDirectoryAndCheck(LocalDAZImportFolder)) return false; - if (!FDazToUnrealUtils::MakeDirectoryAndCheck(LocalDAZAnimationImportFolder)) return false; - if (!FDazToUnrealUtils::MakeDirectoryAndCheck(LocalCharacterFolder)) return false; - if (!FDazToUnrealUtils::MakeDirectoryAndCheck(LocalCharacterTexturesFolder)) return false; - if (!FDazToUnrealUtils::MakeDirectoryAndCheck(LocalCharacterMaterialFolder)) return false; + if (!FDazToUnrealUtils::MakeDirectoryAndCheck(DAZImportFolder)) return false; + if (!FDazToUnrealUtils::MakeDirectoryAndCheck(DAZAnimationImportFolder)) return false; + if (!FDazToUnrealUtils::MakeDirectoryAndCheck(CharacterFolder)) return false; + if (!FDazToUnrealUtils::MakeDirectoryAndCheck(CharacterTexturesFolder)) return false; + if (!FDazToUnrealUtils::MakeDirectoryAndCheck(CharacterMaterialFolder)) return false; if (AssetType == DazAssetType::Environment) { @@ -375,6 +386,12 @@ UObject* FDazToUnrealModule::ImportFromDaz(TSharedPtr JsonObject) return nullptr; } + // If there's an HD FBX File, that's the source + if (FPaths::FileExists(HDFBXFile)) + { + FBXFile = HDFBXFile; + } + // If there isn't an FBX file, stop if (!FPaths::FileExists(FBXFile)) { @@ -579,6 +596,7 @@ UObject* FDazToUnrealModule::ImportFromDaz(TSharedPtr JsonObject) // Daz Studio puts the base bone rotations in a different place than Unreal expects them. if (CachedSettings->FixBoneRotationsOnImport && AssetType == DazAssetType::SkeletalMesh && RootBone) { + FDazToUnrealFbx::RemoveBindPoses(Scene); FDazToUnrealFbx::FixClusterTranformLinks(Scene, RootBone); } @@ -614,104 +632,21 @@ UObject* FDazToUnrealModule::ImportFromDaz(TSharedPtr JsonObject) FDazToUnrealFbx::RenameDuplicateBones(RootBone); - struct local + // If there are any subdivisions, load the base FBX + FbxScene* BaseScene = nullptr; + for (auto SubdivisionInfo : SubdivisionLevels) { - static void GetWeights(FbxNode* SceneNode, TMap>& VertexPolygons, TMap& ClusterWeights, FbxMatrix TargetMatrix, int SearchFromVertex, TArray& TouchedPolygons, TArray& TouchedVertices, double& WeightsOut, double& DistancesOut, int32 Depth) - { - FbxVector4 TargetPosition; - FbxQuaternion TargetNormal; - FbxVector4 TargetShearing; - FbxVector4 TargetScale; - double Sign; - TargetMatrix.GetElements(TargetPosition, TargetNormal, TargetShearing, TargetScale, Sign); - - FbxVector4* VertexLocations = SceneNode->GetMesh()->GetControlPoints(); - for (int PolygonIndex : VertexPolygons[SearchFromVertex]) - { - if (TouchedPolygons.Contains(PolygonIndex)) continue; - TouchedPolygons.Add(PolygonIndex); - FbxVector4 NeedWeightVertexNormal; - /*for (int VertexIndex = 0; VertexIndex < SceneNode->GetMesh()->GetPolygonSize(PolygonIndex); ++VertexIndex) - { - int Vertex = SceneNode->GetMesh()->GetPolygonVertex(PolygonIndex, VertexIndex); - if (Vertex == SearchFromVertex) - { - SceneNode->GetMesh()->GetPolygonVertexNormal(PolygonIndex, VertexIndex, NeedWeightVertexNormal); - } - }*/ - // Set the vertices with no weight, to be the average of the ones with weight. - for (int VertexIndex = 0; VertexIndex < SceneNode->GetMesh()->GetPolygonSize(PolygonIndex); ++VertexIndex) - { - int Vertex = SceneNode->GetMesh()->GetPolygonVertex(PolygonIndex, VertexIndex); - if (TouchedVertices.Contains(Vertex)) continue; - TouchedVertices.Add(Vertex); - if (ClusterWeights.Contains(Vertex)) - { - FbxVector4 CompareVertexNormal; - SceneNode->GetMesh()->GetPolygonVertexNormal(PolygonIndex, VertexIndex, CompareVertexNormal); - //double DotProduct = CompareVertexNormal.DotProduct(NeedWeightVertexNormal); - FbxVector4 CompareLocation = VertexLocations[Vertex]; - FbxVector4 CompareScale = FbxVector4(1.0f, 1.0f, 1.0f); - FbxMatrix CompareMatrix = FbxMatrix(CompareLocation, CompareVertexNormal, CompareScale); - //CompareLocation. - FbxMatrix AdjustedMatrix = CompareMatrix * TargetMatrix.Inverse(); - - FbxVector4 AdjustedComparePosition; - FbxQuaternion AdjustedCompareNormal; - FbxVector4 AdjustedCompareShearing; - FbxVector4 AdjustedCompareScale; - double AdjustedCompareSign; - AdjustedMatrix.GetElements(AdjustedComparePosition, AdjustedCompareNormal, AdjustedCompareShearing, AdjustedCompareScale, AdjustedCompareSign); - - double InverseDotProduct = 1 - AdjustedCompareNormal.DotProduct(TargetNormal); - double AngleAdjustment = InverseDotProduct * 3.14; - //AdjustedComparePosition[2] = 0.0; - //FbxVector4 Test = TargetPosition.; - //double VertexDistance = AdjustedComparePosition.Length();//AdjustedComparePosition.Distance(TargetPosition); - double VertexDistance = CompareLocation.Distance(TargetPosition) * AngleAdjustment; - //VertexDistance = VertexDistance * VertexDistance* VertexDistance* VertexDistance* VertexDistance* VertexDistance* VertexDistance* VertexDistance; - /*if (DotProduct > 0.0) - { - VertexDistance = VertexDistance * DotProduct; - }*/ - - //double AdditionalWeightCount = FurthestDistance / VertexDistance; - //double AdditionalWeight = AdditionalWeightCount * ClusterWeights[Vertex]; - double AdditionalWeightCount = 1 / VertexDistance; - double AdditionalWeight = ClusterWeights[Vertex] / VertexDistance; - - WeightsOut += AdditionalWeight; - DistancesOut += AdditionalWeightCount; - } - - if (Depth > 1) - { - GetWeights(SceneNode, VertexPolygons, ClusterWeights, TargetMatrix, Vertex, TouchedPolygons, TouchedVertices, WeightsOut, DistancesOut, Depth - 1); - } - } - } - } + if (SubdivisionInfo.Value > 0) + { + FbxImporter* BaseImporter = FbxImporter::Create(SdkManager, ""); + const bool bBaseImportStatus = BaseImporter->Initialize(TCHAR_TO_UTF8(*BaseFBXFile)); + BaseScene = FbxScene::Create(SdkManager, ""); + BaseImporter->Import(BaseScene); + break; + } + } - static void FindVertexNeedingWeight(int SearchFromVertex, FbxNode* SceneNode, TArray& NeedWeights, TMap>& VertexPolygons, TArray& NoWeights, int32 Depth) - { - for (int PolygonIndex : VertexPolygons[SearchFromVertex]) - { - for (int VertexIndex = 0; VertexIndex < SceneNode->GetMesh()->GetPolygonSize(PolygonIndex); ++VertexIndex) - { - int Vertex = SceneNode->GetMesh()->GetPolygonVertex(PolygonIndex, VertexIndex); - if (NoWeights.Contains(Vertex)) - { - NeedWeights.AddUnique(Vertex); - } - if (Depth > 1) - { - FindVertexNeedingWeight(Vertex, SceneNode, NeedWeights, VertexPolygons, NoWeights, Depth - 1); - } - } - } - } - }; // Detach geometry from the skeleton for (int NodeIndex = 0; NodeIndex < Scene->GetNodeCount(); ++NodeIndex) @@ -732,182 +667,24 @@ UObject* FDazToUnrealModule::ImportFromDaz(TSharedPtr JsonObject) RootNode->AddChild(SceneNode); } } -#if 1 - if (SubdivisionLevels.Num() > 0) - { - // Create missing weights - if (NodeGeometry) - { - FString GeometryName = UTF8_TO_TCHAR(SceneNode->GetName()); - if (!SubdivisionLevels.Contains(GeometryName)) continue; - if (SubdivisionLevels[GeometryName] == 0) continue; - for (int DeformerIndex = 0; DeformerIndex < NodeGeometry->GetDeformerCount(); ++DeformerIndex) - { - FbxSkin* Skin = static_cast(NodeGeometry->GetDeformer(DeformerIndex)); - if (Skin) - { - - FbxVector4* VertexLocations = SceneNode->GetMesh()->GetControlPoints(); - TArray NoWeights; - TArray HasWeights; - TMap> VertexPolygons; - TMap VertexNormals; - // iterate the polygons - for (int PolygonIndex = 0; PolygonIndex < SceneNode->GetMesh()->GetPolygonCount(); ++PolygonIndex) - { - - for (int VertexIndex = 0; VertexIndex < SceneNode->GetMesh()->GetPolygonSize(PolygonIndex); ++VertexIndex) - { - int Vertex = SceneNode->GetMesh()->GetPolygonVertex(PolygonIndex, VertexIndex); - if (!VertexPolygons.Contains(Vertex)) - { - TArray PolygonList; - VertexPolygons.Add(Vertex, PolygonList); - } - VertexPolygons[Vertex].Add(PolygonIndex); - if (HasWeights.Contains(Vertex)) continue; - if (NoWeights.Contains(Vertex)) continue; - - FbxVector4 VertexNormal; - Vertex, SceneNode->GetMesh()->GetPolygonVertexNormal(PolygonIndex, VertexIndex, VertexNormal); - VertexNormals.Add(Vertex, VertexNormal); - - NoWeights.AddUnique(Vertex); - } - } - - for (int ClusterIndex = 0; ClusterIndex < Skin->GetClusterCount(); ++ClusterIndex) - { - FbxCluster* Cluster = Skin->GetCluster(ClusterIndex); - - for (int ClusterVertexIndex = 0; ClusterVertexIndex < Cluster->GetControlPointIndicesCount(); ++ClusterVertexIndex) - { - int Vertex = Cluster->GetControlPointIndices()[ClusterVertexIndex]; - //if (Cluster->GetControlPointIndices()[ClusterVertexIndex] == Vertex) - { - NoWeights.Remove(Vertex); - HasWeights.AddUnique(Vertex); - } - } - - } + // Fix Subdivision Weights + FString GeometryName = UTF8_TO_TCHAR(SceneNode->GetName()); + if (BaseScene && SubdivisionLevels.Contains(GeometryName) && SubdivisionLevels[GeometryName] > 0 && SceneNode->GetMesh()) + { + // Find a mesh from the BaseScene to match this mesh + for (int BaseNodeIndex = 0; BaseNodeIndex < BaseScene->GetNodeCount(); ++BaseNodeIndex) + { + FbxNode* BaseSceneNode = BaseScene->GetNode(NodeIndex); + if (BaseSceneNode && BaseSceneNode->GetMesh() && UTF8_TO_TCHAR(BaseSceneNode->GetName()) == GeometryName) + { + int32 SubdivisionLevel = SubdivisionLevels[GeometryName]; + FDazToUnrealSubdivision::SubdivideMesh(BaseSceneNode, SceneNode, SubdivisionLevel); + break; + } + } - if (HasWeights.Num() > 0 && NoWeights.Num() > 0) - { - //for (int NoWeightVertex : NoWeights) - { - FScopedSlowTask SubdivisionTask(Skin->GetClusterCount(), LOCTEXT("DazToUnrealSudAutoWeightTask", "Creating Subdivision Weights for Cluster:")); - - for (int ClusterIndex = 0; ClusterIndex < Skin->GetClusterCount(); ++ClusterIndex) - { - SubdivisionTask.EnterProgressFrame(); - FbxCluster* Cluster = Skin->GetCluster(ClusterIndex); - int ClusterVertexCount = Cluster->GetControlPointIndicesCount(); - - //Make a map of all the weights for the cluster - TMap ClusterWeights; - //TMap ClusterVertex; - for (int ClusterVertexIndex = 0; ClusterVertexIndex < ClusterVertexCount; ++ClusterVertexIndex) - { - int WeightVertex = Cluster->GetControlPointIndices()[ClusterVertexIndex]; - double Weight = Cluster->GetControlPointWeights()[ClusterVertexIndex]; - ClusterWeights.Add(WeightVertex, Weight); - //ClusterVertex.Add(ClusterVertexIndex, WeightVertex); - } - - TMap WeightsToAdd; - for (int ClusterVertexIndex = 0; ClusterVertexIndex < ClusterVertexCount; ++ClusterVertexIndex) - { - int WeightVertex = Cluster->GetControlPointIndices()[ClusterVertexIndex]; - //if(NoWeights.Contains(NoWeightVertex)) - { - TArray NeedWeights; - local::FindVertexNeedingWeight(WeightVertex, SceneNode, NeedWeights, VertexPolygons, NoWeights, 1); - /*for (int PolygonIndex : VertexPolygons[WeightVertex]) - { - for (int VertexIndex = 0; VertexIndex < SceneNode->GetMesh()->GetPolygonSize(PolygonIndex); ++VertexIndex) - { - int Vertex = SceneNode->GetMesh()->GetPolygonVertex(PolygonIndex, VertexIndex); - if (NoWeights.Contains(Vertex)) - { - NeedWeights.AddUnique(Vertex); - } - } - }*/ - - for (int NeedWeightVertex : NeedWeights) - { - double WeightCount = 0.0f; - double Weight = 0.0f; - - FbxVector4 NeedWeightVertexLocation = VertexLocations[NeedWeightVertex]; - TArray TouchedPolygons; - TArray TouchedVertices; - FbxVector4 ScaleVector = FbxVector4(1.0, 1.0, 1.0); - FbxMatrix VertexMatrix = FbxMatrix(NeedWeightVertexLocation, VertexNormals[NeedWeightVertex], ScaleVector); - local::GetWeights(SceneNode, VertexPolygons, ClusterWeights, VertexMatrix, NeedWeightVertex, TouchedPolygons, TouchedVertices, Weight, WeightCount, 1); - - - - /*for (int PolygonIndex : VertexPolygons[NeedWeightVertex]) - { - FbxVector4 NeedWeightVertexNormal; - for (int VertexIndex = 0; VertexIndex < SceneNode->GetMesh()->GetPolygonSize(PolygonIndex); ++VertexIndex) - { - int Vertex = SceneNode->GetMesh()->GetPolygonVertex(PolygonIndex, VertexIndex); - if (Vertex == NeedWeightVertex) - { - SceneNode->GetMesh()->GetPolygonVertexNormal(PolygonIndex, VertexIndex, NeedWeightVertexNormal); - } - } - // Set the vertices with no weight, to be the average of the ones with weight. - for (int VertexIndex = 0; VertexIndex < SceneNode->GetMesh()->GetPolygonSize(PolygonIndex); ++VertexIndex) - { - int Vertex = SceneNode->GetMesh()->GetPolygonVertex(PolygonIndex, VertexIndex); - if (ClusterWeights.Contains(Vertex)) - { - FbxVector4 CompareVertexNormal; - SceneNode->GetMesh()->GetPolygonVertexNormal(PolygonIndex, VertexIndex, CompareVertexNormal); - double DotProduct = CompareVertexNormal.DotProduct(NeedWeightVertexNormal); - FbxVector4 CompareLocation = VertexLocations[Vertex]; - double VertexDistance = NeedWeightVertexLocation.Distance(CompareLocation); - if (DotProduct > 0.0) - { - VertexDistance = VertexDistance * DotProduct; - } - - //double AdditionalWeightCount = FurthestDistance / VertexDistance; - //double AdditionalWeight = AdditionalWeightCount * ClusterWeights[Vertex]; - double AdditionalWeightCount = 1 / VertexDistance; - double AdditionalWeight = ClusterWeights[Vertex] / VertexDistance; - - Weight += AdditionalWeight; - WeightCount += AdditionalWeightCount; - } - } - }*/ - if (WeightCount > 0) - { - WeightsToAdd.Add(NeedWeightVertex, Weight / (double)WeightCount); - //Cluster->AddControlPointIndex(NeedWeightVertex, Weight / (double)WeightCount); - } - } - } - } - - for (auto WeightToAdd : WeightsToAdd) - { - Cluster->AddControlPointIndex(WeightToAdd.Key, WeightToAdd.Value); - } - } - } - } - } - } - } } -#endif } // Add IK bones @@ -1104,10 +881,6 @@ UObject* FDazToUnrealModule::ImportFromDaz(TSharedPtr JsonObject) } } } - for (FbxBlendShapeChannel* Channel : ChannelsToRemove) - { - BlendShape->RemoveBlendShapeChannel(Channel); - } } } } @@ -1330,7 +1103,8 @@ UObject* FDazToUnrealModule::ImportFromDaz(TSharedPtr JsonObject) } // Import FBX - UObject* NewObject = ImportFBXAsset(UpdatedFBXFile, CharacterFolder, AssetType, CharacterType, CharacterTypeName); + bool bSetPostProcessAnimation = !FDazToUnrealMorphs::IsAutoJCMImport(JsonObject); + UObject* NewObject = ImportFBXAsset(UpdatedFBXFile, CharacterFolder, AssetType, CharacterType, CharacterTypeName, bSetPostProcessAnimation); // If this is a Pose transfer, an AnimSequence was created. Make a PoseAsset from it. if (AssetType == DazAssetType::Pose) @@ -1346,6 +1120,29 @@ UObject* FDazToUnrealModule::ImportFromDaz(TSharedPtr JsonObject) } } + // Create and attach the Joint Control Anim + if (AssetType == DazAssetType::SkeletalMesh) + { + if (USkeletalMesh* SkeletalMesh = Cast(NewObject)) + { +#if ENGINE_MAJOR_VERSION > 4 + USkeleton* Skeleton = SkeletalMesh->GetSkeleton(); +#else + USkeleton* Skeleton = SkeletalMesh->Skeleton; +#endif + if (UDazJointControlledMorphAnimInstance* JointControlAnim = FDazToUnrealMorphs::CreateJointControlAnimation(JsonObject, CharacterFolder, AssetName, Skeleton, SkeletalMesh)) + { + //JointControlAnim->CurrentSkeleton = SkeletalMesh->Skeleton; +#if ENGINE_MAJOR_VERSION > 4 + SkeletalMesh->SetPostProcessAnimBlueprint(JointControlAnim->GetClass()); +#else + SkeletalMesh->PostProcessAnimBlueprint = JointControlAnim->GetClass(); +#endif + } + } + + } + return NewObject; } @@ -1441,7 +1238,7 @@ bool FDazToUnrealModule::ImportTextureAssets(TArray& SourcePaths, FStri return false; } -UObject* FDazToUnrealModule::ImportFBXAsset(const FString& SourcePath, const FString& ImportLocation, const DazAssetType& AssetType, const DazCharacterType& CharacterType, const FString& CharacterTypeName) +UObject* FDazToUnrealModule::ImportFBXAsset(const FString& SourcePath, const FString& ImportLocation, const DazAssetType& AssetType, const DazCharacterType& CharacterType, const FString& CharacterTypeName, const bool bSetPostProcessAnimation) { FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked("AssetTools"); UDazToUnrealSettings* CachedSettings = GetMutableDefault(); @@ -1497,6 +1294,7 @@ UObject* FDazToUnrealModule::ImportFBXAsset(const FString& SourcePath, const FSt { FbxFactory->ImportUI->bImportAsSkeletal = false; FbxFactory->ImportUI->bImportMaterials = true; + FbxFactory->ImportUI->StaticMeshImportData->bForceFrontXAxis = false; FbxFactory->ImportUI->MeshTypeToImport = FBXIT_StaticMesh; } if (AssetType == DazAssetType::Animation || AssetType == DazAssetType::Pose) @@ -1552,15 +1350,24 @@ UObject* FDazToUnrealModule::ImportFBXAsset(const FString& SourcePath, const FSt { if (USkeletalMesh* SkeletalMesh = Cast(ImportedAsset)) { - if (CachedSettings->SkeletonPostProcessAnimation.Contains(SkeletonPath)) + if (bSetPostProcessAnimation && CachedSettings->SkeletonPostProcessAnimation.Contains(SkeletonPath)) { +#if ENGINE_MAJOR_VERSION > 4 + SkeletalMesh->SetPostProcessAnimBlueprint(CachedSettings->SkeletonPostProcessAnimation[SkeletonPath].TryLoadClass()); +#else SkeletalMesh->PostProcessAnimBlueprint = CachedSettings->SkeletonPostProcessAnimation[SkeletonPath].TryLoadClass(); +#endif } //Get the new skeleton if (!Skeleton) { +#if ENGINE_MAJOR_VERSION > 4 + Skeleton = SkeletalMesh->GetSkeleton(); +#else Skeleton = SkeletalMesh->Skeleton; +#endif + int32 BoneIndex = Skeleton->GetReferenceSkeleton().FindBoneIndex(FName(TEXT("pelvis"))); if (BoneIndex == -1) { diff --git a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealEnvironment.cpp b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealEnvironment.cpp index ed9ac8b..2620e69 100644 --- a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealEnvironment.cpp +++ b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealEnvironment.cpp @@ -4,6 +4,9 @@ #include "Dom/JsonObject.h" #include "EditorLevelLibrary.h" #include "Math/UnrealMathUtility.h" +#if ENGINE_MAJOR_VERSION > 4 +#include "Subsystems/EditorActorSubsystem.h" +#endif void FDazToUnrealEnvironment::ImportEnvironment(TSharedPtr JsonObject) { @@ -11,7 +14,7 @@ void FDazToUnrealEnvironment::ImportEnvironment(TSharedPtr JsonObje const UDazToUnrealSettings* CachedSettings = GetDefault(); TArray> InstanceList = JsonObject->GetArrayField(TEXT("Instances")); TMap GuidToActor; - TMap ParentToChild; + TMap> ParentToChild; for (int32 Index = 0; Index< InstanceList.Num(); Index++) { TSharedPtr Instance = InstanceList[Index]->AsObject(); @@ -23,9 +26,15 @@ void FDazToUnrealEnvironment::ImportEnvironment(TSharedPtr JsonObje double InstanceYPos = Instance->GetNumberField(TEXT("TranslationZ")); double InstanceZPos = Instance->GetNumberField(TEXT("TranslationY")); - double InstanceXRot = FMath::RadiansToDegrees(Instance->GetNumberField(TEXT("RotationX"))); - double InstanceYRot = FMath::RadiansToDegrees(Instance->GetNumberField(TEXT("RotationY"))); - double InstanceZRot = FMath::RadiansToDegrees(Instance->GetNumberField(TEXT("RotationZ"))); + double Pitch = FMath::RadiansToDegrees(Instance->GetNumberField(TEXT("RotationZ"))); + double Yaw = FMath::RadiansToDegrees(Instance->GetNumberField(TEXT("RotationY"))) * -1.0f; + double Roll = FMath::RadiansToDegrees(Instance->GetNumberField(TEXT("RotationX"))); + + // Apply the rotations in the correct order + FQuat PitchQuat(FRotator(Pitch, 0.0f, 0.0f)); + FQuat YawQuat(FRotator(0.0f, Yaw, 0.0f)); + FQuat RollQuat(FRotator(0.0f, 0.0f, Roll)); + FQuat Quat = PitchQuat * YawQuat * RollQuat; double ScaleXPos = Instance->GetNumberField(TEXT("ScaleX")); double ScaleYPos = Instance->GetNumberField(TEXT("ScaleZ")); @@ -34,21 +43,67 @@ void FDazToUnrealEnvironment::ImportEnvironment(TSharedPtr JsonObje FString ParentId = Instance->GetStringField(TEXT("ParentID")); FString InstanceId = Instance->GetStringField(TEXT("Guid")); + // Make the child list if needed + if (!ParentToChild.Contains(ParentId)) + { + ParentToChild.Add(ParentId, TArray()); + } + // Find the asset for this instance - FString AssetPath = CachedSettings->ImportDirectory.Path / InstanceAssetName / InstanceAssetName + TEXT(".") + InstanceAssetName; - UObject* InstanceObject = LoadObject(NULL, *AssetPath, NULL, LOAD_None, NULL); + UObject* InstanceObject = nullptr; + if (InstanceAssetName.Len() > 0) + { + FString AssetPath = CachedSettings->ImportDirectory.Path / InstanceAssetName / InstanceAssetName + TEXT(".") + InstanceAssetName; + InstanceObject = LoadObject(NULL, *AssetPath, NULL, LOAD_None, NULL); + } + + // If this was imported with Force Front X Axis, fix the rotation + if (InstanceObject && FDazToUnrealUtils::IsModelFacingX(InstanceObject)) + { + FQuat FacingUndo(FRotator(0.0f, 90.0f, 0.0f)); + Quat = FacingUndo * PitchQuat * YawQuat * RollQuat; + } // Spawn the object into the scene if (InstanceObject) { FVector Location = FVector(InstanceXPos, InstanceYPos, InstanceZPos); - FRotator Rotation = FRotator(InstanceXRot, InstanceYRot, InstanceZRot); + FRotator Rotation = Quat.Rotator(); + +#if ENGINE_MAJOR_VERSION > 4 + UEditorActorSubsystem* EditorActorSubsystem = GEditor->GetEditorSubsystem(); + AActor* NewActor = EditorActorSubsystem->SpawnActorFromObject(InstanceObject, Location, Rotation); +#else AActor* NewActor = UEditorLevelLibrary::SpawnActorFromObject(InstanceObject, Location, Rotation); +#endif if (NewActor) { NewActor->SetActorScale3D(FVector(ScaleXPos, ScaleYPos, ScaleZPos)); GuidToActor.Add(InstanceId, NewActor); - ParentToChild.Add(ParentId, InstanceId); + ParentToChild[ParentId].Add(InstanceId); + } + } + else + { + // If we didn't find the object, or it was a group node spawn a dummy actor + FVector Location = FVector(InstanceXPos, InstanceYPos, InstanceZPos); + FRotator Rotation = Quat.Rotator(); + +#if ENGINE_MAJOR_VERSION > 4 + UEditorActorSubsystem* EditorActorSubsystem = GEditor->GetEditorSubsystem(); + AActor* NewActor = EditorActorSubsystem->SpawnActorFromClass(AActor::StaticClass(), Location, Rotation); +#else + AActor* NewActor = UEditorLevelLibrary::SpawnActorFromClass(AActor::StaticClass(), Location, Rotation); +#endif + if (NewActor) + { + NewActor->SetActorScale3D(FVector(ScaleXPos, ScaleYPos, ScaleZPos)); + if (USceneComponent* ParentDefaultAttachComponent = NewActor->GetDefaultAttachComponent()) + { + ParentDefaultAttachComponent->Mobility = EComponentMobility::Static; + } + GuidToActor.Add(InstanceId, NewActor); + ParentToChild[ParentId].Add(InstanceId); } } } @@ -56,11 +111,14 @@ void FDazToUnrealEnvironment::ImportEnvironment(TSharedPtr JsonObje // Re-create the hierarchy from Daz Studio for (auto Pair : ParentToChild) { - if (GuidToActor.Contains(Pair.Key) && GuidToActor.Contains(Pair.Value)) + for (FString ChildGuid : Pair.Value) { - AActor* ParentActor = GuidToActor[Pair.Key]; - AActor* ChildActor = GuidToActor[Pair.Value]; - ChildActor->AttachToActor(ParentActor, FAttachmentTransformRules::KeepWorldTransform); + if (GuidToActor.Contains(Pair.Key) && GuidToActor.Contains(ChildGuid)) + { + AActor* ParentActor = GuidToActor[Pair.Key]; + AActor* ChildActor = GuidToActor[ChildGuid]; + ChildActor->AttachToActor(ParentActor, FAttachmentTransformRules::KeepWorldTransform); + } } } diff --git a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealFbx.cpp b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealFbx.cpp index 1191804..4c3f4b2 100644 --- a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealFbx.cpp +++ b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealFbx.cpp @@ -70,6 +70,13 @@ void FDazToUnrealFbx::FixClusterTranformLinks(FbxScene* Scene, FbxNode* RootNode FixClusterTranformLinks(Scene, ChildNode); } } +void FDazToUnrealFbx::RemoveBindPoses(FbxScene* Scene) +{ + for (int32 PoseIndex = Scene->GetPoseCount() - 1; PoseIndex >= 0; --PoseIndex) + { + Scene->RemovePose(PoseIndex); + } +} void FDazToUnrealFbx::AddWeightsToAllNodes(FbxNode* Parent) { diff --git a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealMaterials.cpp b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealMaterials.cpp index ff65ce7..ab5f275 100644 --- a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealMaterials.cpp +++ b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealMaterials.cpp @@ -374,8 +374,11 @@ UMaterialInstanceConstant* FDazToUnrealMaterials::CreateMaterial(const FString C // Create the Material Instance auto MaterialInstanceFactory = NewObject(); - +#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION < 26 UPackage* Package = CreatePackage(nullptr, *(CharacterMaterialFolder / MaterialName)); +#else + UPackage* Package = CreatePackage(*(CharacterMaterialFolder / MaterialName)); +#endif UMaterialInstanceConstant* UnrealMaterialConstant = (UMaterialInstanceConstant*)MaterialInstanceFactory->FactoryCreateNew(UMaterialInstanceConstant::StaticClass(), Package, *MaterialName, RF_Standalone | RF_Public, NULL, GWarn); @@ -430,11 +433,11 @@ UMaterialInstanceConstant* FDazToUnrealMaterials::CreateMaterial(const FString C { if (MaterialProperty.Type == TEXT("Texture")) { - FStringAssetReference TextureAssetPath(CharacterTexturesFolder / MaterialProperty.Value); + FSoftObjectPath TextureAssetPath(CharacterTexturesFolder / MaterialProperty.Value); UObject* TextureAsset = TextureAssetPath.TryLoad(); if (TextureAsset == nullptr) { - FStringAssetReference TextureAssetPathFull(MaterialProperty.Value); + FSoftObjectPath TextureAssetPathFull(MaterialProperty.Value); TextureAsset = TextureAssetPathFull.TryLoad(); } FMaterialParameterInfo ParameterInfo(*MaterialProperty.Name); @@ -692,10 +695,26 @@ USubsurfaceProfile* FDazToUnrealMaterials::CreateSubsurfaceProfileForMaterial(co } FString SubsurfaceProfileName = MaterialName + TEXT("_Profile"); +#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION < 26 UPackage* Package = CreatePackage(nullptr, *(CharacterMaterialFolder / MaterialName)); - //USubsurfaceProfile* SubsurfaceProfile = (USubsurfaceProfile*)SubsurfaceProfileFactory->FactoryCreateNew(USubsurfaceProfile::StaticClass(), Package, *MaterialName, RF_Standalone | RF_Public, NULL, GWarn); - FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked("AssetTools"); - USubsurfaceProfile* SubsurfaceProfile = Cast(AssetToolsModule.Get().CreateAsset(SubsurfaceProfileName, FPackageName::GetLongPackagePath(*(CharacterMaterialFolder / MaterialName)), USubsurfaceProfile::StaticClass(), NULL)); +#else + UPackage* Package = CreatePackage(*(CharacterMaterialFolder / MaterialName)); +#endif + + USubsurfaceProfile* SubsurfaceProfile = nullptr; + FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); + FAssetData AssetData = AssetRegistryModule.Get().GetAssetByObjectPath(*(CharacterMaterialFolder / SubsurfaceProfileName + TEXT(".") + SubsurfaceProfileName)); + UObject* Asset = FindObject(nullptr, *(CharacterMaterialFolder / SubsurfaceProfileName + TEXT(".") + SubsurfaceProfileName)); + if (AssetData.IsValid()) + { + SubsurfaceProfile = Cast(AssetData.GetAsset()); + } + + if (SubsurfaceProfile == nullptr) + { + FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked("AssetTools"); + SubsurfaceProfile = Cast(AssetToolsModule.Get().CreateAsset(SubsurfaceProfileName, FPackageName::GetLongPackagePath(*(CharacterMaterialFolder / MaterialName)), USubsurfaceProfile::StaticClass(), NULL)); + } if (HasMaterialProperty(TEXT("Specular Lobe 1 Roughness"), MaterialProperties)) { SubsurfaceProfile->Settings.Roughness0 = FCString::Atof(*GetMaterialProperty(TEXT("Specular Lobe 1 Roughness"), MaterialProperties)); diff --git a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealMorphs.cpp b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealMorphs.cpp new file mode 100644 index 0000000..45b2f6b --- /dev/null +++ b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealMorphs.cpp @@ -0,0 +1,483 @@ +#include "DazToUnrealMorphs.h" +#include "DazJointControlledMorphAnimInstance.h" +#include "Serialization/JsonReader.h" +#include "Dom/JsonObject.h" +#include "Serialization/JsonSerializer.h" +#include "AssetRegistryModule.h" +#include "Kismet2/KismetEditorUtilities.h" +#include "Animation/AnimBlueprint.h" +#include "Animation/AnimBlueprintGeneratedClass.h" +#include "EdGraphSchema_K2_Actions.h" +#include "AnimGraphNode_LinkedInputPose.h" +#include "Kismet2/BlueprintEditorUtils.h" +#include "Animation/MorphTarget.h" +#include "LODUtilities.h" +#include "Math/DualQuat.h" + +UDazJointControlledMorphAnimInstance* FDazToUnrealMorphs::CreateJointControlAnimation(TSharedPtr JsonObject, FString Folder, FString CharacterName, USkeleton* Skeleton, USkeletalMesh* Mesh) +{ + // Only only create the JCM Anim if the JointLinks object exists + const TArray>* JointLinkList;// = JsonObject->GetArrayField(TEXT("JointLinks")); + if (JsonObject->TryGetArrayField(TEXT("JointLinks"), JointLinkList)) + { + const FString AssetName = CharacterName + TEXT("_JCMAnim"); + const FString PackageName = FPackageName::ObjectPathToPackageName(Folder) / AssetName; + const FString ObjectName = FPackageName::ObjectPathToObjectName(PackageName); + + // Create the new blueprint next to the SkeletalMesh and get the AnimInstance from it. +#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION < 26 + UPackage* NewPackage = CreatePackage(nullptr, *PackageName); +#else + UPackage* NewPackage = CreatePackage(*PackageName); +#endif + UAnimBlueprint* AnimBlueprint = CreateBlueprint(NewPackage, FName(AssetName), Skeleton); + UDazJointControlledMorphAnimInstance* AnimInstance = Cast(AnimBlueprint->GetAnimBlueprintGeneratedClass()->ClassDefaultObject); + + // Get the joint link info for each link + for (int i = 0; i < JointLinkList->Num(); i++) + { + FDazJointControlLink NewJointLink; + const TSharedPtr JointLinkValue = (*JointLinkList)[i]; + TSharedPtr JointLink = JointLinkValue->AsObject(); + NewJointLink.BoneName = FName(JointLink->GetStringField(TEXT("Bone"))); + + // Unreal ends up with different axis + FString DazAxis = JointLink->GetStringField(TEXT("Axis")); + if (DazAxis == TEXT("XRotate")) + { + NewJointLink.PrimaryAxis = EDazMorphAnimInstanceDriver::RotationY; + } + if (DazAxis == TEXT("YRotate")) + { + NewJointLink.PrimaryAxis = EDazMorphAnimInstanceDriver::RotationZ; + } + if (DazAxis == TEXT("ZRotate")) + { + NewJointLink.PrimaryAxis = EDazMorphAnimInstanceDriver::RotationX; + } + + NewJointLink.MorphName = FName(JointLink->GetStringField(TEXT("Morph"))); + NewJointLink.Scalar = JointLink->GetNumberField(TEXT("Scalar")); + + // These two are flipped when we get in Unreal + if (NewJointLink.PrimaryAxis == EDazMorphAnimInstanceDriver::RotationY) + { + NewJointLink.Scalar = NewJointLink.Scalar * -1.0f; + } + if (NewJointLink.PrimaryAxis == EDazMorphAnimInstanceDriver::RotationZ) + { + NewJointLink.Scalar = NewJointLink.Scalar * -1.0f; + } + NewJointLink.Alpha = JointLink->GetNumberField(TEXT("Alpha")); + + // Get the Keys if they exist + const TArray>* JointLinkKeys; + if (JointLink->TryGetArrayField(TEXT("Keys"), JointLinkKeys)) + { + for (int KeyIndex = 0; KeyIndex < JointLinkKeys->Num(); KeyIndex++) + { + FDazJointControlLinkKey NewJointLinkKey; + const TSharedPtr JointLinkKeyValue = (*JointLinkKeys)[KeyIndex]; + TSharedPtr JointLinkKey = JointLinkKeyValue->AsObject(); + NewJointLinkKey.BoneRotation = JointLinkKey->GetNumberField(TEXT("Angle")); + if (NewJointLink.PrimaryAxis == EDazMorphAnimInstanceDriver::RotationY) + { + NewJointLinkKey.BoneRotation = NewJointLinkKey.BoneRotation * -1.0f; + } + if (NewJointLink.PrimaryAxis == EDazMorphAnimInstanceDriver::RotationZ) + { + NewJointLinkKey.BoneRotation = NewJointLinkKey.BoneRotation * -1.0f; + } + NewJointLinkKey.MorphAlpha = JointLinkKey->GetNumberField(TEXT("Value")); + NewJointLink.Keys.Add(NewJointLinkKey); + } + } + if (false) + { + FakeDualQuarternion(NewJointLink.MorphName, NewJointLink.BoneName, NewJointLink.PrimaryAxis, 0.0f, 90.0f, Mesh); + } + AnimInstance->ControlLinks.Add(NewJointLink); + } + +#if 0 +#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION <= 26 + FLODUtilities::RegenerateLOD(Mesh, -1, true, true); +#else + FLODUtilities::RegenerateLOD(Mesh, nullptr, -1, true, true); +#endif +#endif + + // Setup Secondary Axis. We need to know the primary and secondary axis + // because if we check the rotation in the wrong order the morphs can pop. + for (FDazJointControlLink& PrimaryAxisLink : AnimInstance->ControlLinks) + { + for (FDazJointControlLink& SecondaryAxisLink : AnimInstance->ControlLinks) + { + if (PrimaryAxisLink.BoneName == SecondaryAxisLink.BoneName && + PrimaryAxisLink.PrimaryAxis != SecondaryAxisLink.PrimaryAxis) + { + PrimaryAxisLink.SecondaryAxis = SecondaryAxisLink.PrimaryAxis; + SecondaryAxisLink.SecondaryAxis = PrimaryAxisLink.PrimaryAxis; + break; + } + } + } + + // Recompile the AnimBlueprint and return the updated AnimInstance + FBlueprintEditorUtils::MarkBlueprintAsModified(AnimBlueprint); + FKismetEditorUtilities::CompileBlueprint(AnimBlueprint); + + AnimInstance = Cast(AnimBlueprint->GetAnimBlueprintGeneratedClass()->ClassDefaultObject); + return AnimInstance; + } + return nullptr; +} + +// This is a stripped down version of UAnimBlueprintFactory::FactoryCreateNew +UAnimBlueprint* FDazToUnrealMorphs::CreateBlueprint(UObject* InParent, FName Name, USkeleton* Skeleton) +{ + // Create the Blueprint + UClass* ClassToUse = UDazJointControlledMorphAnimInstance::StaticClass(); + UAnimBlueprint* NewBP = CastChecked(FKismetEditorUtilities::CreateBlueprint(ClassToUse, InParent, Name, BPTYPE_Normal, UAnimBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(), NAME_None)); + + NewBP->TargetSkeleton = Skeleton; + + // Because the BP itself didn't have the skeleton set when the initial compile occured, it's not set on the generated classes either + if (UAnimBlueprintGeneratedClass* TypedNewClass = Cast(NewBP->GeneratedClass)) + { + TypedNewClass->TargetSkeleton = Skeleton; + } + if (UAnimBlueprintGeneratedClass* TypedNewClass_SKEL = Cast(NewBP->SkeletonGeneratedClass)) + { + TypedNewClass_SKEL->TargetSkeleton = Skeleton; + } + + // Need to add the Pose Input Node and connect it to the Output Node in the AnimGraph + TArray Graphs; + NewBP->GetAllGraphs(Graphs); + for (UEdGraph* Graph : Graphs) + { + if (Graph->GetFName() == UEdGraphSchema_K2::GN_AnimGraph) + { + UEdGraphNode* OutNode = nullptr; + for (UEdGraphNode* Node : Graph->Nodes) + { + OutNode = Node; + } + // If the Blueprint isn't open in an Editor Window adding the UAnimGraphNode_LinkedInputPose node will crash. + GEditor->GetEditorSubsystem()->OpenEditorForAsset(NewBP); + UAnimGraphNode_LinkedInputPose* const InputTemplate = NewObject(); + UEdGraphNode* const NewNode = FEdGraphSchemaAction_NewNode::SpawnNodeFromTemplate(Graph, InputTemplate, FVector2D(0.0f, 0.0f), false); + if (NewNode && OutNode) + { + UEdGraphPin* NextPin = NewNode->FindPin(TEXT("Pose")); + UEdGraphPin* OutPin = OutNode->FindPin(TEXT("Result")); + + NextPin->MakeLinkTo(OutPin); + } + + // Don't need the editor open anymore, so close it now. + // Note: Turns out I can't close it because part of the action happens on a tick + //GEditor->GetEditorSubsystem()->CloseAllEditorsForAsset(NewBP); + } + } + + return NewBP; + + +} + +bool FDazToUnrealMorphs::IsAutoJCMImport(TSharedPtr JsonObject) +{ + const TArray>* JointLinkList; + if (JsonObject->TryGetArrayField(TEXT("JointLinks"), JointLinkList)) + { + return true; + } + return false; +} + +void FDazToUnrealMorphs::FakeDualQuarternion(FName MorphName, FName BoneName, EDazMorphAnimInstanceDriver Axis, float MinBend, float MaxBend, USkeletalMesh* Mesh) +{ +#if 0 + if (UMorphTarget* Morph = Mesh->FindMorphTarget(MorphName)) + { +#if ENGINE_MAJOR_VERSION > 4 + const FReferenceSkeleton& RefSkeleton = Mesh->GetRefSkeleton(); +#else + const FReferenceSkeleton& RefSkeleton = Mesh->RefSkeleton; +#endif + + TArray ComponentSpaceTransforms; + FAnimationRuntime::FillUpComponentSpaceTransforms(RefSkeleton, RefSkeleton.GetRefBonePose(), ComponentSpaceTransforms); + + int32 BoneIndex = RefSkeleton.FindBoneIndex(BoneName); + //const FMeshBoneInfo& BoneInfo = RefSkeleton.GetRefBoneInfo()[BoneIndex]; + + ///int32 ParentBoneIndex = BoneInfo.ParentIndex; + ///const FMeshBoneInfo& ParentBoneInfo = RefSkeleton.GetRefBoneInfo()[ParentBoneIndex]; + ///FName ParentBoneName = ParentBoneInfo.Name; + + FSkeletalMeshImportData ImportData; + Mesh->LoadLODImportedData(0, ImportData); + + /*TMap ParentBoneVertexToWeight; + TMap ChildBoneVertexToWeight; + + for (SkeletalMeshImportData::FRawBoneInfluence Influence : ImportData.Influences) + { + if (Influence.BoneIndex == ParentBoneIndex) + { + ParentBoneVertexToWeight.Add(Influence.VertexIndex, Influence.Weight); + } + if (Influence.BoneIndex == BoneIndex) + { + ChildBoneVertexToWeight.Add(Influence.VertexIndex, Influence.Weight); + } + }*/ + + // Get the Bone's component position + //const FMeshBoneInfo& RecurseBone = BoneInfo; + /*int32 RecurseBoneIndex = BoneIndex; + FVector BoneComponentPosition = RefSkeleton.GetRefBonePose()[BoneIndex].GetTranslation(); + while (RecurseBoneIndex != 0) + { + const FMeshBoneInfo& RecurseBoneInfo = RefSkeleton.GetRefBoneInfo()[RecurseBoneIndex]; + BoneComponentPosition += RefSkeleton.GetRefBonePose()[RecurseBoneInfo.ParentIndex].GetTranslation(); + RecurseBoneIndex = RecurseBoneInfo.ParentIndex; + }*/ + + int32 ImportMorphIndex = 0; + for (FSkeletalMeshImportData& ImportMorphData : ImportData.MorphTargets) + { + FName ImportMorphName = FName(ImportData.MorphTargetNames[ImportMorphIndex]); + if (ImportMorphName == MorphName) + { + // Setup the Rotation Info + FVector RotationVector = FVector(0.0f, 0.0f, 1.0f); + float Direction = 1.0f; + if (Axis == EDazMorphAnimInstanceDriver::RotationZ) + { + RotationVector = FVector(0.0f, 0.0f, 1.0f); + } + + if (Axis == EDazMorphAnimInstanceDriver::RotationY) + { + RotationVector = FVector(0.0f, 1.0f, 0.0f); + //Direction = -1.0f; + } + FTransform RotationTransform = FTransform(FQuat(RotationVector, FMath::DegreesToRadians(90.0f * Direction))); + + //for (int32 ModifiedPointIndex = 0; ModifiedPointIndex < ImportData.MorphTargetModifiedPoints[ImportMorphIndex].Num(); ModifiedPointIndex++) + int32 ModifiedPointIndex = 0; + for (uint32 ModifiedPointVertexIndex : ImportData.MorphTargetModifiedPoints[ImportMorphIndex]) + { + //auto VertexSet = ImportData.MorphTargetModifiedPoints[ImportMorphIndex]; + //FSetElementId SetId = VertexSet[FSetElementId::FromInteger(ModifiedPointVertexIndex)];//.FindId(ModifiedPointIndex); + //uint32 ModifiedPointVertexIndex = VertexSet[FSetElementId::FromInteger(ModifiedPointVertexIndex)]; + + // Find all the bones influencing this vertex + TMap BoneIndexToWeight; + for (SkeletalMeshImportData::FRawBoneInfluence Influence : ImportData.Influences) + { + if (Influence.VertexIndex == ModifiedPointVertexIndex) + { + BoneIndexToWeight.Add(Influence.BoneIndex, Influence.Weight); + } + } + + // Get the Linear Blend Skinning for the vertex + FVector PointPosition = ImportData.Points[ModifiedPointVertexIndex]; + FVector LinearBlendPostion = FVector(0.0f, 0.0f, 0.0f); + float WeightSum = 0.0f; + FTransform LinearBlendTransform = FTransform::Identity; + FDualQuat SummedDualQuat(FTransform::Identity); + // Sum up from each influence + for (TPair BoneToWeightPair : BoneIndexToWeight) + { + //FTransform BoneRefTransform = ComponentSpaceTransforms[BoneToWeightPair.Key]; + const FMeshBoneInfo& BoneInfo = RefSkeleton.GetRefBoneInfo()[BoneToWeightPair.Key]; + FTransform BoneWorldTransform = ComponentSpaceTransforms[BoneToWeightPair.Key]; + float BoneWeight = BoneToWeightPair.Value; + + //const FMeshBoneInfo& ParentBoneInfo = RefSkeleton.GetRefBoneInfo()[BoneInfo.ParentIndex]; + FTransform ParentWorldTransform = ComponentSpaceTransforms[BoneInfo.ParentIndex]; + FTransform BoneTransformInParentSpace = BoneWorldTransform * ParentWorldTransform.Inverse(); + FVector VertexPositionInBoneSpace = PointPosition - BoneWorldTransform.GetTranslation(); + FTransform VertexTransformInBoneSpace = FTransform::Identity; //BoneTransformInParentSpace; + VertexTransformInBoneSpace.AddToTranslation(VertexPositionInBoneSpace); + FTransform RotatedBoneTransform = BoneWorldTransform; + + //FVector RigidSkinningPosition = BoneRefTransform.TransformPosition(VertexPositionInBoneSpace);//PointPosition; + if (BoneToWeightPair.Key >= BoneIndex) + { + VertexTransformInBoneSpace = VertexTransformInBoneSpace * RotationTransform; + VertexPositionInBoneSpace = VertexTransformInBoneSpace.GetTranslation(); + //RotatedBoneTransform = BoneWorldTransform * RotationTransform; + //FTransform ParentBoneTransform = ComponentSpaceTransforms[ParentBoneIndex]; + //FTransform BoneTransformInParentSpace = BoneRefTransform * ParentBoneTransform.Inverse(); + //FTransform RotatedBonTransformInParentSpace = BoneTransformInParentSpace * RotationTransform; + //FTransform BoneTransformInComponentSpace = RotatedBonTransformInParentSpace * ParentBoneTransform; + //FTransform BoneTransformInComponentSpace = RotationTransform * BoneRefTransform; + + //RigidSkinningPosition = BoneTransformInComponentSpace.TransformPosition(VertexPositionInBoneSpace); + //LinearBlendPostion = RigidSkinningPosition; + //BoneTransform = BoneTransform * ParentBoneTransform.Inverse() * RotationTransform * ParentBoneTransform; + //FTransform RotationTransform = FTransform(FQuat(RotationVector, FMath::DegreesToRadians(90.0f * Direction))); + //RigidSkinningPosition = VertexPositionInBoneSpace.RotateAngleAxis(90.0f * Direction, RotationVector); + } + FTransform VertexWorldTransform = FTransform(VertexTransformInBoneSpace.GetTranslation()) * FTransform(BoneWorldTransform.GetTranslation());// ParentWorldTransform; + //FTransform VertexWorldTransform = BoneWorldTransform * VertexTransformInBoneSpace; + FTransform BoneTransformInVertexSpace = VertexTransformInBoneSpace.Inverse(); + + //FVector V = VertexWorldTransform; + //FQuat Real = TransformForDualQuat.GetRotation(); + //FQuat Dual = FQuat(V.X, V.Y, V.Z, 0.f) * Real * 0.5f; + FDualQuat DualQuatBoneTransform = FDualQuat(VertexWorldTransform); + //LinearBlendPostion = RotatedBoneTransform * BoneWeight; + LinearBlendTransform.Accumulate(VertexWorldTransform, ScalarRegister(BoneWeight)); + SummedDualQuat = SummedDualQuat * (DualQuatBoneTransform * BoneWeight); + // Sum the weighted Rigid Skinning to get the Linear Blend Skinning + //WeightSum += BoneWeight; + //LinearBlendPostion += RigidSkinningPosition * BoneWeight; + } + + // Weight sum should always be 1.0, but just in case; + //LinearBlendPostion = (LinearBlendPostion / WeightSum);// +PointPosition; + + // Get the Dual Quaternion Skinning + + // Sum up from each influence + if (0) + { + int32 QuatCount = 0; + FVector TestVector = FVector(0.0f, 0.0f, 0.0f); + for (TPair BoneToWeightPair : BoneIndexToWeight) + { + const FMeshBoneInfo& BoneInfo = RefSkeleton.GetRefBoneInfo()[BoneToWeightPair.Key]; + FTransform BoneRefTransform = ComponentSpaceTransforms[BoneToWeightPair.Key]; + //FTransform RotationTransform = FTransform(FQuat(RotationVector, FMath::DegreesToRadians(90.0f * Direction))); + FVector VertexPositionInBoneSpace = PointPosition - BoneRefTransform.GetTranslation(); + FVector BonePositionInVertexSpace = BoneRefTransform.GetTranslation() - PointPosition; + FVector RigidSkinningPosition = BoneRefTransform.TransformPosition(VertexPositionInBoneSpace);//PointPosition; + + if (BoneToWeightPair.Key == BoneIndex) + { + ////FTransform ParentBoneTransform = ComponentSpaceTransforms[ParentBoneIndex]; + ////BoneTransform = BoneTransform * ParentBoneTransform.Inverse() * RotationTransform * ParentBoneTransform; + //FTransform BoneTransformInComponentSpace = RotationTransform * BoneTransform; + ////BoneTransform = BoneTransformInComponentSpace; + ////BoneTransform = BoneTransform * RotationTransform; + ////FDualQuat DualQuatBoneTransform = FDualQuat(BoneTransform); + //FTransform RotationTransform = FTransform(FQuat(RotationVector, FMath::DegreesToRadians(90.0f * Direction))); + FTransform ParentBoneTransform = ComponentSpaceTransforms[BoneInfo.ParentIndex]; + FTransform BoneTransformInParentSpace = BoneRefTransform * ParentBoneTransform.Inverse(); + FTransform RotatedBonTransformInParentSpace = BoneTransformInParentSpace * RotationTransform; + //FTransform BoneTransformInComponentSpace = RotatedBonTransformInParentSpace * ParentBoneTransform; + FTransform BoneTransformInComponentSpace = RotationTransform * BoneRefTransform; + + //RigidSkinningPosition = BoneTransformInComponentSpace.TransformPosition(VertexPositionInBoneSpace); + //LinearBlendPostion = RigidSkinningPosition; + FTransform BoneTransformInVertexSpace = FTransform::Identity * FTransform(BonePositionInVertexSpace); + //FTransform TransformForDualQuat = BoneTransformInParentSpace * RotationTransform * ParentBoneTransform * BoneTransformInVertexSpace.Inverse(); + FTransform TransformForDualQuat = RotationTransform * BoneRefTransform; //BoneTransformInParentSpace * RotationTransform * BoneTransformInVertexSpace.Inverse(); + TestVector = TransformForDualQuat.TransformPosition(VertexPositionInBoneSpace); + + //FVector V = TransformForDualQuat.GetTranslation(); + FVector V = TransformForDualQuat.TransformPosition(VertexPositionInBoneSpace); + FQuat Real = TransformForDualQuat.GetRotation(); + FQuat Dual = FQuat(V.X, V.Y, V.Z, 0.f) * Real * 0.5f; + FDualQuat DualQuatBoneTransform = FDualQuat(Real, Dual); + SummedDualQuat = DualQuatBoneTransform; + } + float BoneWeight = BoneToWeightPair.Value; + + //FDualQuat DualQuatBoneTransform = FDualQuat(BoneTransform); + + //FDualQuat WeightedDualQuatBoneTransform = DualQuatBoneTransform * BoneWeight; + /*if (QuatCount == 0) + { + SummedDualQuat = WeightedDualQuatBoneTransform; + } + else + {*/ + //SummedDualQuat = SummedDualQuat * WeightedDualQuatBoneTransform;// .Normalized(); + + //} + QuatCount++; + } + } + LinearBlendTransform.NormalizeRotation(); + SummedDualQuat = SummedDualQuat.Normalized(); + FTransform DualQuatTransform = SummedDualQuat.AsFTransform(); + FVector DualQuaternionPosition = DualQuatTransform.TransformPosition(FVector(0.0f, 0.0f, 0.0f));//DualQuatTransform.TransformPosition(PointPosition); + + FVector Diff = FVector(DualQuaternionPosition.X - LinearBlendPostion.X, + DualQuaternionPosition.Y - LinearBlendPostion.Y, + DualQuaternionPosition.Z - LinearBlendPostion.Z); + + UE_LOG(LogTemp, Warning, TEXT("Diff: %f, %f, %f"), Diff.X, Diff.Y, Diff.Z); + + ImportMorphData.Points[ModifiedPointIndex] = LinearBlendTransform.GetTranslation();// +PointPosition;//ImportMorphData.Points[ModifiedPointIndex] + Diff; + ModifiedPointIndex++; + } + // //auto VertexSet = ImportData.MorphTargetModifiedPoints[ImportMorphIndex]; + // //FSetElementId SetId = VertexSet[ModifiedPointIndex];//.FindId(ModifiedPointIndex); + // //uint32 ModifiedPointVertexIndex = VertexSet[SetId]; + // //uint32 ModifiedPointVertexIndex = VertexSet[ModifiedPointIndex]; + + // if (ParentBoneVertexToWeight.Contains(ModifiedPointVertexIndex) && ChildBoneVertexToWeight.Contains(ModifiedPointVertexIndex)) + // { + // float ParentWeight = ParentBoneVertexToWeight[ModifiedPointVertexIndex]; + // float ChildWeight = ChildBoneVertexToWeight[ModifiedPointVertexIndex]; + // FVector PointPosition = ImportData.Points[ModifiedPointVertexIndex]; + // FVector BonePosition = RefSkeleton.GetRefBonePose()[BoneIndex].GetTranslation(); + + // FVector Diff = FVector(0.0f, 0.0f, 0.0f); + // FVector RotationVector = FVector(0.0f, 0.0f, 1.0f); + // float Direction = 1.0f; + // if (Axis == EDazMorphAnimInstanceDriver::RotationZ) + // { + // RotationVector = FVector(0.0f, 0.0f, 1.0f); + // } + + // if (Axis == EDazMorphAnimInstanceDriver::RotationY) + // { + // RotationVector = FVector(0.0f, 1.0f, 0.0f); + // //Direction = -1.0f; + // } + + // FVector RelativePointPosition = PointPosition - BoneComponentPosition; + // FVector RotatedPosition = RelativePointPosition.RotateAngleAxis(90.0f * Direction, RotationVector); + // FVector RelativeRotationPosition = /*(RotatedPosition - RelativePointPosition)*/ RotatedPosition * ParentBoneVertexToWeight[ModifiedPointVertexIndex]; + + // FQuat DualQuatRotation = FQuat(RotationVector, FMath::DegreesToRadians(90.0f * Direction)); + // //FQuat DualQuatDisplacement = FQuat(RelativePointPosition.X / 2.0f, RelativePointPosition.Y / 2.0f, RelativePointPosition.Z / 2.0f, 0.0f); + // //FQuat DualQuatDisplacement = FQuat(RelativePointPosition.X, RelativePointPosition.Y, RelativePointPosition.Z, 0.0f); + // FQuat DualQuatDisplacement = FQuat(RelativePointPosition.X, RelativePointPosition.Y, RelativePointPosition.Z, 0.0f) * DualQuatRotation * 0.5f; + // FDualQuat DualQuat = FDualQuat(DualQuatRotation, DualQuatDisplacement); + // //DualQuat.AsFTransform().TransformVector + // DualQuat = DualQuat * ParentBoneVertexToWeight[ModifiedPointVertexIndex]; + // FVector DualQuatRelativePostion = DualQuat.AsFTransform().TransformVector(RelativePointPosition); + // /*Diff = FVector(DualQuatRelativePostion.X - RelativePointPosition.X, + // DualQuatRelativePostion.Y - RelativePointPosition.Y, + // DualQuatRelativePostion.Z - RelativePointPosition.Z);*/ + + // Diff = FVector(RelativePointPosition.X - DualQuatRelativePostion.X, + // RelativePointPosition.Y - DualQuatRelativePostion.Y, + // RelativePointPosition.Z - DualQuatRelativePostion.Z); + + // UE_LOG(LogTemp, Warning, TEXT("Diff: %f, %f, %f"), Diff.X, Diff.Y, Diff.Z); + + // ImportMorphData.Points[ModifiedPointIndex] = ImportMorphData.Points[ModifiedPointIndex] + Diff;//FVector(100.0f, 0.0f, 0.0f); + // } + // ModifiedPointIndex++; + // + //} + } + ImportMorphIndex++; + } + Mesh->SaveLODImportedData(0, ImportData); + } +#endif +} \ No newline at end of file diff --git a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealSubdivision.cpp b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealSubdivision.cpp new file mode 100644 index 0000000..cd1d634 --- /dev/null +++ b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealSubdivision.cpp @@ -0,0 +1,215 @@ +#include "DazToUnrealSubdivision.h" + +#ifndef M_PI +#define M_PI_NOT_DEFINED +#define M_PI PI // OpenSubdiv is expecting M_PI to be defined already +#endif + +#if ENGINE_MAJOR_VERSION > 4 +#include "opensubdiv/far/topologyDescriptor.h" +#include "opensubdiv/far/primvarRefiner.h" +#else +#include "far/topologyDescriptor.h" +#include "far/primvarRefiner.h" +#endif + +struct FDtuOsdSkinWeight +{ + FDtuOsdSkinWeight() + { + Weight = 0.0f; + } + + void Clear(void* UnusedPtr = nullptr) + { + Weight = 0.0f; + } + + void AddWithWeight(FDtuOsdSkinWeight const& SourceSkinWeight, float InWeight) + { + Weight += InWeight * SourceSkinWeight.Weight; + } + + void SetWeight(float InWeight) + { + Weight = InWeight; + } + + const float GetWeight() const + { + return Weight; + } + + float Weight; +}; + +struct FDtuOsdVector +{ + void Clear(void* UnusedPtr = nullptr) + { + Position = FVector(0, 0, 0); + } + + void AddWithWeight(FDtuOsdVector const& SourceVertexPosition, float Weight) + { + Position += SourceVertexPosition.Position * Weight; + } + + void SetPosition(FbxVector4 Vector) { + Position[0] = (float)Vector[0]; + Position[1] = (float)Vector[1]; + Position[2] = (float)Vector[2]; + } + + FVector Position; +}; + +void FDazToUnrealSubdivision::SubdivideMesh(FbxNode* BaseNode, FbxNode* TranferWeightsToNode, int SubdLevel) +{ + FbxMesh* BaseMesh = BaseNode->GetMesh(); + + // Get the topology + TArray VertsPerFace; + VertsPerFace.AddDefaulted(BaseMesh->GetPolygonCount()); + TArray VertexIndicesPerFace; + for (int PolygonIndex = 0; PolygonIndex < BaseMesh->GetPolygonCount(); PolygonIndex++) + { + VertsPerFace[PolygonIndex] = BaseMesh->GetPolygonSize(PolygonIndex); + for (int VertexIndex = 0; VertexIndex < VertsPerFace[PolygonIndex]; VertexIndex++) + { + int ControlPointIndex = BaseMesh->GetPolygonVertex(PolygonIndex, VertexIndex); + if (ControlPointIndex < 0) + { + continue; + } + VertexIndicesPerFace.Add(ControlPointIndex); + } + } + + // Setup Options + OpenSubdiv::Sdc::SchemeType type = OpenSubdiv::Sdc::SCHEME_CATMARK; + OpenSubdiv::Sdc::Options Options; + Options.SetVtxBoundaryInterpolation(OpenSubdiv::Sdc::Options::VTX_BOUNDARY_EDGE_ONLY); + + // Setup Data + OpenSubdiv::Far::TopologyDescriptor Descriptor; + Descriptor.numVertices = BaseMesh->GetControlPointsCount(); + Descriptor.numFaces = BaseMesh->GetPolygonCount(); + Descriptor.numVertsPerFace = VertsPerFace.GetData(); + Descriptor.vertIndicesPerFace = VertexIndicesPerFace.GetData(); + + // Create a Refiner + OpenSubdiv::Far::TopologyRefiner* Refiner = OpenSubdiv::Far::TopologyRefinerFactory::Create(Descriptor, + OpenSubdiv::Far::TopologyRefinerFactory::Options(type, Options)); + + // Refine topology + Refiner->RefineUniform(OpenSubdiv::Far::TopologyRefiner::UniformOptions(SubdLevel)); + + // Determine the sizes for our needs: + int CoarseVertsNum = BaseMesh->GetControlPointsCount();; + int FineVertsNum = Refiner->GetLevel(SubdLevel).GetNumVertices(); + int TempVertsNum = Refiner->GetNumVerticesTotal() - CoarseVertsNum; + + // ************************************** + // First Interpolate the vertix positions + + // Get Base Mesh's vertex positions + TArray BasePositionBuffer; + BasePositionBuffer.AddDefaulted(CoarseVertsNum); + FbxVector4* ControlPoints = BaseMesh->GetControlPoints(); + for (int VertexIndex = 0; VertexIndex < CoarseVertsNum; VertexIndex++) + { + BasePositionBuffer[VertexIndex].SetPosition(ControlPoints[VertexIndex]); + } + + // Prepare arrays for in and out data + TArray TempPositionBuffer; + TempPositionBuffer.AddDefaulted(TempVertsNum); + + FDtuOsdVector* SourcePositions = reinterpret_cast(BasePositionBuffer.GetData()); + FDtuOsdVector* DestinationPositions = reinterpret_cast(TempPositionBuffer.GetData()); + + + // Interpolate the positions + OpenSubdiv::Far::PrimvarRefiner primvarRefiner(*Refiner); + + for (int Level = 1; Level <= SubdLevel; ++Level) + { + primvarRefiner.Interpolate(Level, SourcePositions, DestinationPositions); + SourcePositions = DestinationPositions; + DestinationPositions += Refiner->GetLevel(Level).GetNumVertices(); + } + + // ************************************** + // Second Interpolate the new vertex weights + + // Interpolate for each Cluster in the Geometry + FbxGeometry* Geometry = BaseMesh; + int SkinCount = Geometry->GetDeformerCount(FbxDeformer::eSkin); + for (int SkinIndex = 0; SkinIndex < SkinCount; ++SkinIndex) + { + int ClusterCount = ((FbxSkin*)Geometry->GetDeformer(SkinIndex, FbxDeformer::eSkin))->GetClusterCount(); + for (int ClusterIndex = 0; ClusterIndex != ClusterCount; ++ClusterIndex) + { + // Get the Base Cluster weights + FbxCluster* BaseCluster = ((FbxSkin*)Geometry->GetDeformer(SkinIndex, FbxDeformer::eSkin))->GetCluster(ClusterIndex); + int* ControlPointIndices = BaseCluster->GetControlPointIndices(); + double* ControlPointWeights = BaseCluster->GetControlPointWeights(); + + TArray BaseSkinWeightBuffer; + BaseSkinWeightBuffer.AddDefaulted(CoarseVertsNum); + for (int ControlPointIndex = 0; ControlPointIndex < BaseCluster->GetControlPointIndicesCount(); ControlPointIndex++) + { + BaseSkinWeightBuffer[ControlPointIndices[ControlPointIndex]].SetWeight((float)ControlPointWeights[ControlPointIndex]); + } + + // Prepare In and Out Arrays + TArray TempSkinWeightBuffer; + TempSkinWeightBuffer.AddDefaulted(TempVertsNum); + TArray InterpolatedSkinWeightBuffer; + InterpolatedSkinWeightBuffer.AddDefaulted(FineVertsNum); + + // Interpolate skin weights + FDtuOsdSkinWeight* SourceWeights = reinterpret_cast(BaseSkinWeightBuffer.GetData()); + FDtuOsdSkinWeight* DestinationWeights = reinterpret_cast(TempSkinWeightBuffer.GetData()); + for (int Level = 1; Level < SubdLevel; ++Level) + { + primvarRefiner.Interpolate(Level, SourceWeights, DestinationWeights); + SourceWeights = DestinationWeights; + DestinationWeights += Refiner->GetLevel(Level).GetNumVertices(); + } + + // Need to hold onto the last set of weights + primvarRefiner.Interpolate(SubdLevel, SourceWeights, InterpolatedSkinWeightBuffer); + + // The TransferWeightToNode is the node in the HD FBX that's missing the weights. Add the missing weights from the newly interpolated ones. + FbxCluster* TransferCluster = ((FbxSkin*)TranferWeightsToNode->GetMesh()->GetDeformer(SkinIndex, FbxDeformer::eSkin))->GetCluster(ClusterIndex); + int* TransferIndices = TransferCluster->GetControlPointIndices(); + TArray ExistingWeightArray; + for (int TransferControlPointIndex = 0; TransferControlPointIndex < TransferCluster->GetControlPointIndicesCount(); TransferControlPointIndex++) + { + ExistingWeightArray.Add(TransferIndices[TransferControlPointIndex]); + } + + for (int VertexIndex = 0; VertexIndex < FineVertsNum; VertexIndex++) + { + float Weight = InterpolatedSkinWeightBuffer[VertexIndex].GetWeight(); + if (Weight > 0.0f) + { + // If the Vertex already has a weight, keep it. + if (ExistingWeightArray.Contains(VertexIndex)) + { + TransferCluster->GetControlPointWeights()[ExistingWeightArray.Find(VertexIndex)] = Weight; + } + // Otherwise, use the new weight + else + { + TransferCluster->AddControlPointIndex(VertexIndex, Weight); + } + } + } + + } + + } +} \ No newline at end of file diff --git a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealUtils.cpp b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealUtils.cpp index 5539d85..b1392f7 100644 --- a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealUtils.cpp +++ b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Private/DazToUnrealUtils.cpp @@ -1,6 +1,12 @@ #include "DazToUnrealUtils.h" #include "GenericPlatform/GenericPlatformFile.h" #include "Misc/Paths.h" +#include "Engine/SkeletalMesh.h" + + +#include "Engine/StaticMesh.h" +#include "EditorFramework/AssetImportData.h" +#include "Factories/FbxAssetImportData.h" FString FDazToUnrealUtils::SanitizeName(FString OriginalName) { @@ -22,11 +28,42 @@ bool FDazToUnrealUtils::MakeDirectoryAndCheck(FString& Directory) IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); if (!FPaths::DirectoryExists(Directory)) { - PlatformFile.CreateDirectory(*Directory); + PlatformFile.CreateDirectoryTree(*Directory); if (!FPaths::DirectoryExists(Directory)) { return false; } } return true; +} + +bool FDazToUnrealUtils::IsModelFacingX(UObject* MeshObject) +{ + if(USkeletalMesh* SkeletalMesh = Cast(MeshObject)) + { +#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION < 27 + if (UAssetImportData* AssetImportData = SkeletalMesh->AssetImportData) +#else + if (UAssetImportData* AssetImportData = SkeletalMesh->GetAssetImportData()) +#endif + { + UFbxAssetImportData* FbxAssetImportData = Cast(AssetImportData); + if (FbxAssetImportData != nullptr && FbxAssetImportData->bForceFrontXAxis) + { + return true; + } + } + } + if (UStaticMesh* StaticMesh = Cast(MeshObject)) + { + if (UAssetImportData* AssetImportData = StaticMesh->AssetImportData) + { + UFbxAssetImportData* FbxAssetImportData = Cast(AssetImportData); + if (FbxAssetImportData != nullptr && FbxAssetImportData->bForceFrontXAxis) + { + return true; + } + } + } + return false; } \ No newline at end of file diff --git a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnreal.h b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnreal.h index 4c359e7..f03fa20 100644 --- a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnreal.h +++ b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnreal.h @@ -79,7 +79,11 @@ class FDazToUnrealModule : public IModuleInterface FTickerDelegate TickDelegate; /** DelegateHandle for the tick function*/ +#if ENGINE_MAJOR_VERSION > 4 + FTSTicker::FDelegateHandle TickDelegateHandle; +#else FDelegateHandle TickDelegateHandle; +#endif /** Tick function for handling incomming messages from Daz3D*/ bool Tick(float DeltaTime); @@ -95,7 +99,7 @@ class FDazToUnrealModule : public IModuleInterface bool ImportTextureAssets(TArray& SourcePaths, FString& ImportLocation); /** Imports the modified FBX file*/ - UObject* ImportFBXAsset(const FString& SourcePath, const FString& ImportLocation, const DazAssetType& AssetType, const DazCharacterType& CharacterType, const FString& CharacterTypeName); + UObject* ImportFBXAsset(const FString& SourcePath, const FString& ImportLocation, const DazAssetType& AssetType, const DazCharacterType& CharacterType, const FString& CharacterTypeName, const bool bSetPostProcessAnimation); /** Function for creating the Material Instances for the model*/ //bool CreateMaterials(const FString CharacterMaterialFolder, const FString CharacterTexturesFolder, const TArray& MaterialNames, TMap> MaterialProperties, const DazCharacterType CharacterType); diff --git a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnrealFbx.h b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnrealFbx.h index 45d83af..433f1bb 100644 --- a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnrealFbx.h +++ b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnrealFbx.h @@ -61,6 +61,7 @@ class FDazToUnrealFbx public: static void RenameDuplicateBones(FbxNode* RootNode); static void FixClusterTranformLinks(FbxScene* Scene, FbxNode* RootNode); + static void RemoveBindPoses(FbxScene* Scene); static void AddWeightsToAllNodes(FbxNode* Parent); private: static void RenameDuplicateBones(FbxNode* RootNode, TMap& ExistingBones); diff --git a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnrealMorphs.h b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnrealMorphs.h new file mode 100644 index 0000000..4425748 --- /dev/null +++ b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnrealMorphs.h @@ -0,0 +1,28 @@ +#pragma once + +#include "CoreMinimal.h" +#include "DazToUnrealEnums.h" +#include "Dom/JsonObject.h" +#include "DazJointControlledMorphAnimInstance.h" + +class UDazJointControlledMorphAnimInstance; +class USkeleton; +class USkeletalMesh; + +class FDazToUnrealMorphs +{ +public: + // Called to create the JCM AnimInstance + static UDazJointControlledMorphAnimInstance* CreateJointControlAnimation(TSharedPtr JsonObject, FString Folder, FString CharacterName, USkeleton* Skeleton, USkeletalMesh* Mesh); + + // Returns whether the DTU file contains data for AutoJCM + static bool IsAutoJCMImport(TSharedPtr JsonObject); + +private: + + // Internal function for creating the AnimBlueprint for the AnimInstance + static UAnimBlueprint* CreateBlueprint(UObject* InParent, FName Name, USkeleton* Skeleton); + + // Internal function for creating the AnimBlueprint for the AnimInstance + static void FakeDualQuarternion(FName MorphName, FName BoneName, EDazMorphAnimInstanceDriver Axis, float MinBend, float MaxBend, USkeletalMesh* Mesh); +}; \ No newline at end of file diff --git a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnrealSubdivision.h b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnrealSubdivision.h new file mode 100644 index 0000000..e69b3dc --- /dev/null +++ b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnrealSubdivision.h @@ -0,0 +1,63 @@ +#pragma once + +#include "CoreMinimal.h" +#include "DazToUnrealEnums.h" + +// NOTE: This FBX include code was copied from FbxImporter.h +// Temporarily disable a few warnings due to virtual function abuse in FBX source files +#pragma warning( push ) + +#pragma warning( disable : 4263 ) // 'function' : member function does not override any base class virtual member function +#pragma warning( disable : 4264 ) // 'virtual_function' : no override available for virtual member function from base 'class'; function is hidden + +// Include the fbx sdk header +// temp undef/redef of _O_RDONLY because kfbxcache.h (included by fbxsdk.h) does +// a weird use of these identifiers inside an enum. +#ifdef _O_RDONLY +#define TMP_UNFBX_BACKUP_O_RDONLY _O_RDONLY +#define TMP_UNFBX_BACKUP_O_WRONLY _O_WRONLY +#undef _O_RDONLY +#undef _O_WRONLY +#endif + +//Robert G. : Packing was only set for the 64bits platform, but we also need it for 32bits. +//This was found while trying to trace a loop that iterate through all character links. +//The memory didn't match what the debugger displayed, obviously since the packing was not right. +#pragma pack(push,8) + +#if PLATFORM_WINDOWS +// _CRT_SECURE_NO_DEPRECATE is defined but is not enough to suppress the deprecation +// warning for vsprintf and stricmp in VS2010. Since FBX is able to properly handle the non-deprecated +// versions on the appropriate platforms, _CRT_SECURE_NO_DEPRECATE is temporarily undefined before +// including the FBX headers + +// The following is a hack to make the FBX header files compile correctly under Visual Studio 2012 and Visual Studio 2013 +#if _MSC_VER >= 1700 +#define FBX_DLL_MSC_VER 1600 +#endif + + +#endif // PLATFORM_WINDOWS + +// FBX casts null pointer to a reference +THIRD_PARTY_INCLUDES_START +#include +THIRD_PARTY_INCLUDES_END + +#pragma pack(pop) + +#ifdef TMP_UNFBX_BACKUP_O_RDONLY +#define _O_RDONLY TMP_FBX_BACKUP_O_RDONLY +#define _O_WRONLY TMP_FBX_BACKUP_O_WRONLY +#undef TMP_UNFBX_BACKUP_O_RDONLY +#undef TMP_UNFBX_BACKUP_O_WRONLY +#endif + +#pragma warning( pop ) +// end of fbx include + +class FDazToUnrealSubdivision +{ +public: + static void SubdivideMesh(FbxNode* BaseNode, FbxNode* TranferWeightsToNode, int SubdLevel); +}; \ No newline at end of file diff --git a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnrealUtils.h b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnrealUtils.h index cfccc20..cb6f3d5 100644 --- a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnrealUtils.h +++ b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnreal/Public/DazToUnrealUtils.h @@ -8,4 +8,5 @@ class FDazToUnrealUtils public: static FString SanitizeName(FString OriginalName); static bool MakeDirectoryAndCheck(FString& Directory); + static bool IsModelFacingX(UObject* MeshObject); }; \ No newline at end of file diff --git a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnrealRuntime/Private/DazJointControlledMorphAnimInstance.cpp b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnrealRuntime/Private/DazJointControlledMorphAnimInstance.cpp new file mode 100644 index 0000000..0a21895 --- /dev/null +++ b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnrealRuntime/Private/DazJointControlledMorphAnimInstance.cpp @@ -0,0 +1,324 @@ +#include "DazJointControlledMorphAnimInstance.h" +#include "Animation/AnimInstanceProxy.h" +#include "Kismet/KismetMathLibrary.h" + +void UDazJointControlledMorphAnimInstance::NativeInitializeAnimation() +{ + Super::NativeInitializeAnimation(); + +} +void UDazJointControlledMorphAnimInstance::NativeUpdateAnimation(float DeltaTime) +{ + Super::NativeUpdateAnimation(DeltaTime); + + +} + +FAnimInstanceProxy* UDazJointControlledMorphAnimInstance::CreateAnimInstanceProxy() +{ + return new FDazJointControlledMorphAnimInstanceProxy(this); +} + +void FDazJointControlledMorphAnimInstanceProxy::Initialize(UAnimInstance* InAnimInstance) +{ + Super::Initialize(InAnimInstance); + if (UDazJointControlledMorphAnimInstance* Instance = Cast(GetAnimInstanceObject())) + { + ControlLinks = Instance->ControlLinks; + } +} + +void FDazJointControlledMorphAnimInstanceProxy::PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds) +{ + Super::PreUpdate(InAnimInstance, DeltaSeconds); + + if (UDazJointControlledMorphAnimInstance* Instance = Cast(GetAnimInstanceObject())) + { + if (ControlLinks.Num() != Instance->ControlLinks.Num()) + { + ControlLinks = Instance->ControlLinks; + } + } +} + +bool FDazJointControlledMorphAnimInstanceProxy::Evaluate(FPoseContext& Output) +{ + EvaluateAnimationNode(Output); + for (FDazJointControlLink Link : ControlLinks) + { + ProcessLink(Output, Link, Link.PrimaryAxis, Link.SecondaryAxis); + } + + return true; +} + +void FDazJointControlledMorphAnimInstanceProxy::ProcessLink(FPoseContext& Output, FDazJointControlLink Link, EDazMorphAnimInstanceDriver Driver, EDazMorphAnimInstanceDriver SecondaryDriver) +{ + // Get the Local space transform and the ref pose transform to see how the transform for the source bone has changed + const FBoneContainer& BoneContainer = Output.Pose.GetBoneContainer(); + int32 BoneIndex = BoneContainer.GetPoseBoneIndexForBoneName(Link.BoneName); + if (BoneIndex == INDEX_NONE) return; + const FTransform& SourceRefPoseBoneTransform = BoneContainer.GetRefPoseArray()[BoneIndex]; + FCompactPoseBoneIndex CompactBoneIndex = BoneContainer.GetCompactPoseIndexFromSkeletonIndex(BoneIndex); + //Output.Pose. + const FTransform SourceCurrentBoneTransform = Output.Pose[CompactBoneIndex];// .GetLocalSpaceTransform(SourceBone.GetCompactPoseIndex(BoneContainer)); + + float FinalDriverValue = 0.0f; + float BoneBendAngle = ExtractSourceValue(SourceCurrentBoneTransform, SourceRefPoseBoneTransform, Driver, SecondaryDriver); + if (Link.Keys.Num() == 0) + { + FinalDriverValue = BoneBendAngle * Link.Scalar; + FinalDriverValue = FMath::Clamp(FinalDriverValue, 0.0f, 1.0f); + } + else + { + if (Link.Keys.Num() > 1) + { + float FirstAngle = Link.Keys[0].BoneRotation; + float LastAngle = Link.Keys[Link.Keys.Num() - 1].BoneRotation; + float FirstValue = Link.Keys[0].MorphAlpha; + float LastValue = Link.Keys[Link.Keys.Num() - 1].MorphAlpha; + FinalDriverValue = UKismetMathLibrary::MapRangeClamped(BoneBendAngle, FirstAngle, LastAngle, FirstValue, LastValue); + + for (int32 KeyIndex = 1; KeyIndex < Link.Keys.Num(); KeyIndex++) + { + float StartAngle = Link.Keys[KeyIndex - 1].BoneRotation; + float StartValue = Link.Keys[KeyIndex - 1].MorphAlpha; + float StopAngle = Link.Keys[KeyIndex].BoneRotation; + float StopValue = Link.Keys[KeyIndex].MorphAlpha; + float AngleRange = FMath::Abs(StopValue - StartAngle); + + + if (StartAngle < BoneBendAngle && BoneBendAngle < StopAngle) + { + FinalDriverValue = UKismetMathLibrary::MapRangeClamped(BoneBendAngle, StartAngle, StopAngle, StartValue, StopValue); + //FinalDriverValue = FMath::Lerp(StartValue, StopValue, (BoneBendAngle - StartAngle) / AngleRange); + } + if (StartAngle > BoneBendAngle && BoneBendAngle > StopAngle) + { + FinalDriverValue = UKismetMathLibrary::MapRangeClamped(BoneBendAngle, StartAngle, StopAngle, StartValue, StopValue); + //FinalDriverValue = FMath::Lerp(StartValue, StopValue, (BoneBendAngle - StartAngle) / AngleRange); + } + } + } + } + + USkeleton* ProxySkeleton = Output.AnimInstanceProxy->GetSkeleton(); + SmartName::UID_Type NameUID = ProxySkeleton->GetUIDByName(USkeleton::AnimCurveMappingName, FName(Link.MorphName)); + if (NameUID != SmartName::MaxUID) + { + Output.Curve.Set(NameUID, FinalDriverValue); + } +} + + +FDazJointControlledMorphAnimInstanceProxy::~FDazJointControlledMorphAnimInstanceProxy() +{ +} + +const float FDazJointControlledMorphAnimInstanceProxy::ExtractSourceValue(const FTransform& InCurrentBoneTransform, const FTransform& InRefPoseBoneTransform, EDazMorphAnimInstanceDriver Controller, EDazMorphAnimInstanceDriver SecondaryController) +{ + // Resolve source value + float SourceValue = 0.0f; + + + EDazMorphAnimInstanceRotationOrder RotationOrder = EDazMorphAnimInstanceRotationOrder::XYZ; + + if (Controller == EDazMorphAnimInstanceDriver::RotationX) + { + RotationOrder = EDazMorphAnimInstanceRotationOrder::YZX; + if (SecondaryController == EDazMorphAnimInstanceDriver::RotationY) + { + RotationOrder = EDazMorphAnimInstanceRotationOrder::YZX; + } + if (SecondaryController == EDazMorphAnimInstanceDriver::RotationZ) + { + RotationOrder = EDazMorphAnimInstanceRotationOrder::ZYX; + } + } + if (Controller == EDazMorphAnimInstanceDriver::RotationY) + { + RotationOrder = EDazMorphAnimInstanceRotationOrder::XZY; + if (SecondaryController == EDazMorphAnimInstanceDriver::RotationX) + { + RotationOrder = EDazMorphAnimInstanceRotationOrder::XZY; + } + if (SecondaryController == EDazMorphAnimInstanceDriver::RotationZ) + { + RotationOrder = EDazMorphAnimInstanceRotationOrder::ZXY; + } + } + if (Controller == EDazMorphAnimInstanceDriver::RotationZ) + { + RotationOrder = EDazMorphAnimInstanceRotationOrder::YXZ; + if (SecondaryController == EDazMorphAnimInstanceDriver::RotationX) + { + RotationOrder = EDazMorphAnimInstanceRotationOrder::XYZ; + } + if (SecondaryController == EDazMorphAnimInstanceDriver::RotationY) + { + RotationOrder = EDazMorphAnimInstanceRotationOrder::YXZ; + } + } + + + const FVector RotationDiff = EulerFromQuat((InCurrentBoneTransform * InRefPoseBoneTransform.Inverse()).GetRotation(), RotationOrder); + SourceValue = RotationDiff[(int32)Controller - 1]; + if (Controller != EDazMorphAnimInstanceDriver::RotationZ) + { + SourceValue = SourceValue * -1.0f; + } + + + // Determine the resulting value + float FinalDriverValue = SourceValue; + /*if (DrivingCurve != nullptr) + { + // Remap thru the curve if set + FinalDriverValue = DrivingCurve->GetFloatValue(FinalDriverValue); + } + else + { + // Apply the fixed function remapping/clamping + if (bUseRange) + { + const float ClampedAlpha = FMath::Clamp(FMath::GetRangePct(RangeMin, RangeMax, FinalDriverValue), 0.0f, 1.0f); + FinalDriverValue = FMath::Lerp(RemappedMin, RemappedMax, ClampedAlpha); + } + + FinalDriverValue *= Multiplier; + }*/ + + return FinalDriverValue; +} + +// Copied from FControlRigMathLibrary::EulerFromQuat which is in an experimental plugin +FVector FDazJointControlledMorphAnimInstanceProxy::EulerFromQuat(const FQuat& Rotation, EDazMorphAnimInstanceRotationOrder RotationOrder) +{ + float X = Rotation.X; + float Y = Rotation.Y; + float Z = Rotation.Z; + float W = Rotation.W; + float X2 = X * 2.f; + float Y2 = Y * 2.f; + float Z2 = Z * 2.f; + float XX2 = X * X2; + float XY2 = X * Y2; + float XZ2 = X * Z2; + float YX2 = Y * X2; + float YY2 = Y * Y2; + float YZ2 = Y * Z2; + float ZX2 = Z * X2; + float ZY2 = Z * Y2; + float ZZ2 = Z * Z2; + float WX2 = W * X2; + float WY2 = W * Y2; + float WZ2 = W * Z2; + + FVector AxisX, AxisY, AxisZ; + AxisX.X = (1.f - (YY2 + ZZ2)); + AxisY.X = (XY2 + WZ2); + AxisZ.X = (XZ2 - WY2); + AxisX.Y = (XY2 - WZ2); + AxisY.Y = (1.f - (XX2 + ZZ2)); + AxisZ.Y = (YZ2 + WX2); + AxisX.Z = (XZ2 + WY2); + AxisY.Z = (YZ2 - WX2); + AxisZ.Z = (1.f - (XX2 + YY2)); + + FVector Result = FVector::ZeroVector; + + if (RotationOrder == EDazMorphAnimInstanceRotationOrder::XYZ) + { + Result.Y = FMath::Asin(-FMath::Clamp(AxisZ.X, -1.f, 1.f)); + + if (FMath::Abs(AxisZ.X) < 1.f - SMALL_NUMBER) + { + Result.X = FMath::Atan2(AxisZ.Y, AxisZ.Z); + Result.Z = FMath::Atan2(AxisY.X, AxisX.X); + } + else + { + Result.X = 0.f; + Result.Z = FMath::Atan2(-AxisX.Y, AxisY.Y); + } + } + else if (RotationOrder == EDazMorphAnimInstanceRotationOrder::XZY) + { + + Result.Z = FMath::Asin(FMath::Clamp(AxisY.X, -1.f, 1.f)); + + if (FMath::Abs(AxisY.X) < 1.f - SMALL_NUMBER) + { + Result.X = FMath::Atan2(-AxisY.Z, AxisY.Y); + Result.Y = FMath::Atan2(-AxisZ.X, AxisX.X); + } + else + { + Result.X = 0.f; + Result.Y = FMath::Atan2(AxisX.Z, AxisZ.Z); + } + } + else if (RotationOrder == EDazMorphAnimInstanceRotationOrder::YXZ) + { + Result.X = FMath::Asin(FMath::Clamp(AxisZ.Y, -1.f, 1.f)); + + if (FMath::Abs(AxisZ.Y) < 1.f - SMALL_NUMBER) + { + Result.Y = FMath::Atan2(-AxisZ.X, AxisZ.Z); + Result.Z = FMath::Atan2(-AxisX.Y, AxisY.Y); + } + else + { + Result.Y = 0.f; + Result.Z = FMath::Atan2(AxisY.X, AxisX.X); + } + } + else if (RotationOrder == EDazMorphAnimInstanceRotationOrder::YZX) + { + Result.Z = FMath::Asin(-FMath::Clamp(AxisX.Y, -1.f, 1.f)); + + if (FMath::Abs(AxisX.Y) < 1.f - SMALL_NUMBER) + { + Result.X = FMath::Atan2(AxisZ.Y, AxisY.Y); + Result.Y = FMath::Atan2(AxisX.Z, AxisX.X); + } + else + { + Result.X = FMath::Atan2(-AxisY.Z, AxisZ.Z); + Result.Y = 0.f; + } + } + else if (RotationOrder == EDazMorphAnimInstanceRotationOrder::ZXY) + { + Result.X = FMath::Asin(-FMath::Clamp(AxisY.Z, -1.f, 1.f)); + + if (FMath::Abs(AxisY.Z) < 1.f - SMALL_NUMBER) + { + Result.Y = FMath::Atan2(AxisX.Z, AxisZ.Z); + Result.Z = FMath::Atan2(AxisY.X, AxisY.Y); + } + else + { + Result.Y = FMath::Atan2(-AxisZ.X, AxisX.X); + Result.Z = 0.f; + } + } + else if (RotationOrder == EDazMorphAnimInstanceRotationOrder::ZYX) + { + Result.Y = FMath::Asin(FMath::Clamp(AxisX.Z, -1.f, 1.f)); + + if (FMath::Abs(AxisX.Z) < 1.f - SMALL_NUMBER) + { + Result.X = FMath::Atan2(-AxisY.Z, AxisZ.Z); + Result.Z = FMath::Atan2(-AxisX.Y, AxisX.X); + } + else + { + Result.X = FMath::Atan2(AxisZ.Y, AxisY.Y); + Result.Z = 0.f; + } + } + + return Result * 180.f / PI; +} \ No newline at end of file diff --git a/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnrealRuntime/Public/DazJointControlledMorphAnimInstance.h b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnrealRuntime/Public/DazJointControlledMorphAnimInstance.h new file mode 100644 index 0000000..e2e30d5 --- /dev/null +++ b/Unreal/UnrealPlugin/DazToUnreal/Source/DazToUnrealRuntime/Public/DazJointControlledMorphAnimInstance.h @@ -0,0 +1,131 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Animation/AnimInstance.h" +#include "Animation/AnimInstanceProxy.h" +#include "DazJointControlledMorphAnimInstance.generated.h" + +UENUM() +enum class EDazMorphAnimInstanceRotationOrder : uint8 +{ + Auto, + XYZ, + XZY, + YXZ, + YZX, + ZXY, + ZYX +}; + +UENUM() +enum class EDazMorphAnimInstanceDriver : uint8 +{ + None, + RotationX, + RotationY, + RotationZ +}; + +USTRUCT(Blueprintable) +struct DAZTOUNREALRUNTIME_API FDazJointControlLinkKey +{ + GENERATED_USTRUCT_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + float BoneRotation = 0.0f;; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + float MorphAlpha = 0.0f; +}; + +USTRUCT(Blueprintable) +struct DAZTOUNREALRUNTIME_API FDazJointControlLink +{ + GENERATED_USTRUCT_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + FName BoneName; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + EDazMorphAnimInstanceDriver PrimaryAxis = EDazMorphAnimInstanceDriver::RotationY; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + EDazMorphAnimInstanceDriver SecondaryAxis = EDazMorphAnimInstanceDriver::None; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + FName MorphName; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + float Scalar = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + float Alpha = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + TArray Keys; +}; + +/** Proxy override for this UAnimInstance-derived class */ +USTRUCT() +struct DAZTOUNREALRUNTIME_API FDazJointControlledMorphAnimInstanceProxy : public FAnimInstanceProxy +{ + GENERATED_BODY() + +public: + FDazJointControlledMorphAnimInstanceProxy() + { + } + + FDazJointControlledMorphAnimInstanceProxy(UAnimInstance* InAnimInstance) + : FAnimInstanceProxy(InAnimInstance) + { + } + + virtual ~FDazJointControlledMorphAnimInstanceProxy(); + + // FAnimInstanceProxy interface + virtual void Initialize(UAnimInstance* InAnimInstance) override; + virtual bool Evaluate(FPoseContext& Output) override; + //virtual void UpdateAnimationNode(const FAnimationUpdateContext& InContext) override; + virtual void PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds) override; + + //TMap StoredTransforms; + //TMap StoredCurves; + +private: + //TArray XRotation; + //TArray YRotation; + //TArray ZRotation; + TArray ControlLinks; + + const float ExtractSourceValue(const FTransform& InCurrentBoneTransform, const FTransform& InRefPoseBoneTransform, EDazMorphAnimInstanceDriver Controller, EDazMorphAnimInstanceDriver SecondaryController); + FVector EulerFromQuat(const FQuat& Rotation, EDazMorphAnimInstanceRotationOrder RotationOrder); + void ProcessLink(FPoseContext& Output, FDazJointControlLink Link, EDazMorphAnimInstanceDriver Driver, EDazMorphAnimInstanceDriver SecondaryDriver); +}; + +UCLASS(Blueprintable) +class DAZTOUNREALRUNTIME_API UDazJointControlledMorphAnimInstance : public UAnimInstance +{ + GENERATED_BODY() + +public: + + UDazJointControlledMorphAnimInstance() {}; + virtual void NativeInitializeAnimation() override; + virtual void NativeUpdateAnimation(float DeltaTime) override; + + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + TArray ControlLinks; + +private: + //TArray XRotation; + //TArray YRotation; + //TArray ZRotation; + +protected: + + // UAnimInstance interface + virtual FAnimInstanceProxy* CreateAnimInstanceProxy() override; + +}; \ No newline at end of file