diff --git a/.gitignore b/.gitignore index 3e759b7..09fa7bd 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ *.user *.userosscache *.sln.docstates +*.diagsession # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs @@ -23,15 +24,13 @@ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ +[Gg]enerated*Files/ -# Visual Studio 2015/2017 cache/options directory +# Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ -# Visual Studio 2017 auto generated files -Generated\ Files/ - # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* @@ -45,29 +44,20 @@ TestResult.xml [Rr]eleasePS/ dlldata.c -# Benchmark Results -BenchmarkDotNet.Artifacts/ - # .NET Core project.lock.json project.fragment.lock.json artifacts/ **/Properties/launchSettings.json -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio *_i.c *_p.c *_i.h *.ilk *.meta *.obj -*.iobj *.pch *.pdb -*.ipdb *.pgc *.pgd *.rsp @@ -105,9 +95,6 @@ ipch/ *.vspx *.sap -# Visual Studio Trace Files -*.e2e - # TFS 2012 Local Workspace $tf/ @@ -128,10 +115,6 @@ _TeamCity* # DotCover is a Code Coverage Tool *.dotCover -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - # Visual Studio code coverage results *.coverage *.coveragexml @@ -167,7 +150,7 @@ publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, +# TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj @@ -180,11 +163,11 @@ PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* +**/packages/* # except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ +!**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config +#!**/packages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets @@ -202,7 +185,6 @@ AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt -*.appx # Visual Studio cache files # files ending in .cache can be ignored @@ -221,10 +203,6 @@ ClientBin/ *.publishsettings orleans.codegen.cs -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ @@ -239,8 +217,6 @@ _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak # SQL Server files *.mdf @@ -251,7 +227,6 @@ ServiceFabricBackup/ *.rdl.data *.bim.layout *.bim_*.settings -*.rptproj.rsuser # Microsoft Fakes FakesAssemblies/ @@ -263,6 +238,9 @@ FakesAssemblies/ .ntvs_analysis.dat node_modules/ +# Typescript v1 declaration files +typings/ + # Visual Studio 6 build log *.plg @@ -302,9 +280,6 @@ __pycache__/ # tools/** # !tools/packages.config -# Tabs Studio -*.tss - # Telerik's JustMock configuration file *.jmconfig @@ -313,18 +288,3 @@ __pycache__/ *.btm.cs *.odx.cs *.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ diff --git a/Control/IntegerToIndentationConverter.cpp b/Control/IntegerToIndentationConverter.cpp new file mode 100644 index 0000000..4c48054 --- /dev/null +++ b/Control/IntegerToIndentationConverter.cpp @@ -0,0 +1,33 @@ +#pragma once +#include "pch.h" +#include "IntegerToIndentationConverter.h" + +using namespace Platform; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Interop; + +namespace TreeViewControl { + + IntegerToIndentationConverter::IntegerToIndentationConverter() + { + //default + indentMultiplier = 20; + } + + Object^ IntegerToIndentationConverter::Convert(Object^ value, TypeName targetType, Object^ parameter, String^ language) + { + Thickness indent(0); + if (value != nullptr) + { + indent.Left = (int)value * indentMultiplier; + return indent; + } + + return indent; + } + + Object^ IntegerToIndentationConverter::ConvertBack(Object^ value, TypeName targetType, Object^ parameter, String^ language) + { + throw ref new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Control/IntegerToIndentationConverter.h b/Control/IntegerToIndentationConverter.h new file mode 100644 index 0000000..a567d21 --- /dev/null +++ b/Control/IntegerToIndentationConverter.h @@ -0,0 +1,23 @@ +#pragma once + +namespace TreeViewControl { + [Windows::Foundation::Metadata::WebHostHidden] + public ref class IntegerToIndentationConverter sealed : Windows::UI::Xaml::Data::IValueConverter + { + public: + property int IndentMultiplier + { + int get() { return indentMultiplier; }; + void set(int i) { indentMultiplier = i; }; + } + + IntegerToIndentationConverter(); + + virtual Platform::Object^ Convert(Object^ value, Windows::UI::Xaml::Interop::TypeName targetType, Platform::Object^ parameter, Platform::String^ language); + + virtual Platform::Object^ ConvertBack(Object^ value, Windows::UI::Xaml::Interop::TypeName targetType, Platform::Object^ parameter, Platform::String^ language); + private: + int indentMultiplier; + }; +} + diff --git a/Control/TreeNode.cpp b/Control/TreeNode.cpp new file mode 100644 index 0000000..35b9a4f --- /dev/null +++ b/Control/TreeNode.cpp @@ -0,0 +1,208 @@ +#pragma once +#include "pch.h" +#include "TreeNode.h" + +using namespace Platform; +using namespace Platform::Collections; +using namespace Windows::UI::Xaml::Data; +using namespace Windows::UI::Xaml::Interop; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Collections; + +namespace TreeViewControl { + + TreeNode::TreeNode() + { + childrenVector->VectorChanged += ref new VectorChangedEventHandler(this, &TreeNode::ChildrenVectorChanged); + } + + void TreeNode::Append(Object^ value) + { + int count = childrenVector->Size; + TreeNode^ targetNode = (TreeNode^)value; + targetNode->ParentNode = this; + childrenVector->Append(targetNode); + + //If the count was 0 before we appended, then the HasItems property needs to change. + if (count == 0) + { + this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("HasItems")); + } + + this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("Size")); + } + + void TreeNode::Clear() + { + int count = childrenVector->Size; + TreeNode^ childNode; + for (int i = 0; i < (int)Size; i++) + { + childNode = (TreeNode^)GetAt(i); + childNode->ParentNode = nullptr; + } + + childrenVector->Clear(); + + //If the count was not 0 before we cleared, then the HasItems property needs to change. + if (count != 0) + { + this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("HasItems")); + } + + this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("Size")); + } + + IBindableIterator^ TreeNode::First() + { + return dynamic_cast(childrenVector->First()); + } + + Object^ TreeNode::GetAt(unsigned int index) + { + if ((int)index > -1 && index < childrenVector->Size) + { + return childrenVector->GetAt(index); + } + + return nullptr; + } + + IBindableVectorView^ TreeNode::GetView() + { + return safe_cast(childrenVector->GetView()); + } + + bool TreeNode::IndexOf(Object^ value, unsigned int* index) + { + return childrenVector->IndexOf((TreeNode^)value, index); + } + + void TreeNode::InsertAt(unsigned int index, Object^ value) + { + if ((int)index > -1 && index <= childrenVector->Size) + { + int count = childrenVector->Size; + TreeNode^ targetNode = (TreeNode^)value; + targetNode->ParentNode = this; + return childrenVector->InsertAt(index, (TreeNode^)value); + + //If the count was 0 before we insert, then the HasItems property needs to change. + if (count == 0) + { + this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("HasItems")); + } + + this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("Size")); + } + } + + void TreeNode::RemoveAt(unsigned int index) + { + if ((int)index > -1 && index < childrenVector->Size) + { + int count = childrenVector->Size; + TreeNode^ targetNode = childrenVector->GetAt(index); + targetNode->ParentNode = nullptr; + childrenVector->RemoveAt(index); + + //If the count was 1 before we remove, then the HasItems property needs to change. + if (count == 1) + { + this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("HasItems")); + } + + this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("Size")); + } + } + + void TreeNode::RemoveAtEnd() + { + int count = childrenVector->Size; + TreeNode^ targetNode = childrenVector->GetAt(childrenVector->Size - 1); + targetNode->ParentNode = nullptr; + childrenVector->RemoveAtEnd(); + + //If the count was 1 before we remove, then the HasItems property needs to change. + if (count == 1) + { + this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("HasItems")); + } + + this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("Size")); + } + + void TreeNode::SetAt(unsigned int index, Object^ value) + { + if ((int)index > -1 && index <= childrenVector->Size) + { + childrenVector->GetAt(index)->ParentNode = nullptr; + TreeNode^ targetNode = (TreeNode^)value; + targetNode->ParentNode = this; + return childrenVector->SetAt(index, targetNode); + } + } + + void TreeNode::ChildrenVectorChanged(IObservableVector^ sender, IVectorChangedEventArgs^ e) + { + VectorChanged(this, e); + } + + unsigned int TreeNode::Size::get() + { + return childrenVector->Size; + } + + Object^ TreeNode::Data::get() + { + return data; + } + + void TreeNode::Data::set(Object^ value) + { + data = value; + this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("Data")); + } + + TreeNode^ TreeNode::ParentNode::get() + { + return parentNode; + } + + void TreeNode::ParentNode::set(TreeNode^ value) + { + parentNode = value; + this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("ParentNode")); + this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("Depth")); + } + + bool TreeNode::IsExpanded::get() + { + return isExpanded; + } + + void TreeNode::IsExpanded::set(bool value) + { + isExpanded = value; + this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("IsExpanded")); + } + + bool TreeNode::HasItems::get() + { + return (Size != 0); + } + + int TreeNode::Depth::get() + { + TreeNode^ ancestorNode = this; + int depth = -1; + while ((ancestorNode->ParentNode) != nullptr) + { + depth++; + ancestorNode = ancestorNode->ParentNode; + } + + return depth; + } + +} \ No newline at end of file diff --git a/Control/TreeNode.h b/Control/TreeNode.h new file mode 100644 index 0000000..3feab84 --- /dev/null +++ b/Control/TreeNode.h @@ -0,0 +1,100 @@ +#pragma once + +namespace TreeViewControl { + /// + /// The TreeNode class implements the hierarchical layout for the TreeView. + /// It also holds the data that will be bound to in the item template. + /// + [Windows::UI::Xaml::Data::Bindable] + [Windows::Foundation::Metadata::WebHostHidden] + public ref class TreeNode sealed : Windows::UI::Xaml::Interop::IBindableObservableVector, Windows::UI::Xaml::Data::INotifyPropertyChanged + { + public: + TreeNode(); + + virtual void Append(Object^ value); + + virtual void Clear(); + + virtual Windows::UI::Xaml::Interop::IBindableIterator^ First(); + + virtual Object^ GetAt(unsigned int index); + + virtual Windows::UI::Xaml::Interop::IBindableVectorView^ GetView(); + + virtual bool IndexOf(Object^ value, unsigned int* index); + + virtual void InsertAt(unsigned int index, Object^ value); + + virtual void RemoveAt(unsigned int index); + + virtual void RemoveAtEnd(); + + virtual void SetAt(unsigned int index, Object^ value); + + virtual event Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler^ VectorChanged + { + virtual Windows::Foundation::EventRegistrationToken add(Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler^ e) + { + return TreeNodeChanged += e; + } + + virtual void remove(Windows::Foundation::EventRegistrationToken t) + { + TreeNodeChanged -= t; + } + + internal: virtual void raise(Windows::UI::Xaml::Interop::IBindableObservableVector^ vector, Platform::Object^ e) + { + TreeNodeChanged(vector, e); + } + } + + virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged; + + virtual property unsigned int Size + { + unsigned int get(); + } + + property Object^ Data + { + Object^ get(); + void set(Object^ value); + } + + property TreeNode^ ParentNode + { + TreeNode^ get(); + void set(TreeNode^ value); + } + + property bool IsExpanded + { + bool get(); + void set(bool value); + } + + property bool HasItems + { + bool get(); + } + + //A lone TreeNode will have a depth of -1, this is to show that it is not appended + //under the TreeView's invisible root node. Once added into the TreeView via + //that method, the depth of the node will be calculated appropriately. + property int Depth + { + int get(); + } + + event Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler^ TreeNodeChanged; + + private: + TreeNode^ parentNode = nullptr; + Object^ data = nullptr; + bool isExpanded = false; + Platform::Collections::Vector^ childrenVector = ref new Platform::Collections::Vector(); + void ChildrenVectorChanged(Windows::Foundation::Collections::IObservableVector^ sender, Windows::Foundation::Collections::IVectorChangedEventArgs^ e); + }; +} diff --git a/Control/TreeView.cpp b/Control/TreeView.cpp new file mode 100644 index 0000000..95616f7 --- /dev/null +++ b/Control/TreeView.cpp @@ -0,0 +1,189 @@ +#pragma once +#include "pch.h" +#include "TreeView.h" + +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Interop; +using namespace Windows::UI::Xaml::Controls; + +namespace TreeViewControl { + + TreeView::TreeView() + { + flatViewModel = ref new ViewModel; + rootNode = ref new TreeNode(); + + flatViewModel->ExpandNode(rootNode); + + CanReorderItems = true; + AllowDrop = true; + CanDragItems = true; + + rootNode->VectorChanged += ref new BindableVectorChangedEventHandler(flatViewModel, &ViewModel::TreeNodeVectorChanged); + ItemClick += ref new Windows::UI::Xaml::Controls::ItemClickEventHandler(this, &TreeView::TreeView_OnItemClick); + DragItemsStarting += ref new Windows::UI::Xaml::Controls::DragItemsStartingEventHandler(this, &TreeView::TreeView_DragItemsStarting); + DragItemsCompleted += ref new Windows::Foundation::TypedEventHandler(this, &TreeView::TreeView_DragItemsCompleted); + ItemsSource = flatViewModel; + } + + void TreeView::TreeView_OnItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ args) + { + TreeViewItemClickEventArgs^ treeViewArgs = ref new TreeViewItemClickEventArgs(); + treeViewArgs->ClickedItem = args->ClickedItem; + + TreeViewItemClick(this, treeViewArgs); + + if (!treeViewArgs->IsHandled) + { + TreeNode^ targetNode = (TreeNode^)args->ClickedItem; + if (targetNode->IsExpanded) + { + flatViewModel->CollapseNode(targetNode); + } + else + { + flatViewModel->ExpandNode(targetNode); + } + } + } + + void TreeView::TreeView_DragItemsStarting(Platform::Object^ sender, Windows::UI::Xaml::Controls::DragItemsStartingEventArgs^ e) + { + draggedTreeViewItem = (TreeViewItem^)this->ContainerFromItem(e->Items->GetAt(0)); + } + + void TreeView::TreeView_DragItemsCompleted(Windows::UI::Xaml::Controls::ListViewBase^ sender, Windows::UI::Xaml::Controls::DragItemsCompletedEventArgs^ args) + { + draggedTreeViewItem = nullptr; + } + + void TreeView::OnDrop(Windows::UI::Xaml::DragEventArgs^ e) + { + if (e->AcceptedOperation == Windows::ApplicationModel::DataTransfer::DataPackageOperation::Move) + { + Panel^ panel = this->ItemsPanelRoot; + Windows::Foundation::Point point = e->GetPosition(panel); + + int aboveIndex = -1; + int belowIndex = -1; + unsigned int relativeIndex; + + IInsertionPanel^ insertionPanel = (IInsertionPanel^)panel; + + if (insertionPanel != nullptr) + { + insertionPanel->GetInsertionIndexes(point, &aboveIndex, &belowIndex); + + TreeNode^ aboveNode = (TreeNode^)flatViewModel->GetAt(aboveIndex); + TreeNode^ belowNode = (TreeNode^)flatViewModel->GetAt(belowIndex); + TreeNode^ targetNode = (TreeNode^)this->ItemFromContainer(draggedTreeViewItem); + + //Between two items + if (aboveNode && belowNode) + { + targetNode->ParentNode->IndexOf(targetNode, &relativeIndex); + targetNode->ParentNode->RemoveAt(relativeIndex); + + if (belowNode->ParentNode == aboveNode) + { + aboveNode->InsertAt(0, targetNode); + } + else + { + aboveNode->ParentNode->IndexOf(aboveNode, &relativeIndex); + aboveNode->ParentNode->InsertAt(relativeIndex + 1, targetNode); + } + } + //Bottom of the list + else if (aboveNode && !belowNode) + { + targetNode->ParentNode->IndexOf(targetNode, &relativeIndex); + targetNode->ParentNode->RemoveAt(relativeIndex); + + aboveNode->ParentNode->IndexOf(aboveNode, &relativeIndex); + aboveNode->ParentNode->InsertAt(relativeIndex + 1, targetNode); + } + //Top of the list + else if (!aboveNode && belowNode) + { + targetNode->ParentNode->IndexOf(targetNode, &relativeIndex); + targetNode->ParentNode->RemoveAt(relativeIndex); + + rootNode->InsertAt(0, targetNode); + } + } + } + + e->Handled = true; + ListViewBase::OnDrop(e); + } + + void TreeView::OnDragOver(Windows::UI::Xaml::DragEventArgs^ e) + { + Windows::ApplicationModel::DataTransfer::DataPackageOperation savedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::None; + + e->AcceptedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::None; + + Panel^ panel = this->ItemsPanelRoot; + Windows::Foundation::Point point = e->GetPosition(panel); + + int aboveIndex = -1; + int belowIndex = -1; + + IInsertionPanel^ insertionPanel = (IInsertionPanel^)panel; + + if (insertionPanel != nullptr) + { + insertionPanel->GetInsertionIndexes(point, &aboveIndex, &belowIndex); + + if (aboveIndex > -1) + { + TreeNode^ aboveNode = (TreeNode^)flatViewModel->GetAt(aboveIndex); + TreeNode^ targetNode = (TreeNode^)this->ItemFromContainer(draggedTreeViewItem); + + TreeNode^ ancestorNode = aboveNode; + while (ancestorNode != nullptr && ancestorNode != targetNode) + { + ancestorNode = ancestorNode->ParentNode; + } + + if (ancestorNode == nullptr) + { + savedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::Move; + e->AcceptedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::Move; + } + } + else + { + savedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::Move; + e->AcceptedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::Move; + } + } + + ListViewBase::OnDragOver(e); + e->AcceptedOperation = savedOperation; + } + + void TreeView::ExpandNode(TreeNode^ targetNode) + { + flatViewModel->ExpandNode(targetNode); + } + + void TreeView::CollapseNode(TreeNode^ targetNode) + { + flatViewModel->CollapseNode(targetNode); + } + + void TreeView::PrepareContainerForItemOverride(DependencyObject^ element, Object^ item) + { + ((UIElement^)element)->AllowDrop = true; + + ListView::PrepareContainerForItemOverride(element, item); + } + + DependencyObject^ TreeView::GetContainerForItemOverride() + { + TreeViewItem^ targetItem = ref new TreeViewItem(); + return (DependencyObject^)targetItem; + } +} \ No newline at end of file diff --git a/Control/TreeView.h b/Control/TreeView.h new file mode 100644 index 0000000..46519ea --- /dev/null +++ b/Control/TreeView.h @@ -0,0 +1,75 @@ +#pragma once +#include "TreeNode.h" +#include "ViewModel.h" +#include "TreeViewItem.h" + +namespace TreeViewControl { + public ref class TreeViewItemClickEventArgs sealed + { + public: + TreeViewItemClickEventArgs() {} + + property Object^ ClickedItem + { + Object^ get() { return clickedItem; }; + void set(Object^ value) { clickedItem = value; }; + } + + property bool IsHandled + { + bool get() { return isHandled; }; + void set(bool value) { isHandled = value; }; + } + private: + Object^ clickedItem = nullptr; + bool isHandled = false; + }; + + ref class TreeView; + [Windows::Foundation::Metadata::WebHostHidden] + public delegate void TreeViewItemClickHandler(TreeView^ sender, TreeViewItemClickEventArgs^ args); + + [Windows::Foundation::Metadata::WebHostHidden] + public ref class TreeView sealed : Windows::UI::Xaml::Controls::ListView + { + public: + TreeView(); + + //This event is used to expose an alternative to itemclick to developers. + event TreeViewItemClickHandler^ TreeViewItemClick; + + //This RootNode property is used by the TreeView to handle additions into the TreeView and + //accurate VectorChange with multiple 'root level nodes'. This node will not be placed + //in the flatViewModel, but has it's vectorchanged event hooked up to flatViewModel's + //handler. + property TreeNode^ RootNode + { + TreeNode^ get() { return rootNode; }; + } + + void TreeView_OnItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ args); + + void TreeView_DragItemsStarting(Platform::Object^ sender, Windows::UI::Xaml::Controls::DragItemsStartingEventArgs^ e); + + void TreeView_DragItemsCompleted(Windows::UI::Xaml::Controls::ListViewBase^ sender, Windows::UI::Xaml::Controls::DragItemsCompletedEventArgs^ args); + + void ExpandNode(TreeNode^ targetNode); + + void CollapseNode(TreeNode^ targetNode); + + protected: + void PrepareContainerForItemOverride(DependencyObject^ element, Object^ item) override; + Windows::UI::Xaml::DependencyObject^ GetContainerForItemOverride() override; + + void OnDrop(Windows::UI::Xaml::DragEventArgs^ e) override; + void OnDragOver(Windows::UI::Xaml::DragEventArgs^ e) override; + + private: + TreeNode^ rootNode; + ViewModel^ flatViewModel; + + internal: + TreeViewItem^ draggedTreeViewItem; + }; +} + diff --git a/Control/TreeViewControl.vcxproj b/Control/TreeViewControl.vcxproj new file mode 100644 index 0000000..b86f2b4 --- /dev/null +++ b/Control/TreeViewControl.vcxproj @@ -0,0 +1,237 @@ + + + + + Debug + ARM + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + Win32 + + + Release + x64 + + + + {bdc228d2-7929-5a98-b7ba-5e28bd1adbcf} + WindowsRuntimeComponent + TreeViewControl + TreeViewControl + en-US + 14.0 + true + Windows Store + 10.0.16299.0 + 10.0.16299.0 + 10.0 + + + + DynamicLibrary + true + v141 + + + DynamicLibrary + true + v141 + + + DynamicLibrary + true + v141 + + + DynamicLibrary + false + true + v141 + + + DynamicLibrary + false + true + v141 + + + DynamicLibrary + false + true + v141 + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + false + + + false + + + false + + + false + + + false + + + + Use + _WINRT_DLL;%(PreprocessorDefinitions) + pch.h + $(IntDir)pch.pch + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + /bigobj %(AdditionalOptions) + 28204 + + + Console + false + + + + + Use + _WINRT_DLL;NDEBUG;%(PreprocessorDefinitions) + pch.h + $(IntDir)pch.pch + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + /bigobj %(AdditionalOptions) + 28204 + + + Console + false + + + + + Use + _WINRT_DLL;%(PreprocessorDefinitions) + pch.h + $(IntDir)pch.pch + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + /bigobj %(AdditionalOptions) + 28204 + + + Console + false + + + + + Use + _WINRT_DLL;NDEBUG;%(PreprocessorDefinitions) + pch.h + $(IntDir)pch.pch + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + /bigobj %(AdditionalOptions) + 28204 + + + Console + false + + + + + Use + _WINRT_DLL;%(PreprocessorDefinitions) + pch.h + $(IntDir)pch.pch + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + /bigobj %(AdditionalOptions) + 28204 + + + Console + false + + + + + Use + _WINRT_DLL;NDEBUG;%(PreprocessorDefinitions) + pch.h + $(IntDir)pch.pch + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + /bigobj %(AdditionalOptions) + 28204 + + + Console + false + + + + + + + + + + + + + + + Create + Create + Create + Create + Create + Create + + + + + + + + + + + \ No newline at end of file diff --git a/Control/TreeViewControl.vcxproj.filters b/Control/TreeViewControl.vcxproj.filters new file mode 100644 index 0000000..338be7a --- /dev/null +++ b/Control/TreeViewControl.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + 96c875b2-800f-42e2-aba6-ef897e02ec12 + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Control/TreeViewItem.cpp b/Control/TreeViewItem.cpp new file mode 100644 index 0000000..1041031 --- /dev/null +++ b/Control/TreeViewItem.cpp @@ -0,0 +1,127 @@ +#pragma once +#include "pch.h" +#include "TreeViewItem.h" +#include "TreeViewItemAutomationPeer.h" + +using namespace Windows::UI::Xaml; +using namespace Platform; + +namespace TreeViewControl { + TreeViewItem::TreeViewItem() + { + } + + TreeViewItem::~TreeViewItem() + { + } + + Windows::UI::Xaml::Controls::ListView^ TreeViewItem::GetAncestorListView(TreeViewItem^ targetItem) + { + DependencyObject^ TreeViewItemAncestor = (DependencyObject^)this; + Windows::UI::Xaml::Controls::ListView^ ancestorListView = nullptr; + while (TreeViewItemAncestor != nullptr && ancestorListView == nullptr) + { + TreeViewItemAncestor = Windows::UI::Xaml::Media::VisualTreeHelper::GetParent(TreeViewItemAncestor); + ancestorListView = dynamic_cast(TreeViewItemAncestor); + } + return ancestorListView; + } + + void TreeViewItem::OnDrop(Windows::UI::Xaml::DragEventArgs^ e) + { + if (e->AcceptedOperation == Windows::ApplicationModel::DataTransfer::DataPackageOperation::Move) + { + TreeViewItem^ droppedOnItem = (TreeViewItem^)this; + + Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetAncestorListView(droppedOnItem); + + if (ancestorListView) + { + TreeView^ ancestorTreeView = (TreeView^)ancestorListView; + TreeViewItem^ droppedItem = ancestorTreeView->draggedTreeViewItem; + TreeNode^ droppedNode = (TreeNode^)ancestorTreeView->ItemFromContainer(droppedItem); + TreeNode^ droppedOnNode = (TreeNode^)ancestorTreeView->ItemFromContainer(droppedOnItem); + + //Remove the item that was dragged + unsigned int removeIndex; + droppedNode->ParentNode->IndexOf(droppedNode, &removeIndex); + + if (droppedNode != droppedOnNode) + { + droppedNode->ParentNode->RemoveAt(removeIndex); + + //Append the dragged dropped item as a child of the node it was dropped onto + droppedOnNode->Append(droppedNode); + + //If not set to true then the Reorder code of listview wil override what is being done here. + e->Handled = true; + } + else + { + e->AcceptedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::None; + } + } + } + } + + void TreeViewItem::OnDragEnter(Windows::UI::Xaml::DragEventArgs^ e) + { + TreeViewItem^ draggedOverItem = (TreeViewItem^)this; + + e->AcceptedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::None; + + Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetAncestorListView(draggedOverItem); + + if (ancestorListView) + { + TreeView^ ancestorTreeView = (TreeView^)ancestorListView; + TreeViewItem^ draggedTreeViewItem = ancestorTreeView->draggedTreeViewItem; + TreeNode^ draggedNode = (TreeNode^)ancestorTreeView->ItemFromContainer(draggedTreeViewItem); + TreeNode^ draggedOverNode = (TreeNode^)ancestorTreeView->ItemFromContainer(draggedOverItem); + TreeNode^ walkNode = draggedOverNode->ParentNode; + + while (walkNode != nullptr && walkNode != draggedNode) + { + walkNode = walkNode->ParentNode; + } + + if (walkNode != draggedNode && draggedNode != draggedOverNode) + { + e->AcceptedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::Move; + } + } + } + + void TreeViewItem::OnDragOver(Windows::UI::Xaml::DragEventArgs^ e) + { + e->DragUIOverride->IsGlyphVisible = true; + e->AcceptedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::None; + + TreeViewItem^ draggedOverItem = (TreeViewItem^)this; + + Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetAncestorListView(draggedOverItem); + if (ancestorListView) + { + TreeView^ ancestorTreeView = (TreeView^)ancestorListView; + TreeViewItem^ draggedTreeViewItem = ancestorTreeView->draggedTreeViewItem; + TreeNode^ draggedNode = (TreeNode^)ancestorTreeView->ItemFromContainer(draggedTreeViewItem); + TreeNode^ draggedOverNode = (TreeNode^)ancestorTreeView->ItemFromContainer(draggedOverItem); + TreeNode^ walkNode = draggedOverNode->ParentNode; + + while (walkNode != nullptr && walkNode != draggedNode) + { + walkNode = walkNode->ParentNode; + } + + if (walkNode != draggedNode && draggedNode != draggedOverNode) + { + e->AcceptedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::Move; + } + } + } + + Windows::UI::Xaml::Automation::Peers::AutomationPeer^ TreeViewItem::OnCreateAutomationPeer() + { + return ref new TreeViewItemAutomationPeer(this); + } +} \ No newline at end of file diff --git a/Control/TreeViewItem.h b/Control/TreeViewItem.h new file mode 100644 index 0000000..21d9872 --- /dev/null +++ b/Control/TreeViewItem.h @@ -0,0 +1,22 @@ +#pragma once + +namespace TreeViewControl { + [Windows::Foundation::Metadata::WebHostHidden] + [Windows::UI::Xaml::Data::Bindable] + public ref class TreeViewItem sealed : Windows::UI::Xaml::Controls::ListViewItem + { + public: + TreeViewItem(); + + virtual ~TreeViewItem(); + + private: + Windows::UI::Xaml::Controls::ListView^ GetAncestorListView(TreeViewItem^ targetItem); + + protected: + void OnDrop(Windows::UI::Xaml::DragEventArgs^ e) override; + void OnDragEnter(Windows::UI::Xaml::DragEventArgs^ e) override; + void OnDragOver(Windows::UI::Xaml::DragEventArgs^ e) override; + Windows::UI::Xaml::Automation::Peers::AutomationPeer^ OnCreateAutomationPeer() override; + }; +} \ No newline at end of file diff --git a/Control/TreeViewItemAutomationPeer.cpp b/Control/TreeViewItemAutomationPeer.cpp new file mode 100644 index 0000000..5d2af13 --- /dev/null +++ b/Control/TreeViewItemAutomationPeer.cpp @@ -0,0 +1,178 @@ +#pragma once +#include "pch.h" +#include "TreeViewItemAutomationPeer.h" +#include "TreeNode.h" + +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Automation; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Data; + + +namespace TreeViewControl { + //IExpandCollapseProvider + Windows::UI::Xaml::Automation::ExpandCollapseState TreeViewItemAutomationPeer::ExpandCollapseState::get() + { + Windows::UI::Xaml::Automation::ExpandCollapseState currentState = Windows::UI::Xaml::Automation::ExpandCollapseState::Collapsed; + Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetParentListView((DependencyObject^)Owner); + + TreeNode^ targetNode; + TreeNode^ targetParentNode; + + if (ancestorListView) + { + TreeView^ ancestorTreeView = (TreeView^)ancestorListView; + targetNode = (TreeNode^)ancestorTreeView->ItemFromContainer((TreeViewItem^)Owner); + + if (Owner->AllowDrop) + { + if (targetNode->IsExpanded) + { + currentState = Windows::UI::Xaml::Automation::ExpandCollapseState::Expanded; + } + else + { + currentState = Windows::UI::Xaml::Automation::ExpandCollapseState::Collapsed; + } + } + else + { + currentState = Windows::UI::Xaml::Automation::ExpandCollapseState::LeafNode; + } + } + + return currentState; + } + + void TreeViewItemAutomationPeer::Collapse() + { + Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetParentListView((DependencyObject^)Owner); + + if (ancestorListView) + { + TreeView^ ancestorTreeView = (TreeView^)ancestorListView; + TreeNode^ targetNode = (TreeNode^)ancestorTreeView->ItemFromContainer((TreeViewItem^)Owner); + ancestorTreeView->CollapseNode(targetNode); + RaiseExpandCollapseAutomationEvent(Windows::UI::Xaml::Automation::ExpandCollapseState::Collapsed); + } + } + + void TreeViewItemAutomationPeer::Expand() + { + Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetParentListView((DependencyObject^)Owner); + + if (ancestorListView) + { + TreeView^ ancestorTreeView = (TreeView^)ancestorListView; + TreeNode^ targetNode = (TreeNode^)ancestorTreeView->ItemFromContainer((TreeViewItem^)Owner); + ancestorTreeView->ExpandNode(targetNode); + RaiseExpandCollapseAutomationEvent(Windows::UI::Xaml::Automation::ExpandCollapseState::Expanded); + } + } + + void TreeViewItemAutomationPeer::RaiseExpandCollapseAutomationEvent(Windows::UI::Xaml::Automation::ExpandCollapseState newState) + { + Windows::UI::Xaml::Automation::ExpandCollapseState oldState; + + if (newState == Windows::UI::Xaml::Automation::ExpandCollapseState::Expanded) + { + oldState = Windows::UI::Xaml::Automation::ExpandCollapseState::Collapsed; + } + else + { + oldState = Windows::UI::Xaml::Automation::ExpandCollapseState::Expanded; + } + + RaisePropertyChangedEvent(ExpandCollapsePatternIdentifiers::ExpandCollapseStateProperty, oldState, newState); + } + + //Position override + + //These methods are being overridden so that the TreeView under narrator reads out + //the position of an item as compared to it's children, not it's overall position + //in the listview. I've included an override for level as well, to give context on + //how deep in the tree an item is. + int TreeViewItemAutomationPeer::GetSizeOfSetCore() + { + Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetParentListView((DependencyObject^)Owner); + + TreeNode^ targetNode; + TreeNode^ targetParentNode; + + int setSize = 0; + + if (ancestorListView) + { + TreeView^ ancestorTreeView = (TreeView^)ancestorListView; + targetNode = (TreeNode^)ancestorTreeView->ItemFromContainer((TreeViewItem^)Owner); + targetParentNode = targetNode->ParentNode; + setSize = targetParentNode->Size; + } + + return setSize; + } + + int TreeViewItemAutomationPeer::GetPositionInSetCore() + { + Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetParentListView((DependencyObject^)Owner); + + TreeNode^ targetNode; + TreeNode^ targetParentNode; + + int positionInSet = 0; + + if (ancestorListView) + { + TreeView^ ancestorTreeView = (TreeView^)ancestorListView; + targetNode = (TreeNode^)ancestorTreeView->ItemFromContainer((TreeViewItem^)Owner); + unsigned int positionInt; + targetParentNode = targetNode->ParentNode; + targetParentNode->IndexOf(targetNode, &positionInt); + positionInSet = (int)positionInt + 1; + } + + return positionInSet; + } + + int TreeViewItemAutomationPeer::GetLevelCore() + { + Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetParentListView((DependencyObject^)Owner); + + TreeNode^ targetNode; + TreeNode^ targetParentNode; + + int levelValue = 0; + + if (ancestorListView) + { + TreeView^ ancestorTreeView = (TreeView^)ancestorListView; + targetNode = (TreeNode^)ancestorTreeView->ItemFromContainer((TreeViewItem^)Owner); + levelValue = targetNode->Depth + 1; + } + + return levelValue; + } + + Platform::Object^ TreeViewItemAutomationPeer::GetPatternCore(Windows::UI::Xaml::Automation::Peers::PatternInterface patternInterface) + { + if (patternInterface == Windows::UI::Xaml::Automation::Peers::PatternInterface::ExpandCollapse) + { + return this; + } + + return ListViewItemAutomationPeer::GetPatternCore(patternInterface); + } + + Windows::UI::Xaml::Controls::ListView^ TreeViewItemAutomationPeer::GetParentListView(DependencyObject^ Owner) + { + DependencyObject^ treeViewItemAncestor = Owner; + Windows::UI::Xaml::Controls::ListView^ ancestorListView = nullptr; + while (treeViewItemAncestor != nullptr && ancestorListView == nullptr) + { + treeViewItemAncestor = Windows::UI::Xaml::Media::VisualTreeHelper::GetParent(treeViewItemAncestor); + ancestorListView = dynamic_cast(treeViewItemAncestor); + } + + return ancestorListView; + } +} \ No newline at end of file diff --git a/Control/TreeViewItemAutomationPeer.h b/Control/TreeViewItemAutomationPeer.h new file mode 100644 index 0000000..e3e27f1 --- /dev/null +++ b/Control/TreeViewItemAutomationPeer.h @@ -0,0 +1,39 @@ +#pragma once +#include "TreeViewItem.h" +#include "TreeView.h" + +namespace TreeViewControl { + [Windows::Foundation::Metadata::WebHostHidden] + public ref class TreeViewItemAutomationPeer : Windows::UI::Xaml::Automation::Peers::ListViewItemAutomationPeer, Windows::UI::Xaml::Automation::Provider::IExpandCollapseProvider + { + internal: + TreeViewItemAutomationPeer(TreeViewItem^ owner) :Windows::UI::Xaml::Automation::Peers::ListViewItemAutomationPeer(owner) {}; + + public: + //IExpandCollapseProvider + virtual void Collapse(); + + virtual void Expand(); + + property Windows::UI::Xaml::Automation::ExpandCollapseState ExpandCollapseState + { + virtual Windows::UI::Xaml::Automation::ExpandCollapseState get(); + } + + void RaiseExpandCollapseAutomationEvent(Windows::UI::Xaml::Automation::ExpandCollapseState newState); + + //Position override + int GetSizeOfSetCore() override; + + int GetPositionInSetCore() override; + + int GetLevelCore() override; + + protected: + Platform::Object^ GetPatternCore(Windows::UI::Xaml::Automation::Peers::PatternInterface patternInterface) override; + + private: + Windows::UI::Xaml::Controls::ListView^ GetParentListView(DependencyObject^ Owner); + }; +} + diff --git a/Control/ViewModel.cpp b/Control/ViewModel.cpp new file mode 100644 index 0000000..d49e5d4 --- /dev/null +++ b/Control/ViewModel.cpp @@ -0,0 +1,450 @@ +#pragma once +#include "pch.h" +#include "ViewModel.h" + +using namespace Platform; +using namespace Windows::Foundation::Collections; +using namespace Windows::UI::Xaml::Interop; +using namespace Windows::UI::Xaml::Data; + +namespace TreeViewControl { + ViewModel::ViewModel() + { + flatVectorRealizedItems->VectorChanged += ref new VectorChangedEventHandler(this, &ViewModel::UpdateTreeView); + } + + void ViewModel::Append(Object^ value) + { + TreeNode^ targetNode = (TreeNode^)value; + flatVectorRealizedItems->Append(targetNode); + + collectionChangedEventTokenVector.push_back(targetNode->VectorChanged += ref new BindableVectorChangedEventHandler(this, &ViewModel::TreeNodeVectorChanged)); + propertyChangedEventTokenVector.push_back(targetNode->PropertyChanged += ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &ViewModel::TreeNodePropertyChanged)); + } + + void ViewModel::Clear() + { + + while (flatVectorRealizedItems->Size != 0) + { + RemoveAtEnd(); + } + } + + IBindableIterator^ ViewModel::First() + { + return dynamic_cast(flatVectorRealizedItems->First()); + } + + Object^ ViewModel::GetAt(unsigned int index) + { + if ((int)index > -1 && (int)index < flatVectorRealizedItems->Size) + { + return flatVectorRealizedItems->GetAt(index); + } + + return nullptr; + } + + IBindableVectorView^ ViewModel::GetView() + { + return safe_cast(flatVectorRealizedItems->GetView()); + } + + bool ViewModel::IndexOf(Object^ value, unsigned int* index) + { + return flatVectorRealizedItems->IndexOf((TreeNode^)value, index); + } + + void ViewModel::InsertAt(unsigned int index, Object^ value) + { + if ((int)index > -1 && (int)index <= flatVectorRealizedItems->Size) + { + TreeNode^ targetNode = (TreeNode^)value; + flatVectorRealizedItems->InsertAt(index, targetNode); + + auto eventIndex = collectionChangedEventTokenVector.begin() + index; + collectionChangedEventTokenVector.insert(eventIndex, targetNode->VectorChanged += ref new BindableVectorChangedEventHandler(this, &ViewModel::TreeNodeVectorChanged)); + + eventIndex = propertyChangedEventTokenVector.begin() + index; + propertyChangedEventTokenVector.insert(eventIndex,targetNode->PropertyChanged += ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &ViewModel::TreeNodePropertyChanged)); + } + } + + void ViewModel::RemoveAt(unsigned int index) + { + if ((int)index > -1 && (int)index < flatVectorRealizedItems->Size) + { + TreeNode^ targetNode = flatVectorRealizedItems->GetAt(index); + flatVectorRealizedItems->RemoveAt(index); + + auto eventIndex = collectionChangedEventTokenVector.begin() + index; + targetNode->VectorChanged -= collectionChangedEventTokenVector[index]; + collectionChangedEventTokenVector.erase(eventIndex); + + eventIndex = propertyChangedEventTokenVector.begin() + index; + targetNode->PropertyChanged -= propertyChangedEventTokenVector[index]; + propertyChangedEventTokenVector.erase(eventIndex); + } + } + + void ViewModel::RemoveAtEnd() + { + int index = flatVectorRealizedItems->Size - 1; + if (index >= 0) + { + TreeNode^ targetNode = flatVectorRealizedItems->GetAt(index); + flatVectorRealizedItems->RemoveAt(index); + + auto eventIndex = collectionChangedEventTokenVector.begin() + index; + targetNode->VectorChanged -= collectionChangedEventTokenVector[index]; + collectionChangedEventTokenVector.erase(eventIndex); + + eventIndex = propertyChangedEventTokenVector.begin() + index; + targetNode->PropertyChanged -= propertyChangedEventTokenVector[index]; + propertyChangedEventTokenVector.erase(eventIndex); + } + } + + void ViewModel::SetAt(unsigned int index, Object^ value) + { + if ((int)index > -1 && (int)index < flatVectorRealizedItems->Size) + { + TreeNode^ targetNode = (TreeNode^)value; + TreeNode^ removeNode = flatVectorRealizedItems->GetAt(index); + flatVectorRealizedItems->SetAt(index, targetNode); + + auto eventIndex = collectionChangedEventTokenVector.begin() + index; + removeNode->VectorChanged -= collectionChangedEventTokenVector[index]; + collectionChangedEventTokenVector.erase(eventIndex); + collectionChangedEventTokenVector.insert(eventIndex, targetNode->VectorChanged += ref new BindableVectorChangedEventHandler(this, &ViewModel::TreeNodeVectorChanged)); + + eventIndex = propertyChangedEventTokenVector.begin() + index; + targetNode->PropertyChanged -= propertyChangedEventTokenVector[index]; + propertyChangedEventTokenVector.erase(eventIndex); + propertyChangedEventTokenVector.insert(eventIndex, targetNode->PropertyChanged += ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &ViewModel::TreeNodePropertyChanged)); + } + } + + void ViewModel::ExpandNode(TreeNode^ targetNode) + { + if (!targetNode->IsExpanded) + { + targetNode->IsExpanded = true; + } + } + + void ViewModel::CollapseNode(TreeNode^ targetNode) + { + if (targetNode->IsExpanded) + { + targetNode->IsExpanded = false; + } + } + + void ViewModel::AddNodeToView(TreeNode^ targetNode, int index) + { + InsertAt(index, targetNode); + } + + int ViewModel::AddNodeDescendantsToView(TreeNode^ targetNode, int index, int offset) + { + if (targetNode->IsExpanded) + { + TreeNode^ childNode; + for (int i = 0; i < (int)targetNode->Size; i++) + { + childNode = (TreeNode^)targetNode->GetAt(i); + offset++; + AddNodeToView(childNode, index + offset); + offset = AddNodeDescendantsToView(childNode, index, offset); + } + + return offset; + } + + return offset; + } + + void ViewModel::RemoveNodeAndDescendantsFromView(TreeNode^ targetNode) + { + if (targetNode->IsExpanded) + { + TreeNode^ childNode; + for (int i = 0; i < (int)targetNode->Size; i++) + { + childNode = (TreeNode^)targetNode->GetAt(i); + RemoveNodeAndDescendantsFromView(childNode); + } + } + + int index = IndexOf(targetNode); + RemoveAt(index); + } + + int ViewModel::CountDescendants(TreeNode^ targetNode) + { + int descendantCount = 0; + TreeNode^ childNode; + for (int i = 0; i < (int)targetNode->Size; i++) + { + childNode = (TreeNode^)targetNode->GetAt(i); + descendantCount++; + if (childNode->IsExpanded) + { + descendantCount = descendantCount + CountDescendants(childNode); + } + } + + return descendantCount; + } + + int ViewModel::IndexOf(TreeNode^ targetNode) + { + unsigned int index; + bool isIndexed = IndexOf(targetNode, &index); + if (isIndexed) + { + return (int)index; + } + else + { + return -1; + } + } + + void ViewModel::UpdateTreeView(IObservableVector^ sender, IVectorChangedEventArgs^ e) + { + VectorChanged(this, e); + } + + void ViewModel::TreeNodeVectorChanged(IBindableObservableVector^ sender, Platform::Object^ e) + { + CollectionChange CC = ((IVectorChangedEventArgs^)e)->CollectionChange; + switch (CC) + { + //Reset case, commonly seen when a TreeNode is cleared. + //removes all nodes that need removing then + //toggles a collapse / expand to ensure order. + case (CollectionChange::Reset) : + { + TreeNode^ resetNode = (TreeNode^)sender; + int resetIndex = IndexOf(resetNode); + if (resetIndex != Size - 1 && resetNode->IsExpanded) + { + TreeNode^ childNode = resetNode; + TreeNode^ parentNode = resetNode->ParentNode; + int stopIndex; + bool isLastRelativeChild = true; + while (parentNode != nullptr && isLastRelativeChild) + { + unsigned int relativeIndex; + parentNode->IndexOf(childNode, &relativeIndex); + if (parentNode->Size - 1 != relativeIndex) + { + isLastRelativeChild = false; + } + else + { + childNode = parentNode; + parentNode = parentNode->ParentNode; + } + } + + if (parentNode != nullptr) + { + unsigned int siblingIndex; + parentNode->IndexOf(childNode, &siblingIndex); + TreeNode^ siblingNode = (TreeNode^)parentNode->GetAt(siblingIndex + 1); + stopIndex = IndexOf(siblingNode); + } + else + { + stopIndex = Size; + } + + for (int i = stopIndex - 1; i > resetIndex; i--) + { + if ((flatVectorRealizedItems->GetAt(i))->ParentNode == nullptr) + { + RemoveNodeAndDescendantsFromView(flatVectorRealizedItems->GetAt(i)); + } + } + + if (resetNode->IsExpanded) + { + CollapseNode(resetNode); + ExpandNode(resetNode); + } + } + + break; + } + + //Inserts the TreeNode into the correct index of the ViewModel + case (CollectionChange::ItemInserted) : + { + //We will find the correct index of insertion by first checking if the + //node we are inserting into is expanded. If it is we will start walking + //down the tree and counting the open items. This is to ensure we place + //the inserted item in the correct index. If along the way we bump into + //the item being inserted, we insert there then return, because we don't + //need to do anything further. + unsigned int index = ((IVectorChangedEventArgs^)e)->Index; + TreeNode^ targetNode = (TreeNode^)sender->GetAt(index); + TreeNode^ parentNode = targetNode->ParentNode; + TreeNode^ childNode; + int parentIndex = IndexOf(parentNode); + int allOpenedDescendantsCount = 0; + if (parentNode->IsExpanded) + { + for (int i = 0; i < (int)parentNode->Size; i++) + { + childNode = (TreeNode^)parentNode->GetAt(i); + if (childNode == targetNode) + { + AddNodeToView(targetNode, (parentIndex + i + 1 + allOpenedDescendantsCount)); + if (targetNode->IsExpanded) + { + AddNodeDescendantsToView(targetNode, parentIndex + i + 1, allOpenedDescendantsCount); + } + + return; + } + + if (childNode->IsExpanded) + { + allOpenedDescendantsCount += CountDescendants(childNode); + } + } + + AddNodeToView(targetNode, (parentIndex + parentNode->Size + allOpenedDescendantsCount)); + if (targetNode->IsExpanded) + { + AddNodeDescendantsToView(targetNode, parentIndex + parentNode->Size, allOpenedDescendantsCount); + } + } + + break; + } + + //Removes a node from the ViewModel when a TreeNode + //removes a child. + case (CollectionChange::ItemRemoved) : + { + TreeNode^ removeNode = (TreeNode^)sender; + int removeIndex = IndexOf(removeNode); + + if (removeIndex != Size - 1 && removeNode->IsExpanded) + { + TreeNode^ childNode = removeNode; + TreeNode^ parentNode = removeNode->ParentNode; + int stopIndex; + bool isLastRelativeChild = true; + while (parentNode != nullptr && isLastRelativeChild) + { + unsigned int relativeIndex; + parentNode->IndexOf(childNode, &relativeIndex); + if (parentNode->Size - 1 != relativeIndex) + { + isLastRelativeChild = false; + } + else + { + childNode = parentNode; + parentNode = parentNode->ParentNode; + } + } + + if (parentNode != nullptr) + { + unsigned int siblingIndex; + parentNode->IndexOf(childNode, &siblingIndex); + TreeNode^ siblingNode = (TreeNode^)parentNode->GetAt(siblingIndex + 1); + stopIndex = IndexOf(siblingNode); + } + else + { + stopIndex = Size; + } + + for (int i = stopIndex - 1; i > removeIndex; i--) + { + if ((flatVectorRealizedItems->GetAt(i))->ParentNode == nullptr) + { + RemoveNodeAndDescendantsFromView(flatVectorRealizedItems->GetAt(i)); + } + } + } + + break; + } + + //Triggered by a replace such as SetAt. + //Updates the TreeNode that changed in the ViewModel. + case (CollectionChange::ItemChanged) : + { + unsigned int index = ((IVectorChangedEventArgs^)e)->Index; + TreeNode^ targetNode = (TreeNode^)sender->GetAt(index); + TreeNode^ parentNode = targetNode->ParentNode; + TreeNode^ childNode; + int allOpenedDescendantsCount = 0; + int parentIndex = IndexOf(parentNode); + + for (int i = 0; i < (int)parentNode->Size; i++) + { + childNode = (TreeNode^)parentNode->GetAt(i); + if (childNode->IsExpanded) + { + allOpenedDescendantsCount += CountDescendants(childNode); + } + } + + TreeNode^ removeNode = (TreeNode^)GetAt(parentIndex + index + allOpenedDescendantsCount + 1); + if (removeNode->IsExpanded) + { + CollapseNode(removeNode); + } + + RemoveAt(parentIndex + index + allOpenedDescendantsCount + 1); + InsertAt(parentIndex + index + allOpenedDescendantsCount + 1, targetNode); + + break; + } + + } + } + + void ViewModel::TreeNodePropertyChanged(Object^ sender, PropertyChangedEventArgs^ e) + { + if (e->PropertyName == "IsExpanded") + { + TreeNode^ targetNode = (TreeNode^)sender; + if (targetNode->IsExpanded) + { + if (targetNode->Size != 0) + { + int openedDescendantOffset = 0; + int index = IndexOf(targetNode); + index = index + 1; + TreeNode^ childNode; + for (int i = 0; i < (int)targetNode->Size; i++) + { + childNode = (TreeNode^)targetNode->GetAt(i); + AddNodeToView(childNode, ((int)index + i + openedDescendantOffset)); + openedDescendantOffset = AddNodeDescendantsToView(childNode, (index + i), openedDescendantOffset); + } + } + } + else + { + TreeNode^ childNode; + for (int i = 0; i < (int)targetNode->Size; i++) + { + childNode = (TreeNode^)targetNode->GetAt(i); + RemoveNodeAndDescendantsFromView(childNode); + } + } + } + } +} \ No newline at end of file diff --git a/Control/ViewModel.h b/Control/ViewModel.h new file mode 100644 index 0000000..1e22b07 --- /dev/null +++ b/Control/ViewModel.h @@ -0,0 +1,105 @@ +#pragma once +#include "TreeNode.h" + +namespace TreeViewControl { + /// + /// The ViewModel is responsible for flattening the heirarchical data into a flat list. + /// It also tracks changes to the underlying data and updates the flat list accordingly. + /// + [Windows::Foundation::Metadata::WebHostHidden] + ref class ViewModel sealed : Windows::UI::Xaml::Interop::IBindableObservableVector + { + internal: + ViewModel(); + + public: + /// + /// Add root to the view model. In other cases the app should not + /// use this api. + /// + virtual void Append(Object^ value); + + virtual void Clear(); + + virtual Windows::UI::Xaml::Interop::IBindableIterator^ First(); + + virtual Object^ GetAt(unsigned int index); + + virtual Windows::UI::Xaml::Interop::IBindableVectorView^ GetView(); + + virtual bool IndexOf(Object^ value, unsigned int* index); + + virtual void InsertAt(unsigned int index, Object^ value); + + virtual void RemoveAt(unsigned int index); + + virtual void RemoveAtEnd(); + + virtual void SetAt(unsigned int index, Object^ value); + + virtual property unsigned int Size + { + unsigned int get() { return flatVectorRealizedItems->Size; }; + }; + + virtual event Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler^ VectorChanged + { + virtual Windows::Foundation::EventRegistrationToken add(Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler^ args) + { + return ViewModelChanged += args; + } + + virtual void remove(Windows::Foundation::EventRegistrationToken token) + { + return ViewModelChanged -= token; + } + + virtual void raise(Windows::UI::Xaml::Interop::IBindableObservableVector^ vector, Platform::Object^ e) + { + ViewModelChanged(vector, e); + } + } + + /// + /// ExpandNode adds the children and all open descendants of the targetNode + /// to the ViewModel in their correct flattened index. + /// + void ExpandNode(TreeNode^ targetNode); + + /// + /// Collapse node removes all the descendants of the targetNode from the ViewModel. + /// + void CollapseNode(TreeNode^ targetNode); + + /// + /// This is the collection changed handler for individual TreeNodes. The collection changes + /// from the hierarchical TreeNodes need to be flattened so that we can keep our current + /// flat list in sync. + /// + /// TreeNode that has already been changed + void TreeNodeVectorChanged(Windows::UI::Xaml::Interop::IBindableObservableVector^ sender, Platform::Object^ e); + + void TreeNodePropertyChanged(Object^ sender, Windows::UI::Xaml::Data::PropertyChangedEventArgs^ e); + + private: + event Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler^ ViewModelChanged; + + Platform::Collections::Vector^ flatVectorRealizedItems = ref new Platform::Collections::Vector(); + + std::vector collectionChangedEventTokenVector; + + std::vector propertyChangedEventTokenVector; + + void AddNodeToView(TreeNode^ targetNode, int index); + + int AddNodeDescendantsToView(TreeNode^ targetNode, int start, int offset); + + void RemoveNodeAndDescendantsFromView(TreeNode^ targetNode); + + int CountDescendants(TreeNode^ targetNode); + + int IndexOf(TreeNode^ targetNode); + + void UpdateTreeView(Windows::Foundation::Collections::IObservableVector^ sender, Windows::Foundation::Collections::IVectorChangedEventArgs^ e); + }; +} \ No newline at end of file diff --git a/Control/pch.cpp b/Control/pch.cpp new file mode 100644 index 0000000..bcb5590 --- /dev/null +++ b/Control/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/Control/pch.h b/Control/pch.h new file mode 100644 index 0000000..f87e8c1 --- /dev/null +++ b/Control/pch.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/GLBGLTFConverter/GLBGLTFConverter.cpp b/GLBGLTFConverter/GLBGLTFConverter.cpp new file mode 100644 index 0000000..976ce85 Binary files /dev/null and b/GLBGLTFConverter/GLBGLTFConverter.cpp differ diff --git a/GLBGLTFConverter/GLBGLTFConverter.vcxproj b/GLBGLTFConverter/GLBGLTFConverter.vcxproj new file mode 100644 index 0000000..988a9c6 --- /dev/null +++ b/GLBGLTFConverter/GLBGLTFConverter.vcxproj @@ -0,0 +1,166 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {F6FAFE3C-92C0-43E7-A64B-B38DFC793EB2} + Win32Proj + GLBGLTFConverter + 10.0.16299.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Use + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + + + Use + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + + + Use + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + Use + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + + + Create + Create + Create + Create + + + + + {d3b2132d-132e-4b58-a828-b20ef9962a83} + + + + + + \ No newline at end of file diff --git a/GLBGLTFConverter/GLBGLTFConverter.vcxproj.filters b/GLBGLTFConverter/GLBGLTFConverter.vcxproj.filters new file mode 100644 index 0000000..e40c284 --- /dev/null +++ b/GLBGLTFConverter/GLBGLTFConverter.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/GLBGLTFConverter/stdafx.cpp b/GLBGLTFConverter/stdafx.cpp new file mode 100644 index 0000000..aeff3de Binary files /dev/null and b/GLBGLTFConverter/stdafx.cpp differ diff --git a/GLBGLTFConverter/stdafx.h b/GLBGLTFConverter/stdafx.h new file mode 100644 index 0000000..f38b082 Binary files /dev/null and b/GLBGLTFConverter/stdafx.h differ diff --git a/GLBGLTFConverter/targetver.h b/GLBGLTFConverter/targetver.h new file mode 100644 index 0000000..567cd34 Binary files /dev/null and b/GLBGLTFConverter/targetver.h differ diff --git a/ModelViewer/App.xaml b/ModelViewer/App.xaml new file mode 100644 index 0000000..f6a76be --- /dev/null +++ b/ModelViewer/App.xaml @@ -0,0 +1,8 @@ + + + diff --git a/ModelViewer/App.xaml.cpp b/ModelViewer/App.xaml.cpp new file mode 100644 index 0000000..d697b56 --- /dev/null +++ b/ModelViewer/App.xaml.cpp @@ -0,0 +1,155 @@ +// +// App.xaml.cpp +// Implementation of the App class. +// + +#include "pch.h" +#include "RootPage.xaml.h" +#include "DirectXPage.xaml.h" + +using namespace ModelViewer; + +using namespace Platform; +using namespace Windows::ApplicationModel; +using namespace Windows::ApplicationModel::Activation; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Collections; +using namespace Windows::Storage; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Controls::Primitives; +using namespace Windows::UI::Xaml::Data; +using namespace Windows::UI::Xaml::Input; +using namespace Windows::UI::Xaml::Interop; +using namespace Windows::UI::Xaml::Media; +using namespace Windows::UI::Xaml::Navigation; +/// +/// Initializes the singleton application object. This is the first line of authored code +/// executed, and as such is the logical equivalent of main() or WinMain(). +/// +App::App() +{ + InitializeComponent(); + + //builder.registerType<> + + Suspending += ref new SuspendingEventHandler(this, &App::OnSuspending); + Resuming += ref new EventHandler(this, &App::OnResuming); +} + +void App::ExtendAcrylicIntoTitleBar() +{ + Windows::ApplicationModel::Core::CoreApplication::GetCurrentView()->TitleBar->ExtendViewIntoTitleBar = true; + auto titleBar = Windows::ApplicationModel::Core::CoreApplication::GetCurrentView()->TitleBar; + // These properties don't exist... + //titleBar->ButtonBackgroundColor = Colors.Transparent; + //titleBar->ButtonInactiveBackgroundColor = Colors.Transparent; +} + +/// +/// Invoked when the application is launched normally by the end user. Other entry points +/// will be used when the application is launched to open a specific file, to display +/// search results, and so forth. +/// +/// Details about the launch request and process. +void App::OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ e) +{ +#if _DEBUG + if (IsDebuggerPresent()) + { + DebugSettings->EnableFrameRateCounter = true; + } +#endif + + if (m_rootPage == nullptr) + { + m_rootPage = ref new ModelViewer::RootPage(); + } + + if (e->PreviousExecutionState == ApplicationExecutionState::Terminated) + { + m_rootPage->LoadInternalState(ApplicationData::Current->LocalSettings->Values); + } + + auto rootFrame = dynamic_cast(Window::Current->Content); + + // Do not repeat app initialization when the Window already has content, + // just ensure that the window is active + if (rootFrame == nullptr) + { + // Create a Frame to act as the navigation context and associate it with + // a SuspensionManager key + rootFrame = ref new Frame(); + + rootFrame->NavigationFailed += ref new Windows::UI::Xaml::Navigation::NavigationFailedEventHandler(this, &App::OnNavigationFailed); + + if (rootFrame->Content == nullptr) + { + // When the navigation stack isn't restored navigate to the first page, + // configuring the new page by passing required information as a navigation + // parameter + rootFrame->Navigate(TypeName(RootPage::typeid), e->Arguments); + } + + // Place the frame in the current Window + Window::Current->Content = rootFrame; + // Ensure the current window is active + Window::Current->Activate(); + } + else + { + if (rootFrame->Content == nullptr) + { + // When the navigation stack isn't restored navigate to the first page, + // configuring the new page by passing required information as a navigation + // parameter + rootFrame->Navigate(TypeName(RootPage::typeid), e->Arguments); + } + + + // Ensure the current window is active + Window::Current->Activate(); + ExtendAcrylicIntoTitleBar(); + } + + //m_rootPage->GetContentFrame()->Content = ref new DirectXPage(); +} + +/// +/// Invoked when application execution is being suspended. Application state is saved +/// without knowing whether the application will be terminated or resumed with the contents +/// of memory still intact. +/// +/// The source of the suspend request. +/// Details about the suspend request. +void App::OnSuspending(Object^ sender, SuspendingEventArgs^ e) +{ + (void) sender; // Unused parameter + (void) e; // Unused parameter + + m_rootPage->SaveInternalState(ApplicationData::Current->LocalSettings->Values); +} + +/// +/// Invoked when application execution is being resumed. +/// +/// The source of the resume request. +/// Details about the resume request. +void App::OnResuming(Object ^sender, Object ^args) +{ + (void) sender; // Unused parameter + (void) args; // Unused parameter + + m_rootPage->LoadInternalState(ApplicationData::Current->LocalSettings->Values); +} + +/// +/// Invoked when Navigation to a certain page fails +/// +/// The Frame which failed navigation +/// Details about the navigation failure +void App::OnNavigationFailed(Platform::Object ^sender, Windows::UI::Xaml::Navigation::NavigationFailedEventArgs ^e) +{ + throw ref new FailureException("Failed to load Page " + e->SourcePageType.Name); +} + diff --git a/ModelViewer/App.xaml.h b/ModelViewer/App.xaml.h new file mode 100644 index 0000000..d3eb8ff --- /dev/null +++ b/ModelViewer/App.xaml.h @@ -0,0 +1,30 @@ +// +// App.xaml.h +// Declaration of the App class. +// + +#pragma once + +#include "App.g.h" +#include "RootPage.xaml.h" + +namespace ModelViewer +{ + /// + /// Provides application-specific behavior to supplement the default Application class. + /// + ref class App sealed + { + public: + App(); + virtual void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ e) override; + + private: + void ExtendAcrylicIntoTitleBar(); + + void OnSuspending(Platform::Object^ sender, Windows::ApplicationModel::SuspendingEventArgs^ e); + void OnResuming(Platform::Object ^sender, Platform::Object ^args); + void OnNavigationFailed(Platform::Object ^sender, Windows::UI::Xaml::Navigation::NavigationFailedEventArgs ^e); + RootPage^ m_rootPage; + }; +} diff --git a/ModelViewer/Assets/LockScreenLogo.scale-200.png b/ModelViewer/Assets/LockScreenLogo.scale-200.png new file mode 100644 index 0000000..735f57a Binary files /dev/null and b/ModelViewer/Assets/LockScreenLogo.scale-200.png differ diff --git a/ModelViewer/Assets/Shaders/pbrpixel.hlsl b/ModelViewer/Assets/Shaders/pbrpixel.hlsl new file mode 100644 index 0000000..59d8361 --- /dev/null +++ b/ModelViewer/Assets/Shaders/pbrpixel.hlsl @@ -0,0 +1,359 @@ +// +// This fragment shader defines a reference implementation for Physically Based Shading of +// a microfacet surface material defined by a glTF model. +// +// References: +// [1] Real Shading in Unreal Engine 4 +// http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf +// [2] Physically Based Shading at Disney +// http://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf +// [3] README.md - Environment Maps +// https://github.com/KhronosGroup/glTF-WebGL-PBR/#environment-maps +// [4] "An Inexpensive BRDF Model for Physically based Rendering" by Christophe Schlick +// https://www.cs.virginia.edu/~jdl/bib/appearance/analytic%20models/schlick94b.pdf + +#define NORMALS +#define UV +#define HAS_NORMALS +#define USE_IBL +#define USE_TEX_LOD + +Texture2D baseColourTexture : register(t0); +SamplerState baseColourSampler : register(s0); + +Texture2D normalTexture : register(t1); +SamplerState normalSampler : register(s1); + +Texture2D emissionTexture : register(t2); +SamplerState emissionSampler : register(s2); + +Texture2D occlusionTexture : register(t3); +SamplerState occlusionSampler : register(s3); + +Texture2D metallicRoughnessTexture : register(t4); +SamplerState metallicRoughnessSampler : register(s4); + +TextureCube envDiffuseTexture : register(t8); +SamplerState envDiffuseSampler : register(s8); + +Texture2D brdfLutTexture : register(t9); +SamplerState brdfLutSampler : register(s9); + +TextureCube envSpecularTexture : register(t10); +SamplerState envSpecularSampler : register(s10); + +struct Light +{ + float3 dir; + float padding1; + float3 colour; + float padding2; +}; + +cbuffer cbPerFrame : register(b0) +{ + Light light; +}; + +cbuffer cbPerObject : register(b1) +{ + float normalScale; + float3 emissiveFactor; + float occlusionStrength; + float2 metallicRoughnessValues; + float padding1; + float4 baseColorFactor; + float3 camera; + float padding2; + + // debugging flags used for shader output of intermediate PBR variables + float4 scaleDiffBaseMR; + float4 scaleFGDSpec; + float4 scaleIBLAmbient; +}; + +// Per-pixel color data passed through the pixel shader. +struct PixelShaderInput +{ + float4 position : SV_POSITION; + float3 poswithoutw : POSITION; + +#ifdef NORMALS + float3 normal : NORMAL; +#endif +#ifdef UV + float2 texcoord : TEXCOORD0; +#endif +}; + +#ifdef HAS_NORMALS +#ifdef HAS_TANGENTS +varying mat3 v_TBN; +#else +#endif +#endif + +// Encapsulate the various inputs used by the various functions in the shading equation +// We store values in this struct to simplify the integration of alternative implementations +// of the shading terms, outlined in the Readme.MD Appendix. +struct PBRInfo +{ + float NdotL; // cos angle between normal and light direction + float NdotV; // cos angle between normal and view direction + float NdotH; // cos angle between normal and half vector + float LdotH; // cos angle between light direction and half vector + float VdotH; // cos angle between view direction and half vector + float perceptualRoughness; // roughness value, as authored by the model creator (input to shader) + float metalness; // metallic value at the surface + float3 reflectance0; // full reflectance color (normal incidence angle) + float3 reflectance90; // reflectance color at grazing angle + float alphaRoughness; // roughness mapped to a more linear change in the roughness (proposed by [2]) + float3 diffuseColor; // color contribution from diffuse lighting + float3 specularColor; // color contribution from specular lighting +}; + +static const float M_PI = 3.141592653589793; +static const float c_MinRoughness = 0.04; + +float4 SRGBtoLINEAR(float4 srgbIn) +{ +#ifdef MANUAL_SRGB +#ifdef SRGB_FAST_APPROXIMATION + float3 linOut = pow(srgbIn.xyz,float3(2.2, 2.2, 2.2)); +#else //SRGB_FAST_APPROXIMATION + float3 bLess = step(float3(0.04045, 0.04045, 0.04045), srgbIn.xyz); + float3 linOut = lerp(srgbIn.xyz / float3(12.92, 12.92, 12.92), pow((srgbIn.xyz + float3(0.055, 0.055, 0.055)) / float3(1.055, 1.055, 1.055), float3(2.4, 2.4, 2.4)), bLess); +#endif //SRGB_FAST_APPROXIMATION + return float4(linOut,srgbIn.w);; +#else //MANUAL_SRGB + return srgbIn; +#endif //MANUAL_SRGB +} + +// Find the normal for this fragment, pulling either from a predefined normal map +// or from the interpolated mesh normal and tangent attributes. +float3 getNormal(float3 position, float3 normal, float2 uv) +{ + // Retrieve the tangent space matrix +#ifndef HAS_TANGENTS + float3 pos_dx = ddx(position); + float3 pos_dy = ddy(position); + float3 tex_dx = ddx(float3(uv, 0.0)); + float3 tex_dy = ddy(float3(uv, 0.0)); + float3 t = (tex_dy.y * pos_dx - tex_dx.y * pos_dy) / (tex_dx.x * tex_dy.y - tex_dy.x * tex_dx.y); + +#ifdef HAS_NORMALS + float3 ng = normalize(normal); +#else + float3 ng = cross(pos_dx, pos_dy); +#endif + + t = normalize(t - ng * dot(ng, t)); + float3 b = normalize(cross(ng, t)); + row_major float3x3 tbn = float3x3(t, b, ng); + +#else // HAS_TANGENTS + mat3 tbn = v_TBN; +#endif + +#ifdef HAS_NORMALMAP + float3 n = normalTexture.Sample(normalSampler, uv).rgb; + + // Need to check the multiplication is equivalent.. + n = normalize(mul(((2.0 * n - 1.0) * float3(normalScale, normalScale, 1.0)), tbn)); +#else + float3 n = tbn[2].xyz; +#endif + + return n; +} + +#ifdef USE_IBL +// Calculation of the lighting contribution from an optional Image Based Light source. +// Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1]. +// See our README.md on Environment Maps [3] for additional discussion. +float3 getIBLContribution(PBRInfo pbrInputs, float3 n, float3 reflection) +{ + float mipCount = 9.0; // resolution of 512x512 + float lod = (pbrInputs.perceptualRoughness * mipCount); + + // retrieve a scale and bias to F0. See [1], Figure 3 + float2 val = float2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness); + float3 brdf = SRGBtoLINEAR(brdfLutTexture.Sample(brdfLutSampler, val)).rgb; + + float3 diffuseLight = SRGBtoLINEAR(envDiffuseTexture.Sample(envDiffuseSampler, n)).rgb; + +#ifdef USE_TEX_LOD + float3 specularLight = SRGBtoLINEAR(envSpecularTexture.SampleLevel(envSpecularSampler, reflection, 0)).rgb; +#else + float3 specularLight = SRGBtoLINEAR(envSpecularTexture.Sample(envSpecularSampler, reflection)).rgb; +#endif + + float3 diffuse = diffuseLight * pbrInputs.diffuseColor; + float3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y); + + // For presentation, this allows us to disable IBL terms + diffuse *= scaleIBLAmbient.x; + specular *= scaleIBLAmbient.y; + + return diffuse + specular; +} +#endif + +// Basic Lambertian diffuse +// Implementation from Lambert's Photometria https://archive.org/details/lambertsphotome00lambgoog +// See also [1], Equation 1 +float3 diffuse(PBRInfo pbrInputs) +{ + return pbrInputs.diffuseColor / M_PI; +} + +// The following equation models the Fresnel reflectance term of the spec equation (aka F()) +// Implementation of fresnel from [4], Equation 15 +float3 specularReflection(PBRInfo pbrInputs) +{ + return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrInputs.VdotH, 0.0, 1.0), 5.0); +} + +// This calculates the specular geometric attenuation (aka G()), +// where rougher material will reflect less light back to the viewer. +// This implementation is based on [1] Equation 4, and we adopt their modifications to +// alphaRoughness as input as originally proposed in [2]. +float geometricOcclusion(PBRInfo pbrInputs) +{ + float NdotL = pbrInputs.NdotL; + float NdotV = pbrInputs.NdotV; + float r = pbrInputs.alphaRoughness; + + float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL))); + float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV))); + return attenuationL * attenuationV; +} + +// The following equation(s) model the distribution of microfacet normals across the area being drawn (aka D()) +// Implementation from "Average Irregularity Representation of a Roughened Surface for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz +// Follows the distribution function recommended in the SIGGRAPH 2013 course notes from EPIC Games [1], Equation 3. +float microfacetDistribution(PBRInfo pbrInputs) +{ + float roughnessSq = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness; + float f = (pbrInputs.NdotH * roughnessSq - pbrInputs.NdotH) * pbrInputs.NdotH + 1.0; + return roughnessSq / (M_PI * f * f); +} + +float4 main(PixelShaderInput input) : SV_TARGET +{ + // Metallic and Roughness material properties are packed together + // In glTF, these factors can be specified by fixed scalar values + // or from a metallic-roughness map + float perceptualRoughness = metallicRoughnessValues.y; + float metallic = metallicRoughnessValues.x; + +#ifdef HAS_METALROUGHNESSMAP + // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel. + // This layout intentionally reserves the 'r' channel for (optional) occlusion map data + float4 mrSample = metallicRoughnessTexture.Sample(metallicRoughnessSampler, input.texcoord); + + // Had to reverse the order of the channels here - TODO: investigate.. + perceptualRoughness = mrSample.g * perceptualRoughness; + metallic = mrSample.b * metallic; +#endif + + perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0); + metallic = clamp(metallic, 0.0, 1.0); + + // Roughness is authored as perceptual roughness; as is convention, + // convert to material roughness by squaring the perceptual roughness [2]. + float alphaRoughness = perceptualRoughness * perceptualRoughness; + + // The albedo may be defined from a base texture or a flat color + +#ifdef HAS_BASECOLORMAP + float4 baseColor = SRGBtoLINEAR(baseColourTexture.Sample(baseColourSampler, input.texcoord)) * baseColorFactor; +#else + float4 baseColor = baseColorFactor; +#endif + + float3 f0 = float3(0.04, 0.04, 0.04); + float3 diffuseColor = baseColor.rgb * (float3(1.0, 1.0, 1.0) - f0); + + diffuseColor *= 1.0 - metallic; + + float3 specularColor = lerp(f0, baseColor.rgb, metallic); + + // Compute reflectance. + float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b); + + // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect. + // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%. + float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0); + float3 specularEnvironmentR0 = specularColor.rgb; + float3 specularEnvironmentR90 = float3(1.0, 1.0, 1.0) * reflectance90; + + float3 n = getNormal(input.poswithoutw, input.normal, input.texcoord); // normal at surface point + float3 v = normalize(camera - input.poswithoutw); // Vector from surface point to camera + + float3 l = normalize(light.dir); // Vector from surface point to light + float3 h = normalize(l + v); // Half vector between both l and v + float3 reflection = -normalize(reflect(v, n)); + + float NdotL = clamp(dot(n, l), 0.001, 1.0); + float NdotV = abs(dot(n, v)) + 0.001; + float NdotH = clamp(dot(n, h), 0.0, 1.0); + float LdotH = clamp(dot(l, h), 0.0, 1.0); + float VdotH = clamp(dot(v, h), 0.0, 1.0); + + PBRInfo pbrInputs; + pbrInputs.NdotL = NdotL; + pbrInputs.NdotV = NdotV; + pbrInputs.NdotH = NdotH; + pbrInputs.LdotH = LdotH; + pbrInputs.VdotH = VdotH; + pbrInputs.perceptualRoughness = perceptualRoughness; + pbrInputs.metalness = metallic; + pbrInputs.reflectance0 = specularEnvironmentR0; + pbrInputs.reflectance90 = specularEnvironmentR90; + pbrInputs.alphaRoughness = alphaRoughness; + pbrInputs.diffuseColor = diffuseColor; + pbrInputs.specularColor = specularColor; + + // Calculate the shading terms for the microfacet specular shading model + float3 F = specularReflection(pbrInputs); + + float G = geometricOcclusion(pbrInputs); + float D = microfacetDistribution(pbrInputs); + + // Calculation of analytical lighting contribution + float3 diffuseContrib = (1.0 - F) * diffuse(pbrInputs); + float3 specContrib = F * G * D / (4.0 * NdotL * NdotV); + float3 color = NdotL * light.colour * (diffuseContrib + specContrib); + + + // Calculate lighting contribution from image based lighting source (IBL) +#ifdef USE_IBL + color += getIBLContribution(pbrInputs, n, reflection); +#endif + + // Apply optional PBR terms for additional (optional) shading +#ifdef HAS_OCCLUSIONMAP + float ao = occlusionTexture.Sample(occlusionSampler, input.texcoord).r; + color = lerp(color, color * ao, occlusionStrength); +#endif + +#ifdef HAS_EMISSIVEMAP + float3 emissive = SRGBtoLINEAR(emissionTexture.Sample(emissionSampler, input.texcoord)).rgb * emissiveFactor; + color += emissive; +#endif + + // This section uses lerp to override final color for reference app visualization + // of various parameters in the lighting equation. + color = lerp(color, F, scaleFGDSpec.x); + color = lerp(color, float3(G, G, G), scaleFGDSpec.y); + color = lerp(color, float3(D, D, D), scaleFGDSpec.z); + color = lerp(color, specContrib, scaleFGDSpec.w); + color = lerp(color, diffuseContrib, scaleDiffBaseMR.x); + color = lerp(color, baseColor.rgb, scaleDiffBaseMR.y); + color = lerp(color, float3(metallic, metallic, metallic), scaleDiffBaseMR.z); + color = lerp(color, float3(perceptualRoughness, perceptualRoughness, perceptualRoughness), scaleDiffBaseMR.w); + + return float4(color, 1.0); +} \ No newline at end of file diff --git a/ModelViewer/Assets/Shaders/pbrvertex.hlsl b/ModelViewer/Assets/Shaders/pbrvertex.hlsl new file mode 100644 index 0000000..dbd375b --- /dev/null +++ b/ModelViewer/Assets/Shaders/pbrvertex.hlsl @@ -0,0 +1,73 @@ +//#define NORMALS +//#define UV + +// A constant buffer that stores the three basic column-major matrices for composing geometry. +cbuffer ModelViewProjectionConstantBuffer : register(b0) +{ + matrix model; + matrix view; + matrix projection; +}; + +// Per-vertex data used as input to the vertex shader. +struct VertexShaderInput +{ + float4 position : POSITION; +#ifdef NORMALS + float3 normal : NORMAL; +#endif +#ifdef UV + float2 texcoord : TEXCOORD0; +#endif +}; + +// Per-pixel color data passed through the pixel shader. +struct PixelShaderInput +{ + float4 position : SV_POSITION; + float3 poswithoutw : POSITION; + +#ifdef NORMALS + float3 normal : NORMAL; +#endif + + float2 texcoord : TEXCOORD0; +}; + +PixelShaderInput main(VertexShaderInput input) +{ + PixelShaderInput output; + + // Transform the vertex position into projected space. + float4 pos = mul(input.position, model); + output.poswithoutw = float3(pos.xyz) / pos.w; + +#ifdef NORMALS + // If we have normals... + output.normal = normalize(mul(float4(input.normal.xyz, 0.0), model)); +#endif + +#ifdef UV + output.texcoord = input.texcoord; +#else + output.texcoord = float2(0.0f, 0.0f); +#endif + +#ifdef HAS_NORMALS +#ifdef HAS_TANGENTS + vec3 normalW = normalize(vec3(u_ModelMatrix * vec4(a_Normal.xyz, 0.0))); + vec3 tangentW = normalize(vec3(u_ModelMatrix * vec4(a_Tangent.xyz, 0.0))); + vec3 bitangentW = cross(normalW, tangentW) * a_Tangent.w; + v_TBN = mat3(tangentW, bitangentW, normalW); +#else // HAS_TANGENTS != 1 + v_Normal = normalize(vec3(u_ModelMatrix * vec4(a_Normal.xyz, 0.0))); +#endif +#endif + + // Transform the vertex position into projected space. + pos = mul(pos, view); + pos = mul(pos, projection); + output.position = pos; + + return output; +} \ No newline at end of file diff --git a/ModelViewer/Assets/SplashScreen.scale-200.png b/ModelViewer/Assets/SplashScreen.scale-200.png new file mode 100644 index 0000000..023e7f1 Binary files /dev/null and b/ModelViewer/Assets/SplashScreen.scale-200.png differ diff --git a/ModelViewer/Assets/Square150x150Logo.scale-200.png b/ModelViewer/Assets/Square150x150Logo.scale-200.png new file mode 100644 index 0000000..af49fec Binary files /dev/null and b/ModelViewer/Assets/Square150x150Logo.scale-200.png differ diff --git a/ModelViewer/Assets/Square44x44Logo.scale-200.png b/ModelViewer/Assets/Square44x44Logo.scale-200.png new file mode 100644 index 0000000..ce342a2 Binary files /dev/null and b/ModelViewer/Assets/Square44x44Logo.scale-200.png differ diff --git a/ModelViewer/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/ModelViewer/Assets/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 0000000..f6c02ce Binary files /dev/null and b/ModelViewer/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/ModelViewer/Assets/StoreLogo.png b/ModelViewer/Assets/StoreLogo.png new file mode 100644 index 0000000..7385b56 Binary files /dev/null and b/ModelViewer/Assets/StoreLogo.png differ diff --git a/ModelViewer/Assets/Wide310x150Logo.scale-200.png b/ModelViewer/Assets/Wide310x150Logo.scale-200.png new file mode 100644 index 0000000..288995b Binary files /dev/null and b/ModelViewer/Assets/Wide310x150Logo.scale-200.png differ diff --git a/ModelViewer/Assets/textures/brdfLUT.png b/ModelViewer/Assets/textures/brdfLUT.png new file mode 100644 index 0000000..5f6541b Binary files /dev/null and b/ModelViewer/Assets/textures/brdfLUT.png differ diff --git a/ModelViewer/Assets/textures/papermill/diffuse/diffuse_back_0.jpg b/ModelViewer/Assets/textures/papermill/diffuse/diffuse_back_0.jpg new file mode 100644 index 0000000..9ba140c Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/diffuse/diffuse_back_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/diffuse/diffuse_bottom_0.jpg b/ModelViewer/Assets/textures/papermill/diffuse/diffuse_bottom_0.jpg new file mode 100644 index 0000000..d7c18b4 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/diffuse/diffuse_bottom_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/diffuse/diffuse_front_0.jpg b/ModelViewer/Assets/textures/papermill/diffuse/diffuse_front_0.jpg new file mode 100644 index 0000000..c76d5ed Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/diffuse/diffuse_front_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/diffuse/diffuse_left_0.jpg b/ModelViewer/Assets/textures/papermill/diffuse/diffuse_left_0.jpg new file mode 100644 index 0000000..c66a113 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/diffuse/diffuse_left_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/diffuse/diffuse_right_0.jpg b/ModelViewer/Assets/textures/papermill/diffuse/diffuse_right_0.jpg new file mode 100644 index 0000000..7d4a549 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/diffuse/diffuse_right_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/diffuse/diffuse_top_0.jpg b/ModelViewer/Assets/textures/papermill/diffuse/diffuse_top_0.jpg new file mode 100644 index 0000000..216e228 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/diffuse/diffuse_top_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/environment/environment_back_0.jpg b/ModelViewer/Assets/textures/papermill/environment/environment_back_0.jpg new file mode 100644 index 0000000..0f4d556 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/environment/environment_back_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/environment/environment_bottom_0.jpg b/ModelViewer/Assets/textures/papermill/environment/environment_bottom_0.jpg new file mode 100644 index 0000000..03949f3 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/environment/environment_bottom_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/environment/environment_front_0.jpg b/ModelViewer/Assets/textures/papermill/environment/environment_front_0.jpg new file mode 100644 index 0000000..ceff4ce Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/environment/environment_front_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/environment/environment_left_0.jpg b/ModelViewer/Assets/textures/papermill/environment/environment_left_0.jpg new file mode 100644 index 0000000..57bc799 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/environment/environment_left_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/environment/environment_right_0.jpg b/ModelViewer/Assets/textures/papermill/environment/environment_right_0.jpg new file mode 100644 index 0000000..4e03a9d Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/environment/environment_right_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/environment/environment_top_0.jpg b/ModelViewer/Assets/textures/papermill/environment/environment_top_0.jpg new file mode 100644 index 0000000..fac2213 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/environment/environment_top_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_back_0.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_back_0.jpg new file mode 100644 index 0000000..1aa532a Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_back_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_back_1.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_back_1.jpg new file mode 100644 index 0000000..ad5ce3a Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_back_1.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_back_2.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_back_2.jpg new file mode 100644 index 0000000..709ff8b Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_back_2.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_back_3.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_back_3.jpg new file mode 100644 index 0000000..74af954 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_back_3.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_back_4.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_back_4.jpg new file mode 100644 index 0000000..a3a75f2 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_back_4.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_back_5.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_back_5.jpg new file mode 100644 index 0000000..6353e3e Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_back_5.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_back_6.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_back_6.jpg new file mode 100644 index 0000000..7ee1998 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_back_6.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_back_7.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_back_7.jpg new file mode 100644 index 0000000..3683c45 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_back_7.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_back_8.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_back_8.jpg new file mode 100644 index 0000000..b9334db Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_back_8.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_back_9.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_back_9.jpg new file mode 100644 index 0000000..26a8872 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_back_9.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_bottom_0.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_0.jpg new file mode 100644 index 0000000..d21ccb9 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_bottom_1.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_1.jpg new file mode 100644 index 0000000..cfc9594 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_1.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_bottom_2.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_2.jpg new file mode 100644 index 0000000..bb9d84d Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_2.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_bottom_3.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_3.jpg new file mode 100644 index 0000000..7076883 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_3.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_bottom_4.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_4.jpg new file mode 100644 index 0000000..d600ddc Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_4.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_bottom_5.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_5.jpg new file mode 100644 index 0000000..98c08bc Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_5.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_bottom_6.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_6.jpg new file mode 100644 index 0000000..a8aff7b Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_6.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_bottom_7.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_7.jpg new file mode 100644 index 0000000..a68d332 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_7.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_bottom_8.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_8.jpg new file mode 100644 index 0000000..71b78e4 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_8.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_bottom_9.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_9.jpg new file mode 100644 index 0000000..eedd8a6 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_bottom_9.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_front_0.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_front_0.jpg new file mode 100644 index 0000000..566088c Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_front_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_front_1.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_front_1.jpg new file mode 100644 index 0000000..64ff8af Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_front_1.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_front_2.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_front_2.jpg new file mode 100644 index 0000000..d1257f3 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_front_2.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_front_3.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_front_3.jpg new file mode 100644 index 0000000..e9068be Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_front_3.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_front_4.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_front_4.jpg new file mode 100644 index 0000000..cccad9e Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_front_4.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_front_5.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_front_5.jpg new file mode 100644 index 0000000..48b22b3 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_front_5.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_front_6.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_front_6.jpg new file mode 100644 index 0000000..51a9e62 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_front_6.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_front_7.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_front_7.jpg new file mode 100644 index 0000000..55b62f2 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_front_7.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_front_8.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_front_8.jpg new file mode 100644 index 0000000..fce9b41 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_front_8.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_front_9.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_front_9.jpg new file mode 100644 index 0000000..3e86df1 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_front_9.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_left_0.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_left_0.jpg new file mode 100644 index 0000000..c5615fe Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_left_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_left_1.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_left_1.jpg new file mode 100644 index 0000000..d64f720 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_left_1.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_left_2.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_left_2.jpg new file mode 100644 index 0000000..fdc425e Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_left_2.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_left_3.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_left_3.jpg new file mode 100644 index 0000000..bd6829f Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_left_3.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_left_4.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_left_4.jpg new file mode 100644 index 0000000..052bdcd Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_left_4.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_left_5.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_left_5.jpg new file mode 100644 index 0000000..68cb6fb Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_left_5.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_left_6.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_left_6.jpg new file mode 100644 index 0000000..9596a4c Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_left_6.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_left_7.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_left_7.jpg new file mode 100644 index 0000000..7c5dbef Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_left_7.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_left_8.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_left_8.jpg new file mode 100644 index 0000000..d9ae2c3 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_left_8.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_left_9.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_left_9.jpg new file mode 100644 index 0000000..aaa6c58 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_left_9.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_right_0.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_right_0.jpg new file mode 100644 index 0000000..a91fc7d Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_right_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_right_1.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_right_1.jpg new file mode 100644 index 0000000..773fe0c Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_right_1.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_right_2.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_right_2.jpg new file mode 100644 index 0000000..2b6bebd Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_right_2.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_right_3.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_right_3.jpg new file mode 100644 index 0000000..a705735 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_right_3.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_right_4.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_right_4.jpg new file mode 100644 index 0000000..57c1b17 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_right_4.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_right_5.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_right_5.jpg new file mode 100644 index 0000000..a51ebc8 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_right_5.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_right_6.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_right_6.jpg new file mode 100644 index 0000000..63a702c Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_right_6.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_right_7.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_right_7.jpg new file mode 100644 index 0000000..b6b9d33 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_right_7.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_right_8.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_right_8.jpg new file mode 100644 index 0000000..ab5f54b Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_right_8.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_right_9.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_right_9.jpg new file mode 100644 index 0000000..7c6b67b Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_right_9.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_top_0.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_top_0.jpg new file mode 100644 index 0000000..25054f2 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_top_0.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_top_1.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_top_1.jpg new file mode 100644 index 0000000..e10447c Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_top_1.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_top_2.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_top_2.jpg new file mode 100644 index 0000000..1c56448 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_top_2.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_top_3.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_top_3.jpg new file mode 100644 index 0000000..f2554cf Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_top_3.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_top_4.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_top_4.jpg new file mode 100644 index 0000000..3d07988 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_top_4.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_top_5.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_top_5.jpg new file mode 100644 index 0000000..827f748 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_top_5.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_top_6.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_top_6.jpg new file mode 100644 index 0000000..9738eea Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_top_6.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_top_7.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_top_7.jpg new file mode 100644 index 0000000..f861afe Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_top_7.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_top_8.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_top_8.jpg new file mode 100644 index 0000000..24d8248 Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_top_8.jpg differ diff --git a/ModelViewer/Assets/textures/papermill/specular/specular_top_9.jpg b/ModelViewer/Assets/textures/papermill/specular/specular_top_9.jpg new file mode 100644 index 0000000..cd5e40f Binary files /dev/null and b/ModelViewer/Assets/textures/papermill/specular/specular_top_9.jpg differ diff --git a/ModelViewer/Axis.cpp b/ModelViewer/Axis.cpp new file mode 100644 index 0000000..6c98b30 --- /dev/null +++ b/ModelViewer/Axis.cpp @@ -0,0 +1,104 @@ +#include "pch.h" +#include "Axis.h" +#include "Content\ShaderStructures.h" +#include "Common\DirectXHelper.h" + +using namespace ModelViewer; +using namespace DirectX; + +Axis::Axis(float axisLength) : _axisLength(axisLength) +{ +} + +void Axis::Initialise(ID3D11Device *device) +{ + D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc; + D3D11_SUBRESOURCE_DATA vertexData, indexData; + + XMFLOAT3 colour = { 1.0f, 1.0f, 1.0f }; + static const VertexPositionColor vertices [] = + { + { XMFLOAT3(0.0f, _axisLength, 0.0f), colour }, + { XMFLOAT3(0.0f, -_axisLength, 0.0f), colour }, + { XMFLOAT3(-_axisLength, 0.0f, 0.0f), colour }, + { XMFLOAT3(_axisLength, 0.0f, 0.0f), colour }, + { XMFLOAT3(0.0f, 0.0f, -_axisLength), colour }, + { XMFLOAT3(0.0f, 0.0f, _axisLength), colour }, + }; + m_vertexCount = 6; + + static const unsigned long axisIndices [] = + { + 0, 1, 2, 3, 4, 5 + }; + m_indexCount = 6; + + // Set up the description of the static vertex buffer. + vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; + vertexBufferDesc.ByteWidth = sizeof(VertexPositionColor) * m_vertexCount; + vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + vertexBufferDesc.CPUAccessFlags = 0; + vertexBufferDesc.MiscFlags = 0; + vertexBufferDesc.StructureByteStride = 0; + + // Give the subresource structure a pointer to the vertex data. + vertexData.pSysMem = vertices; + vertexData.SysMemPitch = 0; + vertexData.SysMemSlicePitch = 0; + + // Now create the vertex buffer. + DX::ThrowIfFailed(device->CreateBuffer(&vertexBufferDesc, &vertexData, m_vertexBuffer.GetAddressOf())); + + // Set up the description of the static index buffer. + indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; + indexBufferDesc.ByteWidth = sizeof(unsigned long) * m_indexCount; + indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; + indexBufferDesc.CPUAccessFlags = 0; + indexBufferDesc.MiscFlags = 0; + indexBufferDesc.StructureByteStride = 0; + + // Give the subresource structure a pointer to the index data. + indexData.pSysMem = axisIndices; + indexData.SysMemPitch = 0; + indexData.SysMemSlicePitch = 0; + + // Create the index buffer. + DX::ThrowIfFailed(device->CreateBuffer(&indexBufferDesc, &indexData, m_indexBuffer.GetAddressOf())); + + D3D11_RASTERIZER_DESC rasterizerState; + rasterizerState.FillMode = D3D11_FILL_WIREFRAME; + rasterizerState.CullMode = D3D11_CULL_NONE; + rasterizerState.FrontCounterClockwise = true; + rasterizerState.DepthBias = 10; + rasterizerState.DepthBiasClamp = 0; + rasterizerState.SlopeScaledDepthBias = 0.0; + rasterizerState.DepthClipEnable = false; + rasterizerState.ScissorEnable = false; + rasterizerState.MultisampleEnable = true; + rasterizerState.AntialiasedLineEnable = true; + DX::ThrowIfFailed(device->CreateRasterizerState(&rasterizerState, &_pRasterState)); +} + +void Axis::RenderBuffers(ID3D11DeviceContext* deviceContext) +{ + unsigned int stride; + unsigned int offset; + + // Set vertex buffer stride and offset. + stride = sizeof(VertexPositionColor); + offset = 0; + + auto vb = m_vertexBuffer.Get(); + + // Set the vertex buffer to active in the input assembler so it can be rendered. + deviceContext->IASetVertexBuffers(0, 1, &vb, &stride, &offset); + + // Set the index buffer to active in the input assembler so it can be rendered. + deviceContext->IASetIndexBuffer(m_indexBuffer.Get(), DXGI_FORMAT_R32_UINT, 0); + + //Set the render format to line list. + // Set the type of primitive that should be rendered from this vertex buffer, in this case a line list. + deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST); + deviceContext->RSSetState(_pRasterState.Get()); +} + diff --git a/ModelViewer/Axis.h b/ModelViewer/Axis.h new file mode 100644 index 0000000..3938051 --- /dev/null +++ b/ModelViewer/Axis.h @@ -0,0 +1,21 @@ +#pragma once + +using namespace Microsoft::WRL; + +class Axis +{ +public: + Axis(float axisLength); + void Initialise(ID3D11Device *device); + void RenderBuffers(ID3D11DeviceContext* deviceContext); + int IndexCount() { return m_indexCount; }; + +private: + float _axisLength; + int m_vertexCount; + int m_indexCount; + ComPtr m_vertexBuffer, m_indexBuffer; + //ID3D11Buffer *m_vertexBuffer, *m_indexBuffer; + ComPtr _pRasterState; +}; + diff --git a/ModelViewer/BooleanToVisibilityConverter.cpp b/ModelViewer/BooleanToVisibilityConverter.cpp new file mode 100644 index 0000000..0da60c3 --- /dev/null +++ b/ModelViewer/BooleanToVisibilityConverter.cpp @@ -0,0 +1,40 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* +#include "pch.h" +#include "BooleanToVisibilityConverter.h" + +using namespace ModelViewer; + +using namespace Platform; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Interop; + +BooleanToVisibilityConverter::BooleanToVisibilityConverter() +{ +} + +Object^ BooleanToVisibilityConverter::Convert(Object ^value, TypeName targetType, Object ^parameter, String ^language) +{ + IBox^ visibility = dynamic_cast^>(value); + if (visibility != nullptr && visibility->Value != IsInverse) + { + return Visibility::Visible; + } + else + { + return Visibility::Collapsed; + } +} + +Object ^ BooleanToVisibilityConverter::ConvertBack(Object ^value, TypeName targetType, Object ^parameter, String ^language) +{ + throw ref new NotImplementedException(); +} diff --git a/ModelViewer/BooleanToVisibilityConverter.h b/ModelViewer/BooleanToVisibilityConverter.h new file mode 100644 index 0000000..2efaf44 --- /dev/null +++ b/ModelViewer/BooleanToVisibilityConverter.h @@ -0,0 +1,34 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* +#pragma once +namespace ModelViewer +{ + [Windows::UI::Xaml::Data::Bindable] + [Windows::Foundation::Metadata::WebHostHidden] + public ref class BooleanToVisibilityConverter sealed : Windows::UI::Xaml::Data::IValueConverter + { + public: + BooleanToVisibilityConverter(); + + // Inherited via IValueConverter + virtual Platform::Object^ Convert(Platform::Object ^value, Windows::UI::Xaml::Interop::TypeName targetType, Platform::Object ^parameter, Platform::String ^language); + virtual Platform::Object^ ConvertBack(Platform::Object ^value, Windows::UI::Xaml::Interop::TypeName targetType, Platform::Object ^parameter, Platform::String ^language); + + property bool IsInverse + { + bool get() { return isInverse; } + void set(bool value) { isInverse = value; } + } + private: + bool isInverse = false; + }; +} + diff --git a/ModelViewer/BoundingBox.cpp b/ModelViewer/BoundingBox.cpp new file mode 100644 index 0000000..e9fa897 --- /dev/null +++ b/ModelViewer/BoundingBox.cpp @@ -0,0 +1,4 @@ +#include "pch.h" +#include "BoundingBox.h" + + diff --git a/ModelViewer/BoundingBox.h b/ModelViewer/BoundingBox.h new file mode 100644 index 0000000..01bd5c8 --- /dev/null +++ b/ModelViewer/BoundingBox.h @@ -0,0 +1,63 @@ +#pragma once +#include "DirectXMath.h" + +using namespace DirectX; + +template +class BoundingBox +{ +public: + BoundingBox() : + MinX((numeric_limits::max)()), + MaxX((numeric_limits::min)()), + MinY((numeric_limits::max)()), + MaxY((numeric_limits::min)()), + MinZ((numeric_limits::max)()), + MaxZ((numeric_limits::min)()) + { + } + + XMFLOAT3 Centre() + { + XMFLOAT3 ret; + ret.x = bbox.MinX + (bbox.MaxX - bbox.MinX) * 0.5f; + ret.y = bbox.MinY + (bbox.MaxY - bbox.MinY) * 0.5f; + ret.z = bbox.MinZ + (bbox.MaxZ - bbox.MinZ) * 0.5f; + return ret; + } + + void Grow(BoundingBox& other) + { + MinX = min(MinX, other.MinX); + MinY = min(MinY, other.MinY); + MinZ = min(MinZ, other.MinZ); + + MaxX = max(MaxX, other.MaxX); + MaxY = max(MaxY, other.MaxY); + MaxZ = max(MaxZ, other.MaxZ); + } + + static BoundingBox CreateBoundingBoxFromVertexBuffer(void *buffer, unsigned int bufferSize) + { + XMFLOAT3 *buffPtr = (XMFLOAT3 *)buffer; + BoundingBox bbox; + for (unsigned int i = 0; i < bufferSize / (3 * sizeof(float)); i++, buffPtr++) + { + bbox.MaxX = max(buffPtr->x, bbox.MaxX); + bbox.MaxY = max(buffPtr->y, bbox.MaxY); + bbox.MaxZ = max(buffPtr->z, bbox.MaxZ); + bbox.MinX = min(buffPtr->x, bbox.MinX); + bbox.MinY = min(buffPtr->y, bbox.MinY); + bbox.MinZ = min(buffPtr->z, bbox.MinZ); + } + return bbox; + } + + T MinX; + T MaxX; + T MinY; + T MaxY; + T MinZ; + T MaxZ; +}; + diff --git a/ModelViewer/BufferCache.cpp b/ModelViewer/BufferCache.cpp new file mode 100644 index 0000000..190f1f6 --- /dev/null +++ b/ModelViewer/BufferCache.cpp @@ -0,0 +1,13 @@ +#include "pch.h" +#include "BufferCache.h" + +int BufferDescriptor::Hash() +{ + if (!_hashCalculated) + { + hash bufferHash; + _hash = bufferHash(*this); + _hashCalculated = true; + } + return _hash; +} \ No newline at end of file diff --git a/ModelViewer/BufferCache.h b/ModelViewer/BufferCache.h new file mode 100644 index 0000000..d6094f0 --- /dev/null +++ b/ModelViewer/BufferCache.h @@ -0,0 +1,148 @@ +#pragma once + +#include "BoundingBox.h" +#include "Common\DirectXHelper.h" + +using namespace Microsoft::WRL; +using namespace DX; + +class BufferDescriptor +{ +public: + BufferDescriptor(GLTF_BufferData ^ data, shared_ptr deviceResources) : + _data(data), + _deviceResources(deviceResources) + { + } + shared_ptr DevResources() { return _deviceResources; } + const shared_ptr DevResources() const { return _deviceResources; } + + int Hash(); + + const wchar_t *ContentType() const { return _data->BufferDescription->BufferContentType->Data(); } + unsigned int AccessorIdx() const { return _data->BufferDescription->accessorIdx; } + + GLTF_BufferData^ Data() { return _data; } + +private: + bool _hashCalculated = false; + shared_ptr _deviceResources; + int _hash; + unsigned int _accessorIdx; + GLTF_BufferData ^ _data; +}; + +template <> +struct hash +{ + size_t operator()(const BufferDescriptor& descriptor) const + { + size_t res = 0; + hash myHash; + res ^= myHash(descriptor.ContentType()); + hash myHash2; + res ^= myHash2(descriptor.AccessorIdx()); + return res; + } +}; + +//class BufferWrapper +//{ +//public: +// BufferWrapper(GLTF_BufferData^ data, ComPtr buffer) : +// _data(data), +// _buffer(buffer) +// { +// } +// BufferWrapper() {} +// ComPtr& Buffer() { return _buffer; } +// +// GLTF_BufferData^ Data() { return _data; } +// +//private: +// GLTF_BufferData ^ _data; +// ComPtr _buffer; +//}; + +class ID3D11BufferWrapper +{ +public: + static shared_ptr Create(BufferDescriptor descriptor) + { + auto ret = make_shared(); + + int bindFlags = 0; + if (descriptor.Data()->BufferDescription->BufferContentType == L"POSITION" || + descriptor.Data()->BufferDescription->BufferContentType == L"NORMAL" || + descriptor.Data()->BufferDescription->BufferContentType == L"TEXCOORD_0") + { + bindFlags = D3D11_BIND_VERTEX_BUFFER; + } + else if (descriptor.Data()->BufferDescription->BufferContentType == L"INDICES") + { + bindFlags = D3D11_BIND_INDEX_BUFFER; + } + else + { + /*throw new std::exception("Unknown Buffer Type");*/ + return ret; + } + + if (descriptor.Data()->BufferDescription->BufferContentType == L"POSITION") + { + ret->SetBoundingBox( + BoundingBox::CreateBoundingBoxFromVertexBuffer( + (void *)descriptor.Data()->BufferDescription->pSysMem, descriptor.Data()->SubResource->ByteWidth) + ); + } + + // Create the buffers... + D3D11_SUBRESOURCE_DATA vertexBufferData = { 0 }; + vertexBufferData.pSysMem = (void *)descriptor.Data()->BufferDescription->pSysMem; + vertexBufferData.SysMemPitch = 0; + vertexBufferData.SysMemSlicePitch = 0; + + CD3D11_BUFFER_DESC vertexBufferDesc(descriptor.Data()->SubResource->ByteWidth, bindFlags); + vertexBufferDesc.StructureByteStride = descriptor.Data()->SubResource->StructureByteStride; + + auto device = descriptor.DevResources()->GetD3DDevice(); + ThrowIfFailed(device->CreateBuffer(&vertexBufferDesc, &vertexBufferData, ret->AddressOfBuffer())); + //descriptor.DevResources()->GetD3DDeviceContext()->Flush(); + return ret; + } + + BoundingBox& GetBoundingBox() { return _bbox; } + void SetBoundingBox(BoundingBox bbox) { _bbox = bbox; } + + ComPtr& Buffer() { return _buffer; } + ID3D11Buffer **AddressOfBuffer() { return _buffer.GetAddressOf(); } + +private: + BoundingBox _bbox; + ComPtr _buffer; +}; + +template +class BufferCache +{ +public: + BufferCache() {}; + ~BufferCache() {}; + + shared_ptr FindOrCreateBuffer(BufferDescriptor descriptor) + { + // get the hash value... + int hash = descriptor.Hash(); + map>::iterator res = _buffers.find(hash); + if (res != _buffers.end()) + return (*res).second; + + auto ret = TBufferWrapper::Create(descriptor); + _buffers[hash] = ret; + return ret; + } + +private: + map> _buffers; +}; + diff --git a/ModelViewer/BufferManager.cpp b/ModelViewer/BufferManager.cpp new file mode 100644 index 0000000..375b4ea --- /dev/null +++ b/ModelViewer/BufferManager.cpp @@ -0,0 +1,9 @@ +#include "pch.h" +#include "BufferManager.h" + +BufferManager::BufferManager() : + _cbPerObject(0), + _cbPerFrame(1), + _mvpBuffer(0) +{ +} \ No newline at end of file diff --git a/ModelViewer/BufferManager.h b/ModelViewer/BufferManager.h new file mode 100644 index 0000000..24d8dea --- /dev/null +++ b/ModelViewer/BufferManager.h @@ -0,0 +1,77 @@ +#pragma once + +#include "Content\ShaderStructures.h" +#include "Common\DirectXHelper.h" +#include "Common\DeviceResources.h" + +using namespace ModelViewer; +using namespace Microsoft::WRL; +using namespace DX; + +template +class ConstantBufferData +{ +public: + ConstantBufferData(unsigned int slot) : + _slot(slot) + { + } + + ~ConstantBufferData() + { + m_constantBuffer.Reset(); + } + + void Initialise(const DeviceResources& devResources) + { + CD3D11_BUFFER_DESC desc(sizeof(T), D3D11_BIND_CONSTANT_BUFFER); + auto device = devResources.GetD3DDevice(); + DX::ThrowIfFailed(device->CreateBuffer(&desc, nullptr, &m_constantBuffer)); + } + void Release() { m_constantBuffer.Reset(); } + void Update(const DeviceResources& devResources) + { + if (m_constantBuffer == nullptr) + return; + + assert(ConstantBuffer().Get()); + auto context = devResources.GetD3DDeviceContext(); + context->UpdateSubresource1(ConstantBuffer().Get(), 0, NULL, &(BufferData()), 0, 0, 0); + } + + T& BufferData() { return _bufferData; } + ComPtr ConstantBuffer() { return m_constantBuffer; } + +private: + unsigned int _slot; + T _bufferData; + ComPtr m_constantBuffer; +}; + +class BufferManager : public Singleton +{ + friend class Singleton; + +public: + ConstantBufferData& MVPBuffer() + { + return _mvpBuffer; + } + ConstantBufferData& PerObjBuffer() + { + return _cbPerObject; + } + ConstantBufferData& PerFrameBuffer() + { + return _cbPerFrame; + } + +protected: + BufferManager(); + +private: + ConstantBufferData _mvpBuffer; + ConstantBufferData _cbPerObject; + ConstantBufferData _cbPerFrame; +}; + diff --git a/ModelViewer/ClassDiagram.cd b/ModelViewer/ClassDiagram.cd new file mode 100644 index 0000000..3bcbff2 --- /dev/null +++ b/ModelViewer/ClassDiagram.cd @@ -0,0 +1,85 @@ + + + + + + AAQAAAAAAIAAEwASEAIAAAQgAEAAAEEAIIEAQAIAAEA= + Scene\GraphNode.h + + + + + + + + + + + AQSwIIAAAYGAE0ASBYKgAQQgAEAECEkgIINAY5IBADA= + Scene\GraphContainerNode.h + + + + + + + + + + + + + Scene\MeshNode.h + + + + + + + + + + + AACACAAAQIAAA0ACwAAAAQAAAEAEAEFACEAADAIAAAA= + Scene\MeshNode.h + + + + + + AAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAA= + Scene\CameraNode.h + + + + + + AAAAAAAAAAQAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= + Scene\LightNode.h + + + + \ No newline at end of file diff --git a/ModelViewer/Common/BindableBase.cpp b/ModelViewer/Common/BindableBase.cpp new file mode 100644 index 0000000..82f448e --- /dev/null +++ b/ModelViewer/Common/BindableBase.cpp @@ -0,0 +1,42 @@ +#include "pch.h" +#include "BindableBase.h" + +#include +#include + +using namespace Common; + +using namespace Platform; +using namespace Windows::UI::Xaml::Data; + +String^ BindableBase::getCallerName(const char* funName) +{ + std::string str(funName); + std::regex regex("(\\w+)(?=\\:\\:)|(?!\\:\\:)(\\w+)"); + std::sregex_iterator it(str.begin(), str.end(), regex); + std::sregex_iterator it_end; + + std::vector vec; + + while (it != it_end) + { + vec.push_back((*it).str()); + ++it; + } + + std::string last = *(std::end(vec) - 1); + std::wstring ws(last.begin(), last.end()); + + //if property - get the one before last + if (last.compare("set") || last.compare("get")) { + std::string one_before_last = *(std::end(vec) - 2); + ws.assign(one_before_last.begin(), one_before_last.end()); + } + + return ref new String(ws.c_str()); +} + +void BindableBase::OnPropertyChanged(String^ value) +{ + PropertyChanged(this, ref new PropertyChangedEventArgs(value)); +} \ No newline at end of file diff --git a/ModelViewer/Common/BindableBase.h b/ModelViewer/Common/BindableBase.h new file mode 100644 index 0000000..57111d3 --- /dev/null +++ b/ModelViewer/Common/BindableBase.h @@ -0,0 +1,20 @@ +#pragma once + +namespace Common +{ + namespace WUX = Windows::UI::Xaml; + namespace WUXD = Windows::UI::Xaml::Data; + + public ref class BindableBase : public WUXD::INotifyPropertyChanged, WUX::DependencyObject + { + public: + virtual event WUXD::PropertyChangedEventHandler^ PropertyChanged; + + internal: + Platform::String^ getCallerName(const char* funName); + + protected: + virtual void OnPropertyChanged(Platform::String^ value); + }; +} + diff --git a/ModelViewer/Common/DeviceResources.cpp b/ModelViewer/Common/DeviceResources.cpp new file mode 100644 index 0000000..a664e6b --- /dev/null +++ b/ModelViewer/Common/DeviceResources.cpp @@ -0,0 +1,753 @@ +#include "pch.h" +#include "DeviceResources.h" +#include "DirectXHelper.h" +#include + +using namespace D2D1; +using namespace DirectX; +using namespace Microsoft::WRL; +using namespace Windows::Foundation; +using namespace Windows::Graphics::Display; +using namespace Windows::UI::Core; +using namespace Windows::UI::Xaml::Controls; +using namespace Platform; + +namespace DisplayMetrics +{ + // High resolution displays can require a lot of GPU and battery power to render. + // High resolution phones, for example, may suffer from poor battery life if + // games attempt to render at 60 frames per second at full fidelity. + // The decision to render at full fidelity across all platforms and form factors + // should be deliberate. + static const bool SupportHighResolutions = false; + + // The default thresholds that define a "high resolution" display. If the thresholds + // are exceeded and SupportHighResolutions is false, the dimensions will be scaled + // by 50%. + static const float DpiThreshold = 192.0f; // 200% of standard desktop display. + static const float WidthThreshold = 1920.0f; // 1080p width. + static const float HeightThreshold = 1080.0f; // 1080p height. +}; + +// Constants used to calculate screen rotations. +namespace ScreenRotation +{ + // 0-degree Z-rotation + static const XMFLOAT4X4 Rotation0( + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + + // 90-degree Z-rotation + static const XMFLOAT4X4 Rotation90( + 0.0f, 1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + + // 180-degree Z-rotation + static const XMFLOAT4X4 Rotation180( + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + + // 270-degree Z-rotation + static const XMFLOAT4X4 Rotation270( + 0.0f, -1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); +}; + +// Constructor for DeviceResources. +DX::DeviceResources::DeviceResources() : + m_screenViewport(), + m_d3dFeatureLevel(D3D_FEATURE_LEVEL_9_1), + m_d3dRenderTargetSize(), + m_outputSize(), + m_logicalSize(), + m_nativeOrientation(DisplayOrientations::None), + m_currentOrientation(DisplayOrientations::None), + m_dpi(-1.0f), + m_effectiveDpi(-1.0f), + m_compositionScaleX(1.0f), + m_compositionScaleY(1.0f), + m_deviceNotify(nullptr) +{ + CreateDeviceIndependentResources(); + CreateDeviceResources(); +} + +// Configures resources that don't depend on the Direct3D device. +void DX::DeviceResources::CreateDeviceIndependentResources() +{ + // Initialize Direct2D resources. + D2D1_FACTORY_OPTIONS options; + ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS)); + +#if defined(_DEBUG) + // If the project is in a debug build, enable Direct2D debugging via SDK Layers. + options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; +#endif + + // Initialize the Direct2D Factory. + DX::ThrowIfFailed( + D2D1CreateFactory( + D2D1_FACTORY_TYPE_SINGLE_THREADED, + __uuidof(ID2D1Factory3), + &options, + &m_d2dFactory + ) + ); + + // Initialize the DirectWrite Factory. + DX::ThrowIfFailed( + DWriteCreateFactory( + DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory3), + &m_dwriteFactory + ) + ); + + // Initialize the Windows Imaging Component (WIC) Factory. + DX::ThrowIfFailed( + CoCreateInstance( + CLSID_WICImagingFactory2, + nullptr, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&m_wicFactory) + ) + ); +} + +// Configures the Direct3D device, and stores handles to it and the device context. +void DX::DeviceResources::CreateDeviceResources() +{ + // This flag adds support for surfaces with a different color channel ordering + // than the API default. It is required for compatibility with Direct2D. + UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; + +#if defined(_DEBUG) + if (DX::SdkLayersAvailable()) + { + // If the project is in a debug build, enable debugging via SDK Layers with this flag. + creationFlags |= D3D11_CREATE_DEVICE_DEBUG; + } +#endif + + // This array defines the set of DirectX hardware feature levels this app will support. + // Note the ordering should be preserved. + // Don't forget to declare your application's minimum required feature level in its + // description. All applications are assumed to support 9.1 unless otherwise stated. + D3D_FEATURE_LEVEL featureLevels[] = + { + D3D_FEATURE_LEVEL_12_1, + D3D_FEATURE_LEVEL_12_0, + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1 + }; + + // Create the Direct3D 11 API device object and a corresponding context. + ComPtr device; + ComPtr context; + + HRESULT hr = D3D11CreateDevice( + nullptr, // Specify nullptr to use the default adapter. + D3D_DRIVER_TYPE_HARDWARE, // Create a device using the hardware graphics driver. + 0, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE. + creationFlags, // Set debug and Direct2D compatibility flags. + featureLevels, // List of feature levels this app can support. + ARRAYSIZE(featureLevels), // Size of the list above. + D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps. + &device, // Returns the Direct3D device created. + &m_d3dFeatureLevel, // Returns feature level of device created. + &context // Returns the device immediate context. + ); + + if (FAILED(hr)) + { + // If the initialization fails, fall back to the WARP device. + // For more information on WARP, see: + // https://go.microsoft.com/fwlink/?LinkId=286690 + DX::ThrowIfFailed( + D3D11CreateDevice( + nullptr, + D3D_DRIVER_TYPE_WARP, // Create a WARP device instead of a hardware device. + 0, + creationFlags, + featureLevels, + ARRAYSIZE(featureLevels), + D3D11_SDK_VERSION, + &device, + &m_d3dFeatureLevel, + &context + ) + ); + } + + // Store pointers to the Direct3D 11.3 API device and immediate context. + DX::ThrowIfFailed( + device.As(&m_d3dDevice) + ); + + DX::ThrowIfFailed( + context.As(&m_d3dContext) + ); + + // Create the Direct2D device object and a corresponding context. + ComPtr dxgiDevice; + DX::ThrowIfFailed( + m_d3dDevice.As(&dxgiDevice) + ); + + DX::ThrowIfFailed( + m_d2dFactory->CreateDevice(dxgiDevice.Get(), &m_d2dDevice) + ); + + DX::ThrowIfFailed( + m_d2dDevice->CreateDeviceContext( + D2D1_DEVICE_CONTEXT_OPTIONS_NONE, + &m_d2dContext + ) + ); +} + +// These resources need to be recreated every time the window size is changed. +void DX::DeviceResources::CreateWindowSizeDependentResources() +{ + // Clear the previous window size specific context. + ID3D11RenderTargetView* nullViews[] = {nullptr}; + m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr); + m_d3dRenderTargetView = nullptr; + m_d2dContext->SetTarget(nullptr); + m_d2dTargetBitmap = nullptr; + m_d3dDepthStencilView = nullptr; + m_d3dContext->Flush1(D3D11_CONTEXT_TYPE_ALL, nullptr); + + UpdateRenderTargetSize(); + + // The width and height of the swap chain must be based on the window's + // natively-oriented width and height. If the window is not in the native + // orientation, the dimensions must be reversed. + DXGI_MODE_ROTATION displayRotation = ComputeDisplayRotation(); + + bool swapDimensions = displayRotation == DXGI_MODE_ROTATION_ROTATE90 || displayRotation == DXGI_MODE_ROTATION_ROTATE270; + m_d3dRenderTargetSize.Width = swapDimensions ? m_outputSize.Height : m_outputSize.Width; + m_d3dRenderTargetSize.Height = swapDimensions ? m_outputSize.Width : m_outputSize.Height; + + if (m_swapChain != nullptr) + { + // If the swap chain already exists, resize it. + HRESULT hr = m_swapChain->ResizeBuffers( + 2, // Double-buffered swap chain. + lround(m_d3dRenderTargetSize.Width), + lround(m_d3dRenderTargetSize.Height), + DXGI_FORMAT_B8G8R8A8_UNORM, + 0 + ); + + if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) + { + // If the device was removed for any reason, a new device and swap chain will need to be created. + HandleDeviceLost(); + + // Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method + // and correctly set up the new device. + return; + } + else + { + DX::ThrowIfFailed(hr); + } + } + else + { + // Otherwise, create a new one using the same adapter as the existing Direct3D device. + DXGI_SCALING scaling = DisplayMetrics::SupportHighResolutions ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH; + DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0}; + + swapChainDesc.Width = lround(m_d3dRenderTargetSize.Width); // Match the size of the window. + swapChainDesc.Height = lround(m_d3dRenderTargetSize.Height); + swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format. + swapChainDesc.Stereo = false; + swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling. + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency. + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use _FLIP_ SwapEffects. + swapChainDesc.Flags = 0; + swapChainDesc.Scaling = scaling; + swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; + + // This sequence obtains the DXGI factory that was used to create the Direct3D device above. + ComPtr dxgiDevice; + DX::ThrowIfFailed( + m_d3dDevice.As(&dxgiDevice) + ); + + ComPtr dxgiAdapter; + DX::ThrowIfFailed( + dxgiDevice->GetAdapter(&dxgiAdapter) + ); + + ComPtr dxgiFactory; + DX::ThrowIfFailed( + dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory)) + ); + + // When using XAML interop, the swap chain must be created for composition. + ComPtr swapChain; + DX::ThrowIfFailed( + dxgiFactory->CreateSwapChainForComposition( + m_d3dDevice.Get(), + &swapChainDesc, + nullptr, + &swapChain + ) + ); + + DX::ThrowIfFailed( + swapChain.As(&m_swapChain) + ); + + // Associate swap chain with SwapChainPanel + // UI changes will need to be dispatched back to the UI thread + m_swapChainPanel->Dispatcher->RunAsync(CoreDispatcherPriority::High, ref new DispatchedHandler([=]() + { + // Get backing native interface for SwapChainPanel + ComPtr panelNative; + DX::ThrowIfFailed( + reinterpret_cast(m_swapChainPanel)->QueryInterface(IID_PPV_ARGS(&panelNative)) + ); + + DX::ThrowIfFailed( + panelNative->SetSwapChain(m_swapChain.Get()) + ); + }, CallbackContext::Any)); + + // Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and + // ensures that the application will only render after each VSync, minimizing power consumption. + DX::ThrowIfFailed( + dxgiDevice->SetMaximumFrameLatency(1) + ); + } + + // Set the proper orientation for the swap chain, and generate 2D and + // 3D matrix transformations for rendering to the rotated swap chain. + // Note the rotation angle for the 2D and 3D transforms are different. + // This is due to the difference in coordinate spaces. Additionally, + // the 3D matrix is specified explicitly to avoid rounding errors. + + switch (displayRotation) + { + case DXGI_MODE_ROTATION_IDENTITY: + m_orientationTransform2D = Matrix3x2F::Identity(); + m_orientationTransform3D = ScreenRotation::Rotation0; + break; + + case DXGI_MODE_ROTATION_ROTATE90: + m_orientationTransform2D = + Matrix3x2F::Rotation(90.0f) * + Matrix3x2F::Translation(m_logicalSize.Height, 0.0f); + m_orientationTransform3D = ScreenRotation::Rotation270; + break; + + case DXGI_MODE_ROTATION_ROTATE180: + m_orientationTransform2D = + Matrix3x2F::Rotation(180.0f) * + Matrix3x2F::Translation(m_logicalSize.Width, m_logicalSize.Height); + m_orientationTransform3D = ScreenRotation::Rotation180; + break; + + case DXGI_MODE_ROTATION_ROTATE270: + m_orientationTransform2D = + Matrix3x2F::Rotation(270.0f) * + Matrix3x2F::Translation(0.0f, m_logicalSize.Width); + m_orientationTransform3D = ScreenRotation::Rotation90; + break; + + default: + throw ref new FailureException(); + } + + DX::ThrowIfFailed( + m_swapChain->SetRotation(displayRotation) + ); + + // Setup inverse scale on the swap chain + DXGI_MATRIX_3X2_F inverseScale = { 0 }; + inverseScale._11 = 1.0f / m_effectiveCompositionScaleX; + inverseScale._22 = 1.0f / m_effectiveCompositionScaleY; + ComPtr spSwapChain2; + DX::ThrowIfFailed( + m_swapChain.As(&spSwapChain2) + ); + + DX::ThrowIfFailed( + spSwapChain2->SetMatrixTransform(&inverseScale) + ); + + // Create a render target view of the swap chain back buffer. + ComPtr backBuffer; + DX::ThrowIfFailed( + m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer)) + ); + + // Do we want an sRGB buffer? + D3D11_RENDER_TARGET_VIEW_DESC1 rtvDesc = {}; + rtvDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;//DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + + DX::ThrowIfFailed( + m_d3dDevice->CreateRenderTargetView1( + backBuffer.Get(), + &rtvDesc, + &m_d3dRenderTargetView + ) + ); + + // Create a depth stencil view for use with 3D rendering if needed. + CD3D11_TEXTURE2D_DESC1 depthStencilDesc( + DXGI_FORMAT_D24_UNORM_S8_UINT, + lround(m_d3dRenderTargetSize.Width), + lround(m_d3dRenderTargetSize.Height), + 1, // This depth stencil view has only one texture. + 1, // Use a single mipmap level. + D3D11_BIND_DEPTH_STENCIL + ); + + ComPtr depthStencil; + DX::ThrowIfFailed( + m_d3dDevice->CreateTexture2D1( + &depthStencilDesc, + nullptr, + &depthStencil + ) + ); + + CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D); + DX::ThrowIfFailed( + m_d3dDevice->CreateDepthStencilView( + depthStencil.Get(), + &depthStencilViewDesc, + &m_d3dDepthStencilView + ) + ); + + // Set the 3D rendering viewport to target the entire window. + m_screenViewport = CD3D11_VIEWPORT( + 0.0f, + 0.0f, + m_d3dRenderTargetSize.Width, + m_d3dRenderTargetSize.Height + ); + + m_d3dContext->RSSetViewports(1, &m_screenViewport); + + // Create a Direct2D target bitmap associated with the + // swap chain back buffer and set it as the current target. + D2D1_BITMAP_PROPERTIES1 bitmapProperties = + D2D1::BitmapProperties1( + D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, + D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED), + m_dpi, + m_dpi + ); + + ComPtr dxgiBackBuffer; + DX::ThrowIfFailed( + m_swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer)) + ); + + DX::ThrowIfFailed( + m_d2dContext->CreateBitmapFromDxgiSurface( + dxgiBackBuffer.Get(), + &bitmapProperties, + &m_d2dTargetBitmap + ) + ); + + m_d2dContext->SetTarget(m_d2dTargetBitmap.Get()); + m_d2dContext->SetDpi(m_effectiveDpi, m_effectiveDpi); + + // Grayscale text anti-aliasing is recommended for all Windows Store apps. + m_d2dContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE); +} + +// Determine the dimensions of the render target and whether it will be scaled down. +void DX::DeviceResources::UpdateRenderTargetSize() +{ + m_effectiveDpi = m_dpi; + m_effectiveCompositionScaleX = m_compositionScaleX; + m_effectiveCompositionScaleY = m_compositionScaleY; + + // To improve battery life on high resolution devices, render to a smaller render target + // and allow the GPU to scale the output when it is presented. + if (!DisplayMetrics::SupportHighResolutions && m_dpi > DisplayMetrics::DpiThreshold) + { + float width = DX::ConvertDipsToPixels(m_logicalSize.Width, m_dpi); + float height = DX::ConvertDipsToPixels(m_logicalSize.Height, m_dpi); + + // When the device is in portrait orientation, height > width. Compare the + // larger dimension against the width threshold and the smaller dimension + // against the height threshold. + if (max(width, height) > DisplayMetrics::WidthThreshold && min(width, height) > DisplayMetrics::HeightThreshold) + { + // To scale the app we change the effective DPI. Logical size does not change. + m_effectiveDpi /= 2.0f; + m_effectiveCompositionScaleX /= 2.0f; + m_effectiveCompositionScaleY /= 2.0f; + } + } + + // Calculate the necessary render target size in pixels. + m_outputSize.Width = DX::ConvertDipsToPixels(m_logicalSize.Width, m_effectiveDpi); + m_outputSize.Height = DX::ConvertDipsToPixels(m_logicalSize.Height, m_effectiveDpi); + + // Prevent zero size DirectX content from being created. + m_outputSize.Width = max(m_outputSize.Width, 1); + m_outputSize.Height = max(m_outputSize.Height, 1); +} + +// This method is called when the XAML control is created (or re-created). +void DX::DeviceResources::SetSwapChainPanel(SwapChainPanel^ panel) +{ + DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView(); + + m_swapChainPanel = panel; + m_logicalSize = Windows::Foundation::Size(static_cast(panel->ActualWidth), static_cast(panel->ActualHeight)); + m_nativeOrientation = currentDisplayInformation->NativeOrientation; + m_currentOrientation = currentDisplayInformation->CurrentOrientation; + m_compositionScaleX = panel->CompositionScaleX; + m_compositionScaleY = panel->CompositionScaleY; + m_dpi = currentDisplayInformation->LogicalDpi; + m_d2dContext->SetDpi(m_dpi, m_dpi); + + CreateWindowSizeDependentResources(); +} + +// This method is called in the event handler for the SizeChanged event. +void DX::DeviceResources::SetLogicalSize(Windows::Foundation::Size logicalSize) +{ + if (m_logicalSize != logicalSize) + { + m_logicalSize = logicalSize; + CreateWindowSizeDependentResources(); + } +} + +// This method is called in the event handler for the DpiChanged event. +void DX::DeviceResources::SetDpi(float dpi) +{ + if (dpi != m_dpi) + { + m_dpi = dpi; + m_d2dContext->SetDpi(m_dpi, m_dpi); + CreateWindowSizeDependentResources(); + } +} + +// This method is called in the event handler for the OrientationChanged event. +void DX::DeviceResources::SetCurrentOrientation(DisplayOrientations currentOrientation) +{ + if (m_currentOrientation != currentOrientation) + { + m_currentOrientation = currentOrientation; + CreateWindowSizeDependentResources(); + } +} + +// This method is called in the event handler for the CompositionScaleChanged event. +void DX::DeviceResources::SetCompositionScale(float compositionScaleX, float compositionScaleY) +{ + if (m_compositionScaleX != compositionScaleX || + m_compositionScaleY != compositionScaleY) + { + m_compositionScaleX = compositionScaleX; + m_compositionScaleY = compositionScaleY; + CreateWindowSizeDependentResources(); + } +} + +// This method is called in the event handler for the DisplayContentsInvalidated event. +void DX::DeviceResources::ValidateDevice() +{ + // The D3D Device is no longer valid if the default adapter changed since the device + // was created or if the device has been removed. + + // First, get the information for the default adapter from when the device was created. + + ComPtr dxgiDevice; + DX::ThrowIfFailed(m_d3dDevice.As(&dxgiDevice)); + + ComPtr deviceAdapter; + DX::ThrowIfFailed(dxgiDevice->GetAdapter(&deviceAdapter)); + + ComPtr deviceFactory; + DX::ThrowIfFailed(deviceAdapter->GetParent(IID_PPV_ARGS(&deviceFactory))); + + ComPtr previousDefaultAdapter; + DX::ThrowIfFailed(deviceFactory->EnumAdapters1(0, &previousDefaultAdapter)); + + DXGI_ADAPTER_DESC1 previousDesc; + DX::ThrowIfFailed(previousDefaultAdapter->GetDesc1(&previousDesc)); + + // Next, get the information for the current default adapter. + + ComPtr currentFactory; + DX::ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(¤tFactory))); + + ComPtr currentDefaultAdapter; + DX::ThrowIfFailed(currentFactory->EnumAdapters1(0, ¤tDefaultAdapter)); + + DXGI_ADAPTER_DESC1 currentDesc; + DX::ThrowIfFailed(currentDefaultAdapter->GetDesc1(¤tDesc)); + + // If the adapter LUIDs don't match, or if the device reports that it has been removed, + // a new D3D device must be created. + + if (previousDesc.AdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart || + previousDesc.AdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart || + FAILED(m_d3dDevice->GetDeviceRemovedReason())) + { + // Release references to resources related to the old device. + dxgiDevice = nullptr; + deviceAdapter = nullptr; + deviceFactory = nullptr; + previousDefaultAdapter = nullptr; + + // Create a new device and swap chain. + HandleDeviceLost(); + } +} + +// Recreate all device resources and set them back to the current state. +void DX::DeviceResources::HandleDeviceLost() +{ + m_swapChain = nullptr; + + if (m_deviceNotify != nullptr) + { + m_deviceNotify->OnDeviceLost(); + } + + CreateDeviceResources(); + m_d2dContext->SetDpi(m_dpi, m_dpi); + CreateWindowSizeDependentResources(); + + if (m_deviceNotify != nullptr) + { + m_deviceNotify->OnDeviceRestored(); + } +} + +// Register our DeviceNotify to be informed on device lost and creation. +void DX::DeviceResources::RegisterDeviceNotify(DX::IDeviceNotify* deviceNotify) +{ + m_deviceNotify = deviceNotify; +} + +// Call this method when the app suspends. It provides a hint to the driver that the app +// is entering an idle state and that temporary buffers can be reclaimed for use by other apps. +void DX::DeviceResources::Trim() +{ + ComPtr dxgiDevice; + m_d3dDevice.As(&dxgiDevice); + + dxgiDevice->Trim(); +} + +// Present the contents of the swap chain to the screen. +void DX::DeviceResources::Present() +{ + // The first argument instructs DXGI to block until VSync, putting the application + // to sleep until the next VSync. This ensures we don't waste any cycles rendering + // frames that will never be displayed to the screen. + DXGI_PRESENT_PARAMETERS parameters = { 0 }; + HRESULT hr = m_swapChain->Present1(1, 0, ¶meters); + + // Discard the contents of the render target. + // This is a valid operation only when the existing contents will be entirely + // overwritten. If dirty or scroll rects are used, this call should be modified. + m_d3dContext->DiscardView1(m_d3dRenderTargetView.Get(), nullptr, 0); + + // Discard the contents of the depth stencil. + m_d3dContext->DiscardView1(m_d3dDepthStencilView.Get(), nullptr, 0); + + // If the device was removed either by a disconnection or a driver upgrade, we + // must recreate all device resources. + if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) + { + HandleDeviceLost(); + } + else + { + DX::ThrowIfFailed(hr); + } +} + +// This method determines the rotation between the display device's native orientation and the +// current display orientation. +DXGI_MODE_ROTATION DX::DeviceResources::ComputeDisplayRotation() +{ + DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED; + + // Note: NativeOrientation can only be Landscape or Portrait even though + // the DisplayOrientations enum has other values. + switch (m_nativeOrientation) + { + case DisplayOrientations::Landscape: + switch (m_currentOrientation) + { + case DisplayOrientations::Landscape: + rotation = DXGI_MODE_ROTATION_IDENTITY; + break; + + case DisplayOrientations::Portrait: + rotation = DXGI_MODE_ROTATION_ROTATE270; + break; + + case DisplayOrientations::LandscapeFlipped: + rotation = DXGI_MODE_ROTATION_ROTATE180; + break; + + case DisplayOrientations::PortraitFlipped: + rotation = DXGI_MODE_ROTATION_ROTATE90; + break; + } + break; + + case DisplayOrientations::Portrait: + switch (m_currentOrientation) + { + case DisplayOrientations::Landscape: + rotation = DXGI_MODE_ROTATION_ROTATE90; + break; + + case DisplayOrientations::Portrait: + rotation = DXGI_MODE_ROTATION_IDENTITY; + break; + + case DisplayOrientations::LandscapeFlipped: + rotation = DXGI_MODE_ROTATION_ROTATE270; + break; + + case DisplayOrientations::PortraitFlipped: + rotation = DXGI_MODE_ROTATION_ROTATE180; + break; + } + break; + } + return rotation; +} \ No newline at end of file diff --git a/ModelViewer/Common/DeviceResources.h b/ModelViewer/Common/DeviceResources.h new file mode 100644 index 0000000..00d84e7 --- /dev/null +++ b/ModelViewer/Common/DeviceResources.h @@ -0,0 +1,107 @@ +#pragma once + +namespace DX +{ + // Provides an interface for an application that owns DeviceResources to be notified of the device being lost or created. + interface IDeviceNotify + { + virtual void OnDeviceLost() = 0; + virtual void OnDeviceRestored() = 0; + }; + + // Controls all the DirectX device resources. + class DeviceResources + { + public: + DeviceResources(); + void SetSwapChainPanel(Windows::UI::Xaml::Controls::SwapChainPanel^ panel); + void SetLogicalSize(Windows::Foundation::Size logicalSize); + void SetCurrentOrientation(Windows::Graphics::Display::DisplayOrientations currentOrientation); + void SetDpi(float dpi); + void SetCompositionScale(float compositionScaleX, float compositionScaleY); + void ValidateDevice(); + void HandleDeviceLost(); + void RegisterDeviceNotify(IDeviceNotify* deviceNotify); + void Trim(); + void Present(); + + // The size of the render target, in pixels. + Windows::Foundation::Size GetOutputSize() const { return m_outputSize; } + + // The size of the render target, in dips. + Windows::Foundation::Size GetLogicalSize() const { return m_logicalSize; } + float GetDpi() const { return m_effectiveDpi; } + + // D3D Accessors. + ID3D11Device3* GetD3DDevice() const { return m_d3dDevice.Get(); } + ID3D11DeviceContext3* GetD3DDeviceContext() const { return m_d3dContext.Get(); } + IDXGISwapChain3* GetSwapChain() const { return m_swapChain.Get(); } + D3D_FEATURE_LEVEL GetDeviceFeatureLevel() const { return m_d3dFeatureLevel; } + ID3D11RenderTargetView1* GetBackBufferRenderTargetView() const { return m_d3dRenderTargetView.Get(); } + ID3D11DepthStencilView* GetDepthStencilView() const { return m_d3dDepthStencilView.Get(); } + D3D11_VIEWPORT GetScreenViewport() const { return m_screenViewport; } + DirectX::XMFLOAT4X4 GetOrientationTransform3D() const { return m_orientationTransform3D; } + + // D2D Accessors. + ID2D1Factory3* GetD2DFactory() const { return m_d2dFactory.Get(); } + ID2D1Device2* GetD2DDevice() const { return m_d2dDevice.Get(); } + ID2D1DeviceContext2* GetD2DDeviceContext() const { return m_d2dContext.Get(); } + ID2D1Bitmap1* GetD2DTargetBitmap() const { return m_d2dTargetBitmap.Get(); } + IDWriteFactory3* GetDWriteFactory() const { return m_dwriteFactory.Get(); } + IWICImagingFactory2* GetWicImagingFactory() const { return m_wicFactory.Get(); } + D2D1::Matrix3x2F GetOrientationTransform2D() const { return m_orientationTransform2D; } + + private: + void CreateDeviceIndependentResources(); + void CreateDeviceResources(); + void CreateWindowSizeDependentResources(); + void UpdateRenderTargetSize(); + DXGI_MODE_ROTATION ComputeDisplayRotation(); + + // Direct3D objects. + Microsoft::WRL::ComPtr m_d3dDevice; + Microsoft::WRL::ComPtr m_d3dContext; + Microsoft::WRL::ComPtr m_swapChain; + + // Direct3D rendering objects. Required for 3D. + Microsoft::WRL::ComPtr m_d3dRenderTargetView; + Microsoft::WRL::ComPtr m_d3dDepthStencilView; + D3D11_VIEWPORT m_screenViewport; + + // Direct2D drawing components. + Microsoft::WRL::ComPtr m_d2dFactory; + Microsoft::WRL::ComPtr m_d2dDevice; + Microsoft::WRL::ComPtr m_d2dContext; + Microsoft::WRL::ComPtr m_d2dTargetBitmap; + + // DirectWrite drawing components. + Microsoft::WRL::ComPtr m_dwriteFactory; + Microsoft::WRL::ComPtr m_wicFactory; + + // Cached reference to the XAML panel. + Windows::UI::Xaml::Controls::SwapChainPanel^ m_swapChainPanel; + + // Cached device properties. + D3D_FEATURE_LEVEL m_d3dFeatureLevel; + Windows::Foundation::Size m_d3dRenderTargetSize; + Windows::Foundation::Size m_outputSize; + Windows::Foundation::Size m_logicalSize; + Windows::Graphics::Display::DisplayOrientations m_nativeOrientation; + Windows::Graphics::Display::DisplayOrientations m_currentOrientation; + float m_dpi; + float m_compositionScaleX; + float m_compositionScaleY; + + // Variables that take into account whether the app supports high resolution screens or not. + float m_effectiveDpi; + float m_effectiveCompositionScaleX; + float m_effectiveCompositionScaleY; + + // Transforms used for display orientation. + D2D1::Matrix3x2F m_orientationTransform2D; + DirectX::XMFLOAT4X4 m_orientationTransform3D; + + // The IDeviceNotify can be held directly as it owns the DeviceResources. + IDeviceNotify* m_deviceNotify; + }; +} \ No newline at end of file diff --git a/ModelViewer/Common/DirectXHelper.h b/ModelViewer/Common/DirectXHelper.h new file mode 100644 index 0000000..9c2e142 --- /dev/null +++ b/ModelViewer/Common/DirectXHelper.h @@ -0,0 +1,63 @@ +#pragma once + +#include // For create_task + +namespace DX +{ + inline void ThrowIfFailed(HRESULT hr) + { + if (FAILED(hr)) + { + // Set a breakpoint on this line to catch Win32 API errors. + throw Platform::Exception::CreateException(hr); + } + } + + // Function that reads from a binary file asynchronously. + inline Concurrency::task> ReadDataAsync(const std::wstring& filename) + { + using namespace Windows::Storage; + using namespace Concurrency; + + auto folder = Windows::ApplicationModel::Package::Current->InstalledLocation; + + return create_task(folder->GetFileAsync(Platform::StringReference(filename.c_str()))).then([] (StorageFile^ file) + { + return FileIO::ReadBufferAsync(file); + }).then([] (Streams::IBuffer^ fileBuffer) -> std::vector<::byte> + { + std::vector<::byte> returnBuffer; + returnBuffer.resize(fileBuffer->Length); + Streams::DataReader::FromBuffer(fileBuffer)->ReadBytes(Platform::ArrayReference<::byte>(returnBuffer.data(), fileBuffer->Length)); + return returnBuffer; + }); + } + + // Converts a length in device-independent pixels (DIPs) to a length in physical pixels. + inline float ConvertDipsToPixels(float dips, float dpi) + { + static const float dipsPerInch = 96.0f; + return floorf(dips * dpi / dipsPerInch + 0.5f); // Round to nearest integer. + } + +#if defined(_DEBUG) + // Check for SDK Layer support. + inline bool SdkLayersAvailable() + { + HRESULT hr = D3D11CreateDevice( + nullptr, + D3D_DRIVER_TYPE_NULL, // There is no need to create a real hardware device. + 0, + D3D11_CREATE_DEVICE_DEBUG, // Check for the SDK layers. + nullptr, // Any feature level will do. + 0, + D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps. + nullptr, // No need to keep the D3D device reference. + nullptr, // No need to know the feature level. + nullptr // No need to keep the D3D device context reference. + ); + + return SUCCEEDED(hr); + } +#endif +} diff --git a/ModelViewer/Common/StepTimer.h b/ModelViewer/Common/StepTimer.h new file mode 100644 index 0000000..286710c --- /dev/null +++ b/ModelViewer/Common/StepTimer.h @@ -0,0 +1,183 @@ +#pragma once + +#include + +namespace DX +{ + // Helper class for animation and simulation timing. + class StepTimer + { + public: + StepTimer() : + m_elapsedTicks(0), + m_totalTicks(0), + m_leftOverTicks(0), + m_frameCount(0), + m_framesPerSecond(0), + m_framesThisSecond(0), + m_qpcSecondCounter(0), + m_isFixedTimeStep(false), + m_targetElapsedTicks(TicksPerSecond / 60) + { + if (!QueryPerformanceFrequency(&m_qpcFrequency)) + { + throw ref new Platform::FailureException(); + } + + if (!QueryPerformanceCounter(&m_qpcLastTime)) + { + throw ref new Platform::FailureException(); + } + + // Initialize max delta to 1/10 of a second. + m_qpcMaxDelta = m_qpcFrequency.QuadPart / 10; + } + + // Get elapsed time since the previous Update call. + uint64 GetElapsedTicks() const { return m_elapsedTicks; } + double GetElapsedSeconds() const { return TicksToSeconds(m_elapsedTicks); } + + // Get total time since the start of the program. + uint64 GetTotalTicks() const { return m_totalTicks; } + double GetTotalSeconds() const { return TicksToSeconds(m_totalTicks); } + + // Get total number of updates since start of the program. + uint32 GetFrameCount() const { return m_frameCount; } + + // Get the current framerate. + uint32 GetFramesPerSecond() const { return m_framesPerSecond; } + + // Set whether to use fixed or variable timestep mode. + void SetFixedTimeStep(bool isFixedTimestep) { m_isFixedTimeStep = isFixedTimestep; } + + // Set how often to call Update when in fixed timestep mode. + void SetTargetElapsedTicks(uint64 targetElapsed) { m_targetElapsedTicks = targetElapsed; } + void SetTargetElapsedSeconds(double targetElapsed) { m_targetElapsedTicks = SecondsToTicks(targetElapsed); } + + // Integer format represents time using 10,000,000 ticks per second. + static const uint64 TicksPerSecond = 10000000; + + static double TicksToSeconds(uint64 ticks) { return static_cast(ticks) / TicksPerSecond; } + static uint64 SecondsToTicks(double seconds) { return static_cast(seconds * TicksPerSecond); } + + // After an intentional timing discontinuity (for instance a blocking IO operation) + // call this to avoid having the fixed timestep logic attempt a set of catch-up + // Update calls. + + void ResetElapsedTime() + { + if (!QueryPerformanceCounter(&m_qpcLastTime)) + { + throw ref new Platform::FailureException(); + } + + m_leftOverTicks = 0; + m_framesPerSecond = 0; + m_framesThisSecond = 0; + m_qpcSecondCounter = 0; + } + + // Update timer state, calling the specified Update function the appropriate number of times. + template + void Tick(const TUpdate& update) + { + // Query the current time. + LARGE_INTEGER currentTime; + + if (!QueryPerformanceCounter(¤tTime)) + { + throw ref new Platform::FailureException(); + } + + uint64 timeDelta = currentTime.QuadPart - m_qpcLastTime.QuadPart; + + m_qpcLastTime = currentTime; + m_qpcSecondCounter += timeDelta; + + // Clamp excessively large time deltas (e.g. after paused in the debugger). + if (timeDelta > m_qpcMaxDelta) + { + timeDelta = m_qpcMaxDelta; + } + + // Convert QPC units into a canonical tick format. This cannot overflow due to the previous clamp. + timeDelta *= TicksPerSecond; + timeDelta /= m_qpcFrequency.QuadPart; + + uint32 lastFrameCount = m_frameCount; + + if (m_isFixedTimeStep) + { + // Fixed timestep update logic + + // If the app is running very close to the target elapsed time (within 1/4 of a millisecond) just clamp + // the clock to exactly match the target value. This prevents tiny and irrelevant errors + // from accumulating over time. Without this clamping, a game that requested a 60 fps + // fixed update, running with vsync enabled on a 59.94 NTSC display, would eventually + // accumulate enough tiny errors that it would drop a frame. It is better to just round + // small deviations down to zero to leave things running smoothly. + + if (abs(static_cast(timeDelta - m_targetElapsedTicks)) < TicksPerSecond / 4000) + { + timeDelta = m_targetElapsedTicks; + } + + m_leftOverTicks += timeDelta; + + while (m_leftOverTicks >= m_targetElapsedTicks) + { + m_elapsedTicks = m_targetElapsedTicks; + m_totalTicks += m_targetElapsedTicks; + m_leftOverTicks -= m_targetElapsedTicks; + m_frameCount++; + + update(); + } + } + else + { + // Variable timestep update logic. + m_elapsedTicks = timeDelta; + m_totalTicks += timeDelta; + m_leftOverTicks = 0; + m_frameCount++; + + update(); + } + + // Track the current framerate. + if (m_frameCount != lastFrameCount) + { + m_framesThisSecond++; + } + + if (m_qpcSecondCounter >= static_cast(m_qpcFrequency.QuadPart)) + { + m_framesPerSecond = m_framesThisSecond; + m_framesThisSecond = 0; + m_qpcSecondCounter %= m_qpcFrequency.QuadPart; + } + } + + private: + // Source timing data uses QPC units. + LARGE_INTEGER m_qpcFrequency; + LARGE_INTEGER m_qpcLastTime; + uint64 m_qpcMaxDelta; + + // Derived timing data uses a canonical tick format. + uint64 m_elapsedTicks; + uint64 m_totalTicks; + uint64 m_leftOverTicks; + + // Members for tracking the framerate. + uint32 m_frameCount; + uint32 m_framesPerSecond; + uint32 m_framesThisSecond; + uint64 m_qpcSecondCounter; + + // Members for configuring fixed timestep mode. + bool m_isFixedTimeStep; + uint64 m_targetElapsedTicks; + }; +} diff --git a/ModelViewer/Common/ViewModelBase.cpp b/ModelViewer/Common/ViewModelBase.cpp new file mode 100644 index 0000000..92f47f2 --- /dev/null +++ b/ModelViewer/Common/ViewModelBase.cpp @@ -0,0 +1,8 @@ +#include "pch.h" +#include "ViewModelBase.h" + +using namespace Common; + +ViewModelBase::ViewModelBase() +{ +} diff --git a/ModelViewer/Common/ViewModelBase.h b/ModelViewer/Common/ViewModelBase.h new file mode 100644 index 0000000..59c7a0f --- /dev/null +++ b/ModelViewer/Common/ViewModelBase.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Common\BindableBase.h" + +namespace Common +{ + public ref class ViewModelBase : public BindableBase + { + internal: + ViewModelBase(); + }; +} diff --git a/ModelViewer/Container.cpp b/ModelViewer/Container.cpp new file mode 100644 index 0000000..3a80302 --- /dev/null +++ b/ModelViewer/Container.cpp @@ -0,0 +1,3 @@ +#include "pch.h" +#include "Container.h" + diff --git a/ModelViewer/Container.h b/ModelViewer/Container.h new file mode 100644 index 0000000..4ffc06f --- /dev/null +++ b/ModelViewer/Container.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +using namespace std; + +class Container +{ +public: + static Container& Instance() + { + static Container instance; + return instance; + } + + template + T Create() + { + return injector.create(); + } + + shared_ptr ResolveDirectXPageViewModelData() + { + if (_dxPageVMData == nullptr) + _dxPageVMData = make_shared(); + return _dxPageVMData; + } + +private: + Container() + { + } + shared_ptr _dxPageVMData; +}; + + diff --git a/ModelViewer/Content/Sample3DSceneRenderer.cpp b/ModelViewer/Content/Sample3DSceneRenderer.cpp new file mode 100644 index 0000000..15182d6 --- /dev/null +++ b/ModelViewer/Content/Sample3DSceneRenderer.cpp @@ -0,0 +1,674 @@ +#include "pch.h" +#include +#include "Sample3DSceneRenderer.h" + +#include "..\Common\DirectXHelper.h" +#include "Utility.h" + +#include "..\EventShim.h" +#include "SceneManager.h" +#include "BufferManager.h" + +#include "DirectXPageViewModelData.h" + +using namespace Windows::Storage::Streams; +using namespace Windows::Storage; +using namespace Microsoft::WRL; + +using namespace ModelViewer; + +using namespace DirectX; +using namespace Windows::Foundation; +using namespace Microsoft::WRL; + +void updateMathScales(string selected) +{ + float mathDiff = (selected == "mathDiff") ? 1.0f : 0.0f; + float baseColor = (selected == "baseColor") ? 1.0f : 0.0f; + float metallic = (selected == "metallic") ? 1.0f : 0.0f; + float roughness = (selected == "roughness") ? 1.0f : 0.0f; + + BufferManager::Instance().PerObjBuffer().BufferData().scaleDiffBaseMR.x = mathDiff; + BufferManager::Instance().PerObjBuffer().BufferData().scaleDiffBaseMR.y = baseColor; + BufferManager::Instance().PerObjBuffer().BufferData().scaleDiffBaseMR.z = metallic; + BufferManager::Instance().PerObjBuffer().BufferData().scaleDiffBaseMR.w = roughness; + + float mathF = (selected == "mathF") ? 1.0f : 0.0f; + float mathG = (selected == "mathG") ? 1.0f : 0.0f; + float mathD = (selected == "mathD") ? 1.0f : 0.0f; + float mathSpec = (selected == "mathSpec") ? 1.0f : 0.0f; + + BufferManager::Instance().PerObjBuffer().BufferData().scaleFGDSpec.x = mathF; + BufferManager::Instance().PerObjBuffer().BufferData().scaleFGDSpec.y = mathG; + BufferManager::Instance().PerObjBuffer().BufferData().scaleFGDSpec.z = mathD; + BufferManager::Instance().PerObjBuffer().BufferData().scaleFGDSpec.w = mathSpec; +}; + +future GetBuffer(StorageFolder^ imgfolder, String^ imgType, String^ side, int mipmapLevel) +{ + String^ fileName(imgType + "_" + side + "_" + mipmapLevel + ".jpg"); + Utility::Out(L"Loading file [%s\\%s]", imgfolder->Path->Data(), fileName->Data()); + auto file = co_await imgfolder->GetFileAsync(fileName); + auto buffer = co_await FileIO::ReadBufferAsync(file); + return buffer; +} + +future LoadFileDataAsync(IBuffer^ buffer, int& fileSize) +{ + fileSize = buffer->Length; + + // Query the IBufferByteAccess interface. + ComPtr bufferByteAccess; + reinterpret_cast(buffer)->QueryInterface(IID_PPV_ARGS(&bufferByteAccess)); + + ::byte* data = nullptr; + bufferByteAccess->Buffer(&data); + + co_return static_cast(data); +} + +#include "ImgUtils.h" + +future> LoadCubeImagesAsync(StorageFolder^ imgFolder, String^ imgType, String^ side, int mipLevel, uint32_t& width, uint32_t& height) +{ + int dataSize = 0; + auto buffer = co_await GetBuffer(imgFolder, imgType, side, mipLevel); + + auto fileData = co_await LoadFileDataAsync(buffer, dataSize); + auto pth = imgFolder->Path + "\\" + imgType + "_" + side + "_" + mipLevel + ".jpg"; + + auto bytes = ImgUtils::LoadRGBAImage(fileData, dataSize, width, height, true, pth->Data()); + co_return bytes; +} + +const wchar_t *sides[] = +{ + L"back", + L"front", + L"top", + L"bottom", + L"left", + L"right" +}; + + +future Sample3DSceneRenderer::CreateCubeMapAsync(ID3D11Device3 *device, + StorageFolder^ imgFolder, String^ imgType, int mipLevels) +{ + D3D11_TEXTURE2D_DESC texDesc; + texDesc.MipLevels = mipLevels; + texDesc.ArraySize = 6; + texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + texDesc.CPUAccessFlags = 0; + texDesc.SampleDesc.Count = 1; + texDesc.SampleDesc.Quality = 0; + texDesc.Usage = D3D11_USAGE_DEFAULT; + texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + texDesc.CPUAccessFlags = 0; + texDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE; + + D3D11_SHADER_RESOURCE_VIEW_DESC SMViewDesc; + SMViewDesc.Format = texDesc.Format; + SMViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + SMViewDesc.TextureCube.MipLevels = texDesc.MipLevels; + SMViewDesc.TextureCube.MostDetailedMip = 0; + + vector pData(6 * mipLevels); + uint32_t twidth = 0; + uint32_t theight = 0; + vector> bytes(6 * mipLevels); + for (int j = 0; j < mipLevels; j++) + { + for (int i = 0; i < 6; i++) + { + int idx = j * 6 + i; + Utility::Out(L"Loading cube image [%d]", idx); + uint32_t width = 0; + uint32_t height = 0; + auto imgData = co_await LoadCubeImagesAsync(imgFolder, imgType, ref new String(sides[i]), j, width, height); + bytes[idx] = imgData; + Utility::Out(L"cube image size [%d, %d, %d]", width, height, bytes[idx].size()); + if (width > twidth) + twidth = width; + if (height > theight) + theight = height; + Utility::Out(L"Loaded cube image [%d]", idx); + pData[idx].pSysMem = bytes[idx].data(); + pData[idx].SysMemPitch = width * 4; + pData[idx].SysMemSlicePitch = 0; + } + } + + texDesc.Width = twidth; + texDesc.Height = theight; + + ComPtr tex; + HRESULT hr = device->CreateTexture2D(&texDesc, &pData[0], tex.GetAddressOf()); + assert(hr == S_OK); + + ComPtr resourceView; + hr = device->CreateShaderResourceView(tex.Get(), &SMViewDesc, resourceView.ReleaseAndGetAddressOf()); + + assert(hr == S_OK); + + D3D11_SAMPLER_DESC samplerDesc = {}; + samplerDesc.Filter = D3D11_FILTER_MAXIMUM_ANISOTROPIC; + samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; + samplerDesc.MinLOD = 0; + samplerDesc.MaxLOD = D3D11_FLOAT32_MAX; + + ComPtr samplerState; + DX::ThrowIfFailed(device->CreateSamplerState(&samplerDesc, samplerState.ReleaseAndGetAddressOf())); + + TexWrapper ret; + ret.texResourceView = resourceView; + ret.texSampler = samplerState; + + co_return ret; +} + +// Loads vertex and pixel shaders from files and instantiates the cube geometry. +Sample3DSceneRenderer::Sample3DSceneRenderer(const std::shared_ptr& deviceResources) : + m_loadingComplete(false), + m_degreesPerSecond(45), + m_indexCount(0), + m_tracking(false), + m_deviceResources(deviceResources) +{ + CreateDeviceDependentResources(); + CreateWindowSizeDependentResources(); + + _context = make_unique(m_deviceResources->GetD3DDeviceContext()); + + auto data = Container::Instance().ResolveDirectXPageViewModelData(); + data->RegisterForUpdates(boost::bind(&Sample3DSceneRenderer::NotifyDataChanged, this, _1)); + + BufferManager::Instance().MVPBuffer().BufferData().light_direction = XMFLOAT4(1.7f, 11.0f, 5.7f, 1.0f); + BufferManager::Instance().PerFrameBuffer().BufferData().light.dir = XMFLOAT3(0.5f, 0.5f, -0.5f); + BufferManager::Instance().PerFrameBuffer().BufferData().light.colour = XMFLOAT3(10.0f, 10.0f, 10.0f); + + // Just testing by initialising these here... + BufferManager::Instance().PerObjBuffer().BufferData().normalScale = 1.0f; + BufferManager::Instance().PerObjBuffer().BufferData().emissiveFactor = XMFLOAT3(1.0f, 1.0f, 1.0f); + BufferManager::Instance().PerObjBuffer().BufferData().occlusionStrength = 1.0f; + BufferManager::Instance().PerObjBuffer().BufferData().metallicRoughnessValues = XMFLOAT2(1.0f, 1.0f); + + BufferManager::Instance().PerObjBuffer().BufferData().baseColorFactor.x = 1.0f; + BufferManager::Instance().PerObjBuffer().BufferData().baseColorFactor.y = 1.0f; + BufferManager::Instance().PerObjBuffer().BufferData().baseColorFactor.z = 1.0f; + BufferManager::Instance().PerObjBuffer().BufferData().baseColorFactor.w = 1.0f; + + //updateMathScales("baseColor"); + + BufferManager::Instance().PerObjBuffer().BufferData().scaleIBLAmbient.x = 1.0f; + BufferManager::Instance().PerObjBuffer().BufferData().scaleIBLAmbient.y = 1.0f; + BufferManager::Instance().PerObjBuffer().BufferData().scaleIBLAmbient.z = 1.0f; + BufferManager::Instance().PerObjBuffer().BufferData().scaleIBLAmbient.w = 1.0f; + + // Need to find out how to set these... + //XMFLOAT3 camera; + + SceneManager::Instance().SetDevResources(deviceResources); + + _grid = make_unique(); + _grid->Initialise(deviceResources->GetD3DDevice()); + _mainAxes = make_unique(1000.0f); + _mainAxes->Initialise(deviceResources->GetD3DDevice()); +} + +// Initializes view parameters when the window size changes. +void Sample3DSceneRenderer::CreateWindowSizeDependentResources() +{ + Size outputSize = m_deviceResources->GetOutputSize(); + float aspectRatio = outputSize.Width / outputSize.Height; + float fovAngleY = 70.0f * XM_PI / 180.0f; + + // This is a simple example of change that can be made when the app is in + // portrait or snapped view. + if (aspectRatio < 1.0f) + { + fovAngleY *= 2.0f; + } + + // Note that the OrientationTransform3D matrix is post-multiplied here + // in order to correctly orient the scene to match the display orientation. + // This post-multiplication step is required for any draw calls that are + // made to the swap chain render target. For draw calls to other targets, + // this transform should not be applied. + + // This sample makes use of a right-handed coordinate system using row-major matrices. + XMMATRIX perspectiveMatrix = XMMatrixPerspectiveFovRH( + fovAngleY, + aspectRatio, + 0.01f, + 1000.0f + ); + + XMFLOAT4X4 orientation = m_deviceResources->GetOrientationTransform3D(); + + XMMATRIX orientationMatrix = XMLoadFloat4x4(&orientation); + + XMStoreFloat4x4( + &BufferManager::Instance().MVPBuffer().BufferData().projection, + XMMatrixTranspose(perspectiveMatrix * orientationMatrix) + ); + + static const XMVECTORF32 up = { 0.0f, 1.0f, 0.0f, 0.0f }; + + auto camMatrix = XMMatrixRotationRollPitchYaw(_pitch, _yaw, _roll); + + XMVECTORF32 alongZ = { _panx, _pany, _zoom }; + XMVECTORF32 at = { _panx, _pany, 0.0f, 0.0f }; + + auto eye = XMVector3TransformCoord(alongZ, camMatrix); + XMStoreFloat4x4(&BufferManager::Instance().MVPBuffer().BufferData().view, + XMMatrixTranspose(XMMatrixLookAtRH(eye, at, up))); + + XMFLOAT4 eyevec; + XMStoreFloat4(&eyevec, eye); + BufferManager::Instance().PerObjBuffer().BufferData().camera.x = eyevec.x; + BufferManager::Instance().PerObjBuffer().BufferData().camera.y = eyevec.y; + BufferManager::Instance().PerObjBuffer().BufferData().camera.z = eyevec.z; +} + +// Called once per frame, rotates the cube and calculates the model and view matrices. +void Sample3DSceneRenderer::Update(StepTimer const& timer) +{ + if (!m_tracking) + { + // Convert degrees to radians, then convert seconds to rotation angle + //float radiansPerSecond = XMConvertToRadians(m_degreesPerSecond); + //double totalRotation = timer.GetTotalSeconds() * radiansPerSecond; + //float radians = static_cast(fmod(totalRotation, XM_2PI)); + + Rotate(0.0f); + } + + SceneManager::Instance().Current()->Update(timer); +} + +// Rotate the 3D cube model a set amount of radians. +void Sample3DSceneRenderer::Rotate(float radians) +{ + // Prepare to pass the updated model matrix to the shader + //XMStoreFloat4x4(&BufferManager::Instance().MVPBuffer().BufferData().model, XMMatrixTranspose(XMMatrixRotationY(radians))); +} + +void Sample3DSceneRenderer::StartTracking(float positionX, float positionY, VirtualKeyModifiers mod) +{ + Utility::Out(L"StartTracking [%f %f]", positionX, positionY); + + m_tracking = true; + _lastPosY = positionY; + _lastPosX = positionX; +} + +// When tracking, the 3D cube can be rotated around its Y axis by tracking pointer position relative to the output screen width. +void Sample3DSceneRenderer::TrackingUpdate(float positionX, float positionY, VirtualKeyModifiers mod) +{ + if (m_tracking) + { + //Utility::Out(L"TrackingUpdate [%f %f] %d", positionX, positionY, mod); + + if ((int)(mod & VirtualKeyModifiers::Control) != 0) + { + _zoom += (positionY - _lastPosY) / 120.0f; + } + else if ((int)(mod & VirtualKeyModifiers::Shift) != 0) + { + _panx += (positionX - _lastPosX) / 200.0f; + _pany += (positionY - _lastPosY) / 200.0f; + } + else + { + _pitch += (positionY - _lastPosY) / 100.0f; + _yaw += (positionX - _lastPosX) / 100.0f; + } + + + _lastPosY = positionY; + _lastPosX = positionX; + + CreateWindowSizeDependentResources(); + } +} + +void Sample3DSceneRenderer::StopTracking(float positionX, float positionY, VirtualKeyModifiers mod) +{ + Utility::Out(L"StopTracking [%f %f]", positionX, positionY); + m_tracking = false; +} + +// Renders one frame using the vertex and pixel shaders. +void Sample3DSceneRenderer::Render() +{ + // Loading is asynchronous. Only draw geometry after it's loaded. + if (!m_loadingComplete) + { + return; + } + + auto context = m_deviceResources->GetD3DDeviceContext(); + + XMStoreFloat4x4(&BufferManager::Instance().MVPBuffer().BufferData().model, XMMatrixTranspose(XMMatrixRotationY(0.0))); + + DrawAxis(context, _mainAxes.get()); + DrawGrid(context); + + context->RSSetState(_pRasterState1.Get()); + + BufferManager::Instance().PerObjBuffer().Update(*m_deviceResources); + BufferManager::Instance().PerFrameBuffer().Update(*m_deviceResources); + + // Not sure which start slot to use here - maybe we need to keep a count.. + context->PSSetShaderResources(8, 1, _envTexResourceView.GetAddressOf()); + context->PSSetSamplers(8, 1, _envTexSampler.GetAddressOf()); + context->PSSetShaderResources(9, 1, _brdfLutResourceView.GetAddressOf()); + context->PSSetSamplers(9, 1, _brdfLutSampler.GetAddressOf()); + context->PSSetShaderResources(10, 1, _envSpecularTexResourceView.GetAddressOf()); + context->PSSetSamplers(10, 1, _envSpecularTexSampler.GetAddressOf()); + XMFLOAT4X4 matrix; + XMStoreFloat4x4(&matrix, XMMatrixIdentity()); + _context->SetModel(matrix); + SceneManager::Instance().Current()->Draw(*(_context.get()), XMMatrixIdentity()); + return; +} + +void Sample3DSceneRenderer::DrawGrid(ID3D11DeviceContext2 *context) +{ + BufferManager::Instance().MVPBuffer().BufferData().color.x = 0.65f; + BufferManager::Instance().MVPBuffer().BufferData().color.y = 0.65f; + BufferManager::Instance().MVPBuffer().BufferData().color.z = 0.65f; + + BufferManager::Instance().MVPBuffer().Update(*m_deviceResources); + + _grid->RenderBuffers(context); + + context->IASetInputLayout(_lineDrawingInputLayout.Get()); + + // Attach our vertex shader. + context->VSSetShader(_simpleVertexShader.Get(), nullptr, 0); + + // Send the constant buffer to the graphics device. + context->VSSetConstantBuffers(0, 1, BufferManager::Instance().MVPBuffer().ConstantBuffer().GetAddressOf()); + + // Attach our pixel shader. + context->PSSetShader(_simplePixelShader.Get(), nullptr, 0); + + // Draw the objects. + context->DrawIndexed(_grid->IndexCount(), 0, 0); +} + +void Sample3DSceneRenderer::DrawAxis(ID3D11DeviceContext2 *context, Axis *axis) +{ + BufferManager::Instance().MVPBuffer().BufferData().color.x = 0.15f; + BufferManager::Instance().MVPBuffer().BufferData().color.y = 0.15f; + BufferManager::Instance().MVPBuffer().BufferData().color.z = 0.15f; + + BufferManager::Instance().MVPBuffer().Update(*m_deviceResources); + + axis->RenderBuffers(context); + + context->IASetInputLayout(_lineDrawingInputLayout.Get()); + + // Attach our vertex shader. + context->VSSetShader(_simpleVertexShader.Get(), nullptr, 0); + + // Send the constant buffer to the graphics device. + context->VSSetConstantBuffers(0, 1, BufferManager::Instance().MVPBuffer().ConstantBuffer().GetAddressOf()); + + // Attach our pixel shader. + context->PSSetShader(_simplePixelShader.Get(), nullptr, 0); + + // Draw the objects. + context->DrawIndexed(axis->IndexCount(), 0, 0); +} + +void Sample3DSceneRenderer::NotifyDataChanged(DirectXPageViewModelData const& payload) +{ + auto col1 = payload.LightScale() * payload.LightColour()[0] / 255; + auto col2 = payload.LightScale() * payload.LightColour()[1] / 255; + auto col3 = payload.LightScale() * payload.LightColour()[2] / 255; + + BufferManager::Instance().PerFrameBuffer().BufferData().light.colour = XMFLOAT3(col1, col2, col3); + + auto dir1 = payload.LightDirection()[0]; + auto dir2 = payload.LightDirection()[1]; + auto dir3 = payload.LightDirection()[2]; + BufferManager::Instance().PerFrameBuffer().BufferData().light.dir = XMFLOAT3(dir1, dir2, dir3); + + auto scaleF = payload.F() ? 1.0f : 0.0f; + auto scaleG = payload.G() ? 1.0f : 0.0f; + auto scaleD = payload.D() ? 1.0f : 0.0f; + auto specular = payload.Specular() ? 1.0f : 0.0f; + + BufferManager::Instance().PerObjBuffer().BufferData().scaleFGDSpec = XMFLOAT4(scaleF, scaleG, scaleD, specular); + + auto scaleDiffuse = payload.Diffuse() ? 1.0f : 0.0f; + auto scaleBasecolour = payload.BaseColour() ? 1.0f : 0.0f; + auto scaleMetallic = payload.Metallic() ? 1.0f : 0.0f; + auto scaleRoughness = payload.Roughness() ? 1.0f : 0.0f; + + BufferManager::Instance().PerObjBuffer().BufferData().scaleDiffBaseMR = + XMFLOAT4(scaleDiffuse, scaleBasecolour, scaleMetallic, scaleRoughness); + + float ibl = payload.Ibl(); + BufferManager::Instance().PerObjBuffer().BufferData().scaleIBLAmbient = XMFLOAT4(ibl, ibl, 0.0f, 0.0f); +} + +future Sample3DSceneRenderer::CreateBdrfLutAsync(StorageFolder^ imgFolder) +{ + // First load the file... + auto file = co_await imgFolder->GetFileAsync(ref new String(L"brdfLUT.png")); + auto buffer = co_await FileIO::ReadBufferAsync(file); + auto fileSize = buffer->Length; + + // Query the IBufferByteAccess interface. + ComPtr bufferByteAccess; + reinterpret_cast(buffer)->QueryInterface(IID_PPV_ARGS(&bufferByteAccess)); + + ::byte* data = nullptr; + bufferByteAccess->Buffer(&data); + + uint32_t width; + uint32_t height; + + auto image = ImgUtils::LoadRGBAImage((void *)data, fileSize, width, height); + + D3D11_TEXTURE2D_DESC txtDesc = {}; + txtDesc.MipLevels = txtDesc.ArraySize = 1; + txtDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + txtDesc.SampleDesc.Count = 1; + txtDesc.Usage = D3D11_USAGE_IMMUTABLE; + txtDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + + txtDesc.Width = width; + txtDesc.Height = height; + + D3D11_SUBRESOURCE_DATA initialData = {}; + initialData.pSysMem = image.data(); + initialData.SysMemPitch = txtDesc.Width * sizeof(uint32_t); + + ComPtr tex; + DX::ThrowIfFailed(m_deviceResources->GetD3DDevice()->CreateTexture2D(&txtDesc, &initialData, + tex.GetAddressOf())); + + ComPtr resourceView; + DX::ThrowIfFailed(m_deviceResources->GetD3DDevice()->CreateShaderResourceView(tex.Get(), + nullptr, resourceView.GetAddressOf())); + + // Create sampler. + D3D11_SAMPLER_DESC samplerDesc = {}; + samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; + samplerDesc.MinLOD = 0; + samplerDesc.MaxLOD = D3D11_FLOAT32_MAX; + + ComPtr samplerState; + DX::ThrowIfFailed(m_deviceResources->GetD3DDevice()->CreateSamplerState(&samplerDesc, samplerState.GetAddressOf())); + + TexWrapper ret; + ret.texResourceView = resourceView; + ret.texSampler = samplerState; + co_return ret; +} + +future Sample3DSceneRenderer::CreateEnvironmentMapResourcesAsync(String^ envName) +{ + String^ imgType(L"diffuse"); + String^ path(L"\\Assets\\textures\\"); + String^ temp = Windows::ApplicationModel::Package::Current->InstalledLocation->Path + path + envName + "\\" + imgType; + auto imgFolder = co_await Windows::ApplicationModel::Package::Current->InstalledLocation->GetFolderFromPathAsync(temp); + auto res = co_await CreateCubeMapAsync(m_deviceResources->GetD3DDevice(), imgFolder, imgType, 1); + + _envTexResourceView = res.texResourceView; + _envTexSampler = res.texSampler; + + temp = Windows::ApplicationModel::Package::Current->InstalledLocation->Path + path; + auto brdfFolder = co_await Windows::ApplicationModel::Package::Current->InstalledLocation->GetFolderFromPathAsync(temp); + res = co_await CreateBdrfLutAsync(brdfFolder); + + _brdfLutResourceView = res.texResourceView; + _brdfLutSampler = res.texSampler; + + imgType = L"specular"; + temp = Windows::ApplicationModel::Package::Current->InstalledLocation->Path + path + envName + "\\" + imgType; + imgFolder = co_await Windows::ApplicationModel::Package::Current->InstalledLocation->GetFolderFromPathAsync(temp); + + ComPtr rv; + ComPtr ss; + res = co_await CreateCubeMapAsync(m_deviceResources->GetD3DDevice(), imgFolder, imgType, 1); + + _envSpecularTexResourceView = res.texResourceView; + _envSpecularTexSampler = res.texSampler; + + co_return; +} + +future Sample3DSceneRenderer::CreateDeviceDependentResources() +{ + // Create sampler. + D3D11_SAMPLER_DESC samplerDesc = {}; + samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; + samplerDesc.MinLOD = 0; + samplerDesc.MaxLOD = D3D11_FLOAT32_MAX; + + DX::ThrowIfFailed( + m_deviceResources->GetD3DDevice()->CreateSamplerState(&samplerDesc, + _spSampler.ReleaseAndGetAddressOf())); + + D3D11_RASTERIZER_DESC rasterizerState; + + rasterizerState.FillMode = D3D11_FILL_SOLID; + rasterizerState.CullMode = D3D11_CULL_BACK; + rasterizerState.FrontCounterClockwise = true; + rasterizerState.DepthBias = false; + rasterizerState.DepthBiasClamp = 0; + rasterizerState.SlopeScaledDepthBias = 0; + rasterizerState.DepthClipEnable = false; + rasterizerState.ScissorEnable = false; + rasterizerState.MultisampleEnable = true; + rasterizerState.AntialiasedLineEnable = true; + DX::ThrowIfFailed(m_deviceResources->GetD3DDevice()->CreateRasterizerState(&rasterizerState, &_pRasterState1)); + + BufferManager::Instance().MVPBuffer().Initialise(*m_deviceResources); + BufferManager::Instance().PerFrameBuffer().Initialise(*m_deviceResources); + BufferManager::Instance().PerObjBuffer().Initialise(*m_deviceResources); + + // Load shaders asynchronously for line rendering... + auto loadVSTask2 = DX::ReadDataAsync(L"SimpleVertexShader.cso"); + auto loadPSTask2 = DX::ReadDataAsync(L"SimplePixelShader.cso"); + + // After the vertex shader file is loaded, create the shader and input layout. + auto createVSTask2 = loadVSTask2.then([this](const std::vector<::byte>& fileData) { + DX::ThrowIfFailed( + m_deviceResources->GetD3DDevice()->CreateVertexShader( + &fileData[0], + fileData.size(), + nullptr, + &_simpleVertexShader + ) + ); + + static const D3D11_INPUT_ELEMENT_DESC SimpleVertexDesc[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 1, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 } + }; + + DX::ThrowIfFailed( + m_deviceResources->GetD3DDevice()->CreateInputLayout( + SimpleVertexDesc, + ARRAYSIZE(SimpleVertexDesc), + &fileData[0], + fileData.size(), + &_lineDrawingInputLayout + ) + ); + }); + + // After the pixel shader file is loaded, create the shader and constant buffer. + auto createPSTask2 = loadPSTask2.then([this](const std::vector<::byte>& fileData) { + DX::ThrowIfFailed( + m_deviceResources->GetD3DDevice()->CreatePixelShader( + &fileData[0], + fileData.size(), + nullptr, + &_simplePixelShader + ) + ); + + CD3D11_BUFFER_DESC lineDrawingConstantBufferDesc(sizeof(LineDrawingConstantBuffer), D3D11_BIND_CONSTANT_BUFFER); + DX::ThrowIfFailed( + m_deviceResources->GetD3DDevice()->CreateBuffer( + &lineDrawingConstantBufferDesc, + nullptr, + &_lineDrawingConstantBuffer + ) + ); + }); + + co_await CreateEnvironmentMapResourcesAsync(ref new String(L"papermill")); + + (createPSTask2 && createVSTask2).then([this]() + { + m_loadingComplete = true; + }); + + co_return; +} + +void Sample3DSceneRenderer::ReleaseDeviceDependentResources() +{ + m_loadingComplete = false; + m_vertexShader.Reset(); + m_inputLayout.Reset(); + m_pixelShader.Reset(); + + BufferManager::Instance().MVPBuffer().Release(); + BufferManager::Instance().PerFrameBuffer().Release(); + BufferManager::Instance().PerObjBuffer().Release(); + + _lineDrawingConstantBuffer.Reset(); + + m_vertexBuffer.Reset(); + m_indexBuffer.Reset(); + + for (auto& buffer : _buffers) + { + buffer.second.Buffer().Reset(); + } + + _buffers.clear(); + _spTexture.Reset(); +} \ No newline at end of file diff --git a/ModelViewer/Content/Sample3DSceneRenderer.h b/ModelViewer/Content/Sample3DSceneRenderer.h new file mode 100644 index 0000000..f3f5549 --- /dev/null +++ b/ModelViewer/Content/Sample3DSceneRenderer.h @@ -0,0 +1,138 @@ +#pragma once + +#include "..\Common\DeviceResources.h" +#include "ShaderStructures.h" +#include "..\Common\StepTimer.h" +#include "../SharedGLTFParser/gltfparser.h" +#include "DXGrid.h" +#include "Axis.h" +#include "./Scene/GraphContainerNode.h" + +namespace ModelViewer +{ + using namespace WinRTGLTFParser; + //using namespace std; + using namespace Microsoft::WRL; + using namespace Windows::System; + using namespace Platform; + using namespace Windows::Storage; + using namespace DX; + + // This sample renderer instantiates a basic rendering pipeline. + class Sample3DSceneRenderer + { + public: + Sample3DSceneRenderer(const std::shared_ptr& deviceResources); + + class TexWrapper + { + public: + ComPtr texResourceView; + ComPtr texSampler; + }; + + std::future CreateDeviceDependentResources(); + void CreateWindowSizeDependentResources(); + std::future CreateEnvironmentMapResourcesAsync(String^ envName); + std::future CreateCubeMapAsync(ID3D11Device3 *device, StorageFolder^ imgFolder, String^ imgType, int mipLevel); + std::future CreateBdrfLutAsync(StorageFolder^ imgFolder); + void ReleaseDeviceDependentResources(); + void Update(DX::StepTimer const& timer); + void Render(); + void StartTracking(float positionX, float positionY, VirtualKeyModifiers mod); + void TrackingUpdate(float positionX, float positionY, VirtualKeyModifiers mod); + void StopTracking(float positionX, float positionY, VirtualKeyModifiers mod); + bool IsTracking() { return m_tracking; } + void NotifyDataChanged(DirectXPageViewModelData const& data); + void* operator new(size_t i) + { + return _mm_malloc(i, 16); + } + + void operator delete(void* p) + { + _mm_free(p); + } + + private: + void Rotate(float radians); + + private: + class BufferWrapper + { + public: + BufferWrapper(GLTF_BufferData^ data, ComPtr buffer) : + _data(data), + _buffer(buffer) + { + } + BufferWrapper() {} + ComPtr& Buffer() { return _buffer; } + + GLTF_BufferData^ Data() { return _data; } + + private: + GLTF_BufferData^ _data; + ComPtr _buffer; + }; + + void DrawGrid(ID3D11DeviceContext2 *context); + void DrawAxis(ID3D11DeviceContext2 *context, Axis *axis); + + // Cached pointer to device resources. + std::shared_ptr m_deviceResources; + + // Direct3D resources for cube geometry. + ComPtr m_inputLayout; + ComPtr m_vertexBuffer; + ComPtr m_indexBuffer; + ComPtr m_vertexShader; + ComPtr m_pixelShader; + + ComPtr _lineDrawingConstantBuffer; + ComPtr _lineDrawingInputLayout; + ComPtr _simpleVertexShader; + ComPtr _simplePixelShader; + + ComPtr _envTexResourceView; + ComPtr _envTexSampler; + + ComPtr _envSpecularTexResourceView; + ComPtr _envSpecularTexSampler; + + ComPtr _brdfLutSampler; + ComPtr _brdfLutResourceView; + + ComPtr _spSampler; + ComPtr _spTexture; + + std::map _buffers; + + // System resources for cube geometry. + LineDrawingConstantBuffer _lineDrawingConstantBufferData; + + uint32 m_indexCount; + + // Variables used with the rendering loop. + bool m_loadingComplete; + float m_degreesPerSecond; + bool m_tracking; + + float _lastPosX = 0.0f; + float _lastPosY = 0.0f; + + float _yaw = -0.05f; + float _pitch = -0.2f; + float _roll = 0.0f; + float _panx = 0.0f; + float _pany = 0.0f; + float _zoom = 5.0f; + + std::unique_ptr _grid; + std::unique_ptr _mainAxes; + + ComPtr _pRasterState1; + std::unique_ptr _context; + }; +} + diff --git a/ModelViewer/Content/SampleFpsTextRenderer.cpp b/ModelViewer/Content/SampleFpsTextRenderer.cpp new file mode 100644 index 0000000..542731a --- /dev/null +++ b/ModelViewer/Content/SampleFpsTextRenderer.cpp @@ -0,0 +1,122 @@ +#include "pch.h" +#include "SampleFpsTextRenderer.h" + +#include "Common/DirectXHelper.h" + +using namespace ModelViewer; +using namespace Microsoft::WRL; + +// Initializes D2D resources used for text rendering. +SampleFpsTextRenderer::SampleFpsTextRenderer(const std::shared_ptr& deviceResources) : + m_text(L""), + m_deviceResources(deviceResources) +{ + ZeroMemory(&m_textMetrics, sizeof(DWRITE_TEXT_METRICS)); + + // Create device independent resources + ComPtr textFormat; + DX::ThrowIfFailed( + m_deviceResources->GetDWriteFactory()->CreateTextFormat( + L"Segoe UI", + nullptr, + DWRITE_FONT_WEIGHT_LIGHT, + DWRITE_FONT_STYLE_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, + 16.0f, + L"en-US", + &textFormat + ) + ); + + DX::ThrowIfFailed( + textFormat.As(&m_textFormat) + ); + + DX::ThrowIfFailed( + m_textFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR) + ); + + DX::ThrowIfFailed( + m_deviceResources->GetD2DFactory()->CreateDrawingStateBlock(&m_stateBlock) + ); + + CreateDeviceDependentResources(); +} + +// Updates the text to be displayed. +void SampleFpsTextRenderer::Update(DX::StepTimer const& timer) +{ + // Update display text. + uint32 fps = timer.GetFramesPerSecond(); + + m_text = (fps > 0) ? std::to_wstring(fps) + L" FPS" : L" - FPS"; + + ComPtr textLayout; + DX::ThrowIfFailed( + m_deviceResources->GetDWriteFactory()->CreateTextLayout( + m_text.c_str(), + (uint32) m_text.length(), + m_textFormat.Get(), + 240.0f, // Max width of the input text. + 50.0f, // Max height of the input text. + &textLayout + ) + ); + + DX::ThrowIfFailed( + textLayout.As(&m_textLayout) + ); + + DX::ThrowIfFailed( + m_textLayout->GetMetrics(&m_textMetrics) + ); +} + +// Renders a frame to the screen. +void SampleFpsTextRenderer::Render() +{ + ID2D1DeviceContext* context = m_deviceResources->GetD2DDeviceContext(); + Windows::Foundation::Size logicalSize = m_deviceResources->GetLogicalSize(); + + context->SaveDrawingState(m_stateBlock.Get()); + context->BeginDraw(); + + // Position on the bottom right corner + D2D1::Matrix3x2F screenTranslation = D2D1::Matrix3x2F::Translation( + logicalSize.Width - m_textMetrics.layoutWidth, + logicalSize.Height - m_textMetrics.height + ); + + context->SetTransform(screenTranslation * m_deviceResources->GetOrientationTransform2D()); + + DX::ThrowIfFailed( + m_textFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING) + ); + + context->DrawTextLayout( + D2D1::Point2F(0.f, 0.f), + m_textLayout.Get(), + m_whiteBrush.Get() + ); + + // Ignore D2DERR_RECREATE_TARGET here. This error indicates that the device + // is lost. It will be handled during the next call to Present. + HRESULT hr = context->EndDraw(); + if (hr != D2DERR_RECREATE_TARGET) + { + DX::ThrowIfFailed(hr); + } + + context->RestoreDrawingState(m_stateBlock.Get()); +} + +void SampleFpsTextRenderer::CreateDeviceDependentResources() +{ + DX::ThrowIfFailed( + m_deviceResources->GetD2DDeviceContext()->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &m_whiteBrush) + ); +} +void SampleFpsTextRenderer::ReleaseDeviceDependentResources() +{ + m_whiteBrush.Reset(); +} \ No newline at end of file diff --git a/ModelViewer/Content/SampleFpsTextRenderer.h b/ModelViewer/Content/SampleFpsTextRenderer.h new file mode 100644 index 0000000..b01e06e --- /dev/null +++ b/ModelViewer/Content/SampleFpsTextRenderer.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include "..\Common\DeviceResources.h" +#include "..\Common\StepTimer.h" + +namespace ModelViewer +{ + // Renders the current FPS value in the bottom right corner of the screen using Direct2D and DirectWrite. + class SampleFpsTextRenderer + { + public: + SampleFpsTextRenderer(const std::shared_ptr& deviceResources); + void CreateDeviceDependentResources(); + void ReleaseDeviceDependentResources(); + void Update(DX::StepTimer const& timer); + void Render(); + + private: + // Cached pointer to device resources. + std::shared_ptr m_deviceResources; + + // Resources related to text rendering. + std::wstring m_text; + DWRITE_TEXT_METRICS m_textMetrics; + Microsoft::WRL::ComPtr m_whiteBrush; + Microsoft::WRL::ComPtr m_stateBlock; + Microsoft::WRL::ComPtr m_textLayout; + Microsoft::WRL::ComPtr m_textFormat; + }; +} \ No newline at end of file diff --git a/ModelViewer/Content/ShaderStructures.h b/ModelViewer/Content/ShaderStructures.h new file mode 100644 index 0000000..01418f2 --- /dev/null +++ b/ModelViewer/Content/ShaderStructures.h @@ -0,0 +1,65 @@ +#pragma once + +#define DIFFUSE + +namespace ModelViewer +{ + using namespace DirectX; + + // Constant buffer used to send MVP matrices to the vertex shader. + __declspec(align(16)) struct ModelViewProjectionConstantBuffer + { + XMFLOAT4X4 model; + XMFLOAT4X4 view; + XMFLOAT4X4 projection; + +#ifdef DIFFUSE + XMFLOAT4 light_direction; + XMFLOAT3 color; +#endif + + }; + + __declspec(align(16)) struct LineDrawingConstantBuffer + { + XMFLOAT3 pos; + XMFLOAT3 color; + }; + + // Used to send per-vertex data to the vertex shader. + struct VertexPositionColor + { + XMFLOAT3 pos; + XMFLOAT3 color; + }; + + struct Light + { + XMFLOAT3 dir; + float padding1; + XMFLOAT3 colour; + float padding2; + }; + + __declspec(align(16)) struct cbPerFrame + { + Light light; + }; + + __declspec(align(16)) struct cbPerObject + { + float normalScale; + XMFLOAT3 emissiveFactor; + float occlusionStrength; + XMFLOAT2 metallicRoughnessValues; + float padding1; + XMFLOAT4 baseColorFactor; + XMFLOAT3 camera; + float padding2; + + // debugging flags used for shader output of intermediate PBR variables + XMFLOAT4 scaleDiffBaseMR; + XMFLOAT4 scaleFGDSpec; + XMFLOAT4 scaleIBLAmbient; + }; +} \ No newline at end of file diff --git a/ModelViewer/Content/SimplePixelShader.hlsl b/ModelViewer/Content/SimplePixelShader.hlsl new file mode 100644 index 0000000..ba9a56f --- /dev/null +++ b/ModelViewer/Content/SimplePixelShader.hlsl @@ -0,0 +1,12 @@ +// Per-pixel color data passed through the pixel shader. +struct PixelShaderInput +{ + float4 pos : SV_POSITION; + float3 color : COLOR0; +}; + +// A pass-through function for the (interpolated) color data. +float4 main(PixelShaderInput input) : SV_TARGET +{ + return float4(input.color, 1.0f); +} diff --git a/ModelViewer/Content/SimpleVertexShader.hlsl b/ModelViewer/Content/SimpleVertexShader.hlsl new file mode 100644 index 0000000..7c910bd --- /dev/null +++ b/ModelViewer/Content/SimpleVertexShader.hlsl @@ -0,0 +1,41 @@ +// A constant buffer that stores the three basic column-major matrices for composing geometry. +cbuffer ModelViewProjectionConstantBuffer : register(b0) +{ + matrix model; + matrix view; + matrix projection; + float4 light_direction; + float3 color; +}; + +// Per-vertex data used as input to the vertex shader. +struct VertexShaderInput +{ + float3 pos : POSITION; + float3 color : COLOR0; +}; + +// Per-pixel color data passed through the pixel shader. +struct PixelShaderInput +{ + float4 pos : SV_POSITION; + float3 color : COLOR0; +}; + +// Simple shader to do vertex processing on the GPU. +PixelShaderInput main(VertexShaderInput input) +{ + PixelShaderInput output; + float4 pos = float4(input.pos, 1.0f); + + // Transform the vertex position into projected space. + pos = mul(pos, model); + pos = mul(pos, view); + pos = mul(pos, projection); + output.pos = pos; + + // Pass the color through without modification. + output.color = color; + + return output; +} diff --git a/ModelViewer/DXGrid.cpp b/ModelViewer/DXGrid.cpp new file mode 100644 index 0000000..cc760b9 --- /dev/null +++ b/ModelViewer/DXGrid.cpp @@ -0,0 +1,143 @@ +#include "pch.h" +#include "DXGrid.h" +#include "Content\ShaderStructures.h" +#include "Common\DirectXHelper.h" + +using namespace ModelViewer; +using namespace DirectX; + +DXGrid::DXGrid() +{ +} + +void DXGrid::Initialise(ID3D11Device *device) +{ + int gridWidth = 10; + float cellWidth = 1.0f; + float cellHeight = 1.0f; + + unique_ptr vertices; + unique_ptr indices; + int index; + D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc; + D3D11_SUBRESOURCE_DATA vertexData, indexData; + + int num = (gridWidth + 1) / 2; + + int numInRow = (num * 2 + 1)-1; + + // Calculate the number of vertices in the terrain mesh. + m_vertexCount = numInRow * numInRow; + + // Set the index count to the same as the vertex count. + m_indexCount = numInRow * 4; + + // Create the vertex array. + vertices = make_unique(m_vertexCount); + + // Create the index array. + indices = make_unique(m_indexCount); + index = 0; + + for (int i = -num; i <= num; i++) + { + for (int j = -num; j <= num; j++) + { + if (i != 0 && j != 0) + { + vertices[index].pos = XMFLOAT3(cellWidth * i, 0.0f, cellHeight * j); + vertices[index].color = XMFLOAT3(0.4f, 0.4f, 0.4f); + index++; + } + } + } + + index = 0; + + // first one direction... + for (int k = 0; k < numInRow; k++) + { + indices[index] = k; + index++; + indices[index] = k + (numInRow * (numInRow - 1)); + index++; + } + + // then the other... + for (int k = 0; k < numInRow * numInRow; k+=numInRow) + { + indices[index] = k; + index++; + indices[index] = k + numInRow - 1; + index++; + } + + // Set up the description of the static vertex buffer. + vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; + vertexBufferDesc.ByteWidth = sizeof(VertexPositionColor) * m_vertexCount; + vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + vertexBufferDesc.CPUAccessFlags = 0; + vertexBufferDesc.MiscFlags = 0; + vertexBufferDesc.StructureByteStride = 0; + + // Give the subresource structure a pointer to the vertex data. + vertexData.pSysMem = vertices.get(); + vertexData.SysMemPitch = 0; + vertexData.SysMemSlicePitch = 0; + + // Now create the vertex buffer. + DX::ThrowIfFailed(device->CreateBuffer(&vertexBufferDesc, &vertexData, m_vertexBuffer.GetAddressOf())); + + // Set up the description of the static index buffer. + indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; + indexBufferDesc.ByteWidth = sizeof(unsigned long) * m_indexCount; + indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; + indexBufferDesc.CPUAccessFlags = 0; + indexBufferDesc.MiscFlags = 0; + indexBufferDesc.StructureByteStride = 0; + + // Give the subresource structure a pointer to the index data. + indexData.pSysMem = indices.get(); + indexData.SysMemPitch = 0; + indexData.SysMemSlicePitch = 0; + + // Create the index buffer. + DX::ThrowIfFailed(device->CreateBuffer(&indexBufferDesc, &indexData, m_indexBuffer.GetAddressOf())); + + D3D11_RASTERIZER_DESC rasterizerState; + rasterizerState.FillMode = D3D11_FILL_WIREFRAME; + rasterizerState.CullMode = D3D11_CULL_NONE; + rasterizerState.FrontCounterClockwise = true; + rasterizerState.DepthBias = 1; + rasterizerState.DepthBiasClamp = 0; + rasterizerState.SlopeScaledDepthBias = -1.0; + rasterizerState.DepthClipEnable = false; + rasterizerState.ScissorEnable = false; + rasterizerState.MultisampleEnable = false; + rasterizerState.AntialiasedLineEnable = false; + device->CreateRasterizerState(&rasterizerState, &_pRasterState); +} + +void DXGrid::RenderBuffers(ID3D11DeviceContext* deviceContext) +{ + unsigned int stride; + unsigned int offset; + + // Set vertex buffer stride and offset. + stride = sizeof(VertexPositionColor); + offset = 0; + + auto vb = m_vertexBuffer.Get(); + + // Set the vertex buffer to active in the input assembler so it can be rendered. + deviceContext->IASetVertexBuffers(0, 1, &vb, &stride, &offset); + + // Set the index buffer to active in the input assembler so it can be rendered. + deviceContext->IASetIndexBuffer(m_indexBuffer.Get(), DXGI_FORMAT_R32_UINT, 0); + + //Set the render format to line list. + // Set the type of primitive that should be rendered from this vertex buffer, in this case a line list. + deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST); + deviceContext->RSSetState(_pRasterState.Get()); +} + diff --git a/ModelViewer/DXGrid.h b/ModelViewer/DXGrid.h new file mode 100644 index 0000000..882c841 --- /dev/null +++ b/ModelViewer/DXGrid.h @@ -0,0 +1,19 @@ +#pragma once + +using namespace Microsoft::WRL; + +class DXGrid +{ +public: + DXGrid(); + void Initialise(ID3D11Device *device); + void RenderBuffers(ID3D11DeviceContext* deviceContext); + int IndexCount() { return m_indexCount; }; + +private: + int m_vertexCount; + int m_indexCount; + ComPtr m_vertexBuffer, m_indexBuffer; + ComPtr _pRasterState; +}; + diff --git a/ModelViewer/DXUtils.cpp b/ModelViewer/DXUtils.cpp new file mode 100644 index 0000000..4599886 --- /dev/null +++ b/ModelViewer/DXUtils.cpp @@ -0,0 +1,40 @@ +#include "pch.h" +#include "DXUtils.h" + +#include + +HRESULT DXUtils::CompileShader(_In_ LPCWSTR srcFile, const D3D_SHADER_MACRO *defines, _In_ LPCSTR entryPoint, _In_ LPCSTR profile, _Outptr_ ID3DBlob** blob) +{ + if (!srcFile || !entryPoint || !profile || !blob) + return E_INVALIDARG; + + *blob = nullptr; + + UINT flags = D3DCOMPILE_ENABLE_STRICTNESS; +#if defined( DEBUG ) || defined( _DEBUG ) + flags |= D3DCOMPILE_DEBUG; +#endif + + ID3DBlob* shaderBlob = nullptr; + ID3DBlob* errorBlob = nullptr; + HRESULT hr = D3DCompileFromFile(srcFile, defines, D3D_COMPILE_STANDARD_FILE_INCLUDE, + entryPoint, profile, + flags, 0, &shaderBlob, &errorBlob); + if (FAILED(hr)) + { + if (errorBlob) + { + OutputDebugStringA((char*)errorBlob->GetBufferPointer()); + errorBlob->Release(); + } + + if (shaderBlob) + shaderBlob->Release(); + + return hr; + } + + *blob = shaderBlob; + + return hr; +} diff --git a/ModelViewer/DXUtils.h b/ModelViewer/DXUtils.h new file mode 100644 index 0000000..b87687a --- /dev/null +++ b/ModelViewer/DXUtils.h @@ -0,0 +1,8 @@ +#pragma once + +class DXUtils +{ +public: + static HRESULT CompileShader(_In_ LPCWSTR srcFile, const D3D_SHADER_MACRO *defines, _In_ LPCSTR entryPoint, _In_ LPCSTR profile, _Outptr_ ID3DBlob** blob); +}; + diff --git a/ModelViewer/DecimalPlacesConverter.cpp b/ModelViewer/DecimalPlacesConverter.cpp new file mode 100644 index 0000000..4b988ad --- /dev/null +++ b/ModelViewer/DecimalPlacesConverter.cpp @@ -0,0 +1,34 @@ +#include "pch.h" +#include "DecimalPlacesConverter.h" +#include +#include + +using namespace ModelViewer; +using namespace Platform; +using namespace Windows::UI::Xaml::Interop; + +DecimalPlacesConverter::DecimalPlacesConverter() +{ +} + +Object ^ DecimalPlacesConverter::Convert(Object ^value, TypeName targetType, Object ^parameter, String ^language) +{ + if (value != nullptr) + { + wchar_t number[256]; + swprintf_s(number, 256, L"%.2f", (float)value); + return ref new Platform::String(number); + } + return nullptr; +} + +Object ^ DecimalPlacesConverter::ConvertBack(Object ^value, TypeName targetType, Object ^parameter, String ^language) +{ + if (value != nullptr) + { + auto str = value->ToString(); + auto ret = _wtof(str->Data()); + return (float)ret; + } + return nullptr; +} diff --git a/ModelViewer/DecimalPlacesConverter.h b/ModelViewer/DecimalPlacesConverter.h new file mode 100644 index 0000000..cdf6bd0 --- /dev/null +++ b/ModelViewer/DecimalPlacesConverter.h @@ -0,0 +1,23 @@ +#pragma once +namespace ModelViewer +{ + [Windows::UI::Xaml::Data::Bindable] + [Windows::Foundation::Metadata::WebHostHidden] + public ref class DecimalPlacesConverter sealed : Windows::UI::Xaml::Data::IValueConverter + { + public: + DecimalPlacesConverter(); + + // Inherited via IValueConverter + virtual Platform::Object ^ Convert(Platform::Object ^value, Windows::UI::Xaml::Interop::TypeName targetType, Platform::Object ^parameter, Platform::String ^language); + virtual Platform::Object ^ ConvertBack(Platform::Object ^value, Windows::UI::Xaml::Interop::TypeName targetType, Platform::Object ^parameter, Platform::String ^language); + + property int DecimalPlaces + { + int get() { return _dps;; } + void set(int value) { _dps = value; } + } + private: + int _dps = 2; + }; +} diff --git a/ModelViewer/DelegateCommand.cpp b/ModelViewer/DelegateCommand.cpp new file mode 100644 index 0000000..63b1429 --- /dev/null +++ b/ModelViewer/DelegateCommand.cpp @@ -0,0 +1,35 @@ +#include "pch.h" +#include "DelegateCommand.h" + +using namespace ModelViewer; + +DelegateCommand::DelegateCommand(ExecuteDelegate^ execute, CanExecuteDelegate^ canExecute) + : executeDelegate(execute), canExecuteDelegate(canExecute) +{ +} + +void DelegateCommand::Execute(Object^ parameter) +{ + if (executeDelegate != nullptr) + { + executeDelegate(parameter); + } +} + +bool DelegateCommand::CanExecute(Object^ parameter) +{ + if (canExecuteDelegate == nullptr) + { + return true; + } + + bool canExecute = canExecuteDelegate(parameter); + + if (lastCanExecute != canExecute) + { + lastCanExecute = canExecute; + CanExecuteChanged(this, nullptr); + } + + return lastCanExecute; +} \ No newline at end of file diff --git a/ModelViewer/DelegateCommand.h b/ModelViewer/DelegateCommand.h new file mode 100644 index 0000000..c5568f9 --- /dev/null +++ b/ModelViewer/DelegateCommand.h @@ -0,0 +1,26 @@ +#pragma once + +namespace ModelViewer +{ + using namespace Windows::UI::Xaml::Input; + using namespace Windows::Foundation; + using namespace Platform; + + public delegate void ExecuteDelegate(Object^ parameter); + public delegate bool CanExecuteDelegate(Object^ parameter); + + public ref class DelegateCommand sealed : public ICommand + { + public: + DelegateCommand(ExecuteDelegate^ execute, CanExecuteDelegate^ canExecute); + + virtual event EventHandler^ CanExecuteChanged; + virtual void Execute(Object^ parameter); + virtual bool CanExecute(Object^ parameter); + + private: + ExecuteDelegate ^ executeDelegate; + CanExecuteDelegate^ canExecuteDelegate; + bool lastCanExecute; + }; +} diff --git a/ModelViewer/DirectXPage.xaml b/ModelViewer/DirectXPage.xaml new file mode 100644 index 0000000..f25cfd1 --- /dev/null +++ b/ModelViewer/DirectXPage.xaml @@ -0,0 +1,290 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +