1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
3 #include "SceneModel.h"
4 #include "Scene/SceneElementSourceNode.h"
5 #include "Scene/SceneElementPhysProxies.h"
6 #include "Scene/SceneElementProxyGeom.h"
7 #include "Scene/SceneElementSkin.h"
8 #include "Scene/SceneElementTypes.h"
9 #include "Scene/Scene.h"
10 #include "SceneUserData.h"
11 #include "DialogMesh/DialogMesh_SceneUserData.h"
12 #include "FbxMetaData.h"
18 #include <EditorStyleHelper.h>
23 const FbxTool::ENodeExportSetting kDefaultSceneExportSetting
= FbxTool::eNodeExportSetting_Include
;
25 CItemModelAttributeEnumFunc
s_meshTypeAttribute("Mesh Type", []()
28 strs
.reserve(MAX_STATOBJ_LODS_NUM
+ 1);
29 for (int i
= 0; i
< MAX_STATOBJ_LODS_NUM
; ++i
)
31 strs
.push_back(QString("LOD %1").arg(i
));
33 strs
.push_back(QStringLiteral("Physics Proxy"));
39 CSceneModel::CSceneModel()
41 , m_pExportIcon(new CryIcon())
42 , m_pSceneUserData(nullptr)
44 QPixmap enabledIcon
= QStringLiteral("icons:General/Tick_On.ico");
45 QPixmap disabledIcon
= QStringLiteral("icons:General/Tick_Off.ico");
46 assert(enabledIcon
.width() == enabledIcon
.height());
47 assert(disabledIcon
.width() == disabledIcon
.height());
48 assert(enabledIcon
.width() == disabledIcon
.width());
49 m_pExportIcon
->addPixmap(enabledIcon
, CryIcon::Normal
, CryIcon::On
);
50 m_pExportIcon
->addPixmap(disabledIcon
, CryIcon::Normal
, CryIcon::Off
);
51 m_iconDimension
= enabledIcon
.height();
54 CSceneModel::~CSceneModel()
59 static bool IsNotExcluded(CSceneElementSourceNode
* pSceneElement
)
61 const FbxTool::SNode
* pNode
= pSceneElement
->GetNode();
62 const FbxTool::ENodeExportSetting exportSetting
= pNode
->pScene
->GetNodeExportSetting(pNode
);
63 return exportSetting
!= FbxTool::eNodeExportSetting_Exclude
;
66 const FbxMetaData::SSceneUserSettings
& CSceneModel::GetSceneUserSettings() const
68 return m_userSettings
;
71 static bool GetLodIndex(const char* const szName
, const char* szPrefix
, int& lod
)
73 const size_t lodNamePrefixLength
= strlen(szPrefix
);
75 if (strnicmp(szName
, szPrefix
, lodNamePrefixLength
))
80 const int value
= atoi(szName
+ lodNamePrefixLength
);
81 if (value
>= 1 && value
< MAX_STATOBJ_LODS_NUM
)
90 static bool IsSceneRoot(CSceneElementSourceNode
* pSceneElement
)
92 return pSceneElement
->GetNode() && !pSceneElement
->GetNode()->pParent
;
95 static void AutoAssignLods(CScene
& scene
, FbxTool::CScene
* pFbxScene
)
97 for (int i
= 0; i
< scene
.GetElementCount(); ++i
)
99 if (scene
.GetElementByIndex(i
)->GetType() != ESceneElementType::SourceNode
)
104 CSceneElementSourceNode
* const pElement
= (CSceneElementSourceNode
*)scene
.GetElementByIndex(i
);
106 if (!pElement
->IsLeaf())
113 // Try to match 3ds Max convention:
114 // $lod{N}[name] is N-th LOD of parent.
115 if (GetLodIndex(pElement
->GetName().c_str(), "$lod", lod
))
117 pFbxScene
->SetNodeLod(pElement
->GetNode(), lod
);
120 // Try to match Maya convention:
121 // single child of node with name _lod{N}_name_group is N-th LOD of parent.
122 if (pElement
->GetParent() && GetLodIndex(pElement
->GetParent()->GetName().c_str(), "_lod", lod
))
124 pFbxScene
->SetNodeLod(pElement
->GetNode(), lod
);
129 void CSceneModel::SetScene(FbxTool::CScene
* pScene
, const FbxMetaData::SSceneUserSettings
& userSettings
)
131 CSceneModelCommon::SetScene(pScene
);
133 m_userSettings
= userSettings
;
135 for (int i
= 0; i
< GetElementCount(); ++i
)
137 if (GetElementById(i
)->GetType() != ESceneElementType::SourceNode
)
142 CSceneElementSourceNode
* const pElement
= (CSceneElementSourceNode
*)GetElementById(i
);
143 const FbxTool::SNode
* const pNode
= pElement
->GetNode();
145 pScene
->SetNodeExportSetting(pNode
, GetDefaultNodeExportSetting(pNode
));
148 AutoAssignLods(*GetModelScene(), GetScene());
151 void CSceneModel::SetSceneUserData(DialogMesh::CSceneUserData
* pSceneUserData
)
153 m_pSceneUserData
= pSceneUserData
;
156 void CSceneModel::SetExportSetting(const QModelIndex
& index
, FbxTool::ENodeExportSetting value
)
158 setData(index
, (int)value
, eRoleType_Export
);
161 static void ResetExportSettingsInSubtree(FbxTool::CScene
* pScene
, const FbxTool::SNode
* pFbxNode
)
163 pScene
->SetNodeExportSetting(pFbxNode
, FbxTool::eNodeExportSetting_Include
);
165 for (int i
= 0; i
< pFbxNode
->numChildren
; ++i
)
167 ResetExportSettingsInSubtree(pScene
, pFbxNode
->ppChildren
[i
]);
171 void CSceneModel::ResetExportSettingsInSubtree(const QModelIndex
& index
)
173 CSceneElementCommon
* const pSelf
= GetSceneElementFromModelIndex(index
);
174 if (!pSelf
|| pSelf
->GetType() != ESceneElementType::SourceNode
)
179 CSceneElementSourceNode
* const pSourceNodeElement
= (CSceneElementSourceNode
*)pSelf
;
180 CRY_ASSERT(IsSceneRoot(pSourceNodeElement
));
182 // Reset settings of this LOD's FBX root element.
183 ::ResetExportSettingsInSubtree(GetScene(), pSourceNodeElement
->GetNode());
188 void CSceneModel::OnDataSerialized(CSceneElementCommon
* pElement
, bool bChanged
)
192 QModelIndex index
= GetModelIndexFromSceneElement(pElement
);
193 assert(index
.isValid());
194 const QModelIndex firstChanged
= sibling(index
.row(), 0, index
);
195 const QModelIndex lastChanged
= sibling(index
.row(), columnCount(index
) - 1, index
);
196 dataChanged(firstChanged
, lastChanged
);
200 int CSceneModel::columnCount(const QModelIndex
& index
) const
202 return eColumnType_COUNT
;
205 static FbxTool::ENodeExportSetting
EvaluateExportSetting(const FbxTool::CScene
* pFbxScene
, const FbxTool::SNode
* pFbxNode
)
207 return pFbxScene
->IsNodeIncluded(pFbxNode
) ? FbxTool::eNodeExportSetting_Include
: FbxTool::eNodeExportSetting_Exclude
;
210 void CSceneModel::GetCheckStateRole(CSceneElementSourceNode
* pSelf
, EState
& state
, bool& bPartial
) const
212 const CSceneElementSourceNode
* pParent
= nullptr;
213 if (pSelf
->GetParent() && pSelf
->GetParent()->GetType() == ESceneElementType::SourceNode
)
215 pParent
= (CSceneElementSourceNode
*)pSelf
->GetParent();
218 if (EvaluateExportSetting(GetScene(), pSelf
->GetNode()) == FbxTool::eNodeExportSetting_Include
)
220 state
= eState_Enabled_Included
;
222 else if (pParent
&& EvaluateExportSetting(GetScene(), pParent
->GetNode()) == FbxTool::eNodeExportSetting_Include
)
224 state
= eState_Enabled_Excluded
;
226 else if (GetScene()->GetNodeExportSetting(pSelf
->GetNode()) == FbxTool::eNodeExportSetting_Include
)
228 state
= eState_Disabled_Included
;
232 state
= eState_Disabled_Excluded
;
237 // TODO: Compute this for all elements in a single pass.
238 std::vector
<const CSceneElementCommon
*> elementStack
;
239 elementStack
.push_back(pSelf
);
240 while (!elementStack
.empty() && !bPartial
)
242 const CSceneElementCommon
* pElement
= elementStack
.back();
243 elementStack
.pop_back();
245 if (pElement
->GetType() == ESceneElementType::SourceNode
)
247 const FbxTool::SNode
* const pNode
= ((CSceneElementSourceNode
*)pElement
)->GetNode();
248 if (GetScene()->GetNodeExportSetting(pNode
) == FbxTool::eNodeExportSetting_Exclude
)
254 for (int i
= 0; i
< pElement
->GetNumChildren(); ++i
)
256 elementStack
.push_back(pElement
->GetChild(i
));
262 bool CanBeLod(const FbxTool::SNode
* pNode
)
266 !pNode
->numChildren
&&
267 EvaluateExportSetting(pNode
->pScene
, pNode
) == FbxTool::eNodeExportSetting_Include
&&
272 QVariant
CreateSerializationVariant(const T
& t
)
274 std::unique_ptr
<Serialization::SStruct
> sstruct(new Serialization::SStruct(t
));
275 return QVariant::fromValue((void*)sstruct
.release());
278 QVariant
CSceneModel::GetSourceNodeData(CSceneElementSourceNode
* pSelf
, const QModelIndex
& index
, int role
) const
280 if (role
== eItemDataRole_YasliSStruct
)
282 return CreateSerializationVariant(*pSelf
);
285 const FbxTool::SNode
* const pNode
= pSelf
->GetNode();
289 GetCheckStateRole(pSelf
, state
, bPartial
);
291 switch (index
.column())
293 case eColumnType_Name
:
294 if (role
== Qt::DisplayRole
)
296 return QtUtil::ToQString(pSelf
->GetName());
298 else if (role
== Qt::CheckStateRole
)
300 if (m_pSceneUserData
->GetSelectedSkin())
305 if (state
== eState_Enabled_Included
|| state
== eState_Disabled_Included
)
307 return bPartial
? Qt::PartiallyChecked
: Qt::Checked
;
309 else if (state
== eState_Enabled_Excluded
|| state
== eState_Disabled_Excluded
)
311 return Qt::Unchecked
;
314 else if (role
== Qt::ForegroundRole
)
316 if (state
== eState_Disabled_Included
|| state
== eState_Disabled_Excluded
)
318 return QBrush(GetStyleHelper()->disabledTextColor());
322 return QBrush(GetStyleHelper()->textColor());
326 case eColumnType_Type
:
327 if (role
== Qt::EditRole
&& CanBeLod(pNode
))
329 return GetScene()->IsProxy(pNode
) ? MAX_STATOBJ_LODS_NUM
: GetScene()->GetNodeLod(pNode
);
331 else if (role
== Qt::DisplayRole
&& CanBeLod(pNode
))
333 return QString("LOD %1").arg(GetScene()->IsProxy(pNode
) ? MAX_STATOBJ_LODS_NUM
: GetScene()->GetNodeLod(pNode
));
335 else if (role
== Qt::DisplayRole
&& !IsSceneRoot(pSelf
) && !CanBeLod(pNode
))
337 return "LOD 0"; // Just display lod, non-editable.
340 case eColumnType_SourceNodeAttribute
:
341 if (role
== Qt::DisplayRole
&& !pNode
->namedAttributes
.empty())
343 return QtUtil::ToQString(pNode
->namedAttributes
[0]);
350 if (role
== Qt::ToolTipRole
)
352 return GetToolTipForColumn(index
.column());
355 return CSceneModelCommon::data(index
, role
);
358 bool IsSkinElementSelected(DialogMesh::CSceneUserData
* pSceneUserData
, CSceneElementSkin
* pSkinElement
)
360 CSceneElementSourceNode
* const pSourceNodeElement
= (CSceneElementSourceNode
*)pSkinElement
->GetParent();
361 return pSceneUserData
->GetSelectedSkin() && pSourceNodeElement
->GetNode() == pSceneUserData
->GetSelectedSkin()->pNode
;
364 QVariant
CSceneModel::GetSkinData(CSceneElementSkin
* pSelf
, const QModelIndex
& index
, int role
) const
366 switch (index
.column())
368 case eColumnType_Name
:
369 if (role
== Qt::DisplayRole
)
371 return QtUtil::ToQString(pSelf
->GetName());
373 else if (role
== Qt::CheckStateRole
)
375 if (IsSkinElementSelected(m_pSceneUserData
, pSelf
))
381 return Qt::Unchecked
;
385 case eColumnType_Type
:
386 if (role
== Qt::DisplayRole
)
394 if (role
== Qt::ToolTipRole
)
396 return GetToolTipForColumn(index
.column());
399 return CSceneModelCommon::data(index
, role
);
402 QVariant
CSceneModel::data(const QModelIndex
& index
, int role
) const
404 if (!index
.isValid())
409 CSceneElementCommon
* const pSelf
= GetSceneElementFromModelIndex(index
);
412 if (pSelf
->GetType() == ESceneElementType::SourceNode
)
414 return GetSourceNodeData((CSceneElementSourceNode
*)pSelf
, index
, role
);
416 else if(pSelf
->GetType() == ESceneElementType::Skin
)
418 return GetSkinData((CSceneElementSkin
*)pSelf
, index
, role
);
420 else if (pSelf
->GetType() == ESceneElementType::ProxyGeom
|| pSelf
->GetType() == ESceneElementType::PhysProxy
)
422 if (role
== eItemDataRole_YasliSStruct
)
424 if (pSelf
->GetType() == ESceneElementType::PhysProxy
)
426 return CreateSerializationVariant(*(CSceneElementPhysProxies
*)pSelf
);
430 return CreateSerializationVariant(*(CSceneElementProxyGeom
*)pSelf
);
433 else if (index
.column() == eColumnType_Name
&& role
== Qt::DisplayRole
)
435 return QtUtil::ToQString(pSelf
->GetName());
438 return CSceneModelCommon::data(index
, role
);
441 bool CSceneModel::SetDataSourceNodeElement(const QModelIndex
& index
, const QVariant
& value
, int role
)
443 CSceneElementSourceNode
* const pSourceNodeElement
= (CSceneElementSourceNode
*)GetSceneElementFromModelIndex(index
);
445 const FbxTool::SNode
* const pNode
= pSourceNodeElement
->GetNode();
447 if (role
== Qt::CheckStateRole
)
449 const auto checkState
= (Qt::CheckState
)value
.toUInt();
451 if (GetScene()->GetNodeExportSetting(pNode
) == FbxTool::eNodeExportSetting_Include
&& checkState
== Qt::Checked
)
453 GetScene()->SetNodeExportSetting(pNode
, FbxTool::eNodeExportSetting_Exclude
);
457 const auto exportSetting
=
458 checkState
== Qt::Checked
459 ? FbxTool::eNodeExportSetting_Include
460 : FbxTool::eNodeExportSetting_Exclude
;
461 GetScene()->SetNodeExportSetting(pNode
, exportSetting
);
468 if (role
== eRoleType_Export
)
470 GetScene()->SetNodeExportSetting(pNode
, static_cast<FbxTool::ENodeExportSetting
>(value
.value
<int>()));
476 else if (index
.column() == eColumnType_Type
&& role
== Qt::EditRole
)
478 const int i
= value
.toInt();
479 if (i
< MAX_STATOBJ_LODS_NUM
)
481 GetScene()->SetNodeLod(pNode
, i
);
482 GetScene()->SetProxy(pNode
, false);
486 GetScene()->SetNodeLod(pNode
, 0);
487 GetScene()->SetProxy(pNode
, true);
490 const QModelIndex firstChanged
= sibling(index
.row(), 0, index
);
491 const QModelIndex lastChanged
= sibling(index
.row(), eColumnType_COUNT
- 1, index
);
492 dataChanged(firstChanged
, lastChanged
);
499 bool CSceneModel::SetDataSkinElement(const QModelIndex
& index
, const QVariant
& value
, int role
)
501 CSceneElementSkin
* const pSkinElement
= (CSceneElementSkin
*)GetSceneElementFromModelIndex(index
);
503 CRY_ASSERT(pSkinElement
->GetParent() && pSkinElement
->GetParent()->GetType() == ESceneElementType::SourceNode
);
504 CSceneElementSourceNode
* const pSourceNodeElement
= (CSceneElementSourceNode
*)pSkinElement
->GetParent();
506 const FbxTool::SNode
* const pNode
= pSourceNodeElement
->GetNode();
508 if (role
== Qt::CheckStateRole
)
510 const auto checkState
= (Qt::CheckState
)value
.toUInt();
512 if (checkState
== Qt::Checked
)
514 m_pSceneUserData
->SelectAnySkin(pNode
);
518 m_pSceneUserData
->DeselectSkin();
529 bool CSceneModel::setData(const QModelIndex
& index
, const QVariant
& value
, int role
)
531 if (!index
.isValid())
536 CSceneElementCommon
* const pElement
= GetSceneElementFromModelIndex(index
);
537 CRY_ASSERT(pElement
);
539 if (pElement
->GetType() == ESceneElementType::SourceNode
)
541 return SetDataSourceNodeElement(index
, value
, role
);
543 else if (pElement
->GetType() == ESceneElementType::Skin
)
545 return SetDataSkinElement(index
, value
, role
);
551 CItemModelAttribute
* CSceneModel::GetColumnAttribute(int col
) const
555 case eColumnType_Name
:
556 return &Attributes::s_nameAttribute
;
557 case eColumnType_Type
:
558 return &s_meshTypeAttribute
;
559 case eColumnType_SourceNodeAttribute
:
560 return GetSourceNodeAttributeAttribute();
566 QVariant
CSceneModel::headerData(int column
, Qt::Orientation orientation
, int role
) const
570 case eColumnType_Name
:
571 if (role
== Qt::DisplayRole
)
576 case eColumnType_Type
:
577 if (role
== Qt::DisplayRole
)
582 case eColumnType_SourceNodeAttribute
:
583 if (role
== Qt::DisplayRole
)
585 return tr("Attribute");
591 if (role
== Qt::ToolTipRole
)
593 return GetToolTipForColumn(column
);
597 if (role
== Attributes::s_getAttributeRole
)
599 CItemModelAttribute
* const pAttribute
= GetColumnAttribute(column
);
602 return QVariant::fromValue(pAttribute
);
609 Qt::ItemFlags
CSceneModel::flags(const QModelIndex
& modelIndex
) const
611 if (!modelIndex
.isValid())
613 return CSceneModelCommon::flags(modelIndex
);
616 const CSceneElementCommon
* const pSelf
= GetSceneElementFromModelIndex(modelIndex
);
619 bool bCanBeLod
= false;
621 if (pSelf
->GetType() == ESceneElementType::SourceNode
)
623 bCanBeLod
= CanBeLod(((CSceneElementSourceNode
*)pSelf
)->GetNode());
626 if (modelIndex
.column() == eColumnType_Type
&& bCanBeLod
)
628 return Qt::ItemIsEditable
| QAbstractItemModel::flags(modelIndex
);
630 else if (modelIndex
.column() == eColumnType_Name
)
632 return Qt::ItemIsUserCheckable
| QAbstractItemModel::flags(modelIndex
);
636 return QAbstractItemModel::flags(modelIndex
);
640 QVariant
CSceneModel::GetToolTipForColumn(int column
)
644 case eColumnType_Name
:
645 return tr("Name of the node in the scene");
646 case eColumnType_Type
:
647 return tr("Type of this node");
648 case eColumnType_SourceNodeAttribute
:
649 return tr("Attributes of this node");