Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+315
View File
@@ -0,0 +1,315 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_MATERIAL_MATERIAL_H
#define CRYINCLUDE_EDITOR_MATERIAL_MATERIAL_H
#pragma once
#include <IMaterial.h>
#include <IRenderer.h>
#include "BaseLibraryItem.h"
#include "Include/IEditorMaterial.h"
#include "Util/Variable.h"
#include <vector>
// forward declarations,
class CMaterialManager;
class CVarBlock;
enum eMTL_PROPAGATION
{
MTL_PROPAGATE_OPACITY = 1 << 0,
MTL_PROPAGATE_LIGHTING = 1 << 1,
MTL_PROPAGATE_ADVANCED = 1 << 2,
MTL_PROPAGATE_TEXTURES = 1 << 3,
MTL_PROPAGATE_SHADER_PARAMS = 1 << 4,
MTL_PROPAGATE_SHADER_GEN = 1 << 5,
MTL_PROPAGATE_VERTEX_DEF = 1 << 6,
MTL_PROPAGATE_LAYER_PRESETS = 1 << 7,
MTL_PROPAGATE_MATERIAL_SETTINGS = 1 << 8,
MTL_PROPAGATE_ALL = (
MTL_PROPAGATE_OPACITY |
MTL_PROPAGATE_LIGHTING |
MTL_PROPAGATE_ADVANCED |
MTL_PROPAGATE_TEXTURES |
MTL_PROPAGATE_SHADER_PARAMS |
MTL_PROPAGATE_SHADER_GEN |
MTL_PROPAGATE_VERTEX_DEF |
MTL_PROPAGATE_LAYER_PRESETS |
MTL_PROPAGATE_MATERIAL_SETTINGS),
MTL_PROPAGATE_RESERVED = 1 << 9
};
/** CMaterial class
Every Material is a member of material library.
Materials can have child sub materials,
Sub materials are applied to the same geometry of the parent material in the other material slots.
*/
struct SMaterialLayerResources
{
SMaterialLayerResources()
: m_nFlags(MTL_LAYER_USAGE_REPLACEBASE)
, m_bRegetPublicParams(true)
, m_pMatLayer(0)
{
}
uint8 m_nFlags;
bool m_bRegetPublicParams;
QString m_shaderName;
_smart_ptr< IMaterialLayer > m_pMatLayer;
SInputShaderResources m_shaderResources;
XmlNodeRef m_publicVarsCache;
};
AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING
class CRYEDIT_API CMaterial
: public IEditorMaterial
{
AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
public:
//////////////////////////////////////////////////////////////////////////
CMaterial(const QString& name, int nFlags = 0);
CMaterial(const CMaterial& rhs);
~CMaterial();
virtual EDataBaseItemType GetType() const { return EDB_TYPE_MATERIAL; };
void SetName(const QString& name);
//////////////////////////////////////////////////////////////////////////
QString GetFullName() const { return m_name; };
//////////////////////////////////////////////////////////////////////////
// File properties of the material.
//////////////////////////////////////////////////////////////////////////
QString GetFilename() const;
//! Collect filenames of texture sources used in material
//! Return number of filenames
int GetTextureFilenames(QStringList& outFilenames) const;
int GetAnyTextureFilenames(QStringList& outFilenames) const;
void UpdateFileAttributes(bool useSourceControl = true);
uint32 GetFileAttributes();
//////////////////////////////////////////////////////////////////////////
//! Sets one or more material flags from EMaterialFlags enum.
void SetFlags(int flags) { m_mtlFlags = flags; };
//! Query this material flags.
virtual int GetFlags() const { return m_mtlFlags; }
bool IsMultiSubMaterial() const { return (m_mtlFlags & MTL_FLAG_MULTI_SUBMTL) != 0; };
bool IsPureChild() const { return (m_mtlFlags & MTL_FLAG_PURE_CHILD) != 0; }
// Check if material is used.
bool IsUsed() const { /*return m_nUseCount > 0 || (m_mtlFlags & MTL_FLAG_ALWAYS_USED);*/ return true; };
virtual void GatherUsedResources(CUsedResources& resources);
//! Set name of shader used by this material.
void SetShaderName(const QString& shaderName);
//! Get name of shader used by this material.
QString GetShaderName() const { return m_shaderName; };
virtual SInputShaderResources& GetShaderResources() { return m_shaderResources; };
//! Get public parameters of material in variable block.
CVarBlock* GetPublicVars(SInputShaderResources& pShaderResources);
//! Set the shader public param m_script variable into our own m_script, script contains min/max for a given shader param value
void SetShaderParamPublicScript();
//! Sets variable block of public shader parameters.
//! VarBlock must be in same format as returned by GetPublicVars().
void SetPublicVars(CVarBlock* pPublicVars, CMaterial* pMtl);
//! Update names/descriptions in this variable array, return a variable block for replacing
CVarBlock* UpdateTextureNames(CSmartVariableArray textureVars[EFTT_MAX]);
// [Shader System] - Do To: add back with map usage: CVarBlock* UpdateTextureNames(AZStd::unordered_map<ResourceSlotIndex, CSmartVariableArray>& textureVarsMap);
//////////////////////////////////////////////////////////////////////////
CVarBlock* GetShaderGenParamsVars();
void SetShaderGenParamsVars(CVarBlock* pBlock);
uint64 GetShaderGenMask() { return m_nShaderGenMask; }
void SetShaderGenMask(uint64 mask) { m_nShaderGenMask = mask; }
//! Return variable block of shader params.
SShaderItem& GetShaderItem() { return m_shaderItem; };
//! Return material layers resources
SMaterialLayerResources* GetMtlLayerResources() { return m_pMtlLayerResources; };
//! Get texture map usage mask for shader in this material.
unsigned int GetTexmapUsageMask() const;
//! Load new shader.
bool LoadShader();
//! Reload shader, update all shader parameters.
virtual void Update();
// Reload material settings from file.
// NOTICE: The function will remove all the sub-materials and recreate them!
void Reload();
//! Serialize material settings to xml.
virtual void Serialize(SerializeContext& ctx);
//! Assign this material to static geometry.
void AssignToEntity(IRenderNode* pEntity);
//////////////////////////////////////////////////////////////////////////
// Surface types.
//////////////////////////////////////////////////////////////////////////
virtual void SetSurfaceTypeName(const QString& surfaceType);
virtual const QString& GetSurfaceTypeName() const { return m_surfaceType; };
bool IsBreakable2D() const;
//////////////////////////////////////////////////////////////////////////
// Child Sub materials.
//////////////////////////////////////////////////////////////////////////
//! Get number of sub materials childs.
int GetSubMaterialCount() const;
//! Set number of sub materials childs.
void SetSubMaterialCount(int nSubMtlsCount);
//! Get sub material child by index.
CMaterial* GetSubMaterial(int index) const;
//! Find sub material index by name
int FindMaterialIndex(const QString& name);
// Set a material to the sub materials slot.
// Use NULL material pointer to clear slot.
void SetSubMaterial(int nSlot, CMaterial* mtl);
//! Remove all sub materials, does not change number of sub material slots.
void ClearAllSubMaterials();
//! Return pointer to engine material.
virtual _smart_ptr<IMaterial> GetMatInfo(bool bUseExistingEngineMaterial = true);
// Clear stored pointer to engine material.
void ClearMatInfo();
//! Validate materials for errors.
void Validate();
// Check if material file can be modified.
// Will check file attributes if it is not read only.
bool CanModify(bool bSkipReadOnly = true);
// Save material to file.
virtual bool Save(bool bSkipReadOnly = true, const QString& fullPath = "");
// Dummy material is just a placeholder item for materials that have not been found on disk.
void SetDummy(bool bDummy) { m_bDummyMaterial = bDummy; }
bool IsDummy() const { return m_bDummyMaterial != 0; }
// Called by material manager when material selected as a current material.
void OnMakeCurrent();
void SetFromMatInfo(_smart_ptr<IMaterial> pMatInfo);
// Link a submaterial by name (used for value propagation in CMaterialUI)
void LinkToMaterial(const QString& name);
const QString& GetLinkedMaterialName() { return m_linkedMaterial; }
// Return parent material for submaterial
CMaterial* GetParent() const {return m_pParent; }
//! Loads material layers
bool LoadMaterialLayers();
//! Updates material layers
void UpdateMaterialLayers();
void SetHighlightFlags(int highlightFlags);
void UpdateHighlighting();
virtual void DisableHighlightForFrame();
void RecordUndo(const char* sText, bool bForceUpdate = false);
int GetPropagationFlags() const { return m_propagationFlags; }
void SetPropagationFlags(const int flags) { m_propagationFlags = flags; }
bool LayerActivationAllowed() const { return m_allowLayerActivation; }
void SetLayerActivation(bool allowed) { m_allowLayerActivation = allowed; }
uint32 GetDccMaterialHash() const { return m_dccMaterialHash; }
void SetDccMaterialHash(AZ::u32 hash) { m_dccMaterialHash = hash; }
void SetShaderItem(const SShaderItem& shaderItem);
private:
void UpdateMatInfo();
void CheckSpecialConditions();
void NotifyChanged();
private:
//////////////////////////////////////////////////////////////////////////
// Variables.
//////////////////////////////////////////////////////////////////////////
QString m_shaderName;
QString m_surfaceType;
QString m_linkedMaterial;
//! Material flags.
int m_mtlFlags;
// Hash for DCC material attributes, used to check if .dccmtl has changed
// If so, the source .mtl file will need to be rebuilt
uint32 m_dccMaterialHash;
// Parent material, Only valid for Pure Childs.
CMaterial* m_pParent;
//////////////////////////////////////////////////////////////////////////
// Shader resources.
//////////////////////////////////////////////////////////////////////////
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
SShaderItem m_shaderItem;
SInputShaderResources m_shaderResources;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
//CVarBlockPtr m_shaderParamsVar;
//! Common shader flags.
uint64 m_nShaderGenMask;
QString m_pszShaderGenMask;
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
SMaterialLayerResources m_pMtlLayerResources[MTL_LAYER_MAX_SLOTS];
_smart_ptr<IMaterial> m_pMatInfo;
XmlNodeRef m_publicVarsCache;
//! Array of sub materials.
std::vector<_smart_ptr<CMaterial> > m_subMaterials;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
int m_nUseCount;
uint32 m_scFileAttributes;
unsigned char m_highlightFlags;
// The propagation flags are a bit combination of the MTL_PROPAGATION enum above
// and determine which properties get propagated to an optional linked material
// during ui editing
int m_propagationFlags;
//! Material Used in level.
int m_bDummyMaterial : 1; // Dummy material, name specified but material file not found.
int m_bIgnoreNotifyChange : 1; // Do not send notifications about changes.
int m_bRegetPublicParams : 1;
int m_bKeepPublicParamsValues : 1;
bool m_allowLayerActivation;
};
#endif // CRYINCLUDE_EDITOR_MATERIAL_MATERIAL_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,248 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_MATERIAL_MATERIALBROWSER_H
#define CRYINCLUDE_EDITOR_MATERIAL_MATERIALBROWSER_H
#pragma once
#if !defined(Q_MOC_RUN)
#include "Include/IDataBaseManager.h"
#include "Material/Material.h"
#include "Material/MaterialBrowserFilterModel.h"
#include <QMenu>
#include <QDateTime>
#include <QModelIndex>
#include <ISourceControl.h>
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#endif
class CMaterial;
class MaterialBrowserWidget;
class CMaterialImageListCtrl;
class QMaterialImageListModel;
class QTreeView;
class QAction;
struct IDataBaseItem;
class CMaterialBrowserRecord;
typedef std::vector<CMaterialBrowserRecord> TMaterialBrowserRecords;
namespace AzToolsFramework
{
namespace AssetBrowser
{
class AssetBrowserModel;
class AssetBrowserFilterModel;
class AssetTypeFilter;
}
}
enum ESccFileAttributes;
namespace Ui
{
class MaterialBrowser;
}
//////////////////////////////////////////////////////////////////////////
struct IMaterialBrowserListener
{
virtual void OnBrowserSelectItem(IDataBaseItem* pItem, bool bForce) = 0;
};
//////////////////////////////////////////////////////////////////////////
// MaterialBrowserWidget
//////////////////////////////////////////////////////////////////////////
class MaterialBrowserFilterModel;
class MaterialBrowserWidget
: public QWidget
, public IDataBaseManagerListener
, public IEditorNotifyListener
, public MaterialBrowserWidgetBus::Handler
{
Q_OBJECT
public:
enum EViewType
{
VIEW_LEVEL = 0,
VIEW_ALL = 1,
};
enum EFilter
{
eFilter_Materials = 0x01,
eFilter_Textures = 0x02,
eFilter_Materials_And_Textures = 0x03,
eFilter_Submaterials = 0x04
};
MaterialBrowserWidget(QWidget* parent);
~MaterialBrowserWidget();
void SetListener(IMaterialBrowserListener* pListener) { m_pListener = pListener; }
EViewType GetViewType() const { return m_viewType; };
void ClearItems();
void SelectItem(IDataBaseItem* pItem, IDataBaseItem* pParentItem);
void DeleteItem();
void PopulateItems();
void StartRecordUpdateJobs();
bool ShowCheckedOutRecursive(TMaterialBrowserRecords* pRecords);
void ShowOnlyLevelMaterials(bool levelOnly);
void OnCopy();
void OnCopyName();
void OnPaste();
void OnCut();
void OnDuplicate();
void OnAddNewMaterial();
void OnAddNewMultiMaterial();
void OnConvertToMulti();
void OnMergeMaterials();
public slots:
void OnSelectionChanged();
void OnSubMaterialSelectedInPreviewPane(const QModelIndex& current);
void SaveCurrentMaterial();
void OnRefreshSelection();
void OnMaterialAdded();
signals:
void refreshSelection();
void materialAdded();
public:
void OnUpdateShowCheckedOut();
bool CanPaste() const;
void SetImageListCtrl(CMaterialImageListCtrl* pCtrl);
//////////////////////////////////////////////////////////////////////////
// IDataBaseManagerListener implementation.
//////////////////////////////////////////////////////////////////////////
virtual void OnDataBaseItemEvent(IDataBaseItem* pItem, EDataBaseItemEvent event);
//////////////////////////////////////////////////////////////////////////
virtual void OnEditorNotifyEvent(EEditorNotifyEvent event);
void AddContextMenuActionsSingleSelection(QMenu &menu, _smart_ptr<CMaterial> material) const;
void OnContextMenuAction(int command, _smart_ptr<CMaterial> material);
// MaterialBrowserWidgetBus event handlers
void MaterialAddFinished() override;
void MaterialFinishedProcessing(_smart_ptr<CMaterial> material, const QPersistentModelIndex &filterModelIndex) override;
void MaterialRecordUpdateFinished() override;
protected:
// Item definition.
enum ESourceControlOp
{
ESCM_IMPORT,
ESCM_CHECKOUT,
ESCM_UNDO_CHECKOUT,
ESCM_GETLATEST,
ESCM_GETLATESTTEXTURES,
};
void DeleteItem(const CMaterialBrowserRecord &record);
void SetSelectedItem(_smart_ptr<CMaterial> material, const TMaterialBrowserRecords* pMarkedRecords, bool selectInTreeView);
void OnAddSubMtl();
void OnSelectAssignedObjects();
void OnAssignMaterialToSelection();
void OnRenameItem();
void OnResetItem();
void OnSetSubMtlCount(const CMaterialBrowserRecord &record);
void DoSourceControlOp(CMaterialBrowserRecord &record, ESourceControlOp);
void OnMakeSubMtlSlot(const CMaterialBrowserRecord &record);
void OnClearSubMtlSlot(_smart_ptr<CMaterial> subMaterial);
void SetSubMaterial(_smart_ptr<CMaterial> parentMaterial, int slot, _smart_ptr<CMaterial> subMaterial);
void OnSaveToFile(bool bMulti);
void RefreshSelected();
void TickRefreshMaterials();
void TryLoadRecordMaterial(CMaterialBrowserRecord &record);
void ShowContextMenu(const CMaterialBrowserRecord &record, const QPoint& point);
_smart_ptr<CMaterial> GetCurrentMaterial();
uint32 MaterialNameToCrc32(const QString& str);
bool TryGetSelectedRecord(CMaterialBrowserRecord &record);
AZStd::string GetSelectedMaterialID();
private:
void expandAllNotMatchingIndexes(const QModelIndex& parent = QModelIndex());
void ClearImageListControlSelection();
void ClearSelection(QMaterialImageListModel* materialModel);
QMenu *InitializeSearchMenu();
void AddContextMenuActionsMultiSelect(QMenu &menu) const;
void AddContextMenuActionsNoSelection(QMenu &menu) const;
void AddContextMenuActionsSubMaterial(QMenu &menu, _smart_ptr<CMaterial> parentMaterial, _smart_ptr<CMaterial> subMaterial) const;
void AddContextMenuActionsMultiMaterial(QMenu &menu) const;
void AddContextMenuActionsSingleMaterial(QMenu &menu) const;
void AddContextMenuActionsCommon(QMenu &menu, _smart_ptr<CMaterial> material) const;
void AddContextMenuActionsSourceControl(QMenu &menu, _smart_ptr<CMaterial> material, uint32 fileAttributes) const;
QScopedPointer<Ui::MaterialBrowser> m_ui;
AzToolsFramework::AssetBrowser::AssetBrowserModel* m_assetBrowserModel;
QSharedPointer<MaterialBrowserFilterModel> m_filterModel;
int m_selectedSubMaterialIndex = -1;
bool m_bIgnoreSelectionChange;
bool m_bItemsValid;
CMaterialManager* m_pMatMan;
IMaterialBrowserListener* m_pListener;
CMaterialImageListCtrl* m_pMaterialImageListCtrl;
EViewType m_viewType;
bool m_bNeedReload;
bool m_bHighlightMaterial;
uint32 m_timeOfHighlight;
TMaterialBrowserRecords m_markedRecords;
_smart_ptr<CMaterial> m_pLastActiveMultiMaterial;
_smart_ptr<CMaterial> m_delayedSelection;
bool m_bShowOnlyCheckedOut;
QAction* m_cutAction;
QAction* m_copyAction;
QAction* m_pasteAction;
QAction* m_duplicateAction;
QAction* m_deleteAction;
QAction* m_renameItemAction;
QAction* m_addNewMaterialAction;
};
#endif // CRYINCLUDE_EDITOR_MATERIAL_MATERIALBROWSER_H
@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MaterialBrowser</class>
<widget class="QWidget" name="MaterialBrowser">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>250</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="AzToolsFramework::AssetBrowser::SearchWidget" name="m_searchWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>147</width>
<height>22</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="AzToolsFramework::AssetBrowser::AssetBrowserTreeView" name="treeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzToolsFramework::AssetBrowser::SearchWidget</class>
<extends>QWidget</extends>
<header>AzToolsFramework/AssetBrowser/Search/SearchWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzToolsFramework::AssetBrowser::AssetBrowserTreeView</class>
<extends>QTreeView</extends>
<header>AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="MaterialDialog.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,652 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "EditorDefs.h"
#include "MaterialBrowserFilterModel.h"
// AzFramework
#include <AzCore/Jobs/JobFunction.h>
// AzToolsFramework
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/AssetBrowser/EBusFindAssetTypeByName.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
// Editor
#include "MaterialBrowserSearchFilters.h"
#include "MaterialManager.h"
namespace
{
// how often to re-query source control status of an item after querying it, in seconds.
// note that using a source control operation on an item invalidates the cache and will immediately
// refresh its status regardless of this value, so it can be pretty high.
qint64 g_timeRefreshSCCStatus = 60;
}
#define IDC_MATERIAL_TREECTRL 3
//////////////////////////////////////////////////////////////////////////
#define ITEM_IMAGE_SHARED_MATERIAL 0
#define ITEM_IMAGE_SHARED_MATERIAL_SELECTED 1
#define ITEM_IMAGE_FOLDER 2
#define ITEM_IMAGE_FOLDER_OPEN 3
#define ITEM_IMAGE_MATERIAL 4
#define ITEM_IMAGE_MATERIAL_SELECTED 5
#define ITEM_IMAGE_MULTI_MATERIAL 6
#define ITEM_IMAGE_MULTI_MATERIAL_SELECTED 7
#define ITEM_IMAGE_OVERLAY_CGF 8
#define ITEM_IMAGE_OVERLAY_INPAK 9
#define ITEM_IMAGE_OVERLAY_READONLY 10
#define ITEM_IMAGE_OVERLAY_ONDISK 11
#define ITEM_IMAGE_OVERLAY_LOCKED 12
#define ITEM_IMAGE_OVERLAY_CHECKEDOUT 13
#define ITEM_IMAGE_OVERLAY_NO_CHECKOUT 14
//////////////////////////////////////////////////////////////////////////
AZ::Data::AssetId GetMaterialProductAssetIdFromAssetBrowserEntry(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* assetEntry)
{
using namespace AzToolsFramework::AssetBrowser;
if (assetEntry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source ||
assetEntry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Product)
{
AZStd::vector<const ProductAssetBrowserEntry*> products;
assetEntry->GetChildrenRecursively<ProductAssetBrowserEntry>(products);
// Cache the material asset type because this function is called for every submaterial when searching.
// AssetType is a POD struct so it should be fine to leave it as a static that gets destroyed during library unload/program termination.
static AZ::Data::AssetType materialAssetType = AZ::Data::AssetType::CreateNull();
if (materialAssetType.IsNull())
{
EBusFindAssetTypeByName materialAssetTypeResult("Material");
AZ::AssetTypeInfoBus::BroadcastResult(materialAssetTypeResult, &AZ::AssetTypeInfo::GetAssetType);
AZ_Assert(materialAssetTypeResult.Found(), "Could not find asset type for material asset");
materialAssetType = materialAssetTypeResult.GetAssetType();
}
for (const auto* product : products)
{
if (product->GetAssetType() == materialAssetType)
{
return product->GetAssetId();
}
}
}
return AZ::Data::AssetId();
}
MaterialBrowserFilterModel::MaterialBrowserFilterModel(QObject* parent)
: AzToolsFramework::AssetBrowser::AssetBrowserFilterModel(parent)
{
using namespace AzToolsFramework::AssetBrowser;
using namespace AzToolsFramework::MaterialBrowser;
MaterialBrowserSourceControlBus::Handler::BusConnect();
AssetBrowserModelNotificationBus::Handler::BusConnect();
MaterialBrowserRequestBus::Handler::BusConnect();
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
m_imageList.push_back(QPixmap(":/MaterialBrowser/images/material_00.png"));
m_imageList.push_back(QPixmap(":/MaterialBrowser/images/material_01.png"));
m_imageList.push_back(QPixmap(":/MaterialBrowser/images/material_02.png"));
m_imageList.push_back(QPixmap(":/MaterialBrowser/images/material_03.png"));
m_imageList.push_back(QPixmap(":/MaterialBrowser/images/material_04.png"));
m_imageList.push_back(QPixmap(":/MaterialBrowser/images/material_05.png"));
m_imageList.push_back(QPixmap(":/MaterialBrowser/images/material_06.png"));
m_imageList.push_back(QPixmap(":/MaterialBrowser/images/material_07.png"));
m_imageList.push_back(QPixmap(":/MaterialBrowser/images/filestatus_00.png"));
m_imageList.push_back(QPixmap(":/MaterialBrowser/images/filestatus_01.png"));
m_imageList.push_back(QPixmap(":/MaterialBrowser/images/filestatus_02.png"));
m_imageList.push_back(QPixmap(":/MaterialBrowser/images/filestatus_03.png"));
m_imageList.push_back(QPixmap(":/MaterialBrowser/images/filestatus_04.png"));
m_imageList.push_back(QPixmap(":/MaterialBrowser/images/filestatus_05.png"));
m_imageList.push_back(QPixmap(":/MaterialBrowser/images/filestatus_06.png"));
// Create an asset type filter for materials
AssetTypeFilter* assetTypeFilter = new AssetTypeFilter();
assetTypeFilter->SetAssetType("Material");
assetTypeFilter->SetFilterPropagation(AssetBrowserEntryFilter::PropagateDirection::Down); //this will make sure folders that contain materials are displayed
m_assetTypeFilter = FilterConstType(assetTypeFilter);
// Create search filters. SetSearchFilter stores these filters so that they are deleted when this object is deleted.
m_subMaterialSearchFilter = new SubMaterialSearchFilter(this);
m_levelMaterialSearchFilter = new LevelMaterialSearchFilter(this);
InitializeRecordUpdateJob();
}
MaterialBrowserFilterModel::~MaterialBrowserFilterModel()
{
SAFE_DELETE(m_jobCancelGroup);
SAFE_DELETE(m_jobContext);
SAFE_DELETE(m_jobManager);
AzToolsFramework::AssetBrowser::AssetBrowserModelNotificationBus::Handler::BusDisconnect();
MaterialBrowserSourceControlBus::Handler::BusDisconnect();
AzToolsFramework::MaterialBrowser::MaterialBrowserRequestBus::Handler::BusDisconnect();
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
}
void MaterialBrowserFilterModel::UpdateRecord(const QModelIndex& filterModelIndex)
{
using namespace AzToolsFramework::AssetBrowser;
if (filterModelIndex.isValid())
{
QModelIndex modelIndex = mapToSource(filterModelIndex);
AssetBrowserEntry* assetEntry = static_cast<AssetBrowserEntry*>(modelIndex.internalPointer());
if (assetEntry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source ||
assetEntry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Product)
{
AZStd::vector<const ProductAssetBrowserEntry*> products;
assetEntry->GetChildrenRecursively<ProductAssetBrowserEntry>(products);
for (const auto* product : products)
{
if (!m_assetTypeFilter->Match(product))
{
continue;
}
MaterialBrowserRecordAssetBrowserData assetBrowserData;
assetBrowserData.assetId = product->GetAssetId();
assetBrowserData.relativeFilePath = product->GetRelativePath();
assetBrowserData.fullSourcePath = product->GetFullPath();
assetBrowserData.modelIndex = modelIndex;
assetBrowserData.filterModelIndex = filterModelIndex;
UpdateRecord(assetBrowserData);
}
}
}
}
void MaterialBrowserFilterModel::UpdateRecord(const MaterialBrowserRecordAssetBrowserData& assetBrowserData)
{
CMaterialBrowserRecord record;
record.SetAssetBrowserData(assetBrowserData);
record.m_material = GetIEditor()->GetMaterialManager()->LoadMaterial(record.GetRelativeFilePath().c_str());
SetRecord(record);
}
void MaterialBrowserFilterModel::UpdateSourceControlFileInfoCallback(const AZ::Data::AssetId& assetId, const AzToolsFramework::SourceControlFileInfo& info)
{
CMaterialBrowserRecord record;
bool found = m_materialRecordMap.find(assetId, &record);
if (found)
{
// Update the cached source control attributes for the record
record.m_lastCachedSCCAttributes = info;
record.m_lastCachedFileAttributes = static_cast<ESccFileAttributes>(CFileUtil::GetAttributes(record.GetFullSourcePath().c_str(), false));
record.m_lastCheckedSCCAttributes = QDateTime::currentDateTime();
// Update the record
m_materialRecordMap.erase(assetId);
m_materialRecordMap.insert(AZStd::pair<AZ::Data::AssetId, CMaterialBrowserRecord>(record.GetAssetId(), record));
QueueDataChangedEvent(record.GetFilterModelIndex());
}
}
void MaterialBrowserFilterModel::UpdateSourceControlLastCheckedTime(const AZ::Data::AssetId& assetId, const QDateTime& dateTime)
{
CMaterialBrowserRecord record;
bool found = m_materialRecordMap.find(assetId, &record);
if (found)
{
// Update the record
record.m_lastCheckedSCCAttributes = dateTime;
m_materialRecordMap.erase(assetId);
m_materialRecordMap.insert(AZStd::pair<AZ::Data::AssetId, CMaterialBrowserRecord>(record.GetAssetId(), record));
}
}
QVariant MaterialBrowserFilterModel::data(const QModelIndex& index, int role /* = Qt::DisplayRole*/) const
{
// Should return data from an AssetBrowserEntry, or get material specific info from that data
if (index.isValid())
{
// Use the base AssetBrowserFilterModel::data function for display role
if (role != AzToolsFramework::AssetBrowser::AssetBrowserModel::Roles::EntryRole)
{
QModelIndex modelIndex = mapToSource(index);
AzToolsFramework::AssetBrowser::AssetBrowserEntry* assetEntry = static_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(modelIndex.internalPointer());
AZ::Data::AssetId assetId = GetMaterialProductAssetIdFromAssetBrowserEntry(assetEntry);
if (assetId.IsValid())
{
CMaterialBrowserRecord record;
bool found = m_materialRecordMap.find(assetId, &record);
if (role == Qt::UserRole)
{
if (found)
{
return QVariant::fromValue(record);
}
else
{
return QVariant();
}
}
}
}
}
// If the role is Qt::DisplayRole, the item a folder, or the material has not been parsed yet, fall back on the default data result from the underlying AssetBrowserModel
return AzToolsFramework::AssetBrowser::AssetBrowserFilterModel::data(index, role);
}
void MaterialBrowserFilterModel::GetRelativeFilePaths(AZStd::vector<MaterialBrowserRecordAssetBrowserData>& files) const
{
GetRelativeFilePathsRecursive(files, this);
}
void MaterialBrowserFilterModel::GetRelativeFilePathsRecursive(AZStd::vector<MaterialBrowserRecordAssetBrowserData>& files,
const MaterialBrowserFilterModel* model, const QModelIndex& parent) const
{
using namespace AzToolsFramework::AssetBrowser;
for (int r = 0; r < model->rowCount(parent); ++r)
{
QModelIndex index = model->index(r, 0, parent);
QModelIndex modelIndex = model->mapToSource(index);
if (model->hasChildren(index))
{
GetRelativeFilePathsRecursive(files, model, index);
}
else
{
AssetBrowserEntry* assetEntry = static_cast<AssetBrowserEntry*>(modelIndex.internalPointer());
AZStd::vector<const ProductAssetBrowserEntry*> products;
assetEntry->GetChildrenRecursively<ProductAssetBrowserEntry>(products);
for (const auto* product : products)
{
if (!m_assetTypeFilter->Match(product))
{
continue;
}
MaterialBrowserRecordAssetBrowserData item;
item.assetId = product->GetAssetId();
item.fullSourcePath = product->GetFullPath();
item.relativeFilePath = product->GetRelativePath();
item.modelIndex = modelIndex;
item.filterModelIndex = index;
files.push_back(item);
}
}
}
}
QModelIndex MaterialBrowserFilterModel::GetIndexFromMaterial(_smart_ptr<CMaterial> material) const
{
CMaterialBrowserRecord record;
bool found = TryGetRecordFromMaterial(material, record);
if (found)
{
return record.GetFilterModelIndex();
}
return QModelIndex();
}
QModelIndex MaterialBrowserFilterModel::GetFilterModelIndex(const AZ::Data::AssetId& assetId) const
{
QModelIndex filterModelIndex;
if (TryGetFilterModelIndexRecursive(filterModelIndex, assetId, this))
{
return filterModelIndex;
}
return QModelIndex();
}
bool MaterialBrowserFilterModel::TryGetFilterModelIndexRecursive(QModelIndex& filterModelIndex,
const AZ::Data::AssetId& assetId, const MaterialBrowserFilterModel* model, const QModelIndex& parent) const
{
using namespace AzToolsFramework::AssetBrowser;
// Walk through the filter model to find the product entry with the corresponding assetId
for (int r = 0; r < model->rowCount(parent); ++r)
{
QModelIndex index = model->index(r, 0, parent);
QModelIndex modelIndex = model->mapToSource(index);
if (model->hasChildren(index))
{
if (TryGetFilterModelIndexRecursive(filterModelIndex, assetId, model, index))
{
return true;
}
}
else
{
// Check to see if the current entry is the one we're looking for
AssetBrowserEntry* assetEntry = static_cast<AssetBrowserEntry*>(modelIndex.internalPointer());
AZStd::vector<const ProductAssetBrowserEntry*> products;
assetEntry->GetChildrenRecursively<ProductAssetBrowserEntry>(products);
for (const auto* product : products)
{
if (assetId == product->GetAssetId())
{
filterModelIndex = index;
return true;
}
}
}
}
return false;
}
void MaterialBrowserFilterModel::EntryAdded(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
{
// If the entry is a product material
if (entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Product && m_assetTypeFilter->Match(entry))
{
// capture the data here, so that entry cannot disappear before we actually get to the job.
MaterialBrowserRecordAssetBrowserData assetBrowserData;
assetBrowserData.assetId = GetMaterialProductAssetIdFromAssetBrowserEntry(entry);
assetBrowserData.relativeFilePath = entry->GetRelativePath();
assetBrowserData.fullSourcePath = entry->GetFullPath();
assetBrowserData.filterModelIndex = GetFilterModelIndex(assetBrowserData.assetId);
// Create a job to add/update the entry in the underlying map
AZ::Job* updateEntryJob = AZ::CreateJobFunction([this, assetBrowserData]()
{
CMaterialBrowserRecord record;
record.SetAssetBrowserData(assetBrowserData);
record.m_material = GetIEditor()->GetMaterialManager()->LoadMaterialWithFullSourcePath(record.GetRelativeFilePath().c_str(), record.GetFullSourcePath().c_str());
SetRecord(record);
MaterialBrowserWidgetBus::Broadcast(&MaterialBrowserWidgetEvents::MaterialAddFinished);
}, true, m_jobContext);
// Start the job immediately
AZ::Job* currentJob = m_jobContext->GetJobManager().GetCurrentJob();
if (currentJob)
{
// Suspend the current job until the new job completes so that
// if a new material is created by the user it's ready to use sooner
currentJob->StartAsChild(updateEntryJob);
currentJob->WaitForChildren();
}
else
{
updateEntryJob->Start();
}
}
}
void MaterialBrowserFilterModel::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
{
CMaterialBrowserRecord record;
bool found = m_materialRecordMap.find(assetId, &record);
if (found)
{
record.m_material->Reload();
//Send out event to notify UI update if it's currently selected
MaterialBrowserWidgetBus::Broadcast(&MaterialBrowserWidgetEvents::MaterialFinishedProcessing, record.m_material, record.GetFilterModelIndex());
}
}
bool MaterialBrowserFilterModel::HasRecord(const AZ::Data::AssetId& assetId)
{
return m_materialRecordMap.find(assetId);
}
bool MaterialBrowserFilterModel::IsMultiMaterial(const AZ::Data::AssetId& assetId)
{
CMaterialBrowserRecord record;
bool found = m_materialRecordMap.find(assetId, &record);
if (found)
{
if (record.m_material && record.m_material->IsMultiSubMaterial())
{
return true;
}
}
return false;
}
bool MaterialBrowserFilterModel::TryGetRecordFromMaterial(_smart_ptr<CMaterial> material, CMaterialBrowserRecord& record) const
{
if (material)
{
// Get the relative path for the material
bool pathFound = false;
AZStd::string relativePath;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(pathFound, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetRelativeProductPathFromFullSourceOrProductPath, material->GetFilename().toUtf8().data(), relativePath);
AZ_Assert(pathFound, "Failed to get engine relative path from %s", material->GetFilename().toUtf8().data());
// Get the assetId from the relative path
AZ::Data::AssetId assetId;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetId, &AZ::Data::AssetCatalogRequests::GetAssetIdByPath, relativePath.c_str(), GetIEditor()->GetMaterialManager()->GetMaterialAssetType(), false);
return TryGetRecordFromAssetId(assetId, record);
}
return false;
}
bool MaterialBrowserFilterModel::TryGetRecordFromAssetId(const AZ::Data::AssetId& assetId, CMaterialBrowserRecord& record) const
{
bool recordFound = m_materialRecordMap.find(assetId, &record);
return recordFound;
}
void MaterialBrowserFilterModel::SetRecord(const CMaterialBrowserRecord& record)
{
m_materialRecordMap.erase(record.GetAssetId());
m_materialRecordMap.insert({ record.GetAssetId(), record });
QueueDataChangedEvent(record.GetFilterModelIndex());
}
void MaterialBrowserFilterModel::SetSearchFilter(const AzToolsFramework::AssetBrowser::SearchWidget* searchWidget)
{
/*
* The filter matches the following rule:
* A. If the entry is a material
* 1. The material's name matches the search text
* 2. The sub material's name matches the search text
* B. If the entry is a folder:
* 1. The folder's name matches the search text
* 2. The folder contains a material matching A
* 3. The folder contains a folder matching B.1 & B.2
*/
using namespace AzToolsFramework::AssetBrowser;
m_searchWidget = searchWidget;
// Create a search filter where a search text either matches entry/entry parent's name or sub material name
CompositeFilter* nameFilter = new CompositeFilter(CompositeFilter::LogicOperatorType::OR);
QSharedPointer<CompositeFilter> searchWidgetFilter = m_searchWidget->GetFilter();
// The default setting for search widget filter is Down
// Since now we only need to match for the entry itself in this filter, set back to None
searchWidgetFilter->SetFilterPropagation(AssetBrowserEntryFilter::PropagateDirection::None);
nameFilter->AddFilter(FilterConstType(m_subMaterialSearchFilter));
nameFilter->AddFilter(FilterConstType(searchWidgetFilter));
EntryTypeFilter* productsFilter = new EntryTypeFilter();
productsFilter->SetEntryType(AssetBrowserEntry::AssetEntryType::Product);
InverseFilter* noProductsFilter = new InverseFilter();
noProductsFilter->SetFilter(FilterConstType(productsFilter));
// Create a filter where the entry needs to match the previous name filter and it needs to be material itself or it contains material.
CompositeFilter* isMaterialAndMatchesSearchFilter = new CompositeFilter(CompositeFilter::LogicOperatorType::AND);
isMaterialAndMatchesSearchFilter->AddFilter(FilterConstType(nameFilter));
isMaterialAndMatchesSearchFilter->AddFilter(FilterConstType(m_assetTypeFilter));
isMaterialAndMatchesSearchFilter->AddFilter(FilterConstType(noProductsFilter));
isMaterialAndMatchesSearchFilter->AddFilter(FilterConstType(m_levelMaterialSearchFilter));
// Make sure any folder contains the matching result is included
isMaterialAndMatchesSearchFilter->SetFilterPropagation(AssetBrowserEntryFilter::PropagateDirection::Down);
// Set the filter for the MaterialBrowserFilterModel
SetFilter(FilterConstType(isMaterialAndMatchesSearchFilter));
}
void MaterialBrowserFilterModel::ShowOnlyLevelMaterials(bool levelOnly, bool invalidateFilterNow)
{
m_levelMaterialSearchFilter->ShowOnlyLevelMaterials(levelOnly);
if (levelOnly)
{
m_levelMaterialSearchFilter->CacheLoadedMaterials();
}
if (invalidateFilterNow)
{
// we need to invalid the filter immediately, for example this is used when we are changing level, otherwise
// the current filter is used when getting file paths for all of the materials as part of StartRecordUpdateJobs.
invalidateFilter();
}
else
{
filterUpdatedSlot();
}
}
void MaterialBrowserFilterModel::SearchFilterUpdated()
{
m_subMaterialSearchFilter->SetFilterString(m_searchWidget->textFilter());
filterUpdatedSlot();
}
void MaterialBrowserFilterModel::QueueDataChangedEvent(const QPersistentModelIndex& filterModelIndex)
{
// Queue a data changed event to be executed on the main thread
AZStd::function<void()> emitDataChanged =
[this, filterModelIndex]()
{
if (filterModelIndex.isValid())
{
Q_EMIT dataChanged(filterModelIndex, filterModelIndex);
}
};
AZ::TickBus::QueueFunction(emitDataChanged);
}
void MaterialBrowserFilterModel::InitializeRecordUpdateJob()
{
AZ::JobManagerDesc desc;
AZ::JobManagerThreadDesc threadDesc;
for (size_t i = 0; i < AZStd::thread::hardware_concurrency(); ++i)
{
desc.m_workerThreads.push_back(threadDesc);
}
// Check to ensure these have not already been initialized.
AZ_Error("Material Browser", !m_jobManager && !m_jobCancelGroup && !m_jobContext, "MaterialBrowserFilterModel::InitializeRecordUpdateJob is being called again after it has already been initialized");
m_jobManager = aznew AZ::JobManager(desc);
m_jobCancelGroup = aznew AZ::JobCancelGroup();
m_jobContext = aznew AZ::JobContext(*m_jobManager, *m_jobCancelGroup);
}
void MaterialBrowserFilterModel::StartRecordUpdateJobs()
{
// Generate a list of file paths, assetId's, and asset browser model indices
// This must be done on the main thread, otherwise it can lead to a crash when the tree view UI is being initialized
AZStd::vector<MaterialBrowserRecordAssetBrowserData> files;
GetRelativeFilePaths(files);
// Kick off the background process that will iterate over the list of file paths and update the material record map
m_mainUpdateRecordJob = aznew MaterialBrowserUpdateJobCreator(this, files, m_jobContext);
m_mainUpdateRecordJob->Start();
}
void MaterialBrowserFilterModel::CancelRecordUpdateJobs()
{
m_jobContext->GetCancelGroup()->Cancel();
m_jobContext->GetCancelGroup()->Reset();
}
void MaterialBrowserFilterModel::ClearRecordMap()
{
CancelRecordUpdateJobs();
m_materialRecordMap.clear();
}
MaterialBrowserUpdateJobCreator::MaterialBrowserUpdateJobCreator(MaterialBrowserFilterModel* model, AZStd::vector<MaterialBrowserRecordAssetBrowserData>& files, AZ::JobContext* context /*= NULL*/)
: Job(true, context)
, m_files(files)
, m_filterModel(model)
{
}
void MaterialBrowserUpdateJobCreator::Process()
{
// Split the files to be processed evenly among threads
int numJobs = GetContext()->GetJobManager().GetNumWorkerThreads();
int materialsPerJob = (m_files.size() / numJobs) + 1;
for (auto it = m_files.begin(); it <= m_files.end(); it += materialsPerJob)
{
// Create a subset of the list of material files to be processed by another job
auto start = it;
auto end = it + materialsPerJob;
if (end > m_files.end())
{
end = m_files.end();
}
AZStd::vector<MaterialBrowserRecordAssetBrowserData> subset(start, end);
if (subset.size() > 0)
{
AZ::Job* materialBrowserUpdateJob = aznew MaterialBrowserUpdateJob(m_filterModel, subset, GetContext());
StartAsChild(materialBrowserUpdateJob);
}
}
WaitForChildren();
MaterialBrowserWidgetBus::Broadcast(&MaterialBrowserWidgetEvents::MaterialRecordUpdateFinished);
}
MaterialBrowserUpdateJob::MaterialBrowserUpdateJob(MaterialBrowserFilterModel* model, AZStd::vector<MaterialBrowserRecordAssetBrowserData>& files, AZ::JobContext* context /*= NULL*/)
: Job(true, context)
, m_filterModel(model)
, m_files(files)
{
}
void MaterialBrowserUpdateJob::Process()
{
for (size_t i = 0; i < m_files.size(); ++i)
{
// Early out when the job is cancelled
if (IsCancelled())
{
return;
}
CMaterialBrowserRecord record;
record.SetAssetBrowserData(m_files[i]);
// Get the writable status of the file, but don't update source control status until it is actually needed
record.m_lastCachedFileAttributes = static_cast<ESccFileAttributes>(CFileUtil::GetAttributes(record.GetFullSourcePath().c_str(), false));
record.m_material = GetIEditor()->GetMaterialManager()->LoadMaterialWithFullSourcePath(record.GetRelativeFilePath().c_str(), record.GetFullSourcePath().c_str());
m_filterModel->SetRecord(record);
}
}
@@ -0,0 +1,240 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/std/parallel/containers/concurrent_unordered_map.h>
#include <AzCore/std/parallel/atomic.h>
#include <AzCore/Jobs/Job.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/Search/SearchWidget.h>
#include <AzToolsFramework/MaterialBrowser/MaterialBrowserBus.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <ISourceControl.h>
#include <QDateTime>
namespace AZ
{
class Job;
class JobManager;
class JobCancelGroup;
class JobContext;
}
class SubMaterialSearchFilter;
class LevelMaterialSearchFilter;
//////////////////////////////////////////////////////////////////////////
// Item class for browser.
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
struct MaterialBrowserRecordAssetBrowserData
{
AZ::Data::AssetId assetId;
AZStd::string relativeFilePath;
AZStd::string fullSourcePath;
QPersistentModelIndex modelIndex;
QPersistentModelIndex filterModelIndex;
};
class CMaterialBrowserRecord
{
public:
CMaterialBrowserRecord()
{
InitializeSourceControlAttributes();
}
CMaterialBrowserRecord(const CMaterialBrowserRecord &rhs)
: m_material(rhs.m_material)
, m_lastCachedSCCAttributes(rhs.m_lastCachedSCCAttributes)
, m_lastCachedFileAttributes(rhs.m_lastCachedFileAttributes)
, m_lastCheckedSCCAttributes(rhs.m_lastCheckedSCCAttributes)
, m_assetBrowserData(rhs.m_assetBrowserData)
{
}
AZ::Data::AssetId GetAssetId() const { return m_assetBrowserData.assetId; }
AZStd::string GetRelativeFilePath() const { return m_assetBrowserData.relativeFilePath; }
AZStd::string GetFullSourcePath() const { return m_assetBrowserData.fullSourcePath; }
QPersistentModelIndex GetModelIndex() const { return m_assetBrowserData.modelIndex; }
QPersistentModelIndex GetFilterModelIndex() const { return m_assetBrowserData.filterModelIndex; }
void SetAssetBrowserData(const MaterialBrowserRecordAssetBrowserData &assetBrowserData) { m_assetBrowserData = assetBrowserData; }
void InitializeSourceControlAttributes()
{
// Force an update by setting the last update time to 1 / 1 / 1
m_lastCachedSCCAttributes = AzToolsFramework::SourceControlFileInfo();
m_lastCachedFileAttributes = SCC_FILE_ATTRIBUTE_INVALID;
m_lastCheckedSCCAttributes = QDate(1, 1, 1).startOfDay();
}
public:
_smart_ptr<CMaterial> m_material = nullptr;
AzToolsFramework::SourceControlFileInfo m_lastCachedSCCAttributes;
ESccFileAttributes m_lastCachedFileAttributes;
QDateTime m_lastCheckedSCCAttributes;
private:
MaterialBrowserRecordAssetBrowserData m_assetBrowserData;
};
Q_DECLARE_METATYPE(CMaterialBrowserRecord)
//! MaterialBrowserSourceControlEvents
//! This bus informs the material browser filter model
//! when an ASYNC source control command has completed
class MaterialBrowserSourceControlEvents
: public AZ::EBusTraits
{
public:
typedef AZStd::recursive_mutex MutexType;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; // there's only one listener
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; // theres only one listener
virtual ~MaterialBrowserSourceControlEvents() {}
//! Signals the callback for the GetFileInfo source control op
virtual void UpdateSourceControlFileInfoCallback(const AZ::Data::AssetId &assetId, const AzToolsFramework::SourceControlFileInfo& fileInfo) = 0;
//! Updates the timestamp for when the source control status was last checked
virtual void UpdateSourceControlLastCheckedTime(const AZ::Data::AssetId &assetId, const QDateTime &dateTime) = 0;
};
using MaterialBrowserSourceControlBus = AZ::EBus<MaterialBrowserSourceControlEvents>;
//! MaterialBrowserWidgetEvents
//! This bus is used to send events to the material browser widget
class MaterialBrowserWidgetEvents
: public AZ::EBusTraits
{
public:
typedef AZStd::recursive_mutex MutexType;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; // there's only one listener
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; // theres only one listener
virtual ~MaterialBrowserWidgetEvents() {}
//! Indicates that a material has finished being processed by the asset processor
virtual void MaterialFinishedProcessing(_smart_ptr<CMaterial> material, const QPersistentModelIndex &filterModelIndex) = 0;
//! Indicates that a material has finished being added to the material browser
virtual void MaterialAddFinished() = 0;
//! Indicates that the material record update for initially populating the material browser has finished
virtual void MaterialRecordUpdateFinished() = 0;
};
using MaterialBrowserWidgetBus = AZ::EBus<MaterialBrowserWidgetEvents>;
/**
* Get the product material assetId for a given AssetBrowserEntry
* If there is no valid product material, the material has not been processed, or there are multiple product materials
* and thus there is not an individual material that can be assumed based on the source, an invalid assetId is returned
* @param assetEntry An asset entry that may be a source or a product
* @return The assetId of the product material.
*/
AZ::Data::AssetId GetMaterialProductAssetIdFromAssetBrowserEntry(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* assetEntry);
class MaterialBrowserFilterModel
: public AzToolsFramework::AssetBrowser::AssetBrowserFilterModel
, public MaterialBrowserSourceControlBus::Handler
, public AzToolsFramework::AssetBrowser::AssetBrowserModelNotificationBus::Handler
, public AzToolsFramework::MaterialBrowser::MaterialBrowserRequestBus::Handler
, private AzFramework::AssetCatalogEventBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(MaterialBrowserFilterModel, AZ::SystemAllocator, 0);
MaterialBrowserFilterModel(QObject* parent);
~MaterialBrowserFilterModel();
void UpdateRecord(const QModelIndex& filterModelIndex);
void UpdateRecord(const MaterialBrowserRecordAssetBrowserData &assetBrowserData);
void UpdateSourceControlFileInfoCallback(const AZ::Data::AssetId &assetId, const AzToolsFramework::SourceControlFileInfo& info) override;
void UpdateSourceControlLastCheckedTime(const AZ::Data::AssetId &assetId, const QDateTime &dateTime) override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
bool TryGetRecordFromMaterial(_smart_ptr<CMaterial> material, CMaterialBrowserRecord& record) const;
bool TryGetRecordFromAssetId(const AZ::Data::AssetId &assetId, CMaterialBrowserRecord& record) const;
void SetRecord(const CMaterialBrowserRecord& record);
void SetSearchFilter(const AzToolsFramework::AssetBrowser::SearchWidget* searchWidget);
void StartRecordUpdateJobs();
void CancelRecordUpdateJobs();
void ClearRecordMap();
void GetRelativeFilePaths(AZStd::vector<MaterialBrowserRecordAssetBrowserData> &files) const;
QModelIndex GetIndexFromMaterial(_smart_ptr<CMaterial> material) const;
QModelIndex GetFilterModelIndex(const AZ::Data::AssetId &assetId) const;
// AssetBrowserModelNotificationBus event handlers
void EntryAdded(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) override;
void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override;
// MaterialBrowserRequestBus
bool HasRecord(const AZ::Data::AssetId& assetId) override;
bool IsMultiMaterial(const AZ::Data::AssetId& assetId) override;
void ShowOnlyLevelMaterials(bool levelOnly, bool invalidateFilterNow = false);
public Q_SLOTS:
void SearchFilterUpdated();
private:
void GetRelativeFilePathsRecursive(AZStd::vector<MaterialBrowserRecordAssetBrowserData> &files, const MaterialBrowserFilterModel* model, const QModelIndex &parent = QModelIndex()) const;
bool TryGetFilterModelIndexRecursive(QModelIndex &filterModelIndex, const AZ::Data::AssetId &assetId, const MaterialBrowserFilterModel* model, const QModelIndex &parent = QModelIndex()) const;
void QueueDataChangedEvent(const QPersistentModelIndex &filterModelIndex);
AZStd::concurrent_unordered_map<AZ::Data::AssetId, CMaterialBrowserRecord> m_materialRecordMap;
QVector<QPixmap> m_imageList;
AzToolsFramework::AssetBrowser::FilterConstType m_assetTypeFilter;
SubMaterialSearchFilter* m_subMaterialSearchFilter;
LevelMaterialSearchFilter* m_levelMaterialSearchFilter;
const AzToolsFramework::AssetBrowser::SearchWidget* m_searchWidget;
void InitializeRecordUpdateJob();
AZ::JobManager* m_jobManager = nullptr;
AZ::JobCancelGroup* m_jobCancelGroup = nullptr;
AZ::JobContext* m_jobContext = nullptr;
AZ::Job* m_mainUpdateRecordJob = nullptr;
};
/**
* Job that walks through a MaterialBrowserFilterModel to generate a list of files,
* then divides the list amongst child jobs for processing
*/
class MaterialBrowserUpdateJobCreator
: public AZ::Job
{
public:
AZ_CLASS_ALLOCATOR(MaterialBrowserUpdateJobCreator, AZ::ThreadPoolAllocator, 0);
MaterialBrowserUpdateJobCreator(MaterialBrowserFilterModel* model, AZStd::vector<MaterialBrowserRecordAssetBrowserData>& files, AZ::JobContext* context = nullptr);
void Process() override;
private:
MaterialBrowserFilterModel* m_filterModel;
AZStd::vector<MaterialBrowserRecordAssetBrowserData> m_files;
};
/**
* Job that walks through a list of material files, loads them, and then populates
* the MaterialBrowserFilterModel's map of material data
*/
class MaterialBrowserUpdateJob
: public AZ::Job
{
public:
AZ_CLASS_ALLOCATOR(MaterialBrowserUpdateJob, AZ::ThreadPoolAllocator, 0);
MaterialBrowserUpdateJob(MaterialBrowserFilterModel* model, AZStd::vector<MaterialBrowserRecordAssetBrowserData> &files, AZ::JobContext* context = nullptr);
void Process() override;
private:
MaterialBrowserFilterModel* m_filterModel;
AZStd::vector<MaterialBrowserRecordAssetBrowserData> m_files;
};
@@ -0,0 +1,140 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "EditorDefs.h"
#include "MaterialBrowserSearchFilters.h"
// Editor
#include "MaterialBrowserFilterModel.h"
#include "Material.h"
SubMaterialSearchFilter::SubMaterialSearchFilter(const MaterialBrowserFilterModel* filterModel)
: m_filterModel(filterModel)
{
AzToolsFramework::AssetBrowser::AssetBrowserEntryFilter::SetFilterPropagation(AzToolsFramework::AssetBrowser::AssetBrowserEntryFilter::PropagateDirection::Down);
}
void SubMaterialSearchFilter::SetFilterString(const QString& filterString)
{
m_filterString = filterString;
}
QString SubMaterialSearchFilter::GetNameInternal() const
{
return "SubMaterialSearchFilter";
}
bool SubMaterialSearchFilter::MatchInternal(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const
{
// All entries match if there is no search string
if (m_filterString.isEmpty())
{
return true;
}
if (m_filterModel)
{
// Get the product material for the given asset browser entry
AZ::Data::AssetId assetId = GetMaterialProductAssetIdFromAssetBrowserEntry(entry);
if (assetId.IsValid())
{
CMaterialBrowserRecord record;
bool found = m_filterModel->TryGetRecordFromAssetId(assetId, record);
if (found)
{
// If there is a valid product material, check to see if any of its sub-materials match the search string
if (record.m_material)
{
for (int i = 0; i < record.m_material->GetSubMaterialCount(); ++i)
{
CMaterial* subMaterial = record.m_material->GetSubMaterial(i);
if (subMaterial)
{
// If any of the product sub-materials matches the string, return true for this entry
if (subMaterial->GetName().contains(m_filterString, Qt::CaseInsensitive))
{
return true;
}
}
}
}
}
}
}
return false;
}
LevelMaterialSearchFilter::LevelMaterialSearchFilter(const MaterialBrowserFilterModel* filterModel)
: m_filterModel(filterModel)
{
AzToolsFramework::AssetBrowser::AssetBrowserEntryFilter::SetFilterPropagation(AzToolsFramework::AssetBrowser::AssetBrowserEntryFilter::PropagateDirection::Down);
}
QString LevelMaterialSearchFilter::GetNameInternal() const
{
return "LoadedMaterialSearchFilter";
}
void LevelMaterialSearchFilter::CacheLoadedMaterials()
{
m_localMap.clear();
AZStd::vector<IRenderNode*> foundRenderNodes;
unsigned int numFoundObjects = 0;
numFoundObjects = GetIEditor()->Get3DEngine()->GetObjectsByFlags(0);
foundRenderNodes.resize(numFoundObjects, nullptr);
GetIEditor()->Get3DEngine()->GetObjectsByFlags(0, foundRenderNodes.data());
AZStd::vector<_smart_ptr<IMaterial>> materials;
materials.reserve(foundRenderNodes.size());
for (IRenderNode* renderNode : foundRenderNodes)
{
renderNode->GetMaterials(materials);
}
for (size_t i = 0; i < materials.size(); ++i)
{
if (materials[i])
{
m_localMap[materials[i]->GetName()] = materials[i];
}
}
}
bool LevelMaterialSearchFilter::MatchInternal(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const
{
// All entries match if show level materials isn't selected
if (!m_onlyShowLevelMaterials)
{
return true;
}
// Get the product material for the given asset browser entry
AZ::Data::AssetId assetId = GetMaterialProductAssetIdFromAssetBrowserEntry(entry);
if (assetId.IsValid() && m_filterModel)
{
CMaterialBrowserRecord record;
bool found = m_filterModel->TryGetRecordFromAssetId(assetId, record);
if (found && record.m_material)
{
// If there is a valid product material, check to see if it is used by the level
return m_localMap.find(record.m_material->GetName()) != m_localMap.end();
}
}
return false;
}
#include <Material/moc_MaterialBrowserSearchFilters.cpp>
@@ -0,0 +1,64 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <QHash>
#endif
class MaterialBrowserFilterModel;
//! Filter that checks the name of each sub-material in a material to see if it contains the filter string
class SubMaterialSearchFilter
: public AzToolsFramework::AssetBrowser::AssetBrowserEntryFilter
{
Q_OBJECT
public:
SubMaterialSearchFilter(const MaterialBrowserFilterModel* filterModel);
~SubMaterialSearchFilter() override = default;
void SetFilterString(const QString& filterString);
protected:
QString GetNameInternal() const override;
bool MatchInternal(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const override;
private:
bool TextMatchesFilter(const QString &text) const;
QString m_filterString = "";
const MaterialBrowserFilterModel* m_filterModel = nullptr;
};
class LevelMaterialSearchFilter
: public AzToolsFramework::AssetBrowser::AssetBrowserEntryFilter
{
Q_OBJECT
public:
LevelMaterialSearchFilter(const MaterialBrowserFilterModel* filterModel);
~LevelMaterialSearchFilter() override = default;
void ShowOnlyLevelMaterials(bool onlyLevel) { m_onlyShowLevelMaterials = onlyLevel; }
void CacheLoadedMaterials();
protected:
QString GetNameInternal() const override;
bool MatchInternal(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const override;
private:
bool m_onlyShowLevelMaterials = false;
const MaterialBrowserFilterModel* m_filterModel = nullptr;
using MaterialMap = QHash<QString, _smart_ptr<IMaterial> >;
MaterialMap m_localMap;
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,176 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#if !defined(Q_MOC_RUN)
#include "MaterialBrowser.h"
#include <QMainWindow>
#include <QPointer>
#include <QScopedPointer>
#endif
static const char* MATERIAL_EDITOR_NAME = "Material Editor";
static const char* MATERIAL_EDITOR_VER = "1.00";
class QComboBox;
class QLabel;
class CMaterial;
class CMaterialManager;
class CMatEditPreviewDlg;
class CMaterialSender;
class CMaterialImageListCtrl;
class QMaterialImageListModel;
class TwoColumnPropertyControl;
/** Dialog which hosts entity prototype library.
*/
struct SMaterialExcludedVars
{
CMaterial* pMaterial;
CVarBlock vars;
SMaterialExcludedVars()
: pMaterial(nullptr)
{
}
};
class CMaterialDialog
: public QMainWindow
, public IMaterialBrowserListener
, public IDataBaseManagerListener
, public IEditorNotifyListener
{
Q_OBJECT
public:
CMaterialDialog(QWidget* parent = 0);
~CMaterialDialog();
static void RegisterViewClass();
static const GUID& GetClassID();
public slots:
void OnAssignMaterialToSelection();
void OnResetMaterialOnSelection();
void OnGetMaterialFromSelection();
protected:
BOOL OnInitDialog();
void closeEvent(QCloseEvent *ev) override;
protected slots:
void OnAddItem();
void OnDeleteItem();
void OnSaveItem();
void OnPickMtl();
void OnCopy();
void OnPaste();
void OnMaterialPreview();
void OnSelectAssignedObjects();
void OnChangedBrowserListType(int);
void OnResetMaterialViewport();
void UpdateActions();
protected:
//////////////////////////////////////////////////////////////////////////
// Some functions can be overriden to modify standart functionality.
//////////////////////////////////////////////////////////////////////////
virtual void InitToolbar(UINT nToolbarResID);
virtual void SelectItem(CBaseLibraryItem* item, bool bForceReload = false);
virtual void DeleteItem(CBaseLibraryItem* pItem);
virtual bool SetItemName(CBaseLibraryItem* item, const QString& groupName, const QString& itemName);
virtual void ReloadItems();
//////////////////////////////////////////////////////////////////////////
// IMaterialBrowserListener implementation.
//////////////////////////////////////////////////////////////////////////
virtual void OnBrowserSelectItem(IDataBaseItem* pItem, bool bForce);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// IDataBaseManagerListener implementation.
//////////////////////////////////////////////////////////////////////////
virtual void OnDataBaseItemEvent(IDataBaseItem* pItem, EDataBaseItemEvent event);
//////////////////////////////////////////////////////////////////////////
// IEditorNotifyListener implementation.
virtual void OnEditorNotifyEvent(EEditorNotifyEvent event);
//////////////////////////////////////////////////////////////////////////
CMaterial* GetSelectedMaterial();
void OnUpdateProperties(IVariable* var);
void OnUndo(IVariable* pVar);
void UpdateShaderParamsUI(CMaterial* pMtl);
void UpdatePreview();
//void SetTextureVars( CVariableArray *texVar,CMaterial *mtl,int id,const CString &name );
void SetMaterialVars(CMaterial* mtl);
MaterialBrowserWidget* m_wndMtlBrowser;
QStatusBar* m_statusBar;
//CXTCaption m_wndCaption;
TwoColumnPropertyControl* m_propsCtrl;
bool m_bForceReloadPropsCtrl;
QLabel* m_placeHolderLabel;
CBaseLibraryItem* m_pPrevSelectedItem;
// Material manager.
CMaterialManager* m_pMatManager;
CVarBlockPtr m_vars;
CVarBlockPtr m_publicVars;
// collection of excluded vars from m_publicVars for remembering values
// when updating shader params
SMaterialExcludedVars m_excludedPublicVars;
CVarBlockPtr m_shaderGenParamsVars;
CVarBlockPtr m_textureSlots;
class CMaterialUI* m_pMaterialUI;
QPointer<CMatEditPreviewDlg> m_pPreviewDlg;
QScopedPointer<CMaterialImageListCtrl> m_pMaterialImageListCtrl;
QScopedPointer<QMaterialImageListModel> m_pMaterialImageListModel;
QToolBar* m_toolbar;
QComboBox* m_filterTypeSelection;
QAction* m_addAction;
QAction* m_assignToSelectionAction;
QAction* m_copyAction;
QAction* m_getFromSelectionAction;
QAction* m_pasteAction;
QAction* m_pickAction;
QAction* m_previewAction;
QAction* m_removeAction;
QAction* m_resetAction;
QAction* m_saveAction;
QAction* m_resetViewporAction;
};
@@ -0,0 +1,58 @@
<RCC>
<qresource prefix="/MaterialDialog/ToolBar">
<file>images/materialdialog_add_disabled.png</file>
<file>images/materialdialog_copy_disabled.png</file>
<file>images/materialdialog_paste_disabled.png</file>
<file>images/materialdialog_preview_disabled.png</file>
<file>images/materialdialog_remove_disabled.png</file>
<file>images/materialdialog_save_disabled.png</file>
<file>images/materialdialog_assignselection_disabled.png</file>
<file>images/materialdialog_getfromselection_disabled.png</file>
<file>images/materialdialog_pick_disabled.png</file>
<file>images/materialdialog_reset_disabled.png</file>
<file>images/materialdialog_assignselection_active.png</file>
<file>images/materialdialog_assignselection_normal.png</file>
<file>images/materialdialog_add_active.png</file>
<file>images/materialdialog_add_normal.png</file>
<file>images/materialdialog_copy_active.png</file>
<file>images/materialdialog_copy_normal.png</file>
<file>images/materialdialog_getfromselection_active.png</file>
<file>images/materialdialog_getfromselection_normal.png</file>
<file>images/materialdialog_paste_active.png</file>
<file>images/materialdialog_paste_normal.png</file>
<file>images/materialdialog_pick_active.png</file>
<file>images/materialdialog_pick_normal.png</file>
<file>images/materialdialog_preview_active.png</file>
<file>images/materialdialog_preview_normal.png</file>
<file>images/materialdialog_remove_active.png</file>
<file>images/materialdialog_remove_normal.png</file>
<file>images/materialdialog_reset_active.png</file>
<file>images/materialdialog_reset_normal.png</file>
<file>images/materialdialog_save_active.png</file>
<file>images/materialdialog_save_normal.png</file>
<file>images/materialdialog_reset_viewport_active.png</file>
<file>images/materialdialog_reset_viewport_disabled.png</file>
<file>images/materialdialog_reset_viewport_normal.png</file>
</qresource>
<qresource prefix="/MaterialBrowser">
<file>images/material_browser_00.png</file>
<file>images/material_browser_01.png</file>
<file>images/material_browser_02.png</file>
<file>images/material_browser_03.png</file>
<file>images/material_00.png</file>
<file>images/material_01.png</file>
<file>images/material_02.png</file>
<file>images/material_03.png</file>
<file>images/material_04.png</file>
<file>images/material_05.png</file>
<file>images/material_06.png</file>
<file>images/material_07.png</file>
<file>images/filestatus_00.png</file>
<file>images/filestatus_01.png</file>
<file>images/filestatus_02.png</file>
<file>images/filestatus_03.png</file>
<file>images/filestatus_04.png</file>
<file>images/filestatus_05.png</file>
<file>images/filestatus_06.png</file>
</qresource>
</RCC>
@@ -0,0 +1,372 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "MaterialHelpers.h"
namespace MaterialHelpers
{
//////////////////////////////////////////////////////////////////////////
static void ParsePublicParamsScript(const char* sUIScript, IVariable* pVar)
{
string uiscript = sUIScript;
string element[3];
int p1 = 0;
string itemToken = uiscript.Tokenize(";", p1);
while (!itemToken.empty())
{
int nElements = 0;
int p2 = 0;
string token = itemToken.Tokenize(" \t\r\n=", p2);
while (!token.empty())
{
element[nElements++] = token;
if (nElements == 2)
{
element[nElements] = itemToken.substr(p2);
element[nElements].Trim(" =\t\"");
break;
}
token = itemToken.Tokenize(" \t\r\n=", p2);
}
float minLimit, maxLimit;
pVar->GetLimits(minLimit, maxLimit);
if (azstricmp(element[1], "UIWidget") == 0)
{
if (azstricmp(element[2], "Color") == 0)
{
pVar->SetDataType(IVariable::DT_COLOR);
}
}
else if (azstricmp(element[1], "UIHelp") == 0)
{
string help = element[2];
help.replace("\\n", "\n");
pVar->SetDescription(help);
}
else if (azstricmp(element[1], "UIName") == 0)
{
pVar->SetHumanName(element[2].c_str());
}
else if (azstricmp(element[1], "UIMin") == 0)
{
pVar->SetLimits(atof(element[2]), maxLimit);
}
else if (azstricmp(element[1], "UIMax") == 0)
{
pVar->SetLimits(minLimit, atof(element[2]));
}
else if (azstricmp(element[1], "UIStep") == 0)
{
}
itemToken = uiscript.Tokenize(";", p1);
}
}
//////////////////////////////////////////////////////////////////////////
static void AddRealNameToDescription(IVariable* pIVar, const SShaderParam* pParam)
{
// In order to help the user discover the true names of parameters, info they need using certain script functions,
// we embed the real parameter name in the description.
QString description = pIVar->GetDescription();
if (!description.isEmpty())
{
description += "\n";
}
description += "(Script Param Name = ";
description += pParam->m_Name.c_str();
description += ")";
pIVar->SetDescription(description);
}
//////////////////////////////////////////////////////////////////////////
CVarBlock* GetPublicVars(SInputShaderResources& pShaderResources)
{
if (pShaderResources.m_ShaderParams.empty())
{
return 0;
}
CVarBlock* pPublicVars = new CVarBlock;
for (int i = 0; i < pShaderResources.m_ShaderParams.size(); i++)
{
IVariable* pIVar = NULL;
SShaderParam* pParam = &pShaderResources.m_ShaderParams[i];
switch (pParam->m_Type)
{
case eType_BYTE:
pIVar = new CVariable<int>(pParam->m_Value.m_Byte);
break;
case eType_SHORT:
pIVar = new CVariable<int>(pParam->m_Value.m_Short);
break;
case eType_INT:
pIVar = new CVariable<int>(pParam->m_Value.m_Int);
break;
case eType_FLOAT:
pIVar = new CVariable<float>(pParam->m_Value.m_Float);
break;
/* case eType_STRING:
pIVar = new CVariable<string>(pParam->m_Value.m_String);
break; */
case eType_FCOLOR:
pIVar = new CVariable<Vec3>(Vec3(pParam->m_Value.m_Color[0], pParam->m_Value.m_Color[1], pParam->m_Value.m_Color[2]));
pIVar->SetDataType(IVariable::DT_COLOR);
break;
case eType_FCOLORA:
pIVar = new CVariable<Vec4>(Vec4(pParam->m_Value.m_Color[0], pParam->m_Value.m_Color[1], pParam->m_Value.m_Color[2], pParam->m_Value.m_Color[3]));
pIVar->SetDataType(IVariable::DT_COLORA);
break;
case eType_VECTOR:
pIVar = new CVariable<Vec3>(Vec3(pParam->m_Value.m_Vector[0], pParam->m_Value.m_Vector[1], pParam->m_Value.m_Vector[2]));
break;
default:
break;
}
if (pIVar)
{
pIVar->SetName(pParam->m_Name.c_str());
pPublicVars->AddVariable(pIVar);
if (pParam->m_Script.size())
{
ParsePublicParamsScript(pParam->m_Script.c_str(), pIVar);
}
AddRealNameToDescription(pIVar, pParam);
}
}
return pPublicVars;
}
void SetPublicVars(CVarBlock* pPublicVars, SInputShaderResources& pInputShaderResources)
{
assert(pPublicVars);
if (pInputShaderResources.m_ShaderParams.empty())
{
return;
}
int numVars = pPublicVars->GetNumVariables();
for (int i = 0; i < numVars; i++)
{
if (i >= numVars)
{
break;
}
IVariable* pVar = pPublicVars->GetVariable(i);
SShaderParam* pParam = NULL;
for (int j = 0; j < pInputShaderResources.m_ShaderParams.size(); j++)
{
if (QString::compare(pVar->GetName(), pInputShaderResources.m_ShaderParams[j].m_Name.c_str()) == 0)
{
pParam = &pInputShaderResources.m_ShaderParams[j];
break;
}
}
if (!pParam)
{
continue;
}
switch (pParam->m_Type)
{
case eType_BYTE:
if (pVar->GetType() == IVariable::INT)
{
int val = 0;
pVar->Get(val);
pParam->m_Value.m_Byte = val;
}
break;
case eType_SHORT:
if (pVar->GetType() == IVariable::INT)
{
int val = 0;
pVar->Get(val);
pParam->m_Value.m_Short = val;
}
break;
case eType_INT:
if (pVar->GetType() == IVariable::INT)
{
int val = 0;
pVar->Get(val);
pParam->m_Value.m_Int = val;
}
break;
case eType_FLOAT:
if (pVar->GetType() == IVariable::FLOAT)
{
float val = 0;
pVar->Get(val);
pParam->m_Value.m_Float = val;
}
break;
/*
case eType_STRING:
if (pVar->GetType() == IVariable::STRING)
{
CString str;
int val = 0;
pVar->Get(val);
pParam->m_Value.m_Byte = val;
}
break;
*/
case eType_FCOLOR:
case eType_FCOLORA:
if (pVar->GetType() == IVariable::VECTOR4 && (pVar->GetDataType() == IVariable::DT_COLOR || pVar->GetDataType() == IVariable::DT_COLORA))
{
Vec4 val(0, 0, 0, 0);
pVar->Get(val);
pParam->m_Value.m_Color[0] = val.x;
pParam->m_Value.m_Color[1] = val.y;
pParam->m_Value.m_Color[2] = val.z;
pParam->m_Value.m_Color[3] = val.w;
}
else if (pVar->GetType() == IVariable::VECTOR && (pVar->GetDataType() == IVariable::DT_COLOR || pVar->GetDataType() == IVariable::DT_COLORA))
{
Vec3 val(0, 0, 0);
pVar->Get(val);
pParam->m_Value.m_Color[0] = val.x;
pParam->m_Value.m_Color[1] = val.y;
pParam->m_Value.m_Color[2] = val.z;
}
break;
case eType_VECTOR:
if (pVar->GetType() == IVariable::VECTOR)
{
Vec3 val(0, 0, 0);
pVar->Get(val);
pParam->m_Value.m_Vector[0] = val.x;
pParam->m_Value.m_Vector[1] = val.y;
pParam->m_Value.m_Vector[2] = val.z;
}
break;
default:
break;
}
}
}
void SetPublicVars(CVarBlock* pPublicVars, SInputShaderResources& pInputShaderResources, IRenderShaderResources* pRenderShaderResources, IShader* pShader)
{
SetPublicVars(pPublicVars, pInputShaderResources);
// Set shader params.
if (pRenderShaderResources)
{
pRenderShaderResources->SetShaderParams(&pInputShaderResources, pShader);
}
}
//////////////////////////////////////////////////////////////////////////
CVarBlock* GetShaderGenParamsVars(IShader* pShader, uint64 nShaderGenMask)
{
IShader* pTemplShader = pShader;
if (!pTemplShader)
{
return 0;
}
SShaderGen* pShaderGen = pTemplShader->GetGenerationParams();
if (!pShaderGen)
{
return 0;
}
CVarBlock* pBlock = new CVarBlock;
for (int i = 0; i < pShaderGen->m_BitMask.size(); i++)
{
SShaderGenBit* pGenBit = pShaderGen->m_BitMask[i];
if (pGenBit->m_Flags & SHGF_HIDDEN)
{
continue;
}
if (!pGenBit->m_ParamProp.empty())
{
CVariable<bool>* pVar = new CVariable<bool>;
pBlock->AddVariable(pVar);
pVar->SetName(pGenBit->m_ParamProp.c_str());
*pVar = (pGenBit->m_Mask & nShaderGenMask) != 0;
pVar->SetDescription(pGenBit->m_ParamDesc.c_str());
}
}
/*
// make sure if no valid generation parameters to not create new tab
if (!pBlock->GetVarsCount())
{
SAFE_DELETE(pBlock);
return 0;
}
*/
return pBlock;
}
//////////////////////////////////////////////////////////////////////////
uint64 SetShaderGenParamsVars(IShader* pShader, CVarBlock* pBlock)
{
IShader* pTemplShader = pShader;
if (!pTemplShader)
{
return 0;
}
SShaderGen* pShaderGen = pTemplShader->GetGenerationParams();
if (!pShaderGen)
{
return 0;
}
uint64 nGenMask = 0;
for (int i = 0; i < pShaderGen->m_BitMask.size(); i++)
{
SShaderGenBit* pGenBit = pShaderGen->m_BitMask[i];
if (pGenBit->m_Flags & SHGF_HIDDEN)
{
continue;
}
if (!pGenBit->m_ParamProp.empty())
{
IVariable* pVar = pBlock->FindVariable(pGenBit->m_ParamProp.c_str());
if (!pVar)
{
continue;
}
bool bFlagOn = false;
pVar->Get(bFlagOn);
if (bFlagOn)
{
nGenMask |= pGenBit->m_Mask;
}
}
}
return nGenMask;
}
}
@@ -0,0 +1,67 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include "Material.h"
namespace MaterialHelpers
{
//////////////////////////////////////////////////////////////////////////
//! Get public parameters of material in variable block.
CVarBlock* GetPublicVars(SInputShaderResources& pShaderResources);
//! Sets variable block of public shader parameters.
//! VarBlock must be in same format as returned by GetPublicVars().
void SetPublicVars(CVarBlock* pPublicVars, SInputShaderResources& pInputShaderResources);
void SetPublicVars(CVarBlock* pPublicVars, SInputShaderResources& pInputShaderResources, IRenderShaderResources* pRenderShaderResources, IShader* pShader);
//////////////////////////////////////////////////////////////////////////
CVarBlock* GetShaderGenParamsVars(IShader* pShader, uint64 nShaderGenMask);
uint64 SetShaderGenParamsVars(IShader* pShader, CVarBlock* pBlock);
//////////////////////////////////////////////////////////////////////////
// [Shader System TO DO] change the usage of these functions to retrieve by slot name
inline EEfResTextures FindTexSlot(const char* texName) { return GetIEditor()->Get3DEngine()->GetMaterialHelpers().FindTexSlot(texName); }
inline const char* FindTexName(EEfResTextures texSlot) { return GetIEditor()->Get3DEngine()->GetMaterialHelpers().FindTexName(texSlot); }
inline const char* LookupTexName(EEfResTextures texSlot) { return GetIEditor()->Get3DEngine()->GetMaterialHelpers().LookupTexName(texSlot); }
inline const char* LookupTexDesc(EEfResTextures texSlot) { return GetIEditor()->Get3DEngine()->GetMaterialHelpers().LookupTexDesc(texSlot); }
//--------------------------------------------------------------------------
// Adjustable means that the slot is not virtual, i.e. using a sub-channel from another
// slot (for example - smoothness that uses the normal's alpha)
inline bool IsAdjustableTexSlot(EEfResTextures texSlot) { return GetIEditor()->Get3DEngine()->GetMaterialHelpers().IsAdjustableTexSlot(texSlot); }
//////////////////////////////////////////////////////////////////////////
inline void SetTexModFromXml(SEfTexModificator& pShaderResources, const XmlNodeRef& node) { GetIEditor()->Get3DEngine()->GetMaterialHelpers().SetTexModFromXml(pShaderResources, node); }
inline void SetXmlFromTexMod(const SEfTexModificator& pShaderResources, XmlNodeRef& node) { GetIEditor()->Get3DEngine()->GetMaterialHelpers().SetXmlFromTexMod(pShaderResources, node); }
//////////////////////////////////////////////////////////////////////////
inline void SetTexturesFromXml(SInputShaderResources& pShaderResources, const XmlNodeRef& node) { GetIEditor()->Get3DEngine()->GetMaterialHelpers().SetTexturesFromXml(pShaderResources, node); }
inline void SetXmlFromTextures( SInputShaderResources& pShaderResources, XmlNodeRef& node) { GetIEditor()->Get3DEngine()->GetMaterialHelpers().SetXmlFromTextures(pShaderResources, node); }
//////////////////////////////////////////////////////////////////////////
inline void SetVertexDeformFromXml(SInputShaderResources& pShaderResources, const XmlNodeRef& node) { GetIEditor()->Get3DEngine()->GetMaterialHelpers().SetVertexDeformFromXml(pShaderResources, node); }
inline void SetXmlFromVertexDeform(const SInputShaderResources& pShaderResources, XmlNodeRef& node) { GetIEditor()->Get3DEngine()->GetMaterialHelpers().SetXmlFromVertexDeform(pShaderResources, node); }
//////////////////////////////////////////////////////////////////////////
inline void SetLightingFromXml(SInputShaderResources& pShaderResources, const XmlNodeRef& node) { GetIEditor()->Get3DEngine()->GetMaterialHelpers().SetLightingFromXml(pShaderResources, node); }
inline void SetXmlFromLighting(const SInputShaderResources& pShaderResources, XmlNodeRef& node) { GetIEditor()->Get3DEngine()->GetMaterialHelpers().SetXmlFromLighting(pShaderResources, node); }
//////////////////////////////////////////////////////////////////////////
inline void SetShaderParamsFromXml(SInputShaderResources& pShaderResources, const XmlNodeRef& node) { GetIEditor()->Get3DEngine()->GetMaterialHelpers().SetShaderParamsFromXml(pShaderResources, node); }
inline void SetXmlFromShaderParams(const SInputShaderResources& pShaderResources, XmlNodeRef& node) { GetIEditor()->Get3DEngine()->GetMaterialHelpers().SetXmlFromShaderParams(pShaderResources, node); }
//////////////////////////////////////////////////////////////////////////
inline void MigrateXmlLegacyData(SInputShaderResources& pShaderResources, const XmlNodeRef& node) { GetIEditor()->Get3DEngine()->GetMaterialHelpers().MigrateXmlLegacyData(pShaderResources, node); }
}
@@ -0,0 +1,834 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "MaterialImageListCtrl.h"
// Qt
#include <QMenu>
// Editor
#include "Material.h"
#include "MaterialManager.h"
#include "MaterialPreviewModelView.h"
#include "MaterialBrowser.h"
#include "Util/Image.h"
#define ME_BG_TEXTURE "Editor/Materials/Stripes.dds"
#define MATERIAL_EDITOR_SPHERE_MODEL_FILE "Editor/Objects/MtlSphere.cgf"
#define MATERIAL_EDITOR_SPHERE_CAMERA_RADIUS 1.6f
#define MATERIAL_EDITOR_SPHERE_CAMERA_FROM_DIRECTION Vec3(0.1f, -1.0f, -0.1f)
#define MATERIAL_EDITOR_BOX_MODEL_FILE "Editor/Objects/MtlBox.cgf"
#define MATERIAL_EDITOR_BOX_CAMERA_RADIUS 2.0f
#define MATERIAL_EDITOR_BOX_CAMERA_FROM_DIRECTION Vec3(0.75f, -0.75f, -0.5f)
#define MATERIAL_EDITOR_TEAPOT_MODEL_FILE "Editor/Objects/MtlTeapot.cgf"
#define MATERIAL_EDITOR_TEAPOT_CAMERA_RADIUS 1.6f
#define MATERIAL_EDITOR_TEAPOT_CAMERA_FROM_DIRECTION Vec3(0.1f, -0.75f, -0.25f)
#define MATERIAL_EDITOR_PLANE_MODEL_FILE "Editor/Objects/MtlPlane.cgf"
#define MATERIAL_EDITOR_PLANE_CAMERA_RADIUS 1.6f
#define MATERIAL_EDITOR_PLANE_CAMERA_FROM_DIRECTION Vec3(-0.5f, 0.5f, -0.5f)
#define MATERIAL_EDITOR_SWATCH_MODEL_FILE "Editor/Objects/MtlSwatch.cgf"
#define MATERIAL_EDITOR_SWATCH_CAMERA_RADIUS 1.0f
#define MATERIAL_EDITOR_SWATCH_CAMERA_FROM_DIRECTION Vec3(0.0f, 0.0f, -1.0f)
_smart_ptr<CMaterial> ResolveTerrainLayerPreviewMaterial(_smart_ptr<CMaterial> material, _smart_ptr<CMaterial> pMatPreview)
{
if (!QString::compare(material->GetShaderName(), "Terrain.Layer"))
{
XmlNodeRef node = XmlHelpers::CreateXmlNode("Material");
CBaseLibraryItem::SerializeContext ctx(node, false);
material->Serialize(ctx);
if (!pMatPreview)
{
int flags = 0;
if (node->getAttr("MtlFlags", flags))
{
flags |= MTL_FLAG_UIMATERIAL;
node->setAttr("MtlFlags", flags);
}
pMatPreview = GetIEditor()->GetMaterialManager()->CreateMaterial("_NewPreview_", node);
}
else
{
CBaseLibraryItem::SerializeContext ctx2(node, true);
pMatPreview->Serialize(ctx2);
}
pMatPreview->SetShaderName("Illum");
pMatPreview->Update();
return pMatPreview;
}
else
{
return material;
}
}
struct QMaterialImageListModel::Item
{
QImage image;
void* pUserData;
QPoint position;
QSize size;
_smart_ptr<CMaterial> pMaterial;
QStringList vVisibleTextures;
};
//////////////////////////////////////////////////////////////////////////
void CMaterialImageListCtrl::OnCreate()
{
// m_largePreviewCtrl is used to draw the 3D preview for the selected material
m_largePreviewCtrl.reset(new MaterialPreviewModelView(this));
m_largePreviewCtrl->hide();
// m_renderCtrl is used to draw all the sub-materials
m_renderCtrl.reset(new MaterialPreviewModelView(this, false /* disable idle updates since this is only used to create the preview list images */));
m_renderCtrl->UnSetFlag(CPreviewModelView::PreviewModelViewFlag::SHOW_GRID);
m_renderCtrl->UnSetFlag(CPreviewModelView::PreviewModelViewFlag::SHOW_GRID_AXIS);
if (gEnv->pSystem)
{
gEnv->pSystem->GetISystemEventDispatcher()->RegisterListener(this);
}
// m_resizeTimer is used to stall updateGeometries being called via resizeEvents until RESIZE_TIMEOUT ms after resizing
// This prevents an Editor freeze caused by constant resizing of the Material Editor when viewing
// a high sub-material count material (LY-58389)
m_resizeTimer = new QTimer(this);
connect(m_resizeTimer, &QTimer::timeout, this, &CMaterialImageListCtrl::ResizeTimeout);
}
void CMaterialImageListCtrl::resizeEvent([[maybe_unused]] QResizeEvent* event)
{
m_resizeTimer->stop();
m_resizeTimer->start(RESIZE_TIMEOUT);
}
void CMaterialImageListCtrl::ResizeTimeout()
{
m_resizeTimer->stop();
updateGeometries();
}
//////////////////////////////////////////////////////////////////////////
void CMaterialImageListCtrl::OnDestroy()
{
if (gEnv->pSystem)
{
gEnv->pSystem->GetISystemEventDispatcher()->RemoveListener(this);
}
}
//////////////////////////////////////////////////////////////////////////
CMaterialImageListCtrl::CMaterialImageListCtrl(QWidget* parent)
: CImageListCtrl(parent)
{
OnCreate();
}
//////////////////////////////////////////////////////////////////////////
CMaterialImageListCtrl::~CMaterialImageListCtrl()
{
OnDestroy();
}
//////////////////////////////////////////////////////////////////////////
QModelIndex QMaterialImageListModel::AddMaterial(CMaterial* pMaterial, void* pUserData)
{
Item* pItem = new Item;
pItem->pMaterial = pMaterial;
pItem->pUserData = pUserData;
pMaterial->GetAnyTextureFilenames(pItem->vVisibleTextures);
const int row = m_items.count();
beginInsertRows(QModelIndex(), row, row);
m_items.insert(row, pItem);
endInsertRows();
return index(row, 0);
}
//////////////////////////////////////////////////////////////////////////
void QMaterialImageListModel::SetMaterial(int nItemIndex, CMaterial* pMaterial, void* pUserData)
{
assert(nItemIndex <= (int)m_items.size());
Item* pItem = m_items.at(nItemIndex);
pItem->vVisibleTextures.clear();
pItem->pMaterial = pMaterial;
pItem->pUserData = pUserData;
pMaterial->GetAnyTextureFilenames(pItem->vVisibleTextures);
pItem->image = QImage();
QModelIndex idx = index(nItemIndex, 0);
emit dataChanged(idx, idx, QVector<int>() << Qt::DisplayRole << Qt::DecorationRole);
}
//////////////////////////////////////////////////////////////////////////
QModelIndex QMaterialImageListModel::FindMaterial(CMaterial* const pMaterial)
{
for (int i = 0; i < m_items.count(); i++)
{
Item* item = m_items[i];
if (pMaterial == item->pMaterial)
{
return index(i, 0);
}
}
return QModelIndex();
}
//////////////////////////////////////////////////////////////////////////
void CMaterialImageListCtrl::SelectMaterial(CMaterial* pMaterial)
{
// Force the material to load the highest resolution textures
pMaterial->GetMatInfo()->DisableTextureStreaming();
selectionModel()->clearSelection();
QMaterialImageListModel* materialModel = qobject_cast<QMaterialImageListModel*>(model());
Q_ASSERT(materialModel);
QModelIndex index = materialModel->FindMaterial(pMaterial);
if (index.isValid())
{
selectionModel()->select(index, QItemSelectionModel::SelectCurrent);
m_largePreviewMaterial = pMaterial;
}
else
{
// If the parent material was selected, set the first sub-material as the large preview's material so it has something to render
if (pMaterial->GetSubMaterialCount() > 0)
{
m_largePreviewMaterial = pMaterial->GetSubMaterial(0);
}
}
GenerateAllImages();
}
//////////////////////////////////////////////////////////////////////////
void QMaterialImageListModel::InvalidateMaterial(CMaterial* pMaterial)
{
QModelIndex idx = FindMaterial(pMaterial);
Item* pItem = ItemFromIndex(idx);
if ((pItem) && (m_renderCtrl))
{
if (pMaterial)
{
// Ensure the full resolution textures are loaded for the material editor
pMaterial->GetMatInfo()->DisableTextureStreaming();
}
pItem->vVisibleTextures.clear();
pMaterial->GetAnyTextureFilenames(pItem->vVisibleTextures);
pItem->image = QImage();
GenerateImage(pItem);
emit dataChanged(idx, idx, QVector<int>() << Qt::DecorationRole);
}
}
//////////////////////////////////////////////////////////////////////////
void QMaterialImageListModel::DeleteAllItems()
{
if (m_renderCtrl)
{
m_renderCtrl->SetMaterial(nullptr);
}
beginResetModel();
qDeleteAll(m_items);
m_items.clear();
endResetModel();
}
//////////////////////////////////////////////////////////////////////////
void QMaterialImageListModel::GenerateImage(Item* pItem)
{
// Make a bitmap from image.
Q_ASSERT(pItem);
if (!m_renderCtrl || !pItem->size.isValid())
{
return;
}
if (pItem->image.size() == pItem->size)
{
return;
}
Item* pMtlItem = pItem;
CImageEx image;
bool bPreview = false;
if (pMtlItem->pMaterial)
{
if (!(pMtlItem->pMaterial->GetFlags() & MTL_FLAG_NOPREVIEW))
{
if (!m_renderCtrl->GetStaticModel())
{
AZ_Warning("Material Editor", false, "Preview renderer has no object loaded!");
return;
}
// keep m_renderCtrl off screen, but visible
m_renderCtrl->setGeometry(QRect(-QPoint(pItem->size.width(), pItem->size.height()), pItem->size));
_smart_ptr<CMaterial> matPreview = ResolveTerrainLayerPreviewMaterial(pItem->pMaterial, m_pMatPreview);
m_renderCtrl->SetMaterial(matPreview->GetMatInfo());
m_renderCtrl->GetImageOffscreen(image, pMtlItem->size);
}
bPreview = true;
}
if (!bPreview)
{
image.Allocate(pItem->size.width(), pItem->size.height());
image.Clear();
}
pItem->image = QImage(image.GetWidth(), image.GetHeight(), QImage::Format_RGB32);
memcpy(pItem->image.bits(), image.GetData(), image.GetSize());
}
#define MENU_USE_DEFAULT 1
#define MENU_USE_BOX 2
#define MENU_USE_PLANE 3
#define MENU_USE_SPHERE 4
#define MENU_USE_TEAPOT 5
#define MENU_BG_BLACK 6
#define MENU_BG_GRAY 7
#define MENU_BG_WHITE 8
#define MENU_BG_TEXTURE 9
#define MENU_USE_BACKLIGHT 10
//////////////////////////////////////////////////////////////////////////
void CMaterialImageListCtrl::contextMenuEvent(QContextMenuEvent* event)
{
QAction* action;
QMenu menu;
action = menu.addAction(tr("Use Default Object"));
action->setData(ModelDefault);
action->setCheckable(true);
action->setChecked(m_modelType == ModelType::Default);
action = menu.addAction(tr("Use Plane"));
action->setData(ModelPlane);
action->setCheckable(true);
action->setChecked(m_modelType == ModelType::Plane);
action = menu.addAction(tr("Use Box"));
action->setData(ModelBox);
action->setCheckable(true);
action->setChecked(m_modelType == ModelType::Box);
action = menu.addAction(tr("Use Sphere"));
action->setData(ModelSphere);
action->setCheckable(true);
action->setChecked(m_modelType == ModelType::Sphere);
action = menu.addAction(tr("Use Teapot"));
action->setData(ModelTeapot);
action->setCheckable(true);
action->setChecked(m_modelType == ModelType::Teapot);
menu.addSeparator();
// If there is a currently selected material
if (m_materialBrowserWidget && m_largePreviewMaterial)
{
// Add context menu actions that are common to both the material browser and the preview swatches
m_materialBrowserWidget->AddContextMenuActionsSingleSelection(menu, m_largePreviewMaterial);
}
action = menu.exec(mapToGlobal(event->pos()));
if (!action)
{
return;
}
int cmd = action->data().toInt();
switch (cmd)
{
case ModelDefault:
m_modelType = ModelType::Default;
LoadModel();
break;
case ModelPlane:
m_modelType = ModelType::Plane;
LoadModel();
break;
case ModelBox:
m_modelType = ModelType::Box;
LoadModel();
break;
case ModelSphere:
m_modelType = ModelType::Sphere;
LoadModel();
break;
case ModelTeapot:
m_modelType = ModelType::Teapot;
LoadModel();
break;
default:
// If there is a currently selected material
if (m_materialBrowserWidget && m_largePreviewMaterial)
{
// Handle context menu actions that are common to both the material browser and the preview swatches
m_materialBrowserWidget->OnContextMenuAction(cmd, m_largePreviewMaterial);
}
break;
}
QMaterialImageListModel* materialModel =
qobject_cast<QMaterialImageListModel*>(model());
Q_ASSERT(materialModel);
materialModel->GenerateImages();
update();
}
//////////////////////////////////////////////////////////////////////////
void CMaterialImageListCtrl::LoadModel()
{
switch (m_modelType)
{
case ModelType::Default:
case ModelType::Sphere:
m_largePreviewCtrl->LoadModelFile(MATERIAL_EDITOR_SPHERE_MODEL_FILE);
m_largePreviewCtrl->SetCameraLookAt(MATERIAL_EDITOR_SPHERE_CAMERA_RADIUS, MATERIAL_EDITOR_SPHERE_CAMERA_FROM_DIRECTION);
break;
case ModelType::Box:
m_largePreviewCtrl->LoadModelFile(MATERIAL_EDITOR_BOX_MODEL_FILE);
m_largePreviewCtrl->SetCameraLookAt(MATERIAL_EDITOR_BOX_CAMERA_RADIUS, MATERIAL_EDITOR_BOX_CAMERA_FROM_DIRECTION);
break;
case ModelType::Teapot:
m_largePreviewCtrl->LoadModelFile(MATERIAL_EDITOR_TEAPOT_MODEL_FILE);
m_largePreviewCtrl->SetCameraLookAt(MATERIAL_EDITOR_TEAPOT_CAMERA_RADIUS, MATERIAL_EDITOR_TEAPOT_CAMERA_FROM_DIRECTION);
break;
case ModelType::Plane:
m_largePreviewCtrl->LoadModelFile(MATERIAL_EDITOR_PLANE_MODEL_FILE);
m_largePreviewCtrl->SetCameraLookAt(MATERIAL_EDITOR_PLANE_CAMERA_RADIUS, MATERIAL_EDITOR_PLANE_CAMERA_FROM_DIRECTION);
break;
}
GenerateAllImages();
}
//////////////////////////////////////////////////////////////////////////
void CMaterialImageListCtrl::updateGeometries()
{
ClearItemGeometries();
m_updatingGeometries = true;
if (!model())
{
return;
}
const int rowCount = model()->rowCount();
if (!rowCount)
{
m_largePreviewCtrl->hide();
m_largePreviewMaterial = nullptr;
return;
}
m_largePreviewCtrl->setParent(nullptr);
m_largePreviewCtrl->setParent(this);
m_largePreviewCtrl->show();
const int bwidth = BorderSize().width();
const int bheight = BorderSize().height();
m_largePreviewCtrl->move(bwidth, bheight);
QRect rc = viewport()->contentsRect()
.adjusted(bwidth, bheight, -bwidth, -bheight);
int cy = rc.height();
// Preview item is big.
if (model()->rowCount() > 0)
{
QSize size(cy, rc.height());
m_largePreviewCtrl->SetSize(size);
m_largePreviewCtrl->resize(size);
rc.setLeft(rc.left() + cy + bwidth * 2);
m_largePreviewCtrl->show();
}
int cx = rc.width() - bwidth;
int itemSize = cy;
// Adjust all other bitmaps as tight as possible.
int numItems = model()->rowCount();
int div = 0;
for (div = 1; div < 1000 && itemSize > 0; div++)
{
int nX = cx / (itemSize + 2);
if (nX >= numItems)
{
break;
}
if (nX > 0)
{
int nY = numItems / nX + 1;
if (nY * (itemSize + 2) < cy)
{
//itemSize = itemSize -= 2;
break;
}
}
itemSize = itemSize -= 2;
}
if (itemSize < 0)
{
itemSize = 0;
}
QPoint pos(rc.topLeft());
const QSize size(itemSize, itemSize);
m_renderCtrl->SetSize(size);
for (int row = 0; row < numItems; ++row)
{
QModelIndex index = model()->index(row, 0);
SetItemGeometry(index, QRect(pos, size));
model()->setData(index, pos, QMaterialImageListModel::PositionRole);
model()->setData(index, size, Qt::SizeHintRole);
pos.rx() += itemSize + 2;
if (pos.rx() + itemSize >= rc.right())
{
pos.rx() = rc.left();
pos.ry() += itemSize + 2;
}
}
m_updatingGeometries = false;
update();
}
//////////////////////////////////////////////////////////////////////////
void CMaterialImageListCtrl::setModel(QAbstractItemModel* model)
{
QMaterialImageListModel* materialModel = qobject_cast<QMaterialImageListModel*>(model);
if (materialModel)
{
materialModel->SetPreviewModelCtrl(m_renderCtrl.data());
}
CImageListCtrl::setModel(model);
auto delegate = qobject_cast<QImageListDelegate*>(itemDelegate());
// in case the delegate misses a pixmap we can generate it (done only once not at every painting)
connect(delegate, SIGNAL(invalidPixmapGenerated(const QModelIndex&)), materialModel, SLOT(GenerateImage(const QModelIndex&)));
connect(model, &QAbstractItemModel::dataChanged, this, &CMaterialImageListCtrl::ModelDataChanged);
}
void CMaterialImageListCtrl::ModelDataChanged(const QModelIndex& index)
{
// Prevent the hundreads of resize calls done in a row
// to trigger a new image computation that we already have
if (m_updatingGeometries)
{
return;
}
GenerateImage(index);
}
//////////////////////////////////////////////////////////////////////////
void CMaterialImageListCtrl::showEvent(QShowEvent* event)
{
CImageListCtrl::showEvent(event);
if (!m_largePreviewCtrl->GetStaticModel())
{
LoadModel();
}
if (!m_renderCtrl->GetStaticModel())
{
m_renderCtrl->LoadModelFile(MATERIAL_EDITOR_SWATCH_MODEL_FILE);
m_renderCtrl->SetCameraLookAt(MATERIAL_EDITOR_SWATCH_CAMERA_RADIUS, MATERIAL_EDITOR_SWATCH_CAMERA_FROM_DIRECTION);
}
}
//////////////////////////////////////////////////////////////////////////
void CMaterialImageListCtrl::OnSystemEvent(ESystemEvent event, UINT_PTR wparam, [[maybe_unused]] UINT_PTR lparam)
{
switch (event)
{
// Toggle visibility of this control whenever the main editor window has a change of focus.
case ESYSTEM_EVENT_CHANGE_FOCUS:
{
setAttribute(Qt::WA_WState_Visible, (wparam != 0));
break;
}
}
;
}
void CMaterialImageListCtrl::UpdateLargePreview()
{
if (m_largePreviewMaterial)
{
m_largePreviewCtrl->SetMaterial(ResolveTerrainLayerPreviewMaterial(m_largePreviewMaterial, m_tempTerrainMaterial)->GetMatInfo());
m_largePreviewCtrl->show();
m_largePreviewCtrl->update();
}
}
void CMaterialImageListCtrl::GenerateImage(const QModelIndex& index)
{
QMaterialImageListModel* materialModel =
qobject_cast<QMaterialImageListModel*>(model());
Q_ASSERT(materialModel);
if (m_largePreviewMaterial == materialModel->MaterialFromIndex(index))
{
UpdateLargePreview();
}
materialModel->SetPreviewModelCtrl(m_renderCtrl.data());
materialModel->GenerateImage(index);
update();
}
void CMaterialImageListCtrl::GenerateAllImages()
{
QMaterialImageListModel* materialModel =
qobject_cast<QMaterialImageListModel*>(model());
Q_ASSERT(materialModel);
UpdateLargePreview();
materialModel->SetPreviewModelCtrl(m_renderCtrl.data());
materialModel->GenerateImages();
update();
}
//////////////////////////////////////////////////////////////////////////
QMaterialImageListModel::QMaterialImageListModel(QObject* parent)
: QAbstractListModel(parent)
, m_renderCtrl(nullptr)
{
BusConnect(AZ_CRC("dds", 0x780234cb));
}
//////////////////////////////////////////////////////////////////////////
QMaterialImageListModel::~QMaterialImageListModel()
{
BusDisconnect();
qDeleteAll(m_items);
}
//////////////////////////////////////////////////////////////////////////
MaterialPreviewModelView* QMaterialImageListModel::PreviewModelCtrl() const
{
return m_renderCtrl;
}
//////////////////////////////////////////////////////////////////////////
void QMaterialImageListModel::SetPreviewModelCtrl(MaterialPreviewModelView* ctrl)
{
if (m_renderCtrl)
{
disconnect(m_renderCtrl, &MaterialPreviewModelView::destroyed,
this, &QMaterialImageListModel::ClearPreviewModelCtrl);
}
m_renderCtrl = ctrl;
if (m_renderCtrl)
{
connect(m_renderCtrl, &MaterialPreviewModelView::destroyed,
this, &QMaterialImageListModel::ClearPreviewModelCtrl);
}
}
//////////////////////////////////////////////////////////////////////////
void QMaterialImageListModel::ClearPreviewModelCtrl()
{
m_renderCtrl = nullptr;
}
//////////////////////////////////////////////////////////////////////////
int QMaterialImageListModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid())
{
return 0;
}
return m_items.count();
}
//////////////////////////////////////////////////////////////////////////
QVariant QMaterialImageListModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
Item* pItem = m_items.at(index.row());
Q_ASSERT(pItem);
switch (role)
{
case Qt::DisplayRole:
return pItem->pMaterial->GetShortName();
case Qt::DecorationRole:
return QPixmap::fromImage(pItem->image);
case PositionRole:
return pItem->position;
case Qt::SizeHintRole:
return pItem->size;
default:
break;
}
return QVariant();
}
//////////////////////////////////////////////////////////////////////////
bool QMaterialImageListModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (!index.isValid())
{
return false;
}
Item* pItem = ItemFromIndex(index);
if (!pItem)
{
return false;
}
if (Qt::SizeHintRole == role)
{
pItem->size = value.toSize();
emit dataChanged(index, index, QVector<int>() << Qt::DecorationRole << Qt::SizeHintRole);
return true;
}
if (PositionRole == role)
{
pItem->position = value.toPoint();
}
return false;
}
//////////////////////////////////////////////////////////////////////////
Qt::ItemFlags QMaterialImageListModel::flags(const QModelIndex& index) const
{
return QAbstractListModel::flags(index);
}
//////////////////////////////////////////////////////////////////////////
CMaterial* QMaterialImageListModel::MaterialFromIndex(QModelIndex index) const
{
Item* pItem = ItemFromIndex(index);
if (!pItem)
{
return nullptr;
}
return pItem->pMaterial;
}
//////////////////////////////////////////////////////////////////////////
void* QMaterialImageListModel::UserDataFromIndex(QModelIndex index) const
{
Item* pItem = ItemFromIndex(index);
if (!pItem)
{
return nullptr;
}
return pItem->pUserData;
}
//////////////////////////////////////////////////////////////////////////
void QMaterialImageListModel::GenerateImages()
{
if ((!m_renderCtrl) || (m_items.isEmpty()))
{
return;
}
for (Item* materialImageListItem : m_items)
{
GenerateImage(materialImageListItem);
}
}
void QMaterialImageListModel::GenerateImage(const QModelIndex& index)
{
if ((!m_renderCtrl) || (m_items.isEmpty()))
{
return;
}
GenerateImage(ItemFromIndex(index));
}
//////////////////////////////////////////////////////////////////////////
QMaterialImageListModel::Item* QMaterialImageListModel::ItemFromIndex(QModelIndex index) const
{
if (!index.isValid())
{
return nullptr;
}
return m_items.at(index.row());
}
//////////////////////////////////////////////////////////////////////////
void QMaterialImageListModel::OnFileChanged(AZStd::string assetPath)
{
const int rowCount = m_items.count();
// update all previews who's texture(s) changed
for (int row = 0; row < rowCount; ++row)
{
Item* pItem = m_items.at(row);
QStringList::const_iterator textureIt = std::find(pItem->vVisibleTextures.begin(), pItem->vVisibleTextures.end(), assetPath.c_str());
if (textureIt != pItem->vVisibleTextures.end())
{
pItem->image = QImage();
QModelIndex idx = index(row, 0);
emit dataChanged(idx, idx, QVector<int>() << Qt::DecorationRole);
}
}
}
#include <Material/moc_MaterialImageListCtrl.cpp>
@@ -0,0 +1,164 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_MATERIAL_MATERIALIMAGELISTCTRL_H
#define CRYINCLUDE_EDITOR_MATERIAL_MATERIALIMAGELISTCTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include "Controls/ImageListCtrl.h"
#include <QAbstractListModel>
#include "Material.h"
#include "IRenderer.h" // IAsyncTextureCompileListener
#include "IResourceCompilerHelper.h"
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <QScopedPointer>
#include <QPointer>
#endif
class MaterialPreviewModelView;
class MaterialBrowserWidget;
//////////////////////////////////////////////////////////////////////////
class CMaterialImageListCtrl
: public CImageListCtrl
, public ISystemEventListener
{
Q_OBJECT
public:
enum class ModelType
{
Default,
Box,
Sphere,
Teapot,
Plane
};
enum MenuAction
{
ModelDefault = 0,
ModelPlane,
ModelBox,
ModelSphere,
ModelTeapot,
// Context menu actions that are common to both the material browser and the preview swatches
// are handled by the same switch statement, so they need to be unique. This sets the starting point for the common actions
MaterialBrowserWidgetActionsStart
};
CMaterialImageListCtrl(QWidget* parent = nullptr);
~CMaterialImageListCtrl();
void setModel(QAbstractItemModel* model) override;
void EnableAutoRefresh(bool autoRefreshState, unsigned int refreshInterval);
void SelectMaterial(CMaterial* pMaterial);
void LoadModel();
void SetMaterialBrowserWidget(MaterialBrowserWidget* materialBrowserWidget){ m_materialBrowserWidget = materialBrowserWidget; }
// ISystemEventListener
// Due to the material editor working on a ProcessEvents -> Timer based system, rather than the OnIdle update event loops
// that the other editor windows use, make sure that when the editor loses focus that the Material Editor itself loses focus.
// This will pause updates/renderings in the material editor when it does not have focus.
// This prevents certain materials from re-creating themselves and eventually overflowing a few resource buffers.
// The main window cleans up those resources during its main update, which is bypassed when the window does not have focus.
virtual void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam);
// ~ISystemEventListener
// Override resizeEvent and use this const to rate limit it such that it only fires RESIZE_TIMEOUT ms after resizing stops
static const int RESIZE_TIMEOUT = 100;
void resizeEvent(QResizeEvent* event) override;
public slots:
void ModelDataChanged(const QModelIndex& index);
void ResizeTimeout();
protected:
void OnCreate();
void OnDestroy();
void contextMenuEvent(QContextMenuEvent* event) override;
void showEvent(QShowEvent* event) override;
void updateGeometries() override;
private:
void GenerateImage(const QModelIndex& index);
void GenerateAllImages();
void UpdateLargePreview();
QScopedPointer<MaterialPreviewModelView> m_largePreviewCtrl; // Used to draw the main 3D preview viewport for the selected sub-material
_smart_ptr<CMaterial> m_largePreviewMaterial;
_smart_ptr<CMaterial> m_tempTerrainMaterial;
QScopedPointer<MaterialPreviewModelView> m_renderCtrl; // Used to draw the swatches for all the sub-materials
int m_nColor;
bool m_updatingGeometries = false;
ModelType m_modelType = ModelType::Default;
MaterialBrowserWidget* m_materialBrowserWidget;
QTimer* m_resizeTimer; // Used to stall a resizeEvent from firing until RESIZE_TIMEOUT ms have passed since resizing stopped
};
class QMaterialImageListModel
: public QAbstractListModel
, public AzFramework::LegacyAssetEventBus::Handler
{
struct Item;
Q_OBJECT
public:
enum CustomRoles
{
PositionRole = Qt::UserRole
};
QMaterialImageListModel(QObject* parent = nullptr);
~QMaterialImageListModel();
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
QModelIndex AddMaterial(CMaterial* pMaterial, void* pUserData = nullptr);
void SetMaterial(int nItemIndex, CMaterial* pMaterial, void* pUserData = nullptr);
QModelIndex FindMaterial(CMaterial* pMaterial);
void InvalidateMaterial(CMaterial* pMaterial);
void DeleteAllItems();
MaterialPreviewModelView* PreviewModelCtrl() const;
void SetPreviewModelCtrl(MaterialPreviewModelView* ctrl);
CMaterial* MaterialFromIndex(QModelIndex index) const;
void* UserDataFromIndex(QModelIndex index) const;
public slots:
void GenerateImages();
void GenerateImage(const QModelIndex& index);
protected:
void GenerateImage(Item* pItem);
Item* ItemFromIndex(QModelIndex index) const;
private slots:
void ClearPreviewModelCtrl();
private:
void OnFileChanged(AZStd::string assetPath) override;
private:
_smart_ptr<CMaterial> m_pMatPreview;
QPointer<MaterialPreviewModelView> m_renderCtrl;
QVector<Item*> m_items;
};
#endif // CRYINCLUDE_EDITOR_MATERIAL_MATERIALIMAGELISTCTRL_H
@@ -0,0 +1,151 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "MaterialLibrary.h"
// Editor
#include "BaseLibraryItem.h"
//////////////////////////////////////////////////////////////////////////
// CMaterialLibrary implementation.
//////////////////////////////////////////////////////////////////////////
bool CMaterialLibrary::Save()
{
return SaveLibrary("MaterialLibrary");
}
//////////////////////////////////////////////////////////////////////////
bool CMaterialLibrary::Load(const QString& filename)
{
if (filename.isEmpty())
{
return false;
}
SetFilename(filename);
XmlNodeRef root = XmlHelpers::LoadXmlFromFile(filename.toUtf8().data());
if (!root)
{
return false;
}
Serialize(root, true);
return true;
}
//////////////////////////////////////////////////////////////////////////
void CMaterialLibrary::Serialize([[maybe_unused]] XmlNodeRef& root, [[maybe_unused]] bool bLoading)
{
/*
if (bLoading)
{
// Loading.
CString name = GetName();
root->getAttr( "Name",name );
SetName( name );
for (int i = 0; i < root->getChildCount(); i++)
{
XmlNodeRef itemNode = root->getChild(i);
CMaterial *material = new CMaterial(itemNode->getAttr("Name"));
AddItem( material );
CBaseLibraryItem::SerializeContext ctx( itemNode,bLoading );
material->Serialize( ctx );
}
SetModified(false);
}
else
{
// Saving.
root->setAttr( "Name",GetName() );
root->setAttr( "SandboxVersion",(const char*)GetIEditor()->GetFileVersion().ToFullString() );
// Serialize prototypes.
for (int i = 0; i < GetItemCount(); i++)
{
CMaterial *pMtl = (CMaterial*)GetItem(i);
// Save materials with parents under thier parent xml node.
if (pMtl->GetParent())
continue;
XmlNodeRef itemNode = root->newChild( "Material" );
CBaseLibraryItem::SerializeContext ctx( itemNode,bLoading );
GetItem(i)->Serialize( ctx );
}
}
*/
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void CMaterialLibrary::AddItem(IDataBaseItem* item, bool bRegister)
{
CBaseLibraryItem* pLibItem = (CBaseLibraryItem*)item;
// Check if item is already assigned to this library.
if (pLibItem->GetLibrary() != this)
{
pLibItem->SetLibrary(this);
if (bRegister)
{
m_pManager->RegisterItem(pLibItem);
}
m_items.push_back(pLibItem);
}
}
//////////////////////////////////////////////////////////////////////////
IDataBaseItem* CMaterialLibrary::GetItem(int index)
{
assert(index >= 0 && index < m_items.size());
return m_items[index];
}
//////////////////////////////////////////////////////////////////////////
void CMaterialLibrary::RemoveItem(IDataBaseItem* item)
{
for (int i = 0; i < m_items.size(); i++)
{
if (m_items[i] == item)
{
m_items.erase(m_items.begin() + i);
SetModified();
break;
}
}
}
//////////////////////////////////////////////////////////////////////////
IDataBaseItem* CMaterialLibrary::FindItem(const QString& name)
{
for (int i = 0; i < m_items.size(); i++)
{
if (QString::compare(m_items[i]->GetName(), name, Qt::CaseInsensitive) == 0)
{
return m_items[i];
}
}
return NULL;
}
//////////////////////////////////////////////////////////////////////////
void CMaterialLibrary::RemoveAllItems()
{
AddRef();
for (int i = 0; i < m_items.size(); i++)
{
// Clear library item.
m_items[i]->SetLibrary(NULL);
}
m_items.clear();
Release();
}
@@ -0,0 +1,47 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_MATERIAL_MATERIALLIBRARY_H
#define CRYINCLUDE_EDITOR_MATERIAL_MATERIALLIBRARY_H
#pragma once
#include "BaseLibrary.h"
/** Library of prototypes.
*/
class CRYEDIT_API CMaterialLibrary
: public CBaseLibrary
{
public:
CMaterialLibrary(IBaseLibraryManager* pManager)
: CBaseLibrary(pManager) {};
virtual bool Save();
virtual bool Load(const QString& filename);
virtual void Serialize(XmlNodeRef& node, bool bLoading);
//////////////////////////////////////////////////////////////////////////
// CBaseLibrary override.
//////////////////////////////////////////////////////////////////////////
void AddItem(IDataBaseItem* item, bool bRegister = true);
int GetItemCount() const { return m_items.size(); }
IDataBaseItem* GetItem(int index);
void RemoveItem(IDataBaseItem* item);
void RemoveAllItems();
IDataBaseItem* FindItem(const QString& name);
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
std::vector<CBaseLibraryItem*> m_items;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
#endif // CRYINCLUDE_EDITOR_MATERIAL_MATERIALLIBRARY_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,300 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_MATERIAL_MATERIALMANAGER_H
#define CRYINCLUDE_EDITOR_MATERIAL_MATERIALMANAGER_H
#pragma once
#include "BaseLibraryManager.h"
#include "Material.h"
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <Include/IEditorMaterialManager.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/std/string/wildcard.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzCore/std/parallel/mutex.h>
#include <AzCore/std/parallel/binary_semaphore.h>
#include <AzCore/std/parallel/thread.h>
class CMaterial;
class CMaterialLibrary;
class CMaterialHighlighter;
namespace AZ
{
struct Uuid;
}
namespace AzToolsFramework
{
namespace AssetBrowser
{
struct SourceFileOpenerDetails;
typedef AZStd::vector<SourceFileOpenerDetails> SourceFileOpenerList;
}
}
enum EHighlightMode
{
eHighlight_Pick = BIT(0),
eHighlight_Breakable = BIT(1),
eHighlight_NoSurfaceType = BIT(2),
eHighlight_All = 0xFFFFFFFF
};
AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
/** Manages all entity prototypes and material libraries.
*/
class CRYEDIT_API CMaterialManager
: public IEditorMaterialManager
, public CBaseLibraryManager
, public IMaterialManagerListener
, protected AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler
, public AzToolsFramework::AssetBrowser::AssetBrowserModelNotificationBus::Handler
, public AzFramework::AssetCatalogEventBus::Handler
, public AzToolsFramework::EditorEvents::Bus::Handler
{
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
public:
//! Notification callback.
typedef Functor0 NotifyCallback;
CMaterialManager(CRegistrationContext& regCtx);
~CMaterialManager();
void Set3DEngine();
// Clear all prototypes and material libraries.
void ClearAll();
//////////////////////////////////////////////////////////////////////////
// Materials.
//////////////////////////////////////////////////////////////////////////
// Loads material.
CMaterial* LoadMaterial(const QString& sMaterialName, bool bMakeIfNotFound = true);
XmlNodeRef LoadXmlNode(const QString &fullSourcePath, const QString &relativeFilePath);
//! Loads a material, avoiding a call to CMaterialManager::MaterialToFilename if the full path is already known
CMaterial* LoadMaterialWithFullSourcePath(const QString& relativeFilePath, const QString& fullSourcePath, bool makeIfNotFound = true);
virtual CMaterial* LoadMaterial(const char* sMaterialName, bool bMakeIfNotFound = true);
virtual void OnRequestMaterial(_smart_ptr<IMaterial> pMaterial);
// Creates a new material from a xml node.
CMaterial* CreateMaterial(const QString& sMaterialName, const XmlNodeRef& node = XmlNodeRef(), int nMtlFlags = 0, unsigned long nLoadingFlags = 0);
virtual CMaterial* CreateMaterial(const char* sMaterialName, const XmlNodeRef& node = XmlNodeRef(), int nMtlFlags = 0, unsigned long nLoadingFlags = 0);
// Duplicate material and do nothing more.
CMaterial* DuplicateMaterial(const char* newName, CMaterial* pOriginal);
// Delete specified material, erases material file, and unassigns from all objects.
virtual void DeleteMaterial(CMaterial* pMtl);
virtual void RemoveMaterialFromDisk(const char* fileName);
//! Export property manager to game.
void Export(XmlNodeRef& node);
int ExportLib(CMaterialLibrary* pLib, XmlNodeRef& libNode);
virtual void SetSelectedItem(IDataBaseItem* pItem);
void SetCurrentMaterial(CMaterial* pMtl);
//! Get currently active material.
CMaterial* GetCurrentMaterial() const;
void SetCurrentFolder(const QString& folder);
// This material will be highlighted
void SetHighlightedMaterial(CMaterial* pMtl);
void GetHighlightColor(ColorF* color, float* intensity, int flags);
void HighlightedMaterialChanged(CMaterial* pMtl);
// highlightMask is a combination of EHighlightMode flags
void SetHighlightMask(int highlightMask);
int GetHighlightMask() const{ return m_highlightMask; }
void SetMarkedMaterials(const std::vector<_smart_ptr<CMaterial> >& markedMaterials);
void OnLoadShader(CMaterial* pMaterial);
//! Serialize property manager.
virtual void Serialize(XmlNodeRef& node, bool bLoading);
virtual void SaveAllLibs();
//////////////////////////////////////////////////////////////////////////
// IMaterialManagerListener implementation
//////////////////////////////////////////////////////////////////////////
// Called when material manager tries to load a material.
virtual void OnCreateMaterial(_smart_ptr<IMaterial> pMaterial);
virtual void OnDeleteMaterial(_smart_ptr<IMaterial> pMaterial);
virtual bool IsCurrentMaterial(_smart_ptr<IMaterial> pMaterial) const;
//////////////////////////////////////////////////////////////////////////
// Convert filename of material file into the name of the material.
QString FilenameToMaterial(const QString& filename);
// Convert name of the material to the filename.
QString MaterialToFilename(const QString& sMaterialName);
//! Get the full file path of the source material
const AZ::Data::AssetType& GetMaterialAssetType();
//////////////////////////////////////////////////////////////////////////
// Convert 3DEngine IMaterial to Editor's CMaterial pointer.
CMaterial* FromIMaterial(_smart_ptr<IMaterial> pMaterial);
// Open File selection dialog to create a new material.
CMaterial* SelectNewMaterial(int nMtlFlags, const char* sStartPath = NULL);
// Synchronize material between 3dsMax and editor.
void SyncMaterialEditor();
//////////////////////////////////////////////////////////////////////////
void GotoMaterial(CMaterial* pMaterial);
void GotoMaterial(_smart_ptr<IMaterial> pMaterial);
// Gather resources from the game material.
static void GatherResources(_smart_ptr<IMaterial> pMaterial, CUsedResources& resources);
void Command_Create();
void Command_CreateMulti();
void Command_ConvertToMulti();
void Command_Duplicate();
void Command_Merge();
void Command_Delete();
void Command_AssignToSelection();
void Command_ResetSelection();
void Command_SelectAssignedObjects();
void Command_SelectFromObject();
protected:
//////////////////////////////////////////////////////////////////////////////////////////////////////
// protected AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler
void AddSourceFileOpeners(const char* fullSourceFileName, const AZ::Uuid& sourceUUID, AzToolsFramework::AssetBrowser::SourceFileOpenerList& openers) override;
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
// public AzToolsFramework::AssetBrowser::AssetBrowserModelNotificationsBus::Handler
void EntryAdded(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry);
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
// AssetCatalogEventBus Functions
void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override;
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
// EditorEventsBus Functions
bool SkipEditorStartupUI() override;
//////////////////////////////////////////////////////////////////////////////////////////////////////
void SaveDccMaterial(const AZStd::string& relativeDccMaterialPath);
bool DccMaterialRequiresSave(const AZStd::string& relativeDccMaterialPath, const AZStd::string& fullSourcePath);
void DccMaterialSourceControlCheck(const AZStd::string& relativeDccMaterialPath);
void AddDccMaterialPath(const AZStd::string relativeDccMaterialPath);
void TickSourceControl();
void QueueSourceControlTick();
// Duplicate the source material and set it as a submaterial of the target material at subMaterialIndex. Returns true if successful.
bool DuplicateAsSubMaterialAtIndex(CMaterial* pSourceMaterial, CMaterial* pTargetMaterial, int subMaterialIndex);
// Generates a unique variant of the name of the source material to resolve name collisions with the names of the submaterials in the target material.
void GenerateUniqueSubmaterialName(const CMaterial* pSourceMaterial, const CMaterial* pTargetMaterial, QString& uniqueSubmaterialName) const;
// Open save as dialog for saving materials.
bool SelectSaveMaterial(QString& itemName, QString& fullPath, const char* defaultStartPath);
void OnEditorNotifyEvent(EEditorNotifyEvent event);
virtual CBaseLibraryItem* MakeNewItem();
virtual CBaseLibrary* MakeNewLibrary();
//! Root node where this library will be saved.
virtual QString GetRootNodeName();
//! Path to libraries in this manager.
virtual QString GetLibsPath();
virtual void ReportDuplicateItem(CBaseLibraryItem* pItem, CBaseLibraryItem* pOldItem);
void RegisterCommands(CRegistrationContext& regCtx);
void UpdateHighlightedMaterials();
void AddForHighlighting(CMaterial* pMaterial);
void RemoveFromHighlighting(CMaterial* pMaterial, int mask);
int GetHighlightFlags(CMaterial* pMaterial) const;
// For material syncing with 3dsMax.
void PickPreviewMaterial();
void InitMatSender();
//! Reloads any registered materials that have been modified by the runtime.
void ReloadDirtyMaterials();
protected:
QString m_libsPath;
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
// Currently selected (focused) material, in material browser.
_smart_ptr<CMaterial> m_pCurrentMaterial;
// current selected folder
QString m_currentFolder;
// List of materials selected in material browser tree.
std::vector<_smart_ptr<CMaterial> > m_markedMaterials;
// IMaterial is needed to not let 3Dengine release selected IMaterial
_smart_ptr<IMaterial> m_pCurrentEngineMaterial;
// Paths of .dccmtl that might require saving. They will be fed to
// DccMaterialSourceControlCheck() and if saving is required, they
// will be added to m_dccMaterialSaveBuffer
AZStd::vector<AZStd::string> m_sourceControlBuffer;
AZStd::mutex m_sourceControlBufferMutex;
AZStd::atomic_bool m_sourceControlFunctionQueued;
// Paths of .dccmtl that require saving. These paths have
// gone through DccMaterialSourceControlCheck() and will
// be saved by the dcc material save thread
AZStd::vector<AZStd::string> m_dccMaterialSaveBuffer;
AZStd::mutex m_dccMaterialSaveMutex;
// Material highlighting
_smart_ptr<CMaterial> m_pHighlightMaterial;
CMaterialHighlighter* m_pHighlighter;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
int m_highlightMask;
bool m_bHighlightingMaterial;
// Only begin processing .dccmtl filepaths once editor is ready
// and we can display appropriate error messages
bool m_bEditorUiReady;
// Only report source control error to the user once,
// no need to spam them for every material
bool m_bSourceControlErrorReported;
class CMaterialSender* m_MatSender;
private:
CMaterial* LoadMaterialInternal(const QString &materialNameClear, const QString &fullSourcePath, const QString &relativeFilePath, bool bMakeIfNotFound);
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
AZ::Data::AssetType m_materialAssetType;
AZ::Data::AssetType m_dccMaterialAssetType;
void StartDccMaterialSaveThread();
void DccMaterialSaveThreadFunc();
AZStd::thread m_dccMaterialSaveThread;
AZStd::atomic_bool m_joinThreads;
AZStd::binary_semaphore m_dccMaterialSaveSemaphore;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
#endif // CRYINCLUDE_EDITOR_MATERIAL_MATERIALMANAGER_H
@@ -0,0 +1,170 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "MaterialPickTool.h"
// Editor
#include "MaterialManager.h"
#include "SurfaceInfoPicker.h"
#include "Viewport.h"
#define RENDER_MESH_TEST_DISTANCE 0.2f
static IClassDesc * s_ToolClass = NULL;
//////////////////////////////////////////////////////////////////////////
CMaterialPickTool::CMaterialPickTool()
{
m_pClassDesc = s_ToolClass;
m_statusText = tr("Left Click To Pick Material");
}
//////////////////////////////////////////////////////////////////////////
CMaterialPickTool::~CMaterialPickTool()
{
SetMaterial(0);
}
//////////////////////////////////////////////////////////////////////////
bool CMaterialPickTool::MouseCallback(CViewport* view, EMouseEvent event, QPoint& point, int flags)
{
if (event == eMouseLDown)
{
if (m_pMaterial)
{
CMaterial* pMtl = GetIEditor()->GetMaterialManager()->FromIMaterial(m_pMaterial);
if (pMtl)
{
GetIEditor()->GetMaterialManager()->SetHighlightedMaterial(0);
GetIEditor()->OpenMaterialLibrary(pMtl);
Abort();
return true;
}
}
}
else if (event == eMouseMove)
{
return OnMouseMove(view, flags, point);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
void CMaterialPickTool::Display(DisplayContext& dc)
{
QPoint mousePoint = QCursor::pos();
dc.view->ScreenToClient(mousePoint);
Vec3 wp = dc.view->ViewToWorld(mousePoint);
if (m_pMaterial)
{
float color[4] = {1, 1, 1, 1};
dc.renderer->Draw2dLabel(mousePoint.x() + 12, mousePoint.y ()+ 8, 1.2f, color, false, "%s", m_displayString.toUtf8().data());
}
float fScreenScale = dc.view->GetScreenScaleFactor(m_HitInfo.vHitPos) * 0.06f;
dc.DepthTestOff();
dc.SetColor(ColorB(0, 0, 255, 255));
if (!m_HitInfo.vHitNormal.IsZero())
{
dc.DrawLine(m_HitInfo.vHitPos, m_HitInfo.vHitPos + m_HitInfo.vHitNormal * fScreenScale);
Vec3 raySrc, rayDir;
dc.view->ViewToWorldRay(mousePoint, raySrc, rayDir);
Matrix34 tm;
Vec3 zAxis = m_HitInfo.vHitNormal;
Vec3 xAxis = rayDir.Cross(zAxis);
if (!xAxis.IsZero())
{
xAxis.Normalize();
Vec3 yAxis = xAxis.Cross(zAxis).GetNormalized();
tm.SetFromVectors(xAxis, yAxis, zAxis, m_HitInfo.vHitPos);
dc.PushMatrix(tm);
dc.DrawCircle(Vec3(0, 0, 0), 0.5f * fScreenScale);
dc.PopMatrix();
}
}
dc.DepthTestOn();
}
//////////////////////////////////////////////////////////////////////////
bool CMaterialPickTool::OnMouseMove(CViewport* view, [[maybe_unused]] UINT nFlags, const QPoint& point)
{
view->SetCurrentCursor(STD_CURSOR_HIT, "");
_smart_ptr<IMaterial> pNearestMaterial(NULL);
m_Mouse2DPosition = point;
CSurfaceInfoPicker surfacePicker;
int nPickObjectGroupFlag = CSurfaceInfoPicker::ePOG_All;
if (surfacePicker.Pick(point, pNearestMaterial, m_HitInfo, NULL, nPickObjectGroupFlag))
{
SetMaterial(pNearestMaterial);
return true;
}
SetMaterial(0);
return false;
}
const GUID& CMaterialPickTool::GetClassID()
{
// {FD20F6F2-7B87-4349-A5D4-7533538E357F}
static const GUID guid = {
0xfd20f6f2, 0x7b87, 0x4349, { 0xa5, 0xd4, 0x75, 0x33, 0x53, 0x8e, 0x35, 0x7f }
};
return guid;
}
//////////////////////////////////////////////////////////////////////////
void CMaterialPickTool::RegisterTool(CRegistrationContext& rc)
{
rc.pClassFactory->RegisterClass(s_ToolClass = new CQtViewClass<CMaterialPickTool>("EditTool.PickMaterial", "Material", ESYSTEM_CLASS_EDITTOOL));
}
//////////////////////////////////////////////////////////////////////////
void CMaterialPickTool::SetMaterial(_smart_ptr<IMaterial> pMaterial)
{
if (pMaterial == m_pMaterial)
{
return;
}
m_pMaterial = pMaterial;
CMaterial* pCMaterial = GetIEditor()->GetMaterialManager()->FromIMaterial(m_pMaterial);
GetIEditor()->GetMaterialManager()->SetHighlightedMaterial(pCMaterial);
m_displayString = "";
if (pMaterial)
{
QString sfType;
sfType = QStringLiteral("%1 : %2").arg(pMaterial->GetSurfaceType()->GetId()).arg(pMaterial->GetSurfaceType()->GetName());
m_displayString = "\n";
m_displayString += pMaterial->GetName();
m_displayString += "\n";
m_displayString += sfType;
}
}
#include <Material/moc_MaterialPickTool.cpp>
@@ -0,0 +1,57 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Definition of PickObjectTool, tool used to pick objects.
#ifndef CRYINCLUDE_EDITOR_MATERIAL_MATERIALPICKTOOL_H
#define CRYINCLUDE_EDITOR_MATERIAL_MATERIALPICKTOOL_H
#pragma once
#include "EditTool.h"
//////////////////////////////////////////////////////////////////////////
class CMaterialPickTool
: public CEditTool
{
Q_OBJECT
public:
Q_INVOKABLE CMaterialPickTool();
static const GUID& GetClassID();
static void RegisterTool(CRegistrationContext& rc);
//////////////////////////////////////////////////////////////////////////
// CEditTool implementation
//////////////////////////////////////////////////////////////////////////
virtual bool MouseCallback(CViewport* view, EMouseEvent event, QPoint& point, int flags);
virtual void Display(DisplayContext& dc);
//////////////////////////////////////////////////////////////////////////
protected:
bool OnMouseMove(CViewport* view, UINT nFlags, const QPoint& point);
void SetMaterial(_smart_ptr<IMaterial> pMaterial);
virtual ~CMaterialPickTool();
// Delete itself.
void DeleteThis() { delete this; };
_smart_ptr<IMaterial> m_pMaterial;
QString m_displayString;
QPoint m_Mouse2DPosition;
SRayHitInfo m_HitInfo;
};
#endif // CRYINCLUDE_EDITOR_MATERIAL_MATERIALPICKTOOL_H
@@ -0,0 +1,77 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "EditorDefs.h"
#include "MaterialPreviewModelView.h"
MaterialPreviewModelView::MaterialPreviewModelView(QWidget* parent, bool enableIdleUpdate)
: CPreviewModelView(parent)
, m_enableIdleUpdate(enableIdleUpdate)
{}
MaterialPreviewModelView::~MaterialPreviewModelView()
{}
void MaterialPreviewModelView::SetCameraLookAt(float fRadiusScale, const Vec3& fromDir)
{
IStatObj* target = GetStaticModel();
if (target)
{
Vec3 dir = fromDir.GetNormalized();
// Calculate the camera matrix
Matrix34 tm = Matrix33::CreateRotationVDir(dir, 0);
Vec3 translation = target->GetAABB().GetCenter() - dir * target->GetAABB().GetRadius() * fRadiusScale;
tm.SetTranslation(translation);
// Convert it to a quaternion and move the viewport's camera
QuatT quat(tm);
CameraMoved(quat, true);
}
}
void MaterialPreviewModelView::SetMaterial(_smart_ptr<IMaterial> material)
{
IStatObj* staticModel = GetStaticModel();
if (staticModel)
{
staticModel->SetMaterial(material);
}
}
void MaterialPreviewModelView::resizeEvent(QResizeEvent* event)
{
if (m_enableIdleUpdate)
{
CPreviewModelView::resizeEvent(event);
}
}
void MaterialPreviewModelView::OnEditorNotifyEvent(EEditorNotifyEvent event)
{
switch (event)
{
case eNotify_OnIdleUpdate:
{
if (m_enableIdleUpdate)
{
Update();
}
break;
}
default:
break;
}
}
@@ -0,0 +1,35 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include "PreviewModelView.h"
class MaterialPreviewModelView
: public CPreviewModelView
{
public:
// set enableIdleUpdate to false if you don't want the view to update itself during
// application idle notification (and resize events). That makes sense when this view
// is only used to render into memory bitmaps. Note that the view has to been visible
// for that, but can be somewhere of-screen.
MaterialPreviewModelView(QWidget* parent, bool enableIdleUpdate = true);
virtual ~MaterialPreviewModelView();
void SetCameraLookAt(float fRadiusScale, const Vec3& fromDir);
void SetMaterial(_smart_ptr<IMaterial> material);
void OnEditorNotifyEvent(EEditorNotifyEvent event) override;
protected:
void resizeEvent(QResizeEvent* event) override;
private:
bool m_enableIdleUpdate;
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,34 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Include/SandboxAPI.h>
#include <AzCore/Component/Component.h>
namespace AzToolsFramework
{
//! A component to reflect scriptable commands for materials in the Editor
class MaterialPythonFuncsHandler
: public AZ::Component
{
public:
AZ_COMPONENT(MaterialPythonFuncsHandler, "{E437BCF2-DE71-43E1-A7EC-DD243EB41F0B}")
SANDBOX_API static void Reflect(AZ::ReflectContext* context);
// AZ::Component ...
void Activate() override {}
void Deactivate() override {}
};
} // namespace AzToolsFramework
@@ -0,0 +1,416 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "EditorDefs.h"
//AZCore
#include <AzCore/Math/MathUtils.h>
//CRY
#include <ILogFile.h>
#include <IMaterial.h>
//Editor
#include <Settings.h>
#include <IEditor.h>
#include <IResourceSelectorHost.h>
#include <Util/Variable.h>
#include <Util/VariablePropertyType.h>
#include <QViewport.h>
#include <QViewportSettings.h>
#include <DisplaySettings.h>
//EditorCommon
#include <RenderHelpers/AxisHelper.h>
//Local
#include "PreviewModelView.h"
//QT
#include <qapplication.h>
CPreviewModelView::CPreviewModelView(QWidget* parent)
: QViewport(parent, QViewport::StartupMode_Manual) // Manual since we need to set WA_DontCreateNativeAncestors before QViewport::Startup() creates the internal native window and propagates
, m_Flags(0)
, m_GridColor(150, 150, 150, 40)
, m_BackgroundColor(0.5f, 0.5f, 0.5f)
, m_TimeScale(1.0f)
, m_PlayState(PlayState::NONE)
, m_pStaticModel(nullptr)
, m_PostUpdateCallback(nullptr)
, m_ContextMenuCallback(nullptr)
{
#ifdef Q_OS_MACOS
// Don't propagate the nativeness up, as dockwidgets on macOS don't like it
setAttribute(Qt::WA_DontCreateNativeAncestors);
#endif
Startup();
//////////////////////////////////////////////////////////
//QViewport
AddConsumer(this);
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
//IEditorNotifyListener
GetIEditor()->RegisterNotifyListener(this);
//////////////////////////////////////////////////////////
FocusOnScreen();//update the camera ...
SetDefaultFlags();
UpdateSettings();
}
CPreviewModelView::~CPreviewModelView()
{
CRY_ASSERT(GetIEditor());
//////////////////////////////////////////////////////////
//IEditorNotifyListener
GetIEditor()->UnregisterNotifyListener(this);
//////////////////////////////////////////////////////////
ReleaseModel();
}
bool CPreviewModelView::IsFlagSet(PreviewModelViewFlag flag) const
{
CRY_ASSERT(flag < PreviewModelViewFlag::END_POSSIBLE_ITEMS);
return (m_Flags & (1 << static_cast<unsigned int>(flag)));
}
void CPreviewModelView::ToggleFlag(PreviewModelViewFlag flag)
{
CRY_ASSERT(flag < PreviewModelViewFlag::END_POSSIBLE_ITEMS);
if (IsFlagSet(flag))
{
UnSetFlag(flag);
}
else
{
SetFlag(flag);
}
}
void CPreviewModelView::SetFlag(PreviewModelViewFlag flag)
{
CRY_ASSERT(flag < PreviewModelViewFlag::END_POSSIBLE_ITEMS);
m_Flags |= (1 << static_cast<unsigned int>(flag));
}
void CPreviewModelView::UnSetFlag(PreviewModelViewFlag flag)
{
CRY_ASSERT(flag < PreviewModelViewFlag::END_POSSIBLE_ITEMS);
m_Flags &= ~(1 << static_cast<unsigned int>(flag));
}
void CPreviewModelView::ResetPlaybackControls()
{
UnSetFlag(CPreviewModelView::PreviewModelViewFlag::LOOPING_PLAY);
m_TimeScale = 1.0f;
}
void CPreviewModelView::ResetBackgroundColor()
{
SetBackgroundColor(ColorF(0.5f, 0.5f, 0.5f));
}
void CPreviewModelView::ResetGridColor()
{
SetGridColor(ColorF(150, 150, 150, 40));
}
void CPreviewModelView::ResetCamera()
{
FocusOnScreen();
}
void CPreviewModelView::ResetAll()
{
ResetPlaybackControls();
ResetGridColor();
ResetBackgroundColor();
ReleaseModel();
SetDefaultFlags();
}
void CPreviewModelView::SetGridColor(ColorF color)
{
m_GridColor = color;
}
void CPreviewModelView::SetBackgroundColor(ColorF color)
{
m_BackgroundColor = color;
}
void CPreviewModelView::SetPlayState(PlayState state)
{
m_PlayState = state;
}
void CPreviewModelView::SetTimeScale(float scale)
{
m_TimeScale = scale;
}
CPreviewModelView::PlayState CPreviewModelView::GetPlayState() const
{
return m_PlayState;
}
float CPreviewModelView::GetTimeScale() const
{
return m_TimeScale;
}
ColorF CPreviewModelView::GetGridColor() const
{
return m_GridColor;
}
ColorF CPreviewModelView::GetBackgroundColor() const
{
return m_BackgroundColor;
}
void CPreviewModelView::ImportModel()
{
SResourceSelectorContext x;
x.typeName = Prop::GetPropertyTypeToResourceType(ePropertyModel);
QString currPath = m_ModelFilename.toLower();
QString selected = GetIEditor()->GetResourceSelectorHost()->SelectResource(x, currPath);
LoadModelFile(selected);
}
void CPreviewModelView::SetPostUpdateCallback(PostUpdateCallback callback)
{
m_PostUpdateCallback = callback;
}
void CPreviewModelView::SetContextMenuCallback(ContextMenuCallback callback)
{
m_ContextMenuCallback = callback;
}
////////////////////////////////////////////////////////
//QViewportConsumer
void CPreviewModelView::OnViewportRender(const SRenderContext& rc)
{
///UPDATE
UpdateSettings();//Some changes may take effect next frame ...
//External updating ...
if (m_PostUpdateCallback)
{
m_PostUpdateCallback();
}
//RENDER
CRY_ASSERT(rc.renderParams);
CRY_ASSERT(rc.passInfo);
RenderModels(*rc.renderParams, *rc.passInfo);
}
void CPreviewModelView::OnViewportKey([[maybe_unused]] const SKeyEvent& ev)
{
}
void CPreviewModelView::OnViewportMouse([[maybe_unused]] const SMouseEvent& ev)
{
}
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
//IEditorNotifyListener
void CPreviewModelView::OnEditorNotifyEvent(EEditorNotifyEvent event)
{
switch (event)
{
case eNotify_OnCloseScene:
{
ResetAll();
break;
}
case eNotify_OnIdleUpdate:
{
Update();
}
break;
default:
break;
}
}
///////////////////////////////////////////////////////
void CPreviewModelView::UpdateSettings()
{
//Update the settings
SViewportSettings allsettings;
allsettings.background.topColor = m_BackgroundColor;
allsettings.background.useGradient = false;
allsettings.grid.showGrid = IsFlagSet(PreviewModelViewFlag::SHOW_GRID);
allsettings.grid.middleColor = m_BackgroundColor;
allsettings.grid.mainColor = m_GridColor;
allsettings.rendering.wireframe = IsFlagSet(PreviewModelViewFlag::DRAW_WIREFRAME);
allsettings.rendering.sunlight = IsFlagSet(PreviewModelViewFlag::ENABLE_TIME_OF_DAY);
allsettings.rendering.fps = false;
// Set zoom speed to 1.0f for better UI workflow, instead of the default 0.1f
allsettings.camera.zoomSpeed = 1.0f;
allsettings.camera.moveSpeed = GetIEditor()->GetEditorSettings()->cameraMoveSpeed;
allsettings.camera.fastMoveMultiplier = GetIEditor()->GetEditorSettings()->cameraFastMoveSpeed;
allsettings.camera.rotationSpeed = GetIEditor()->GetEditorSettings()->cameraRotateSpeed;
allsettings.camera.showViewportOrientation = IsFlagSet(PreviewModelViewFlag::SHOW_GRID_AXIS);
//Set them ...
SetSettings(allsettings);
}
void CPreviewModelView::RenderModels(SRendParams& rendParams, SRenderingPassInfo& passInfo)
{
if (m_pStaticModel)
{
if (IsFlagSet(PreviewModelViewFlag::PRECACHE_MATERIAL))
{
_smart_ptr<IMaterial> pCurMat = m_pStaticModel->GetMaterial();
if (pCurMat)
{
pCurMat->PrecacheMaterial(0.0f, nullptr, true, true);
}
}
m_pStaticModel->Render(rendParams, passInfo);
}
}
void CPreviewModelView::ReleaseModel()
{
m_ModelFilename = "";
SAFE_RELEASE(m_pStaticModel);
}
void CPreviewModelView::SetDefaultFlags()
{
UnSetFlag(PreviewModelViewFlag::SHOW_OVERDRAW);
m_Flags = 0;
SetFlag(PreviewModelViewFlag::SHOW_GRID);
SetFlag(PreviewModelViewFlag::SHOW_GRID_AXIS);
}
void CPreviewModelView::LoadModelFile(const QString& modelFile)
{
//Something to load
if (!modelFile.isEmpty())
{
//Make sure we are not loading the same thing ...
if (m_ModelFilename != modelFile)
{
ReleaseModel();//release any old mesh
QString strFileExt = Path::GetExt(modelFile);
bool isCGF = (QString::compare(strFileExt, CRY_GEOMETRY_FILE_EXT, Qt::CaseInsensitive) == 0);
// NOTE: have to create a local buffer and evaluate the full message here due to calling into another address space to evaluate va_args
// this prevents passing of random data to the log system ... any plugin that does not do it this way is rolling the dice each time.
char buffer[2046];
CRY_ASSERT(GetIEditor());
if (isCGF)
{
CRY_ASSERT(GetIEditor()->Get3DEngine());
// Load object.
m_pStaticModel = GetIEditor()->Get3DEngine()->LoadStatObjUnsafeManualRef(modelFile.toUtf8().data(), nullptr, nullptr, false);
if (m_pStaticModel)
{
m_pStaticModel->AddRef();
}
else
{
CRY_ASSERT(GetIEditor()->GetLogFile());
azsprintf(buffer, "Loading of geometry object %s failed.", modelFile.toUtf8().data());
GetIEditor()->GetLogFile()->Warning(buffer);
}
}
else
{
CRY_ASSERT(GetIEditor()->GetLogFile());
azsprintf(buffer, "Unknown model file (%s) attempting to be loaded.", modelFile.toUtf8().data());
GetIEditor()->GetLogFile()->Warning(buffer);
}
//if something was loaded then we store off the model path for the future
if (m_pStaticModel)
{
m_ModelFilename = modelFile;
}
}
}
}
IStatObj* CPreviewModelView::GetStaticModel()
{
return m_pStaticModel;
}
void CPreviewModelView::FocusOnScreen()
{
CCamera* camera = Camera();
if (camera)
{
AABB accumulated(2);
if (m_pStaticModel)
{
AABB temp;
temp.min = m_pStaticModel->GetBoxMin();
temp.max = m_pStaticModel->GetBoxMax();
accumulated.Add(temp);
}
Vec3 fromDir(1.0f, 1.0f, -0.5f);
Vec3 target = accumulated.GetCenter();
float bbRadius = accumulated.GetRadius();
Vec3 dir = fromDir.GetNormalized();
Matrix34 tm = Matrix33::CreateRotationVDir(dir, 0);
tm.SetTranslation(target - dir * bbRadius);
CameraMoved(QuatT(tm), true);
}
}
float CPreviewModelView::GetSpeedScale() const
{
CRY_ASSERT(GetIEditor());
CRY_ASSERT(GetIEditor()->GetSystem());
CRY_ASSERT(GetIEditor()->GetSystem()->GetITimer());
//Taken from CRenderViewport to mirror controls from there ...
float speedScale = 60.0f * GetIEditor()->GetSystem()->GetITimer()->GetFrameTime();
if (speedScale > 20.0f)
{
speedScale = 20.0f;
}
return speedScale;
}
@@ -0,0 +1,173 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
//Editor
#if !defined(Q_MOC_RUN)
#include "IEditor.h"
#include "../EditorCommon/QViewport.h"
#include "../EditorCommon/QViewportConsumer.h"
#endif
struct IStatObj;
struct SRenderingPassInfo;
struct SRendParams;
class CParticleItem;
class CAxisHelper;
struct HitContext;
struct IGizmoMouseDragHandler;
struct SLodInfo;
namespace RotationDrawHelper
{
class Axis;
}
AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING
class CPreviewModelView
: public QViewport
, public QViewportConsumer
, public IEditorNotifyListener
{
AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
public:
enum class PlayState
{
NONE,
PLAY,
PAUSE,
STEP,
RESET
};
enum class SplineMode
{
NONE,
LINE,
SINEWAVE,
COIL
};
//NOTE: these need to be limited to 32 as these are being used for bit wise operations.
enum class PreviewModelViewFlag
{
DRAW_WIREFRAME = 0,
SHOW_BOUNDINGBOX,
SHOW_GIZMO,
SHOW_GRID,
SHOW_GRID_AXIS,
SHOW_EMITTER_SHAPE,
SHOW_OVERDRAW,
SHOW_FIRST_CONTAINER,
LOOPING_PLAY,
//Spline
SPLINE_LOOPING,
SPLINE_PINGPONG,
PRECACHE_MATERIAL,
ENABLE_TIME_OF_DAY,
END_POSSIBLE_ITEMS = 32,
};
explicit CPreviewModelView(QWidget* parent);
virtual ~CPreviewModelView();
//Flags
bool IsFlagSet(PreviewModelViewFlag flag) const;
void ToggleFlag(PreviewModelViewFlag flag);
virtual void SetFlag(PreviewModelViewFlag flag);
virtual void UnSetFlag(PreviewModelViewFlag flag);
//Resets
void ResetPlaybackControls();
void ResetBackgroundColor();
void ResetGridColor();
virtual void ResetCamera();
virtual void ResetAll();
void SetGridColor(ColorF color);
void SetBackgroundColor(ColorF color);
void SetPlayState(PlayState state);
void SetTimeScale(float scale);
PlayState GetPlayState() const;
float GetTimeScale() const;
ColorF GetGridColor() const;
ColorF GetBackgroundColor() const;
void ImportModel();
void LoadModelFile(const QString& modelFile);
IStatObj* GetStaticModel();
typedef std::function<void()> PostUpdateCallback;
void SetPostUpdateCallback(PostUpdateCallback callback);
typedef std::function<void(Vec2i mousePos)> ContextMenuCallback;
void SetContextMenuCallback(ContextMenuCallback callback);
////////////////////////////////////////////////////////
//QViewportConsumer
virtual void OnViewportRender(const SRenderContext& rc) override;
virtual void OnViewportKey(const SKeyEvent& ev) override;
virtual void OnViewportMouse(const SMouseEvent& ev) override;
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
//IEditorNotifyListener
virtual void OnEditorNotifyEvent(EEditorNotifyEvent event) override;
////////////////////////////////////////////////////////
private:
//Update
void UpdateSettings();
protected:
private:
//Render
void RenderModels(SRendParams& rendParams, SRenderingPassInfo& passInfo);
//Misc
void ReleaseModel();
void SetDefaultFlags();
protected:
void FocusOnScreen();
private:
float GetSpeedScale() const;
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
ColorF m_GridColor;
ColorF m_BackgroundColor;
QString m_ModelFilename;
IStatObj* m_pStaticModel;
protected:
PostUpdateCallback m_PostUpdateCallback;
ContextMenuCallback m_ContextMenuCallback;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
protected:
PlayState m_PlayState;
float m_TimeScale;
private:
unsigned int m_Flags;
};
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3e5d226d043630028a1fe88af240b627a08966066b605294cc79ff8273588166
size 194
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f80544a0537398979dd423ffdc8e99c08e47dbe814cd4225646761cbe281eecc
size 206
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bee8a3d1d5571e5afe0bd02edf0fc58d8297f96494a7c074fd2a37ecda7ae4dc
size 208
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d3d0fc234e3154c06abd4e3b8d3d7e52a75f80d527b1d05541f55221cba199d7
size 183
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ffb3586ef4309d8bfa93a581c38fe970e8b0411f170955921054522b0bf52bd3
size 182
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f258e9617a1d4c7be16fcab12533a4f0bfe4072151f2140ca41364ef1a3cea62
size 196
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:68c609d7bde1796f281690148c19694afddcc2cf4ea306116b546327cb50aad2
size 249
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bd5106eebb6cf264fdac5f3568977fc8f944df4a4d4de6b0e9f35b3a001a3001
size 206
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2b08a8d94c64aed944901b76afcb921c3df2c122c9eac6d52140552c2eb4e5cc
size 227
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:623a937f09f1de3ebb889d7ac9e042f91bf67bef34695801b3603f4517166c8e
size 223
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:623a937f09f1de3ebb889d7ac9e042f91bf67bef34695801b3603f4517166c8e
size 223
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0ad56ab7be0a5a40b644045ac01c90fed6e70433a693e80c5b4b88d63801d75f
size 14604
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9cc845c4eb0ed6fb772064d73f1c679e3b28288411a02f0cafaf248b83b44ca2
size 221
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:966fd5ef5c1e0fe6e920511952d7aea98ad0f165f9ce0197f000f9f48ccd338b
size 14628
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:966fd5ef5c1e0fe6e920511952d7aea98ad0f165f9ce0197f000f9f48ccd338b
size 14628
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c4a8dc85c9928faacd184148aaf0f17ef7b68c185b04b0b5f16e942fb11c528e
size 209
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d9047378404e0893b79bafaad9e6b312d467033daab2191a8c8839e4ff494bd8
size 321
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2e79122edef52c3f9a31fd662f9f5cfd08b06e44e861c7379d8869635fd1d245
size 168
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8cc16bbb4c4c0c1bea14c94d0609f4b11a7cd0ead4af3d236d329a404e8223a8
size 200
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:703f6875258486629bc1db68ce80fe1653f0d2876cd1252d03f72f6eae04dd84
size 392
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3f3c05f8425956e9c1e380fde9a65df8f0d341868900a3589d9f629f34a0ddf6
size 251
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:25554e93a4f0d9b904a2a3628c315ee55c0ad8831bb5891a9d7e27e5cb9a5416
size 261
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b2ee886c6e487a6490361609fdd44c64438e40c7e5a4c40fda866ec399ec4727
size 256
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:943ff19774cbe8d47c1e8001a48e6d137fdac2c0af63c9092911c37be0d6d6a8
size 471
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:400d669d7a658968549f6ee776ee415b871b207c444d8797a404b76d536131b1
size 325
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:02bdc8aad92bb75b118c4a703a3e5b1369376620b3836a125faedc7f6b42b49b
size 330
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:38097d3041defeec0179390a73bb463e54e7f4c1f0b1a20e77ab3f69ea9cf13c
size 332
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e1d9fe8e97655bf776b20e242d66900980721ba2f3ee93c02cd4062a8b872eed
size 433
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:468b68c44d8ef183b292c1bc6ca4dc583357f693db9ea2cd198fb2af22537be1
size 249
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b27a1346f2edebe34ee06d7892a467bfc67952d0f8e4458e09a74a6dbf62fe98
size 336
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c3f40524a96689b8c8549bc662415df654494baeeda6eed47e66b455ac902eeb
size 254
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cebe97de0d879d239fcd61595fd28b73760aa6452dcf4dabc54bfe034e2e33c1
size 476
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:699ee40aeedfc50b7766e94ef66e645762eb183ddd6ce134b4bdab01c3957ab9
size 327
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5f1fd8a7cecb22901c6d73f2e6129f3bada0f94cf73d9a8fd2676a972faa5719
size 348
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d432de9e921d23fbbf3c1b805f644b9554aa206011a87c063d167c30c7c4579e
size 335
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c006e70efd8bdb1dd5aac867db9d97de587e8d518ba0693d19c319e34a74c7d0
size 501
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:00c291361664d357502f5267fccd0a6fff0c3f2e906c0402d57ad9fa5ca7f023
size 255
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d68fc8604a23eb3bcc0cac9bb6e83dd4dc4cdab70b0bdaec3c456559727c6b5b
size 354
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4055c7f029d7711e5c32f3f74901bf1dcc1540fd2c0f3f30dc74743d7f2cc042
size 355
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:99e2c0378137bef94eb6f281e0168852540bf1ca823b9bcd4365ef2849db9956
size 420
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:34075016c93f1914fe4d12ead7c5ee322a18466f08a476eff7c127e4e1429224
size 290
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dc289621f8a9d3e714cd985651d7d6f20495fb7be46e1f60c283d8920c730d0b
size 307
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7e9d5c878acf9fee4252cb854f192690d3f50d46dc4e5f5b1b5812b79fc2cdf7
size 296
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e2e912c40f07ca3d2a9b2aa1c5caf3acc181436b6a1b560fc021450701331b4c
size 403
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:00663e473803ba6c2798c494676b0a65fd62d7484f6d7b51d9fc8955ad586477
size 273
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:34b7d5520cfc8f00ff580558db4f0ec4297458d310321471204daa8678db5fc1
size 298
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7bca1a8811739949aa2e87486f8697dfae5bc5238894dd1b95ae80aa6f80d517
size 279
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:db86d857b651a2fec80418f8b251447bb3fcf4c0cf64bdee12c3b656adbde29a
size 422
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fef72444f077879d5c0186c9583f67aa45aafc23214d9e7de90a7c595609270f
size 258
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d2117fda3ed6a28032ae8f03ed7f9a7a0960f4691e49903c63b2150027dfe1ea
size 302
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:06b384ecc11ed2547a9bf466f585889d647c29a4883840070e444a382e80c41c
size 266
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4b9ee01e7fe2f19b0e736efe09c2b81d3243e369375b4f26346e6abd499e54a6
size 476
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:935a041332be1fe11ed01c09a3cd367fb9ede7e7eb04909614943f80e741d35f
size 334
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2b343233ab25a73fcdb4fff5509011497814fc4cb591065bdb3043a0f1092577
size 342
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1cc0183d7f57f15c4efeffb32795921f2f4e9c7965955fd0a392f826d5f6b6c8
size 337
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9a8467c1dcc343f637e0f7f5071a20b8881a43d567fb32fd96f5383f7b69bbb6
size 430
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0fd5ec79fc2c679f99ac5eb63d725f2cc815b09286bd0f3b5d2921eeb2e1e8cc
size 406
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3e19713b6586581d2336e833d8ecbd78b4b8f15b1c49a29d80acbf05e8aae3df
size 418
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4de0f4a102e5c4990d5d23e22b5cdd16532192f3a125758b608cd8c731278898
size 355
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d0b6f7c69112d124eac1123ffa840268c16148fef9ded3a67f84d3ad8d73eb96
size 212
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0b795fb88a6f301d7ecc9ce75141bdcf9e6dca25043730661aa4d1f09b055d6f
size 222
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:88fbce753cdd9381a814e3b2ec5d16df7cc26f2a37ad30b7010be71ae9183510
size 214