yichael 4 yıl önce
ebeveyn
işleme
475f583edd
100 değiştirilmiş dosya ile 10967 ekleme ve 0 silme
  1. 43 0
      Communication_Org.uproject
  2. 7 0
      Config/Android/AndroidEngine.ini
  3. 1 0
      Config/DefaultEditor.ini
  4. 119 0
      Config/DefaultEngine.ini
  5. 8 0
      Config/DefaultGame.ini
  6. 93 0
      Config/DefaultInput.ini
  7. 7 0
      Config/IOS/IOSEngine.ini
  8. 7 0
      Config/Linux/LinuxEngine.ini
  9. 7 0
      Config/Mac/MacEngine.ini
  10. 7 0
      Config/PS4/PS4Engine.ini
  11. 7 0
      Config/Switch/SwitchEngine.ini
  12. 2 0
      Config/Windows/DefaultGameUserSettings.ini
  13. 7 0
      Config/Windows/WindowsEngine.ini
  14. 7 0
      Config/XboxOne/XboxOneEngine.ini
  15. 15 0
      Plugins/BluePrintTools/.gitignore
  16. 25 0
      Plugins/BluePrintTools/BlueprintUtility.uplugin
  17. 8 0
      Plugins/BluePrintTools/Config/FilterPlugin.ini
  18. BIN
      Plugins/BluePrintTools/Content/BlueTools/BP_BluePrintFunction.uasset
  19. BIN
      Plugins/BluePrintTools/Content/BlueTools/BP_EndStopSequence_CMP.uasset
  20. BIN
      Plugins/BluePrintTools/Content/BlueTools/Comp/BP_EndStopSequence_CMP.uasset
  21. BIN
      Plugins/BluePrintTools/Content/BlueTools/Comp/BP_FaceToPlayerCamera.uasset
  22. BIN
      Plugins/BluePrintTools/Content/Map/Function_ShowCase.umap
  23. BIN
      Plugins/BluePrintTools/Content/Mateirals/M_TextureParameter.uasset
  24. BIN
      Plugins/BluePrintTools/Content/Mateirals/M_TextureParameter_Inst.uasset
  25. BIN
      Plugins/BluePrintTools/Content/Texture/T_ICon.uasset
  26. 28 0
      Plugins/BluePrintTools/README.md
  27. BIN
      Plugins/BluePrintTools/Resources/Icon128.png
  28. 71 0
      Plugins/BluePrintTools/Source/BlueprintUtility/BlueprintUtility.Build.cs
  29. 22 0
      Plugins/BluePrintTools/Source/BlueprintUtility/Private/BlueprintUtility.cpp
  30. 715 0
      Plugins/BluePrintTools/Source/BlueprintUtility/Private/BlueprintUtilityBPLibrary.cpp
  31. 102 0
      Plugins/BluePrintTools/Source/BlueprintUtility/Private/DynamicLoader.cpp
  32. 14 0
      Plugins/BluePrintTools/Source/BlueprintUtility/Public/BlueprintUtility.h
  33. 170 0
      Plugins/BluePrintTools/Source/BlueprintUtility/Public/BlueprintUtilityBPLibrary.h
  34. 89 0
      Plugins/BluePrintTools/Source/BlueprintUtility/Public/DynamicLoader.h
  35. 23 0
      Plugins/CustomWidget/CustomWidget.uplugin
  36. BIN
      Plugins/CustomWidget/Resources/Icon128.png
  37. 60 0
      Plugins/CustomWidget/Source/CustomWidget/CustomWidget.Build.cs
  38. 23 0
      Plugins/CustomWidget/Source/CustomWidget/Private/CustomWidget.cpp
  39. 85 0
      Plugins/CustomWidget/Source/CustomWidget/Private/DropSlate.cpp
  40. 95 0
      Plugins/CustomWidget/Source/CustomWidget/Private/DropWidget.cpp
  41. 15 0
      Plugins/CustomWidget/Source/CustomWidget/Public/CustomWidget.h
  42. 22 0
      Plugins/CustomWidget/Source/CustomWidget/Public/DropSlate.h
  43. 44 0
      Plugins/CustomWidget/Source/CustomWidget/Public/DropWidget.h
  44. 25 0
      Plugins/EasyFileDialog/EasyFileDialog.uplugin
  45. BIN
      Plugins/EasyFileDialog/Resources/Icon128.png
  46. 53 0
      Plugins/EasyFileDialog/Source/EasyFileDialog/EasyFileDialog.Build.cs
  47. 266 0
      Plugins/EasyFileDialog/Source/EasyFileDialog/Private/EFDCore.cpp
  48. 40 0
      Plugins/EasyFileDialog/Source/EasyFileDialog/Private/EFDFunctionLibrary.cpp
  49. 22 0
      Plugins/EasyFileDialog/Source/EasyFileDialog/Private/EasyFileDialog.cpp
  50. 16 0
      Plugins/EasyFileDialog/Source/EasyFileDialog/Private/EasyFileDialogBPLibrary.cpp
  51. 40 0
      Plugins/EasyFileDialog/Source/EasyFileDialog/Public/EFDCore.h
  52. 30 0
      Plugins/EasyFileDialog/Source/EasyFileDialog/Public/EFDFunctionLibrary.h
  53. 14 0
      Plugins/EasyFileDialog/Source/EasyFileDialog/Public/EasyFileDialog.h
  54. 32 0
      Plugins/EasyFileDialog/Source/EasyFileDialog/Public/EasyFileDialogBPLibrary.h
  55. 15 0
      Plugins/HTTP_UPLOAD/.gitignore
  56. 23 0
      Plugins/HTTP_UPLOAD/HTTP_UPLOAD.uplugin
  57. 17 0
      Plugins/HTTP_UPLOAD/ReadMe.md
  58. BIN
      Plugins/HTTP_UPLOAD/Resources/Icon128.png
  59. 54 0
      Plugins/HTTP_UPLOAD/Source/HTTP_UPLOAD/HTTP_UPLOAD.Build.cs
  60. 22 0
      Plugins/HTTP_UPLOAD/Source/HTTP_UPLOAD/Private/HTTP_UPLOAD.cpp
  61. 484 0
      Plugins/HTTP_UPLOAD/Source/HTTP_UPLOAD/Private/HTTP_UPLOADBPLibrary.cpp
  62. 16 0
      Plugins/HTTP_UPLOAD/Source/HTTP_UPLOAD/Public/HTTP_UPLOAD.h
  63. 77 0
      Plugins/HTTP_UPLOAD/Source/HTTP_UPLOAD/Public/HTTP_UPLOADBPLibrary.h
  64. BIN
      Plugins/RuntimeMeshImportExport/Resources/Icon128.png
  65. 33 0
      Plugins/RuntimeMeshImportExport/RuntimeMeshImportExport.uplugin
  66. 646 0
      Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Private/AssimpCustom.cpp
  67. 189 0
      Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Private/AssimpCustom.h
  68. 8 0
      Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Private/Interface/MeshExportable.cpp
  69. 413 0
      Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Private/RuntimeMeshExporter.cpp
  70. 54 0
      Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Private/RuntimeMeshImportExport.cpp
  71. 1083 0
      Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Private/RuntimeMeshImportExportLibrary.cpp
  72. 99 0
      Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Private/RuntimeMeshImportExportTypes.cpp
  73. 36 0
      Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Public/AssimpProgressHandler.h
  74. 44 0
      Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Public/Interface/MeshExportable.h
  75. 172 0
      Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Public/RuntimeMeshExporter.h
  76. 22 0
      Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Public/RuntimeMeshImportExport.h
  77. 130 0
      Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Public/RuntimeMeshImportExportLibrary.h
  78. 649 0
      Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Public/RuntimeMeshImportExportTypes.h
  79. 126 0
      Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/RuntimeMeshImportExport.Build.cs
  80. 10 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/LICENSE.txt
  81. BIN
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/bin/x64/Debug/assimp-vc141-mtd.pdb
  82. 8 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/.editorconfig
  83. 418 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/BaseImporter.h
  84. 125 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/Bitmap.h
  85. 338 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/BlobIOSystem.h
  86. 287 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/ByteSwapper.h
  87. 22 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/Compiler/poppack1.h
  88. 912 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/Compiler/pstdint.h
  89. 43 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/Compiler/pushpack1.h
  90. 58 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/CreateAnimMesh.h
  91. 140 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/DefaultIOStream.h
  92. 93 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/DefaultIOSystem.h
  93. 188 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/DefaultLogger.hpp
  94. 49 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/Defines.h
  95. 125 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/Exceptional.h
  96. 505 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/Exporter.hpp
  97. 133 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/GenericProperty.h
  98. 118 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/Hash.h
  99. 142 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/IOStream.hpp
  100. 355 0
      Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/IOStreamBuffer.h

+ 43 - 0
Communication_Org.uproject

@@ -0,0 +1,43 @@
+{
+	"FileVersion": 3,
+	"EngineAssociation": "4.25",
+	"Category": "",
+	"Description": "",
+	"Modules": [
+		{
+			"Name": "Communication_Org",
+			"Type": "Runtime",
+			"LoadingPhase": "Default",
+			"AdditionalDependencies": [
+				"Engine"
+			]
+		}
+	],
+	"Plugins": [
+		{
+			"Name": "EasyFileDialog",
+			"Enabled": true,
+			"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/bcf64fe2e00c48189d8385a9c39b6e61"
+		},
+		{
+			"Name": "JSONParser",
+			"Enabled": true,
+			"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/8eda577c5b594d6b990f91b8dec8a014"
+		},
+		{
+			"Name": "WebCommunication",
+			"Enabled": false,
+			"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/3b04f3c4af5445959cee4448846b55a0"
+		},
+		{
+			"Name": "ColorWheelPlugin",
+			"Enabled": true,
+			"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/ce01648a2cde41338991bb855a624ffa"
+		},
+		{
+			"Name": "RuntimeMeshImportExport",
+			"Enabled": true,
+			"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/4a300859ba06426494dbaf22349273ba"
+		}
+	]
+}

+ 7 - 0
Config/Android/AndroidEngine.ini

@@ -0,0 +1,7 @@
+[/Script/AndroidRuntimeSettings.AndroidRuntimeSettings]
+;AudioSampleRate=48000
+AudioMaxChannels=12
+;AudioCallbackBufferFrameSize=1024
+;AudioNumBuffersToEnqueue=2
+;AudioNumSourceWorkers=0
+

+ 1 - 0
Config/DefaultEditor.ini

@@ -0,0 +1 @@
+

+ 119 - 0
Config/DefaultEngine.ini

@@ -0,0 +1,119 @@
+[URL]
+[/Script/Engine.RendererSettings]
+r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True
+
+[/Script/HardwareTargeting.HardwareTargetingSettings]
+TargetedHardwareClass=Desktop
+AppliedTargetedHardwareClass=Desktop
+DefaultGraphicsPerformance=Maximum
+AppliedDefaultGraphicsPerformance=Maximum
+
+[/Script/Engine.CollisionProfile]
+-Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision",bCanModify=False)
+-Profiles=(Name="BlockAll",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldStatic",CustomResponses=,HelpMessage="WorldStatic object that blocks all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
+-Profiles=(Name="OverlapAll",CollisionEnabled=QueryOnly,ObjectTypeName="WorldStatic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
+-Profiles=(Name="BlockAllDynamic",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldDynamic",CustomResponses=,HelpMessage="WorldDynamic object that blocks all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
+-Profiles=(Name="OverlapAllDynamic",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
+-Profiles=(Name="IgnoreOnlyPawn",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that ignores Pawn and Vehicle. All other channels will be set to default.",bCanModify=False)
+-Profiles=(Name="OverlapOnlyPawn",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that overlaps Pawn, Camera, and Vehicle. All other channels will be set to default. ",bCanModify=False)
+-Profiles=(Name="Pawn",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Pawn",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object. Can be used for capsule of any playerable character or AI. ",bCanModify=False)
+-Profiles=(Name="Spectator",CollisionEnabled=QueryOnly,ObjectTypeName="Pawn",CustomResponses=((Channel="WorldStatic",Response=ECR_Block),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="Pawn object that ignores all other actors except WorldStatic.",bCanModify=False)
+-Profiles=(Name="CharacterMesh",CollisionEnabled=QueryOnly,ObjectTypeName="Pawn",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object that is used for Character Mesh. All other channels will be set to default.",bCanModify=False)
+-Profiles=(Name="PhysicsActor",CollisionEnabled=QueryAndPhysics,ObjectTypeName="PhysicsBody",CustomResponses=,HelpMessage="Simulating actors",bCanModify=False)
+-Profiles=(Name="Destructible",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Destructible",CustomResponses=,HelpMessage="Destructible actors",bCanModify=False)
+-Profiles=(Name="InvisibleWall",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldStatic object that is invisible.",bCanModify=False)
+-Profiles=(Name="InvisibleWallDynamic",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that is invisible.",bCanModify=False)
+-Profiles=(Name="Trigger",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that is used for trigger. All other channels will be set to default.",bCanModify=False)
+-Profiles=(Name="Ragdoll",CollisionEnabled=QueryAndPhysics,ObjectTypeName="PhysicsBody",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Simulating Skeletal Mesh Component. All other channels will be set to default.",bCanModify=False)
+-Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.",bCanModify=False)
+-Profiles=(Name="UI",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Block),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
++Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision",bCanModify=False)
++Profiles=(Name="BlockAll",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldStatic",CustomResponses=,HelpMessage="WorldStatic object that blocks all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
++Profiles=(Name="OverlapAll",CollisionEnabled=QueryOnly,ObjectTypeName="WorldStatic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
++Profiles=(Name="BlockAllDynamic",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldDynamic",CustomResponses=,HelpMessage="WorldDynamic object that blocks all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
++Profiles=(Name="OverlapAllDynamic",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
++Profiles=(Name="IgnoreOnlyPawn",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that ignores Pawn and Vehicle. All other channels will be set to default.",bCanModify=False)
++Profiles=(Name="OverlapOnlyPawn",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that overlaps Pawn, Camera, and Vehicle. All other channels will be set to default. ",bCanModify=False)
++Profiles=(Name="Pawn",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Pawn",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object. Can be used for capsule of any playerable character or AI. ",bCanModify=False)
++Profiles=(Name="Spectator",CollisionEnabled=QueryOnly,ObjectTypeName="Pawn",CustomResponses=((Channel="WorldStatic"),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="Pawn object that ignores all other actors except WorldStatic.",bCanModify=False)
++Profiles=(Name="CharacterMesh",CollisionEnabled=QueryOnly,ObjectTypeName="Pawn",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object that is used for Character Mesh. All other channels will be set to default.",bCanModify=False)
++Profiles=(Name="PhysicsActor",CollisionEnabled=QueryAndPhysics,ObjectTypeName="PhysicsBody",CustomResponses=,HelpMessage="Simulating actors",bCanModify=False)
++Profiles=(Name="Destructible",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Destructible",CustomResponses=,HelpMessage="Destructible actors",bCanModify=False)
++Profiles=(Name="InvisibleWall",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldStatic object that is invisible.",bCanModify=False)
++Profiles=(Name="InvisibleWallDynamic",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that is invisible.",bCanModify=False)
++Profiles=(Name="Trigger",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that is used for trigger. All other channels will be set to default.",bCanModify=False)
++Profiles=(Name="Ragdoll",CollisionEnabled=QueryAndPhysics,ObjectTypeName="PhysicsBody",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Simulating Skeletal Mesh Component. All other channels will be set to default.",bCanModify=False)
++Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.",bCanModify=False)
++Profiles=(Name="UI",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility"),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
++DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,Name="ToolkitGizmo",DefaultResponse=ECR_Ignore,bTraceType=False,bStaticObject=False)
+-ProfileRedirects=(OldName="BlockingVolume",NewName="InvisibleWall")
+-ProfileRedirects=(OldName="InterpActor",NewName="IgnoreOnlyPawn")
+-ProfileRedirects=(OldName="StaticMeshComponent",NewName="BlockAllDynamic")
+-ProfileRedirects=(OldName="SkeletalMeshActor",NewName="PhysicsActor")
+-ProfileRedirects=(OldName="InvisibleActor",NewName="InvisibleWallDynamic")
++ProfileRedirects=(OldName="BlockingVolume",NewName="InvisibleWall")
++ProfileRedirects=(OldName="InterpActor",NewName="IgnoreOnlyPawn")
++ProfileRedirects=(OldName="StaticMeshComponent",NewName="BlockAllDynamic")
++ProfileRedirects=(OldName="SkeletalMeshActor",NewName="PhysicsActor")
++ProfileRedirects=(OldName="InvisibleActor",NewName="InvisibleWallDynamic")
+-CollisionChannelRedirects=(OldName="Static",NewName="WorldStatic")
+-CollisionChannelRedirects=(OldName="Dynamic",NewName="WorldDynamic")
+-CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle")
+-CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn")
++CollisionChannelRedirects=(OldName="Static",NewName="WorldStatic")
++CollisionChannelRedirects=(OldName="Dynamic",NewName="WorldDynamic")
++CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle")
++CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn")
+
+[/Script/EngineSettings.GameMapsSettings]
+EditorStartupMap=/Game/Map/Map_0.Map_0
+GameDefaultMap=/Game/Map/Map_0.Map_0
+GameInstanceClass=/Game/Blueprint/BP_GameInstance.BP_GameInstance_C
+GlobalDefaultGameMode=/Game/Blueprint/BP_GameMode.BP_Gamemode_C
+
+[/Script/Engine.PhysicsSettings]
+DefaultGravityZ=-980.000000
+DefaultTerminalVelocity=4000.000000
+DefaultFluidFriction=0.300000
+SimulateScratchMemorySize=262144
+RagdollAggregateThreshold=4
+TriangleMeshTriangleMinAreaThreshold=5.000000
+bEnableShapeSharing=False
+bEnablePCM=True
+bEnableStabilization=False
+bWarnMissingLocks=True
+bEnable2DPhysics=False
+PhysicErrorCorrection=(PingExtrapolation=0.100000,PingLimit=100.000000,ErrorPerLinearDifference=1.000000,ErrorPerAngularDifference=1.000000,MaxRestoredStateError=1.000000,MaxLinearHardSnapDistance=400.000000,PositionLerp=0.000000,AngleLerp=0.400000,LinearVelocityCoefficient=100.000000,AngularVelocityCoefficient=10.000000,ErrorAccumulationSeconds=0.500000,ErrorAccumulationDistanceSq=15.000000,ErrorAccumulationSimilarity=100.000000)
+LockedAxis=Invalid
+DefaultDegreesOfFreedom=Full3D
+BounceThresholdVelocity=200.000000
+FrictionCombineMode=Average
+RestitutionCombineMode=Average
+MaxAngularVelocity=3600.000000
+MaxDepenetrationVelocity=0.000000
+ContactOffsetMultiplier=0.020000
+MinContactOffset=2.000000
+MaxContactOffset=8.000000
+bSimulateSkeletalMeshOnDedicatedServer=True
+DefaultShapeComplexity=CTF_UseSimpleAndComplex
+bDefaultHasComplexCollision=True
+bSuppressFaceRemapTable=False
+bSupportUVFromHitResults=False
+bDisableActiveActors=False
+bDisableKinematicStaticPairs=False
+bDisableKinematicKinematicPairs=False
+bDisableCCD=False
+bEnableEnhancedDeterminism=False
+AnimPhysicsMinDeltaTime=0.000000
+bSimulateAnimPhysicsAfterReset=False
+MaxPhysicsDeltaTime=0.033333
+bSubstepping=False
+bSubsteppingAsync=False
+MaxSubstepDeltaTime=0.016667
+MaxSubsteps=6
+SyncSceneSmoothingFactor=0.000000
+InitialAverageFrameRate=0.016667
+PhysXTreeRebuildRate=10
+DefaultBroadphaseSettings=(bUseMBPOnClient=False,bUseMBPOnServer=False,bUseMBPOuterBounds=False,MBPBounds=(Min=(X=0.000000,Y=0.000000,Z=0.000000),Max=(X=0.000000,Y=0.000000,Z=0.000000),IsValid=0),MBPOuterBounds=(Min=(X=0.000000,Y=0.000000,Z=0.000000),Max=(X=0.000000,Y=0.000000,Z=0.000000),IsValid=0),MBPNumSubdivs=2)
+ChaosSettings=(DefaultThreadingModel=DedicatedThread,DedicatedThreadTickMode=VariableCappedWithTarget,DedicatedThreadBufferMode=Double)
+

+ 8 - 0
Config/DefaultGame.ini

@@ -0,0 +1,8 @@
+[/Script/EngineSettings.GeneralProjectSettings]
+ProjectID=E699ED8B417B7A0606FEABABCB42A2EE
+ProjectName=Visual
+
+[/Script/UnrealEd.ProjectPackagingSettings]
+bCompressed=False
+BuildConfiguration=PPBC_Development
+

+ 93 - 0
Config/DefaultInput.ini

@@ -0,0 +1,93 @@
+[/Script/Engine.InputSettings]
+-AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
+-AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
+-AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
+-AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
+-AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
+-AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
++AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusGo_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusGo_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusGo_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusGo_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Touch",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+bAltEnterTogglesFullscreen=True
+bF11TogglesFullscreen=True
+bUseMouseForTouch=False
+bEnableMouseSmoothing=True
+bEnableFOVScaling=True
+bCaptureMouseOnLaunch=True
+bAlwaysShowTouchInterface=False
+bShowConsoleOnFourFingerTap=True
+bEnableGestureRecognizer=False
+bUseAutocorrect=False
+DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown
+DefaultViewportMouseLockMode=LockOnCapture
+FOVScale=0.011110
+DoubleClickTime=0.200000
++AxisMappings=(AxisName="moveforward",Scale=1.000000,Key=W)
++AxisMappings=(AxisName="moveforward",Scale=-1.000000,Key=S)
++AxisMappings=(AxisName="moveright",Scale=1.000000,Key=A)
++AxisMappings=(AxisName="moveright",Scale=-1.000000,Key=D)
++AxisMappings=(AxisName="moveforward",Scale=1.000000,Key=Up)
++AxisMappings=(AxisName="moveforward",Scale=-1.000000,Key=Down)
++AxisMappings=(AxisName="moveright",Scale=1.000000,Key=Left)
++AxisMappings=(AxisName="moveright",Scale=-1.000000,Key=Right)
+DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks
+-ConsoleKeys=Tilde
++ConsoleKeys=Tilde
+

+ 7 - 0
Config/IOS/IOSEngine.ini

@@ -0,0 +1,7 @@
+[/Script/IOSRuntimeSettings.IOSRuntimeSettings]
+;AudioSampleRate=48000
+AudioMaxChannels=16
+;AudioCallbackBufferFrameSize=1024
+;AudioNumBuffersToEnqueue=2
+;AudioNumSourceWorkers=0
+

+ 7 - 0
Config/Linux/LinuxEngine.ini

@@ -0,0 +1,7 @@
+[/Script/LinuxTargetPlatform.LinuxTargetSettings]
+;AudioSampleRate=48000
+AudioMaxChannels=16
+;AudioCallbackBufferFrameSize=1024
+;AudioNumBuffersToEnqueue=2
+;AudioNumSourceWorkers=0
+

+ 7 - 0
Config/Mac/MacEngine.ini

@@ -0,0 +1,7 @@
+[/Script/MacTargetPlatform.MacTargetSettings]
+;AudioSampleRate=48000
+;AudioMaxChannels=32
+;AudioCallbackBufferFrameSize=1024
+;AudioNumBuffersToEnqueue=2
+;AudioNumSourceWorkers=0
+

+ 7 - 0
Config/PS4/PS4Engine.ini

@@ -0,0 +1,7 @@
+[/Script/PS4PlatformEditor.PS4TargetSettings]
+;AudioSampleRate=48000
+;AudioMaxChannels=32
+;AudioCallbackBufferFrameSize=1024
+;AudioNumBuffersToEnqueue=2
+;AudioNumSourceWorkers=0
+

+ 7 - 0
Config/Switch/SwitchEngine.ini

@@ -0,0 +1,7 @@
+[/Script/SwitchRuntimeSettings.SwitchRuntimeSettings]
+;AudioSampleRate=48000
+;AudioMaxChannels=32
+;AudioCallbackBufferFrameSize=1024
+;AudioNumBuffersToEnqueue=2
+;AudioNumSourceWorkers=0
+

+ 2 - 0
Config/Windows/DefaultGameUserSettings.ini

@@ -0,0 +1,2 @@
+[/Script/Engine.GameUserSettings]
+FullscreenMode=2

+ 7 - 0
Config/Windows/WindowsEngine.ini

@@ -0,0 +1,7 @@
+[/Script/WindowsTargetPlatform.WindowsTargetSettings]
+;AudioSampleRate=48000
+;AudioMaxChannels=32
+AudioCallbackBufferFrameSize=256
+AudioNumBuffersToEnqueue=7
+;AudioNumSourceWorkers=0
+

+ 7 - 0
Config/XboxOne/XboxOneEngine.ini

@@ -0,0 +1,7 @@
+[/Script/XboxOnePlatformEditor.XboxOneTargetSettings]
+;AudioSampleRate=48000
+;AudioMaxChannels=32
+AudioCallbackBufferFrameSize=256
+AudioNumBuffersToEnqueue=7
+;AudioNumSourceWorkers=0
+

+ 15 - 0
Plugins/BluePrintTools/.gitignore

@@ -0,0 +1,15 @@
+Binaries
+DerivedDataCache
+Intermediate
+Saved
+.vscode
+.vs
+*.VC.db
+*.opensdf
+*.opendb
+*.sdf
+*.sln
+*.suo
+*.xcodeproj
+*.xcworkspace
+PA

+ 25 - 0
Plugins/BluePrintTools/BlueprintUtility.uplugin

@@ -0,0 +1,25 @@
+{
+  "FileVersion": 3,
+  "Version": 1,
+  "VersionName": "1.0",
+  "FriendlyName": "BlueprintUtility",
+  "Description": "",
+  "Category": "Other",
+  "CreatedBy": "ECG",
+  "CreatedByURL": "https://github.com/ecg82465/BluePrintTools",
+  "DocsURL": "https://github.com/ecg82465/BluePrintTools",
+  "MarketplaceURL": "",
+  "SupportURL": "",
+  "CanContainContent": true,
+  "IsBetaVersion": false,
+  "Installed": false,
+  "EnabledByDefault": true,
+  "SupportedTargetPlatforms": [ "Win64" ],
+  "Modules": [
+    {
+      "Name": "BlueprintUtility",
+      "Type": "Runtime",
+      "LoadingPhase": "Default"
+    }
+  ]
+}

+ 8 - 0
Plugins/BluePrintTools/Config/FilterPlugin.ini

@@ -0,0 +1,8 @@
+[FilterPlugin]
+; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
+; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
+;
+; Examples:
+;    /README.txt
+;    /Extras/...
+;    /Binaries/ThirdParty/*.dll

BIN
Plugins/BluePrintTools/Content/BlueTools/BP_BluePrintFunction.uasset


BIN
Plugins/BluePrintTools/Content/BlueTools/BP_EndStopSequence_CMP.uasset


BIN
Plugins/BluePrintTools/Content/BlueTools/Comp/BP_EndStopSequence_CMP.uasset


BIN
Plugins/BluePrintTools/Content/BlueTools/Comp/BP_FaceToPlayerCamera.uasset


BIN
Plugins/BluePrintTools/Content/Map/Function_ShowCase.umap


BIN
Plugins/BluePrintTools/Content/Mateirals/M_TextureParameter.uasset


BIN
Plugins/BluePrintTools/Content/Mateirals/M_TextureParameter_Inst.uasset


BIN
Plugins/BluePrintTools/Content/Texture/T_ICon.uasset


+ 28 - 0
Plugins/BluePrintTools/README.md

@@ -0,0 +1,28 @@
+# UE4 Plugins of BluePrint Functions
+
+It provides features not found in the engine blueprint
+
+Like Runtime Load Texture Or .wav Sound ,config ,open exe ....
+
+When I develop new features 
+I Will Update
+
+## How It Use 
+  In Your Project Create A Plugins Folder
+  
+  ![CreateAPluginFolder](https://raw.githubusercontent.com/ecg82465/TESTPlugins/master/Image/CreatPluginsFolder.png)
+
+  Put The BlueprintUtility In Your Plugins Floder
+  
+  ![PutIn](https://raw.githubusercontent.com/ecg82465/TESTPlugins/master/Image/InPlugins.png)
+  
+  Then Open Project You Can See The Plugin Login
+  
+   ![ViewPlugins](https://raw.githubusercontent.com/ecg82465/TESTPlugins/master/Image/ViewPlugins.png)
+  
+  You Can See ShowCase Map For More Detail
+  
+  ![ViewPlugins](https://raw.githubusercontent.com/ecg82465/TESTPlugins/master/Image/SeePlugins.png)
+  ![ViewPlugins](https://raw.githubusercontent.com/ecg82465/TESTPlugins/master/Image/SeeContent.png)
+  
+  

BIN
Plugins/BluePrintTools/Resources/Icon128.png


+ 71 - 0
Plugins/BluePrintTools/Source/BlueprintUtility/BlueprintUtility.Build.cs

@@ -0,0 +1,71 @@
+// Some copyright should be here...
+
+using UnrealBuildTool;
+
+public class BlueprintUtility : ModuleRules
+{
+	public BlueprintUtility(ReadOnlyTargetRules Target) : base(Target)
+	{
+		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
+
+        //PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
+
+        PublicIncludePaths.AddRange(
+			new string[] {
+				// ... add public include paths required here ...
+			}
+			);
+				
+		
+		PrivateIncludePaths.AddRange(
+			new string[] {
+				// ... add other private include paths required here ...
+			}
+			);
+			
+		
+		PublicDependencyModuleNames.AddRange(
+			new string[]
+			{
+				"Core",
+				// ... add other public dependencies that you statically link with here ...
+			}
+			);
+			
+		
+		PrivateDependencyModuleNames.AddRange(
+			new string[]
+			{
+				"CoreUObject",
+				"Engine",
+				"Slate",
+				"SlateCore",
+                "UMG",
+
+				"RenderCore",
+                "ImageWrapper",
+				"Http",
+				//"Json",
+				"ProceduralMeshComponent"
+				//"CryptoPP",
+				// ... add private dependencies that you statically link with here ...	
+			}
+			);
+		
+		
+		DynamicallyLoadedModuleNames.AddRange(
+			new string[]
+			{
+				// ... add any modules that your module loads dynamically here ...
+			}
+			);
+
+
+		//PublicIncludePaths.Add("ThirdParty/CryptoPP/5.6.5/include");
+
+
+
+		//PublicDefinitions.Add(" WITH_OGGVORBIS");
+
+	}
+}

+ 22 - 0
Plugins/BluePrintTools/Source/BlueprintUtility/Private/BlueprintUtility.cpp

@@ -0,0 +1,22 @@
+// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
+
+#include "BlueprintUtility.h"
+
+#define LOCTEXT_NAMESPACE "FBlueprintUtilityModule"
+
+void FBlueprintUtilityModule::StartupModule()
+{
+	// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
+	
+}
+
+void FBlueprintUtilityModule::ShutdownModule()
+{
+	// This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
+	// we call this function before unloading the module.
+	
+}
+
+#undef LOCTEXT_NAMESPACE
+	
+IMPLEMENT_MODULE(FBlueprintUtilityModule, BlueprintUtility)

+ 715 - 0
Plugins/BluePrintTools/Source/BlueprintUtility/Private/BlueprintUtilityBPLibrary.cpp

@@ -0,0 +1,715 @@
+// Copyright  ECG . All Rights Reserved.
+
+#include "BlueprintUtilityBPLibrary.h"
+#include "BlueprintUtility.h"
+
+#include "Runtime/Engine/Classes/Engine/Engine.h"
+
+#include "ImageUtils.h"
+#include "IImageWrapper.h"
+#include "IImageWrapperModule.h"
+
+//#include "ImageLoader.h"
+
+//#include "../Public/FileHelper.h"
+
+#include "SecureHash.h"
+
+#include "Engine/Texture2D.h"
+#include "HighResScreenshot.h"
+#include "Kismet/KismetRenderingLibrary.h"
+
+
+
+UBlueprintUtilityBPLibrary::UBlueprintUtilityBPLibrary(const FObjectInitializer& ObjectInitializer)
+    : Super(ObjectInitializer)
+{
+
+}
+
+//Use ContentDir()
+FString UBlueprintUtilityBPLibrary::GetFullPath(FString FilePath)
+{
+	//Check Relative Or absolute path
+	FString FullFilePath;
+	if (FilePath.StartsWith(".", ESearchCase::CaseSensitive))
+	{
+
+		FullFilePath = *FPaths::Combine(FPaths::ProjectContentDir(), FilePath);
+
+		FullFilePath = *FPaths::ConvertRelativePathToFull(FullFilePath);
+
+	}
+	else
+	{
+
+		FullFilePath = FilePath;
+	}
+
+	return FullFilePath;
+}
+
+float UBlueprintUtilityBPLibrary::BlueprintUtilitySampleFunction(float Param)
+{
+	return -1;
+}
+
+//Discern Texture Type
+static TSharedPtr<IImageWrapper> GetImageWrapperByExtention(const FString InImagePath)
+{
+	IImageWrapperModule& ImageWrapperModule = FModuleManager::GetModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
+	
+
+
+	if (InImagePath.EndsWith(".png"))
+	{
+		return ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
+	}
+	else if (InImagePath.EndsWith(".jpg") || InImagePath.EndsWith(".jpeg"))
+	{
+		return ImageWrapperModule.CreateImageWrapper(EImageFormat::JPEG);
+	}
+	else if (InImagePath.EndsWith(".bmp"))
+	{
+		return ImageWrapperModule.CreateImageWrapper(EImageFormat::BMP);
+	}
+	else if (InImagePath.EndsWith(".ico"))
+	{
+		return ImageWrapperModule.CreateImageWrapper(EImageFormat::ICO);
+	}
+	else if (InImagePath.EndsWith(".exr"))
+	{
+		return ImageWrapperModule.CreateImageWrapper(EImageFormat::EXR);
+	}
+	else if (InImagePath.EndsWith(".icns"))
+	{
+		return ImageWrapperModule.CreateImageWrapper(EImageFormat::ICNS);
+	}
+	return nullptr;
+}
+
+
+UTexture2D* UBlueprintUtilityBPLibrary::LoadTexture2DFromFile(const FString& FilePath,
+	bool& IsValid, int32& Width, int32& Height)
+{
+
+	FString FullFilePath = GetFullPath(FilePath);
+
+	if (!IsVaildPath(FullFilePath))
+	{
+		return NULL;
+	}
+	
+
+	IsValid = false;
+	UTexture2D* LoadedT2D = NULL;
+
+
+	TSharedPtr<IImageWrapper> ImageWrapper = GetImageWrapperByExtention(FullFilePath);
+	
+	TArray<uint8> RawFileData;
+	if (!FFileHelper::LoadFileToArray(RawFileData, *FullFilePath, 0)) return NULL ;
+
+	if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(RawFileData.GetData(), RawFileData.Num()))
+	{
+		TArray<uint8> UncompressedBGRA ;
+		if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA))
+		{
+
+			LoadedT2D = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8);
+
+			if (!LoadedT2D) return NULL;
+
+			Width = ImageWrapper->GetWidth();
+			Height = ImageWrapper->GetHeight();
+
+			void* TextureData = LoadedT2D->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
+			FMemory::Memcpy(TextureData, UncompressedBGRA.GetData(), UncompressedBGRA.Num());
+			LoadedT2D->PlatformData->Mips[0].BulkData.Unlock();
+
+			LoadedT2D->UpdateResource();
+		}
+	}
+
+	IsValid = true;
+	return LoadedT2D;
+
+}
+
+
+UImageLoader* UBlueprintUtilityBPLibrary::LoadTexture2DFromFile_Async(const FString& FilePath,const FString& ID)
+{
+	UImageLoader* Loader = NewObject<UImageLoader>();
+	Loader->LoadImageAsync(FilePath,ID);
+
+	return Loader;
+}
+
+
+UTexture2D* UBlueprintUtilityBPLibrary::BytesToTexture2d(const TArray<uint8> bytes)
+{
+
+	UTexture2D* LoadedT2D = NULL;
+
+	IImageWrapperModule& ImageWrapperModule = FModuleManager::Get().LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
+	EImageFormat Format = ImageWrapperModule.DetectImageFormat(bytes.GetData(), bytes.Num());
+	TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(Format);
+
+
+	if (!ImageWrapper.IsValid())
+	{
+		return nullptr;
+	}
+
+	if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(bytes.GetData(), bytes.Num()))
+	{
+		TArray<uint8> UncompressedBGRA;
+		if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA))
+		{
+
+			LoadedT2D = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8);
+
+			if (!LoadedT2D) return NULL;
+
+			void* TextureData = LoadedT2D->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
+			FMemory::Memcpy(TextureData, UncompressedBGRA.GetData(), UncompressedBGRA.Num());
+			LoadedT2D->PlatformData->Mips[0].BulkData.Unlock();
+
+			LoadedT2D->UpdateResource();
+		}
+	}
+
+	return LoadedT2D;
+
+}
+
+bool UBlueprintUtilityBPLibrary::ReadOggWaveData(class USoundWave* sw, TArray<uint8>* rawFile)
+{
+	//FSoundQualityInfo info;
+	//FVorbisAudioInfo vorbisObj ;
+
+	//if (!vorbisObj.ReadCompressedInfo(rawFile->GetData(), rawFile->Num(), &info))
+	//{
+	//	//Debug("Can't load header");
+	//	return true;
+	//}
+
+	//if (!sw) return true;
+	//sw->SoundGroup = ESoundGroup::SOUNDGROUP_Default;
+	//sw->NumChannels = info.NumChannels;
+	//sw->Duration = info.Duration;
+	//sw->RawPCMDataSize = info.SampleDataSize;
+	//sw->SetSampleRate(info.SampleRate);
+	//sw->RawData  ;
+
+	return false;
+}
+
+bool UBlueprintUtilityBPLibrary::ReadWavWaveData(class USoundWave* sw, TArray<uint8>* rawFile)
+{
+	return false;
+}
+
+
+class USoundWave* UBlueprintUtilityBPLibrary::LoadWaveDataFromFile(const FString& FilePath)
+{
+	USoundWave* sw = NewObject<USoundWave>(USoundWave::StaticClass());
+
+	if (!sw)
+		return nullptr;
+
+	FString FullPath = GetFullPath(FilePath);
+
+	TArray < uint8 > rawFile;
+
+	FFileHelper::LoadFileToArray(rawFile, FullPath.GetCharArray().GetData());
+	FWaveModInfo WaveInfo;
+
+	if (WaveInfo.ReadWaveInfo(rawFile.GetData(), rawFile.Num()))
+	{
+		sw->InvalidateCompressedData();
+
+		sw->RawData.Lock(LOCK_READ_WRITE);
+		void* LockedData = sw->RawData.Realloc(rawFile.Num());
+		FMemory::Memcpy(LockedData, rawFile.GetData(), rawFile.Num());
+		sw->RawData.Unlock();
+
+		int32 DurationDiv = *WaveInfo.pChannels * *WaveInfo.pBitsPerSample * *WaveInfo.pSamplesPerSec;
+		if (DurationDiv)
+		{
+			sw->Duration = *WaveInfo.pWaveDataSize * 8.0f / DurationDiv;
+		}
+		else
+		{
+			sw->Duration = 0.0f;
+		}
+		
+		sw->SetSampleRate(*WaveInfo.pSamplesPerSec);
+		sw->NumChannels = *WaveInfo.pChannels;
+		sw->RawPCMDataSize = WaveInfo.SampleDataSize;
+		sw->SoundGroup = ESoundGroup::SOUNDGROUP_Default;
+	}
+	else {
+		return nullptr;
+	}
+
+	return sw;
+}
+
+class USoundWave* UBlueprintUtilityBPLibrary::LoadOggDataFromFile(const FString& FilePath)
+{
+
+	USoundWave* sw = NewObject<USoundWave>(USoundWave::StaticClass());
+
+	if (!sw)
+		return NULL;
+
+	//* If true the song was successfully loaded
+	bool loaded = false;
+	
+	FString FullPath = GetFullPath(FilePath);
+
+
+	//* loaded song file (binary, encoded)
+	TArray < uint8 > rawFile;
+
+	loaded = FFileHelper::LoadFileToArray(rawFile, FullPath.GetCharArray().GetData());
+
+	if (loaded)
+	{
+		FByteBulkData* bulkData = &sw->CompressedFormatData.GetFormat(TEXT("OGG"));
+		//sw->RawData = sw->CompressedFormatData.GetFormat(TEXT("OGG"));
+
+		bulkData->Lock(LOCK_READ_WRITE);
+		FMemory::Memcpy(bulkData->Realloc(rawFile.Num()), rawFile.GetData(), rawFile.Num());
+		bulkData->Unlock();
+		
+		sw->RawData = *bulkData;
+
+		loaded = ReadOggWaveData(sw, &rawFile) == 0 ? true : false;
+	}
+
+	if (!loaded)
+		return NULL;
+
+	return sw;
+
+}
+
+
+void UBlueprintUtilityBPLibrary::ReadConfig(const FString& SectionName, const FString& ValueName, bool& succeed, FString &ReturnValue)
+{
+
+	//GConfig->Flush(true, GGameIni);
+
+	//bool succeed = false;
+
+	 succeed = GConfig->GetString(
+		*SectionName,
+		*ValueName,
+		ReturnValue,
+		GGameIni
+	);
+
+
+	UE_LOG(LogTemp, Warning, TEXT("Read Config %s "),succeed ? TEXT("Succeed") : TEXT("Fail"));
+	
+}
+
+
+void UBlueprintUtilityBPLibrary::WriteConfig(const FString& SectionName, const FString& ValueName, const FString &ReturnValue)
+{
+
+	//FString newSection = "/Script/CommunicationSetting";
+	//FString TA = "DefaultMyConfig";
+	GConfig->SetString(
+		*SectionName,
+		*ValueName,
+		*ReturnValue,
+		GGameIni
+	);
+
+	GConfig->Flush(false, GGameIni);
+	/*
+		FString log;
+		ReadConfig(ReturnValue, ValueName, log);
+
+		UE_LOG(LogTemp, Warning, TEXT("Set Config As %s "), *log);*/
+
+}
+
+
+bool UBlueprintUtilityBPLibrary::ReadCustomPathConfig(const FString&FilePath, const FString& SectionName, const FString& ValueName, FString &ReturnString)
+{
+	FString FullPath = GetFullPath(FilePath);
+
+	GConfig->Flush(true, FullPath);
+	bool succeed = GConfig->GetString(*SectionName, *ValueName, ReturnString, FullPath);
+	return succeed;
+
+}
+
+
+ void UBlueprintUtilityBPLibrary::WriteCustomPathConfig(const FString&FilePath, const FString& SectionName, const FString& ValueName, const FString &WriteString)
+{
+	FString FullPath = GetFullPath(FilePath);
+
+	IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
+
+	// Does the file exist?
+	if (!PlatformFile.FileExists(*FullPath))
+	{
+		// File doesn't exist; (Attempt to) create a new one.
+		FFileHelper::SaveStringToFile(TEXT(""), *FullPath);
+	}
+
+
+	GConfig->SetString(*SectionName, *ValueName, *WriteString, FullPath);
+
+	GConfig->Flush(false, FullPath);
+
+}
+
+
+ void UBlueprintUtilityBPLibrary::RefrashAllSkeletallAnimation()
+ {
+	 for (TObjectIterator<AActor> iterator; iterator; ++iterator)
+	 {
+		 if (iterator)
+		 {
+			 for (auto actor_Component : iterator->GetComponents())
+			 {
+				 if (actor_Component->IsA(USkeletalMeshComponent::StaticClass()))
+				 {
+					 (Cast<USkeletalMeshComponent>(actor_Component))->TickAnimation(0.0f, false);
+					 (Cast<USkeletalMeshComponent>(actor_Component))->RefreshBoneTransforms();
+				 }
+			 }
+		 }
+	 }
+ };
+
+
+
+
+ bool UBlueprintUtilityBPLibrary::ReadFile(const FString FilePath, FString& ReturnString)
+ {
+	 FString FullPath = GetFullPath(FilePath);
+
+	 FString Cache = "";
+	 bool Sucess = false;
+	 Sucess = FFileHelper::LoadFileToString(Cache, FullPath.GetCharArray().GetData());
+	 ReturnString = Cache;
+	 return Sucess;
+ }
+
+ bool UBlueprintUtilityBPLibrary::WriteFile(const FString FilePath, const FString ReturnString)
+ {
+	 FString FullPath = GetFullPath(FilePath);
+
+	 bool Sucess;
+	 Sucess = FFileHelper::SaveStringToFile(ReturnString, *FullPath);
+	 return Sucess;
+ }
+
+ bool UBlueprintUtilityBPLibrary::WriteFileByte(TArray<uint8> bytes, const FString FilePath)
+ {
+	 FString FullPath = GetFullPath(FilePath);
+
+	 bool Sucess;
+	 Sucess = FFileHelper::SaveArrayToFile(bytes, *FilePath);
+
+	
+
+	 return Sucess;
+ }
+
+
+ bool UBlueprintUtilityBPLibrary::DeleteFile(const FString FilePath)
+ {
+	 FString FullPath = GetFullPath(FilePath);
+
+	 IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
+
+	 
+
+	 if (PlatformFile.DeleteFile(*FullPath))
+	 {
+		 UE_LOG(LogTemp, Warning, TEXT("deleteFile: Delete the flie successfully!"));
+	 
+		 return true;
+	 }
+	 else
+	 {
+		 UE_LOG(LogTemp, Warning, TEXT("deleteFile: Not delete the flie!"));
+		 
+		 return false;
+	 }
+
+ }
+
+
+ bool UBlueprintUtilityBPLibrary::DeleteFiles(const FString FilePath)
+ {
+	 FString FullPath = GetFullPath(FilePath);
+
+	 IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
+
+
+
+	 if (PlatformFile.DeleteDirectoryRecursively(*FullPath))
+	 {
+		 UE_LOG(LogTemp, Warning, TEXT("deleteFile: Delete the flie successfully!"));
+
+		 return true;
+	 }
+	 else
+	 {
+		 UE_LOG(LogTemp, Warning, TEXT("deleteFile: Not delete the flie!"));
+
+		 return false;
+	 }
+
+ }
+
+
+
+ bool UBlueprintUtilityBPLibrary::CopyFile(const FString FilePath, const FString ToPath)
+ {
+	 IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
+
+	
+
+	 return PlatformFile.CopyFile(*ToPath, *FilePath);
+
+ }
+
+ bool UBlueprintUtilityBPLibrary::Texture2d2PNG( UTextureRenderTarget2D* TextureRenderTarget, const FString& FilePath)
+{
+	 FTextureRenderTargetResource* rtResource = TextureRenderTarget->GameThread_GetRenderTargetResource();
+	 FReadSurfaceDataFlags readPixelFlags(RCM_UNorm);
+
+	 TArray<FColor> outBMP;
+
+	 for (FColor& color : outBMP)
+	 {
+		 color.A = 255;
+	 }
+	 outBMP.AddUninitialized(TextureRenderTarget->GetSurfaceWidth() * TextureRenderTarget->GetSurfaceHeight());
+	 rtResource->ReadPixels(outBMP, readPixelFlags);
+
+	 FIntPoint destSize(TextureRenderTarget->GetSurfaceWidth(), TextureRenderTarget->GetSurfaceHeight());
+	 TArray<uint8> CompressedBitmap;
+	 FImageUtils::CompressImageArray(destSize.X, destSize.Y, outBMP, CompressedBitmap);
+	 bool imageSavedOk = FFileHelper::SaveArrayToFile(CompressedBitmap, *FilePath);
+
+	 return imageSavedOk;
+
+
+
+}
+
+
+
+ FString UBlueprintUtilityBPLibrary::GetGamePath(DirType E)
+ {
+	 if (E == DirType::GameDir)
+	 {
+		 return FPaths::ProjectDir();
+	 }
+	 return FPaths::ProjectContentDir();
+
+ }
+
+
+ bool UBlueprintUtilityBPLibrary::IsVaildPath(const FString ImagePath)
+ {
+
+	 if (!FPaths::FileExists(ImagePath))
+	 {
+		 UE_LOG(LogTemp, Warning, TEXT("File not found: %s"), *ImagePath);
+		 return false;
+	 }
+
+	 // Load the compressed byte data from the file
+	 TArray<uint8> FileData;
+	 if (!FFileHelper::LoadFileToArray(FileData, *ImagePath))
+	 {
+		 UE_LOG(LogTemp, Warning, TEXT("Failed to load file: %s"), *ImagePath);
+		 return false;
+	 }
+
+	 return true;
+ }
+ AExeActor* UBlueprintUtilityBPLibrary::OpenExe(UObject* SomeInWorldObject, const FString Path, const FString Args)
+ {
+	 UWorld* world = SomeInWorldObject->GetWorld();
+
+	 FVector pos(150, 0, 20);
+
+	 AExeActor *Temp = world->SpawnActor<AExeActor>(pos, FRotator::ZeroRotator); ;
+
+	 //AExeActor *Temp = NewObject<AExeActor>();
+	 //EE->GetWorld()->AddNetworkActor(Temp);
+	 Temp->SetActorTickEnabled(true);
+	 Temp->ActorToWorld();
+
+	 Temp->CheckProc = FPlatformProcess::CreateProc(*Path, *Args, true, false, false, nullptr, 0, nullptr, nullptr);
+
+
+	 return Temp;
+
+ }
+
+ void UBlueprintUtilityBPLibrary::GenColors(int Length, const FColor color, TArray<FColor>& OuterColor)
+ {
+	 TArray<FColor> setColor;
+	 for (int i=0;i<Length;i++)
+	 {
+		 setColor.Add(color);
+		 
+	 }
+
+	 OuterColor = setColor;
+ }
+
+ void UBlueprintUtilityBPLibrary::UVtimes(FVector2D tims, const TArray<FVector2D> inputUV, TArray<FVector2D>& OuterColor)
+ {
+
+	 OuterColor.Reset(inputUV.Num());
+	 for (const FVector2D& tan : inputUV)
+	 {
+		 OuterColor.Add(tan*tims);
+	 }
+
+	 
+ }
+ 
+ bool UBlueprintUtilityBPLibrary::IsEditorMode()
+ {
+
+#if WITH_EDITOR
+
+	 return true;
+#endif
+	 
+	 return false;
+
+ }
+
+
+
+ FString UBlueprintUtilityBPLibrary::GenMD5(FString inputstring)
+ {
+	 FTCHARToUTF8 EchoStrUtf8(*inputstring);
+
+	 int32 DestLen = EchoStrUtf8.Length();
+
+
+	 FString RST = FMD5::HashBytes((unsigned char*)TCHAR_TO_UTF8(*inputstring), DestLen);
+	
+	 
+
+	 return RST;
+ }
+
+
+ FString UBlueprintUtilityBPLibrary::GenSHA1(FString inputstring)
+ {
+	 FSHAHash StringHash;
+	 FSHA1::HashBuffer(TCHAR_TO_ANSI(*inputstring), inputstring.Len(), StringHash.Hash);
+	 return *(StringHash.ToString());
+
+ }
+
+
+ FString UBlueprintUtilityBPLibrary::EnCryptoPPSHA1(FString inputstring,FString aes_key)
+ {
+
+	 //std::string sKey = TCHAR_TO_UTF8(*aes_key);
+	 //const char* cipherText = TCHAR_TO_ANSI(*aes_content);
+	 //std::string outstr;
+	 //try
+	 //{
+		// //填key    
+		// SecByteBlock key(AES::MAX_KEYLENGTH);
+		// memset(key, 0x30, key.size());
+		// sKey.size() <= AES::MAX_KEYLENGTH ? memcpy(key, sKey.c_str(), sKey.size()) : memcpy(key, sKey.c_str(), AES::MAX_KEYLENGTH);
+
+		// ECB_Mode<AES >::Decryption ecbDecryption((byte*)key, AES::MAX_KEYLENGTH);
+
+		// HexDecoder decryptor(new StreamTransformationFilter(ecbDecryption, new StringSink(outstr), BlockPaddingSchemeDef::BlockPaddingScheme::ZEROS_PADDING,
+		//	 true
+		// ));
+		// decryptor.Put((byte*)cipherText, strlen(cipherText));
+		// decryptor.MessageEnd();
+		// _isSuc = true;
+	 //}
+	 //catch (...)
+	 //{
+		// outstr = "error";
+		// _isSuc = false;
+	 //}
+
+
+	 return "123";
+
+ }
+
+
+ bool UBlueprintUtilityBPLibrary::CheckActorInView(UPrimitiveComponent* actorComp,float checktime)
+ {
+
+	 
+
+	 float c = actorComp->GetLastRenderTimeOnScreen();
+
+	 float d = actorComp->GetWorld()->GetTimeSeconds();
+
+	 //UE_LOG(LogTemp, Warning, TEXT("last render time :%f    Game Time :%f"),c,d);
+
+	 if (d-c >=checktime)
+	 {
+		 return false;
+	 }
+
+	 return true;
+
+ }
+
+
+ void UBlueprintUtilityBPLibrary::GetProduralMeshInfo(UProceduralMeshComponent* InProcMesh, int32 SectionIndex, TArray<FVector>& Vertices, TArray<int32>& Triangles, TArray<FVector>& Normals, TArray<FVector2D>& UVs, TArray<FProcMeshTangent>& Tangents, TArray<FColor>& VerticesColor)
+ {
+	 if (InProcMesh && SectionIndex >= 0 && SectionIndex < InProcMesh->GetNumSections())
+	 {
+		 const FProcMeshSection* Section = InProcMesh->GetProcMeshSection(SectionIndex);
+
+		 const int32 NumOutputVerts = Section->ProcVertexBuffer.Num();
+
+		 // Allocate output buffers for vert data
+		 Vertices.SetNumUninitialized(NumOutputVerts);
+		 Normals.SetNumUninitialized(NumOutputVerts);
+		 UVs.SetNumUninitialized(NumOutputVerts);
+		 Tangents.SetNumUninitialized(NumOutputVerts);
+		 VerticesColor.SetNumUninitialized(NumOutputVerts);
+
+		 // copy data
+		 for (int32 VertIdx = 0; VertIdx < NumOutputVerts; VertIdx++)
+		 {
+			 const FProcMeshVertex& Vert = Section->ProcVertexBuffer[VertIdx];
+			 Vertices[VertIdx] = Vert.Position;
+			 Normals[VertIdx] = Vert.Normal;
+			 UVs[VertIdx] = Vert.UV0;
+			 Tangents[VertIdx] = Vert.Tangent;
+			 VerticesColor[VertIdx] = Vert.Color;
+		 }
+
+		 // Copy index buffer
+		 const int32 NumIndices = Section->ProcIndexBuffer.Num();
+		 Triangles.SetNumUninitialized(NumIndices);
+		 for (int32 IndexIdx = 0; IndexIdx < NumIndices; IndexIdx++)
+		 {
+			 Triangles[IndexIdx] = Section->ProcIndexBuffer[IndexIdx];
+		 }
+	 }
+ }

+ 102 - 0
Plugins/BluePrintTools/Source/BlueprintUtility/Private/DynamicLoader.cpp

@@ -0,0 +1,102 @@
+
+#include "DynamicLoader.h"
+
+#include "Runtime/Engine/Classes/Engine/Engine.h"
+#include "Runtime/Core/Public/Async/Async.h"
+
+#include "IImageWrapper.h"
+#include "ImageUtils.h"
+#include "IImageWrapperModule.h"
+
+#include "BlueprintUtilityBPLibrary.h"
+
+#include "Engine/Texture2D.h"
+
+
+
+
+
+void UImageLoader::LoadImageAsync(const FString& ImagePath,const FString &inputID)
+{
+
+
+	AsyncTask(ENamedThreads::AnyThread, [=]()
+	{
+
+		LoadID = inputID;
+
+		UTexture2D* reTexture;
+		reTexture = LoadImageFromDisk(ImagePath);
+
+		LoadCompleted.Broadcast(reTexture,LoadID);
+
+	});
+
+}
+
+UTexture2D* UImageLoader::LoadImageFromDisk( const FString& ImagePath)
+{
+
+	bool Succss;	int32 Weight;
+	int32 Highting;
+	
+	return UBlueprintUtilityBPLibrary::LoadTexture2DFromFile(ImagePath,Succss, Weight,Highting);
+}
+
+
+
+
+AExeActor::AExeActor()
+{
+	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
+	PrimaryActorTick.bCanEverTick = true;
+
+
+}
+
+bool AExeActor::Getstat()
+{
+
+	bool a = FPlatformProcess::IsProcRunning(CheckProc);
+
+	return a;
+
+	//return true;
+}
+
+
+// Called when the game starts or when spawned
+void AExeActor::BeginPlay()
+{
+
+	Super::BeginPlay();
+
+	UE_LOG(LogTemp, Warning, TEXT("EXE Begin"));
+
+}
+
+void AExeActor::Tick(float DeltaTime)
+{
+	//Super::Tick();
+
+	Super::Tick(DeltaTime);
+
+	//UE_LOG(LogTemp, Warning, TEXT("I AM IN There"));
+
+	bool a = FPlatformProcess::IsProcRunning(CheckProc);
+
+	if (!a && !bisShutDown)
+	{
+
+		UE_LOG(LogTemp, Warning, TEXT("EXE End"));
+
+		bisShutDown = true;
+		ProcShutdown.Broadcast(true);
+		PrimaryActorTick.bCanEverTick = false;
+
+		Destroy();
+
+	}
+	//return true;
+}
+

+ 14 - 0
Plugins/BluePrintTools/Source/BlueprintUtility/Public/BlueprintUtility.h

@@ -0,0 +1,14 @@
+// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include "Modules/ModuleManager.h"
+
+class FBlueprintUtilityModule : public IModuleInterface
+{
+public:
+
+	/** IModuleInterface implementation */
+	virtual void StartupModule() override;
+	virtual void ShutdownModule() override;
+};

+ 170 - 0
Plugins/BluePrintTools/Source/BlueprintUtility/Public/BlueprintUtilityBPLibrary.h

@@ -0,0 +1,170 @@
+// Copyright  ECG . All Rights Reserved.
+
+#pragma once
+
+#include "DDSLoader.h"
+#include "Core.h"
+#include "Engine.h"
+
+//LoadImageAsync
+#include "DynamicLoader.h"
+
+#include "Kismet/BlueprintFunctionLibrary.h"
+
+#include "Runtime/Engine/Classes/Sound/SoundWave.h"
+
+#include "ProceduralMeshComponent.h"
+#include "Components/AudioComponent.h"
+#include "AudioDecompress.h"
+#include "AudioDevice.h"
+#include "ActiveSound.h"
+#include "Audio.h"
+#include "Developer/TargetPlatform/Public/Interfaces/IAudioFormat.h"
+#include "VorbisAudioInfo.h"
+
+#include "BlueprintUtilityBPLibrary.generated.h"
+
+
+//DECLARE_LOG_CATEGORY_EXTERN(CategoryName, Log, All)
+
+/* 
+*
+*	BluePrint Function Collection
+*	
+*      
+*   This Way To More
+*	https://github.com/ecg82465/BluePrintTools
+*/
+
+UENUM(BlueprintType)
+enum class DirType : uint8
+{
+	GameDir,
+	ContentDir
+};
+
+
+UCLASS()
+class UBlueprintUtilityBPLibrary : public UBlueprintFunctionLibrary
+{
+	GENERATED_UCLASS_BODY()
+
+	UFUNCTION(BlueprintCallable, meta = (DisplayName = "TEST", Keywords = "BlueprintUtility sample test testing"), Category = "BlueprintUtility")
+	static float BlueprintUtilitySampleFunction(float Param);
+
+	static FString GetFullPath(FString FilePath);
+
+	/*                   RuntimeLoader                    */
+
+	/** Load Texture2D */
+	UFUNCTION(BlueprintPure, Category = "BlueprintUtility|Loader", meta = (Keywords = "image png jpg jpeg bmp bitmap ico icon exr icns"))
+	static UTexture2D* LoadTexture2DFromFile(const FString& FilePath, bool& IsValid, int32& Width, int32& Height);
+
+	/** Load Texture2D No Blocking Game Logic */
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|Loader", meta = (Keywords = "image png jpg jpeg bmp bitmap ico icon exr icns"))
+	static UImageLoader* LoadTexture2DFromFile_Async( const FString& FilePath,const FString& ID);
+
+	UFUNCTION(BlueprintPure, Category = "BlueprintUtility|Loader", meta = (Keywords = "image png jpg jpeg bmp bitmap ico icon exr icns"))
+	static UTexture2D* BytesToTexture2d(const TArray<uint8> bytes);
+
+	/** Load a Sound from a file!  */
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|Loader", meta = (Keywords = ""))
+	static class USoundWave* LoadOggDataFromFile(const FString& FilePath);
+
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|Loader", meta = (Keywords = ""))
+	static class USoundWave* LoadWaveDataFromFile(const FString& FilePath);
+
+	/**                  Config                           */
+
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|Config", meta = (Keywords = ""))
+	static void ReadConfig(const FString& SectionName,const FString& ValueName, bool &succeed, FString &ReturnValue);
+
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|Config", meta = (Keywords = ""))
+	static void WriteConfig(const FString& SectionName, const FString& ValueName, const FString &ReturnValue);
+
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|Config", meta = (Keywords = ""))
+	static bool ReadCustomPathConfig(const FString&FilePath,const FString& SectionName, const FString& ValueName, FString &ReturnString);
+
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|Config", meta = (Keywords = ""))
+	static void WriteCustomPathConfig(const FString&FilePath, const FString& SectionName, const FString& ValueName,const FString &WriteString);
+
+	UFUNCTION(BlueprintPure, Category = "BlueprintUtility|Config", meta = (Keywords = ""))
+	static bool IsEditorMode( );
+
+
+
+	/** RefrashAllSkeletal Trnasfrom In This Frame (Fix Animation Dither Error)*/
+
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|Animation", meta = (Keywords = ""))
+	static void RefrashAllSkeletallAnimation();
+
+	/**                   File                           */
+
+	/** Read Or Write Custom Text */
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|File", meta = (Keywords = ""))
+	static bool ReadFile(const FString FilePath, FString& ReturnString);
+
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|File", meta = (Keywords = ""))
+	static bool WriteFile(const FString FilePath, const FString ReturnString);
+
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|File", meta = (Keywords = ""))
+    static bool WriteFileByte(TArray<uint8> bytes, const FString FilePath);
+	
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|File", meta = (Keywords = ""))
+	static bool DeleteFile(const FString FilePath);
+
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|File", meta = (Keywords = ""))
+	static bool DeleteFiles(const FString FilePath);
+
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|File", meta = (Keywords = ""))
+	static bool CopyFile (const FString FilePath, const FString ToPath);
+
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|File", meta = (Keywords = ""))
+	static bool Texture2d2PNG(UTextureRenderTarget2D* TextureRenderTarget, const FString& FilePath);
+
+	
+
+	/** Get Path */
+	UFUNCTION(BlueprintPure, Category = "BlueprintUtility|File", meta = (Keywords = ""))
+	static bool IsVaildPath(const FString FilePath);
+
+	UFUNCTION(BlueprintPure, Category = "BlueprintUtility|File", meta = (Keywords = ""))
+	static FString GetGamePath(DirType E);
+
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|OS", meta = (Keywords = "exe"))
+	static AExeActor* OpenExe(UObject* SomeInWorldObject,const FString Path,const FString Args);
+
+
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|Functions", meta = (Keywords = "MD5"))
+	static FString GenMD5(FString InputString);
+
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|Functions", meta = (Keywords = "MD5"))
+	static FString GenSHA1(FString InputString);
+
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|Functions", meta = (Keywords = "MD5"))
+	static FString EnCryptoPPSHA1(FString InputString, FString aes_key);
+
+	UFUNCTION(BlueprintCallable, Category = "BlueprintUtility|Functions", meta = (Keywords = "View"))
+	static bool CheckActorInView(UPrimitiveComponent* actorComp,float checktime);
+
+
+	UFUNCTION(BlueprintPure, Category = "BlueprintUtility|Functions", meta = (Keywords = "color"))
+	static void GenColors(int Length, const FColor color, TArray<FColor>& OuterColor);
+
+	UFUNCTION(BlueprintPure, Category = "BlueprintUtility|Functions", meta = (Keywords = "color"))
+	static void UVtimes(FVector2D tims,const TArray<FVector2D> inputUV, TArray<FVector2D>& OuterUV);
+
+	UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject"), Category = "BlueprintUtility|Functions", meta = (Keywords = "mesh"))
+	static void GetProduralMeshInfo(UProceduralMeshComponent* InProcMesh, int32 SectionIndex, TArray<FVector>& Vertices, TArray<int32>& Triangles, TArray<FVector>& Normals, TArray<FVector2D>& UVs, TArray<FProcMeshTangent>& Tangents, TArray<FColor>& VerticesColor);
+
+
+
+
+private:
+
+	static bool ReadWavWaveData(USoundWave* sw, TArray<uint8>* rawFile);
+
+	static bool ReadOggWaveData(USoundWave* sw, TArray<uint8>* rawFile);
+
+};
+ 

+ 89 - 0
Plugins/BluePrintTools/Source/BlueprintUtility/Public/DynamicLoader.h

@@ -0,0 +1,89 @@
+
+#pragma once
+
+
+#include "Core.h"
+#include "Engine.h"
+
+#include "CoreMinimal.h"
+#include "UObject/NoExportTypes.h"
+
+#include "PixelFormat.h"
+#include "DynamicLoader.generated.h"
+
+// Forward declarations
+class UTexture2D;
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnImageLoadCompleted, UTexture2D*, Texture, FString, ID);
+
+UCLASS()
+class  UImageLoader : public UObject
+{
+	GENERATED_BODY()
+
+public:
+
+	static UTexture2D* LoadImageFromDisk(const FString& ImagePath);
+
+public:
+
+
+	UPROPERTY(BlueprintAssignable, Category = ImageLoader, meta = (AllowPrivateAccess = true))
+	FOnImageLoadCompleted LoadCompleted;
+
+	/** This accessor function allows C++ code to bind to the event. */
+	//FOnImageLoadCompleted& OnLoadCompleted()
+	//{
+	//	return LoadCompleted;
+	//}
+
+
+	/** Helper function that initiates the loading operation and fires the event when loading is done. */
+	void LoadImageAsync(const FString& ImagePath,const FString& ID);
+
+
+private:
+	/**
+	Holds the load completed event delegate.
+	Giving Blueprint access to this private variable allows Blueprint scripts to bind to the event.
+	*/
+	FString LoadID;
+
+	/** Holds the future value which represents the asynchronous loading operation. */
+	TFuture<UTexture2D*> Future;
+
+
+};
+
+UCLASS()
+class AExeActor : public AActor
+{
+	GENERATED_BODY()
+
+public:
+	AExeActor();
+
+
+protected:
+	// Called when the game starts or when spawned
+	virtual void BeginPlay() override;
+
+public:
+
+	virtual void Tick(float DeltaTime) override;
+
+	UFUNCTION(BlueprintCallable, meta = (Keywords = "FFMPEGPort sample test testing"), Category = "FFMPEGPortTesting")
+		bool Getstat();
+
+	DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnProcShutdown, bool, IsOver);
+
+	UPROPERTY(BlueprintAssignable, Category = "BlueprintUtility|OS", meta = (AllowPrivateAccess = true))
+		FOnProcShutdown ProcShutdown;
+
+public:
+
+	bool bisShutDown = false;
+
+	FProcHandle CheckProc;
+
+};

+ 23 - 0
Plugins/CustomWidget/CustomWidget.uplugin

@@ -0,0 +1,23 @@
+{
+	"FileVersion": 3,
+	"Version": 1,
+	"VersionName": "1.0",
+	"FriendlyName": "CustomWidget",
+	"Description": "",
+	"Category": "Other",
+	"CreatedBy": "ECG",
+	"CreatedByURL": "",
+	"DocsURL": "",
+	"MarketplaceURL": "",
+	"SupportURL": "",
+	"CanContainContent": true,
+	"IsBetaVersion": false,
+	"Installed": false,
+	"Modules": [
+		{
+			"Name": "CustomWidget",
+			"Type": "Runtime",
+			"LoadingPhase": "Default"
+		}
+	]
+}

BIN
Plugins/CustomWidget/Resources/Icon128.png


+ 60 - 0
Plugins/CustomWidget/Source/CustomWidget/CustomWidget.Build.cs

@@ -0,0 +1,60 @@
+// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
+
+using UnrealBuildTool;
+
+public class CustomWidget : ModuleRules
+{
+	public CustomWidget(ReadOnlyTargetRules Target) : base(Target)
+	{
+		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
+		
+		PublicIncludePaths.AddRange(
+			new string[] {
+				// ... add public include paths required here ...
+			}
+			);
+				
+		
+		PrivateIncludePaths.AddRange(
+			new string[] {
+				// ... add other private include paths required here ...
+			}
+			);
+			
+		
+		PublicDependencyModuleNames.AddRange(
+			new string[]
+			{
+				"Core",
+				// ... add other public dependencies that you statically link with here 
+				"Slate",
+                "SlateCore",
+                "InputCore",
+				"ApplicationCore", 
+				"ImageWrapper",
+				"RenderCore",
+				"RHI",
+				"UMG",
+			}
+			);
+			
+		
+		PrivateDependencyModuleNames.AddRange(
+			new string[]
+			{
+				"CoreUObject",
+				"Engine",
+
+				// ... add private dependencies that you statically link with here ...	
+			}
+			);
+		
+		
+		DynamicallyLoadedModuleNames.AddRange(
+			new string[]
+			{
+				// ... add any modules that your module loads dynamically here ...
+			}
+			);
+	}
+}

+ 23 - 0
Plugins/CustomWidget/Source/CustomWidget/Private/CustomWidget.cpp

@@ -0,0 +1,23 @@
+// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
+
+#include "CustomWidget.h"
+#include "DropSlate.h"
+
+#define LOCTEXT_NAMESPACE "FCustomWidgetModule"
+
+void FCustomWidgetModule::StartupModule()
+{
+	// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
+
+
+}
+
+void FCustomWidgetModule::ShutdownModule()
+{
+	// This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
+	// we call this function before unloading the module.
+}
+
+#undef LOCTEXT_NAMESPACE
+	
+IMPLEMENT_MODULE(FCustomWidgetModule, CustomWidget)

+ 85 - 0
Plugins/CustomWidget/Source/CustomWidget/Private/DropSlate.cpp

@@ -0,0 +1,85 @@
+#include "DropSlate.h"
+#include "DropWidget.h"
+
+
+
+SDropWidget::SDropWidget()
+{
+
+}
+
+void SDropWidget::Construct(const FArguments& InArgs)
+{
+
+
+}
+
+void SDropWidget::OnDragEnter(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
+{
+	TSharedPtr<FExternalDragOperation> op = DragDropEvent.GetOperationAs<FExternalDragOperation>();
+	
+	
+
+	if (op)
+	{
+		const TArray<FString>& files = op->GetFiles();
+
+
+		if (files.Num() > 0)
+		{
+			mWidget->DropFile(files[0]);
+		}
+	}
+
+}
+
+void SDropWidget::OnDragLeave(const FDragDropEvent& DragDropEvent)
+{
+
+}
+
+FReply SDropWidget::OnDragOver(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
+{
+	if (mWidget->HasFile())
+	{
+		FVector2D delta = DragDropEvent.GetCursorDelta();
+		mWidget->OnMoveImage(delta);
+
+	}
+	return FReply::Handled();
+}
+
+FReply SDropWidget::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
+{
+	if (mWidget->HasFile())
+	{
+		mWidget->OnExportImage();
+		return FReply::Handled();
+	}
+	else
+	{
+		TSharedPtr<FExternalDragOperation> op = DragDropEvent.GetOperationAs<FExternalDragOperation>();
+
+		if (op)
+		{
+			const TArray<FString>& files = op->GetFiles();
+			if (files.Num() > 0)
+			{
+				mWidget->DropFile(files[0]);
+			}
+		}
+
+	}
+
+
+
+	return FReply::Handled();
+}
+
+
+void SDropWidget::AddWidget(TWeakObjectPtr<UBaseBannerMainWidget> widget)
+{
+	mWidget = widget;
+	//ChildSlot[mWidget->TakeWidget()];
+
+}

+ 95 - 0
Plugins/CustomWidget/Source/CustomWidget/Private/DropWidget.cpp

@@ -0,0 +1,95 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#include "DropWidget.h"
+#include "Components/TextBlock.h"
+#include "Components/Image.h"
+//#include "BannerGameModeBase.h"
+#include "DropSlate.h"
+
+#include "Engine/TextureRenderTarget2D.h"
+#include "Slate/SceneViewport.h"
+#include "Slate/WidgetRenderer.h"
+#include "Kismet/KismetRenderingLibrary.h"
+#include "Engine/Engine.h"
+
+void UBaseBannerMainWidget::NativePreConstruct()
+{
+	UUserWidget::NativePreConstruct();
+}
+
+void UBaseBannerMainWidget::NativeConstruct()
+{
+	UUserWidget::NativeConstruct();
+
+	//TSharedRef<SDropWidget> dropWidget = SNew(SDropWidget);
+	//GEngine->GetWorldFromContextObject(this, EGetWorldErrorMode::LogAndReturnNull)->GetGameViewport()->AddViewportWidgetContent(dropWidget);
+	//dropWidget->AddWidget(this);
+
+
+}
+
+void UBaseBannerMainWidget::DropFile(FString file)
+{
+
+		OnDropFile(file);
+		mDropFileName = file;
+
+}
+
+TSharedRef<SWidget> UBaseBannerMainWidget::RebuildWidget()
+{
+	TSharedRef<SDropWidget> mySlateCom = SNew(SDropWidget);
+	mySlateCom->AddWidget(this);
+
+	return mySlateCom;
+}
+
+
+FString UBaseBannerMainWidget::RenderWidgetToFile(UUserWidget* widget, FString title, FString channel, bool isExclusive)
+{
+	if (mDropFileName.IsEmpty())
+	{
+		return "";
+	}
+
+	FString resultFilePath = FPaths::GetPath(mDropFileName);
+	FString resultFileName = FPaths::GetBaseFilename(mDropFileName);
+
+	if (isExclusive)
+	{
+		IFileManager& fileManager = IFileManager::Get();
+		TArray<FString> outFiles;
+		fileManager.FindFiles(outFiles, *resultFilePath, TEXT("*.png"));
+		for (FString file : outFiles)
+		{
+			if (file.Find(resultFileName, ESearchCase::CaseSensitive) == 0)
+			{
+				FString fileFullPath = FPaths::Combine(resultFilePath, file);
+				fileManager.Delete(*fileFullPath);
+			}
+		}
+	}
+
+
+	UTextureRenderTarget2D* widgetRT = NewObject<UTextureRenderTarget2D>(this);
+	bool bIsSRGB = false;
+	EPixelFormat PixelFormat = PF_B8G8R8A8;
+	FIntPoint ScreenshotSize(1440, 900);
+
+	widgetRT->ClearColor = FLinearColor::Transparent;
+	widgetRT->RenderTargetFormat = ETextureRenderTargetFormat::RTF_RGBA8;
+	widgetRT->InitCustomFormat(ScreenshotSize.X, ScreenshotSize.Y, PixelFormat, !bIsSRGB);
+
+	FWidgetRenderer* WidgetRenderer = new FWidgetRenderer(true, false);
+	check(WidgetRenderer);
+	WidgetRenderer->DrawWidget(widgetRT, widget->TakeWidget(), ScreenshotSize, 0.f);
+	FlushRenderingCommands();
+	BeginCleanup(WidgetRenderer);
+
+
+	resultFileName += "_" + title + +"_" + channel + ".png";
+
+	UKismetRenderingLibrary::ExportRenderTarget(this, widgetRT, resultFilePath, resultFileName);
+
+	return FPaths::Combine(resultFilePath, resultFileName);
+}

+ 15 - 0
Plugins/CustomWidget/Source/CustomWidget/Public/CustomWidget.h

@@ -0,0 +1,15 @@
+// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Modules/ModuleManager.h"
+
+class FCustomWidgetModule : public IModuleInterface
+{
+public:
+
+	/** IModuleInterface implementation */
+	virtual void StartupModule() override;
+	virtual void ShutdownModule() override;
+};

+ 22 - 0
Plugins/CustomWidget/Source/CustomWidget/Public/DropSlate.h

@@ -0,0 +1,22 @@
+#pragma once
+#include "CoreMinimal.h"
+#include "SCompoundWidget.h"
+class UBaseBannerMainWidget;
+
+class CUSTOMWIDGET_API SDropWidget : public SCompoundWidget
+{
+public:
+	SLATE_BEGIN_ARGS(SDropWidget) { }
+	SLATE_END_ARGS()
+public:
+	SDropWidget();
+	void Construct(const FArguments& InArgs);
+	virtual void OnDragEnter(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)override;
+	virtual void OnDragLeave(const FDragDropEvent& DragDropEvent)override;
+	virtual FReply OnDragOver(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)override;
+	virtual FReply OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)override;
+public:
+	void AddWidget(TWeakObjectPtr<UBaseBannerMainWidget> widget);
+protected:
+	TWeakObjectPtr<UBaseBannerMainWidget> mWidget;
+};

+ 44 - 0
Plugins/CustomWidget/Source/CustomWidget/Public/DropWidget.h

@@ -0,0 +1,44 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "Blueprint/UserWidget.h"
+#include "DropWidget.generated.h"
+
+UCLASS(Blueprintable, BlueprintType, Abstract, Category = "Banner")
+class CUSTOMWIDGET_API UBaseBannerMainWidget : public UUserWidget
+{
+	GENERATED_BODY()
+public:
+protected:
+	virtual void NativePreConstruct()override;
+	virtual void NativeConstruct()override;
+
+public:
+	bool HasFile()const { return !mDropFileName.IsEmpty(); }
+	void DropFile(FString file);
+
+	UFUNCTION(BlueprintImplementableEvent)
+		void OnMoveImage(FVector2D delta);
+
+	UFUNCTION(BlueprintImplementableEvent)
+		void OnExportImage();
+
+	UFUNCTION(BlueprintCallable)
+		FString RenderWidgetToFile(UUserWidget* widget, FString title, FString channel, bool isExclusive = false);
+
+	UFUNCTION(BlueprintImplementableEvent)
+		void OnDropImage(UTexture2D* texture);
+
+	UFUNCTION(BlueprintImplementableEvent)
+		void OnDropFile(const FString& file);
+
+	
+
+protected:
+
+	virtual TSharedRef<SWidget> RebuildWidget() override;
+
+
+	FString mDropFileName;
+};

+ 25 - 0
Plugins/EasyFileDialog/EasyFileDialog.uplugin

@@ -0,0 +1,25 @@
+{
+	"FileVersion": 3,
+	"Version": 2,
+	"VersionName": "2.0",
+	"FriendlyName": "Easy File Dialog",
+	"Description": "An easy file dialog system",
+	"Category": "File",
+	"CreatedBy": "Firefly Studio",
+	"CreatedByURL": "http://www.fire-fly.studio",
+	"DocsURL": "",
+	"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/bcf64fe2e00c48189d8385a9c39b6e61",
+	"SupportURL": "http://www.fire-fly.studio",
+	"CanContainContent": true,
+	"Installed": true,
+	"Modules": [
+		{
+			"Name": "EasyFileDialog",
+			"Type": "Runtime",
+			"LoadingPhase": "PreLoadingScreen",
+			"WhitelistPlatforms": [
+				"Win64"
+			]
+		}
+	]
+}

BIN
Plugins/EasyFileDialog/Resources/Icon128.png


+ 53 - 0
Plugins/EasyFileDialog/Source/EasyFileDialog/EasyFileDialog.Build.cs

@@ -0,0 +1,53 @@
+// Copyright 2017-2020 Firefly Studio. All Rights Reserved.
+
+using UnrealBuildTool;
+
+public class EasyFileDialog : ModuleRules
+{
+	public EasyFileDialog(ReadOnlyTargetRules Target) : base(Target)
+	{
+		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
+		
+		PublicIncludePaths.AddRange(
+			new string[] {
+				// ... add public include paths required here ...
+			}
+			);
+				
+		
+		PrivateIncludePaths.AddRange(
+			new string[] {
+				// ... add other private include paths required here ...
+			}
+			);
+			
+		
+		PublicDependencyModuleNames.AddRange(
+			new string[]
+			{
+				"Core",
+				// ... add other public dependencies that you statically link with here ...
+			}
+			);
+			
+		
+		PrivateDependencyModuleNames.AddRange(
+			new string[]
+			{
+				"CoreUObject",
+				"Engine",
+				"Slate",
+				"SlateCore",
+				// ... add private dependencies that you statically link with here ...	
+			}
+			);
+		
+		
+		DynamicallyLoadedModuleNames.AddRange(
+			new string[]
+			{
+				// ... add any modules that your module loads dynamically here ...
+			}
+			);
+	}
+}

+ 266 - 0
Plugins/EasyFileDialog/Source/EasyFileDialog/Private/EFDCore.cpp

@@ -0,0 +1,266 @@
+// Copyright 2017-2020 Firefly Studio. All Rights Reserved.
+
+
+#include "EFDCore.h"
+
+#include "shlobj.h" 
+
+#include <Runtime\Core\Public\HAL\FileManager.h>
+#include <Runtime\Core\Public\Misc\Paths.h>
+#include <Runtime\Core\Public\Windows\COMPointer.h>
+
+
+#define MAX_FILETYPES_STR 4096
+#define MAX_FILENAME_STR 65536 // This buffer has to be big enough to contain the names of all the selected files as well as the null characters between them and the null character at the end
+
+bool EFDCore::OpenFileDialogCore(const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypes, uint32 Flags, TArray< FString >& OutFilenames)
+{
+	// Calling the FileDialogShared function using save parameter with false. 
+	int OutFilterIndex=0;
+	return FileDialogShared(false, nullptr, DialogTitle, DefaultPath, DefaultFile, FileTypes, Flags, OutFilenames,OutFilterIndex);
+}
+
+bool EFDCore::SaveFileDialogCore(const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypes, uint32 Flags, TArray< FString >& OutFilenames)
+{
+	// Calling the FileDialogShared function using save parameter. 
+	int OutFilterIndex = 0;
+	return FileDialogShared(true, nullptr, DialogTitle, DefaultPath, DefaultFile, FileTypes, Flags, OutFilenames, OutFilterIndex);
+}
+
+bool EFDCore::OpenFolderDialogCore(const FString& DialogTitle, const FString& DefaultPath, FString& OutFoldername)
+{
+	// Calling the main open folder dialog function.
+	return OpenFolderDialogInner(NULL, DialogTitle, DefaultPath, OutFoldername);
+}
+
+bool EFDCore::FileDialogShared(bool bSave, const void* ParentWindowHandle, const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypes, uint32 Flags, TArray<FString>& OutFilenames, int32& OutFilterIndex)
+{
+#pragma region Windows
+	//FScopedSystemModalMode SystemModalScope;
+#if PLATFORM_WINDOWS
+	WCHAR Filename[MAX_FILENAME_STR];
+	FCString::Strcpy(Filename, MAX_FILENAME_STR, *(DefaultFile.Replace(TEXT("/"), TEXT("\\"))));
+
+	// Convert the forward slashes in the path name to backslashes, otherwise it'll be ignored as invalid and use whatever is cached in the registry
+	WCHAR Pathname[MAX_FILENAME_STR];
+	FCString::Strcpy(Pathname, MAX_FILENAME_STR, *(FPaths::ConvertRelativePathToFull(DefaultPath).Replace(TEXT("/"), TEXT("\\"))));
+
+	// Convert the "|" delimited list of filetypes to NULL delimited then add a second NULL character to indicate the end of the list
+	WCHAR FileTypeStr[MAX_FILETYPES_STR];
+	WCHAR* FileTypesPtr = NULL;
+	const int32 FileTypesLen = FileTypes.Len();
+
+	// Nicely formatted file types for lookup later and suitable to append to filenames without extensions
+	TArray<FString> CleanExtensionList;
+
+	// The strings must be in pairs for windows.
+	// It is formatted as follows: Pair1String1|Pair1String2|Pair2String1|Pair2String2
+	// where the second string in the pair is the extension.  To get the clean extensions we only care about the second string in the pair
+	TArray<FString> UnformattedExtensions;
+	FileTypes.ParseIntoArray(UnformattedExtensions, TEXT("|"), true);
+	for (int32 ExtensionIndex = 1; ExtensionIndex < UnformattedExtensions.Num(); ExtensionIndex += 2)
+	{
+		const FString& Extension = UnformattedExtensions[ExtensionIndex];
+		// Assume the user typed in an extension or doesnt want one when using the *.* extension. We can't determine what extension they wan't in that case
+		if (Extension != TEXT("*.*"))
+		{
+			// Add to the clean extension list, first removing the * wildcard from the extension
+			int32 WildCardIndex = Extension.Find(TEXT("*"));
+			CleanExtensionList.Add(WildCardIndex != INDEX_NONE ? Extension.RightChop(WildCardIndex + 1) : Extension);
+		}
+	}
+
+	if (FileTypesLen > 0 && FileTypesLen - 1 < MAX_FILETYPES_STR)
+	{
+		FileTypesPtr = FileTypeStr;
+		FCString::Strcpy(FileTypeStr, MAX_FILETYPES_STR, *FileTypes);
+
+		TCHAR* Pos = FileTypeStr;
+		while (Pos[0] != 0)
+		{
+			if (Pos[0] == '|')
+			{
+				Pos[0] = 0;
+			}
+
+			Pos++;
+		}
+
+		// Add two trailing NULL characters to indicate the end of the list
+		FileTypeStr[FileTypesLen] = 0;
+		FileTypeStr[FileTypesLen + 1] = 0;
+	}
+
+	OPENFILENAME ofn;
+	FMemory::Memzero(&ofn, sizeof(OPENFILENAME));
+
+	ofn.lStructSize = sizeof(OPENFILENAME);
+	ofn.hwndOwner = (HWND)ParentWindowHandle;
+	ofn.lpstrFilter = FileTypesPtr;
+	ofn.nFilterIndex = 1;
+	ofn.lpstrFile = Filename;
+	ofn.nMaxFile = MAX_FILENAME_STR;
+	ofn.lpstrInitialDir = Pathname;
+	ofn.lpstrTitle = *DialogTitle;
+	if (FileTypesLen > 0)
+	{
+		ofn.lpstrDefExt = &FileTypeStr[0];
+	}
+
+	ofn.Flags = OFN_HIDEREADONLY | OFN_ENABLESIZING | OFN_EXPLORER;
+
+	if (bSave)
+	{
+		ofn.Flags |= OFN_CREATEPROMPT | OFN_OVERWRITEPROMPT | OFN_NOVALIDATE;
+	}
+	else
+	{
+		ofn.Flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
+	}
+
+	if (Flags & EEasyFileDialogFlags::Multiple)
+	{
+		ofn.Flags |= OFN_ALLOWMULTISELECT;
+	}
+
+	bool bSuccess;
+	if (bSave)
+	{
+		bSuccess = !!::GetSaveFileName(&ofn);
+	}
+	else
+	{
+		bSuccess = !!::GetOpenFileName(&ofn);
+	}
+
+	if (bSuccess)
+	{
+		// GetOpenFileName/GetSaveFileName changes the CWD on success. Change it back immediately.
+		//FPlatformProcess::SetCurrentWorkingDirectoryToBaseDir();
+
+		if (Flags & EEasyFileDialogFlags::Multiple)
+		{
+			// When selecting multiple files, the returned string is a NULL delimited list
+			// where the first element is the directory and all remaining elements are filenames.
+			// There is an extra NULL character to indicate the end of the list.
+			FString DirectoryOrSingleFileName = FString(Filename);
+			TCHAR* Pos = Filename + DirectoryOrSingleFileName.Len() + 1;
+
+			if (Pos[0] == 0)
+			{
+				// One item selected. There was an extra trailing NULL character.
+				OutFilenames.Add(DirectoryOrSingleFileName);
+			}
+			else
+			{
+				// Multiple items selected. Keep adding filenames until two NULL characters.
+				FString SelectedFile;
+				do
+				{
+					SelectedFile = FString(Pos);
+					new(OutFilenames) FString(DirectoryOrSingleFileName / SelectedFile);
+					Pos += SelectedFile.Len() + 1;
+				} while (Pos[0] != 0);
+			}
+		}
+		else
+		{
+			new(OutFilenames) FString(Filename);
+		}
+
+		// The index of the filter in OPENFILENAME starts at 1.
+		OutFilterIndex = ofn.nFilterIndex - 1;
+
+		// Get the extension to add to the filename (if one doesnt already exist)
+		FString Extension = CleanExtensionList.IsValidIndex(OutFilterIndex) ? CleanExtensionList[OutFilterIndex] : TEXT("");
+
+		// Make sure all filenames gathered have their paths normalized and proper extensions added
+		for (auto OutFilenameIt = OutFilenames.CreateIterator(); OutFilenameIt; ++OutFilenameIt)
+		{
+			FString& OutFilename = *OutFilenameIt;
+
+			OutFilename = IFileManager::Get().ConvertToRelativePath(*OutFilename);
+
+			if (FPaths::GetExtension(OutFilename).IsEmpty() && !Extension.IsEmpty())
+			{
+				// filename does not have an extension. Add an extension based on the filter that the user chose in the dialog
+				OutFilename += Extension;
+			}
+
+			FPaths::NormalizeFilename(OutFilename);
+		}
+	}
+	else
+	{
+		uint32 Error = ::CommDlgExtendedError();
+		if (Error != ERROR_SUCCESS)
+		{
+			//UE_LOG(LogDesktopPlatform, Warning, TEXT("Error reading results of file dialog. Error: 0x%04X"), Error);
+		}
+	}
+
+	return bSuccess;
+#endif
+#pragma endregion
+
+#pragma region LINUX
+#if PLATFORM_LINUX
+	return false;
+#endif
+#pragma endregion
+	return false;
+}
+
+bool EFDCore:: OpenFolderDialogInner(const void* ParentWindowHandle, const FString& DialogTitle, const FString& DefaultPath, FString& OutFolderName)
+{
+	//FScopedSystemModalMode SystemModalScope;
+
+	bool bSuccess = false;
+
+	TComPtr<IFileOpenDialog> FileDialog;
+	if (SUCCEEDED(::CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&FileDialog))))
+	{
+		// Set this up as a folder picker
+		{
+			DWORD dwFlags = 0;
+			FileDialog->GetOptions(&dwFlags);
+			FileDialog->SetOptions(dwFlags | FOS_PICKFOLDERS);
+		}
+
+		// Set up common settings
+		FileDialog->SetTitle(*DialogTitle);
+		if (!DefaultPath.IsEmpty())
+		{
+			// SHCreateItemFromParsingName requires the given path be absolute and use \ rather than / as our normalized paths do
+			FString DefaultWindowsPath = FPaths::ConvertRelativePathToFull(DefaultPath);
+			DefaultWindowsPath.ReplaceInline(TEXT("/"), TEXT("\\"), ESearchCase::CaseSensitive);
+
+			TComPtr<IShellItem> DefaultPathItem;
+			if (SUCCEEDED(::SHCreateItemFromParsingName(*DefaultWindowsPath, nullptr, IID_PPV_ARGS(&DefaultPathItem))))
+			{
+				FileDialog->SetFolder(DefaultPathItem);
+			}
+		}
+
+		// Show the picker
+		if (SUCCEEDED(FileDialog->Show((HWND)ParentWindowHandle)))
+		{
+			TComPtr<IShellItem> Result;
+			if (SUCCEEDED(FileDialog->GetResult(&Result)))
+			{
+				PWSTR pFilePath = nullptr;
+				if (SUCCEEDED(Result->GetDisplayName(SIGDN_FILESYSPATH, &pFilePath)))
+				{
+					bSuccess = true;
+
+					OutFolderName = pFilePath;
+					FPaths::NormalizeDirectoryName(OutFolderName);
+
+					::CoTaskMemFree(pFilePath);
+				}
+			}
+		}
+	}
+
+	return bSuccess;
+}

+ 40 - 0
Plugins/EasyFileDialog/Source/EasyFileDialog/Private/EFDFunctionLibrary.cpp

@@ -0,0 +1,40 @@
+// Copyright 2017-2020 Firefly Studio. All Rights Reserved.
+
+
+#include "EFDFunctionLibrary.h"
+
+
+bool UEFDFunctionLibrary::OpenFileDialog(const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypes, EEasyFileDialogFlags Flags, TArray< FString >& OutFilenames)
+{
+    // Calling the core class function for open file dialog
+    return  EFDCore::OpenFileDialogCore(DialogTitle, DefaultPath, DefaultFile, FileTypes, Flags, OutFilenames);
+}
+
+
+
+bool UEFDFunctionLibrary::SaveFileDialog(const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypeDescription, const FString& FileType, EEasyFileDialogFlags Flags, TArray<FString>& OutFilenames)
+{
+    // Saving the file type and file type description to append and Removing . if user provides any
+    FString TempFileType = FileType.Replace(TEXT("."), TEXT(""));
+  
+    FString TempFileTypeDescription = FileTypeDescription;  
+
+    // Setting description if user did not provide any
+    if (TempFileTypeDescription.IsEmpty())
+    {
+        TempFileTypeDescription = TempFileType.ToUpper();
+    }
+
+    // Creating final file types by appending FileTypeDescription and FileType
+    const FString FinalFileType = TempFileTypeDescription.Append("|").Append(".").Append(TempFileType);
+
+    // Calling the core class function for save file dialog
+    return EFDCore::SaveFileDialogCore(DialogTitle, DefaultPath, DefaultFile, FinalFileType, Flags, OutFilenames);
+}
+
+
+bool UEFDFunctionLibrary::OpenFolderDialog(const FString& DialogTitle, const FString& DefaultPath, FString& OutFolderName)
+{
+    // Calling the core class function for open folder dialog
+    return EFDCore::OpenFolderDialogCore(DialogTitle, DefaultPath, OutFolderName);
+}

+ 22 - 0
Plugins/EasyFileDialog/Source/EasyFileDialog/Private/EasyFileDialog.cpp

@@ -0,0 +1,22 @@
+// Copyright 2017-2020 Firefly Studio. All Rights Reserved.
+
+#include "EasyFileDialog.h"
+
+#define LOCTEXT_NAMESPACE "FEasyFileDialogModule"
+
+void FEasyFileDialogModule::StartupModule()
+{
+	// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
+	
+}
+
+void FEasyFileDialogModule::ShutdownModule()
+{
+	// This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
+	// we call this function before unloading the module.
+	
+}
+
+#undef LOCTEXT_NAMESPACE
+	
+IMPLEMENT_MODULE(FEasyFileDialogModule, EasyFileDialog)

+ 16 - 0
Plugins/EasyFileDialog/Source/EasyFileDialog/Private/EasyFileDialogBPLibrary.cpp

@@ -0,0 +1,16 @@
+// Copyright 2017-2020 Firefly Studio. All Rights Reserved.
+
+#include "EasyFileDialogBPLibrary.h"
+#include "EasyFileDialog.h"
+
+UEasyFileDialogBPLibrary::UEasyFileDialogBPLibrary(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer)
+{
+
+}
+
+float UEasyFileDialogBPLibrary::EasyFileDialogSampleFunction(float Param)
+{
+	return -1;
+}
+

+ 40 - 0
Plugins/EasyFileDialog/Source/EasyFileDialog/Public/EFDCore.h

@@ -0,0 +1,40 @@
+// Copyright 2017-2020 Firefly Studio. All Rights Reserved.
+
+#pragma once
+#pragma warning(disable : 4668)
+
+// Flag Enum for saving multiple or single file's
+UENUM(BlueprintType)
+enum EEasyFileDialogFlags
+{
+	Single = 0x00, // No flags
+	Multiple = 0x01  // Allow multiple file selections
+};
+
+#include "CoreMinimal.h"
+
+/**
+ * 
+ */
+class EASYFILEDIALOG_API EFDCore
+{
+public:
+
+	// Open file dialog core, called from the blueprint function library
+	static bool OpenFileDialogCore(const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypes, uint32 Flags, TArray< FString >& OutFilenames);
+
+	// Save file dialog core, called from the blueprint function library
+	static bool SaveFileDialogCore(const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypes, uint32 Flags, TArray< FString >& OutFilenames);
+
+	// Open folder dialog core, called from the blueprint function library
+	static bool OpenFolderDialogCore(const FString& DialogTitle, const FString& DefaultPath, FString& OutFolderName);
+
+	
+
+private:
+	// Both open file dialog and save file dialog using this same function with different save parameter.
+	static bool FileDialogShared(bool bSave, const void* ParentWindowHandle, const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypes, uint32 Flags, TArray<FString>& OutFilenames, int32& OutFilterIndex);
+	
+	// The main Open folder dialog functionalities
+	static bool OpenFolderDialogInner(const void* ParentWindowHandle, const FString& DialogTitle, const FString& DefaultPath, FString& OutFolderName);
+};

+ 30 - 0
Plugins/EasyFileDialog/Source/EasyFileDialog/Public/EFDFunctionLibrary.h

@@ -0,0 +1,30 @@
+// Copyright 2017-2020 Firefly Studio. All Rights Reserved.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "EFDCore.h"
+#include "EFDFunctionLibrary.generated.h"
+
+/**
+ * 
+ */
+UCLASS()
+class EASYFILEDIALOG_API UEFDFunctionLibrary : public UBlueprintFunctionLibrary
+{
+	GENERATED_BODY()
+
+    // Open File Dialog
+    UFUNCTION(BlueprintCallable, Category = "EasyFileDialog")
+        static bool OpenFileDialog(const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypes, EEasyFileDialogFlags Flags, TArray< FString >& OutFilenames);
+
+    // Save File Dialog
+    UFUNCTION(BlueprintCallable, Category = "EasyFileDialog")
+        static bool SaveFileDialog(const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypeDescription, const FString& FileType, EEasyFileDialogFlags Flags, TArray< FString >& OutFilenames);
+
+    // Open Folder Dialog
+    UFUNCTION(BlueprintCallable, Category = "EasyFileDialog")
+        static bool OpenFolderDialog(const FString& DialogTitle, const FString& DefaultPath, FString& OutFolderName);
+
+};

+ 14 - 0
Plugins/EasyFileDialog/Source/EasyFileDialog/Public/EasyFileDialog.h

@@ -0,0 +1,14 @@
+// Copyright 2017-2020 Firefly Studio. All Rights Reserved.
+
+#pragma once
+
+#include "Modules/ModuleManager.h"
+
+class FEasyFileDialogModule : public IModuleInterface
+{
+public:
+
+	/** IModuleInterface implementation */
+	virtual void StartupModule() override;
+	virtual void ShutdownModule() override;
+};

+ 32 - 0
Plugins/EasyFileDialog/Source/EasyFileDialog/Public/EasyFileDialogBPLibrary.h

@@ -0,0 +1,32 @@
+// Copyright 2017-2020 Firefly Studio. All Rights Reserved.
+
+#pragma once
+
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "EasyFileDialogBPLibrary.generated.h"
+
+/* 
+*	Function library class.
+*	Each function in it is expected to be static and represents blueprint node that can be called in any blueprint.
+*
+*	When declaring function you can define metadata for the node. Key function specifiers will be BlueprintPure and BlueprintCallable.
+*	BlueprintPure - means the function does not affect the owning object in any way and thus creates a node without Exec pins.
+*	BlueprintCallable - makes a function which can be executed in Blueprints - Thus it has Exec pins.
+*	DisplayName - full name of the node, shown when you mouse over the node and in the blueprint drop down menu.
+*				Its lets you name the node using characters not allowed in C++ function names.
+*	CompactNodeTitle - the word(s) that appear on the node.
+*	Keywords -	the list of keywords that helps you to find node when you search for it using Blueprint drop-down menu. 
+*				Good example is "Print String" node which you can find also by using keyword "log".
+*	Category -	the category your node will be under in the Blueprint drop-down menu.
+*
+*	For more info on custom blueprint nodes visit documentation:
+*	https://wiki.unrealengine.com/Custom_Blueprint_Node_Creation
+*/
+UCLASS()
+class UEasyFileDialogBPLibrary : public UBlueprintFunctionLibrary
+{
+	GENERATED_UCLASS_BODY()
+
+	UFUNCTION(BlueprintCallable, meta = (DisplayName = "Execute Sample function", Keywords = "EasyFileDialog sample test testing"), Category = "EasyFileDialogTesting")
+	static float EasyFileDialogSampleFunction(float Param);
+};

+ 15 - 0
Plugins/HTTP_UPLOAD/.gitignore

@@ -0,0 +1,15 @@
+Binaries
+DerivedDataCache
+Intermediate
+Saved
+.vscode
+.vs
+*.VC.db
+*.opensdf
+*.opendb
+*.sdf
+*.sln
+*.suo
+*.xcodeproj
+*.xcworkspace
+PA

+ 23 - 0
Plugins/HTTP_UPLOAD/HTTP_UPLOAD.uplugin

@@ -0,0 +1,23 @@
+{
+	"FileVersion": 3,
+	"Version": 1,
+	"VersionName": "1.0",
+	"FriendlyName": "HTTP_UPLOAD",
+	"Description": "",
+	"Category": "Other",
+	"CreatedBy": "ECG",
+	"CreatedByURL": "",
+	"DocsURL": "",
+	"MarketplaceURL": "",
+	"SupportURL": "",
+	"CanContainContent": true,
+	"IsBetaVersion": false,
+	"Installed": false,
+	"Modules": [
+		{
+			"Name": "HTTP_UPLOAD",
+			"Type": "Runtime",
+			"LoadingPhase": "PreLoadingScreen"
+		}
+	]
+}

+ 17 - 0
Plugins/HTTP_UPLOAD/ReadMe.md

@@ -0,0 +1,17 @@
+# UE4 Easy Http Blueprint Functions Plugins
+
+#### Suport  Type
+
+| Type                              | method | Type         |
+| --------------------------------- | ------ | ------------ |
+| multipart/form-data               | Post   | File         |
+| application/json                  | Post   | Json         |
+| application/x-www-form-urlencoded | Post   | ParameterKey |
+| URL                               | Get    | String       |
+| URL+ParameterKey                  | Get    | ParameterKey |
+
+​                                                                       
+
+You can use this to get a GET request
+
+![HttpGet](https://raw.githubusercontent.com/ecg82465/TESTPlugins/master/Image/HttpGet.jpg)

BIN
Plugins/HTTP_UPLOAD/Resources/Icon128.png


+ 54 - 0
Plugins/HTTP_UPLOAD/Source/HTTP_UPLOAD/HTTP_UPLOAD.Build.cs

@@ -0,0 +1,54 @@
+// Some copyright should be here...
+
+using UnrealBuildTool;
+
+public class HTTP_UPLOAD : ModuleRules
+{
+	public HTTP_UPLOAD(ReadOnlyTargetRules Target) : base(Target)
+	{
+		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
+		
+		PublicIncludePaths.AddRange(
+			new string[] {
+				// ... add public include paths required here ...
+			}
+			);
+				
+		
+		PrivateIncludePaths.AddRange(
+			new string[] {
+				// ... add other private include paths required here ...
+			}
+			);
+			
+		
+		PublicDependencyModuleNames.AddRange(
+			new string[]
+			{
+				"Core",
+				// ... add other public dependencies that you statically link with here ...
+			}
+			);
+			
+		
+		PrivateDependencyModuleNames.AddRange(
+			new string[]
+			{
+				"CoreUObject",
+				"Engine",
+				"Slate",
+				"SlateCore",
+                "Http"
+				// ... add private dependencies that you statically link with here ...	
+			}
+			);
+		
+		
+		DynamicallyLoadedModuleNames.AddRange(
+			new string[]
+			{
+				// ... add any modules that your module loads dynamically here ...
+			}
+			);
+	}
+}

+ 22 - 0
Plugins/HTTP_UPLOAD/Source/HTTP_UPLOAD/Private/HTTP_UPLOAD.cpp

@@ -0,0 +1,22 @@
+// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
+
+#include "HTTP_UPLOAD.h"
+
+#define LOCTEXT_NAMESPACE "FHTTP_UPLOADModule"
+
+void FHTTP_UPLOADModule::StartupModule()
+{
+	// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
+	
+}
+
+void FHTTP_UPLOADModule::ShutdownModule()
+{
+	// This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
+	// we call this function before unloading the module.
+	
+}
+
+#undef LOCTEXT_NAMESPACE
+	
+IMPLEMENT_MODULE(FHTTP_UPLOADModule, HTTP_UPLOAD)

+ 484 - 0
Plugins/HTTP_UPLOAD/Source/HTTP_UPLOAD/Private/HTTP_UPLOADBPLibrary.cpp

@@ -0,0 +1,484 @@
+// Copyright ECG. All Rights Reserved.
+
+#include "HTTP_UPLOADBPLibrary.h"
+#include "HTTP_UPLOAD.h"
+#include "Http.h"
+#include "Runtime/Core/Public/Misc/FileHelper.h"
+
+UHTTP_UPLOADBPLibrary::UHTTP_UPLOADBPLibrary(const FObjectInitializer& ObjectInitializer)
+	: Super(ObjectInitializer)
+{
+
+}
+
+float UHTTP_UPLOADBPLibrary::HTTP_UPLOADSampleFunction(float Param)
+{
+	return -1;
+}
+
+
+URequerstObj* UHTTP_UPLOADBPLibrary::UPloadFile(FString ID, FString URL,FString FileName, FString FilePath, TMap<FString, FString> Keys)
+{
+
+	// the local image file
+	//FString UpFilePath = "/MyPath/MyFile.jpg";
+
+	URequerstObj* Rbaby = NewObject<URequerstObj>();
+
+	Rbaby->ID = ID;
+
+	// the AU
+	TArray<uint8> UpFileRawData;
+	if (!FFileHelper::LoadFileToArray(UpFileRawData, *FilePath))
+	{
+		UE_LOG(LogTemp, Warning, TEXT("Can'tnot Load TheFile Please Check the Path"));
+		return Rbaby;
+	}
+	 
+	//FFileHelper::SaveArrayToFile(UpFileRawData,TEXT("E:\\TVMGit\\VideoEditor\\ttt.txt"));
+	
+	// the request 
+	//+ FString::FromInt(FDateTime::Now().GetTicks())
+	FString Boundary = "25b65ef91754970cc500eac5" + FString::FromInt(FDateTime::Now().GetTicks());
+
+	TSharedRef<IHttpRequest> FileUploadRequest = (&FHttpModule::Get())->CreateRequest();
+
+
+	FileUploadRequest->OnProcessRequestComplete().BindUObject(Rbaby, &URequerstObj::GetRQ);
+	FileUploadRequest->OnRequestProgress().BindUObject(Rbaby,&URequerstObj::HttpRequestProgress);
+	FileUploadRequest->SetURL(URL);
+	FileUploadRequest->SetVerb("POST");
+	FileUploadRequest->SetHeader("Content-Type", "multipart/form-data; boundary="+ Boundary);
+	//FileUploadRequest->SetHeader("Accept-Encoding", "gzip");
+	//FileUploadRequest->SetHeader("Accept", "");
+
+
+	//UE_LOG(LogTemp, Warning, TEXT("%s"), *FileUploadRequest->GetContentType());
+
+	TArray<uint8> BodyData;
+	FString KeyString;
+
+
+	TArray<FString> KeyNames;
+	Keys.GenerateKeyArray(KeyNames);
+
+
+	//TArray<FString> KeysName = Keys.Num();
+	if (Keys.Num())
+	{
+		for (int i = 0; i < Keys.Num(); i++)
+		{
+
+			KeyString.Append("\r\n--" + Boundary+"\r\n");
+			KeyString.Append("Content-Disposition: form-data; name=\"" + KeyNames[i] + "\"\r\n\r\n");
+			KeyString.Append(*Keys.Find(KeyNames[i]));
+
+		}
+	}
+
+	
+
+	FString FileString;
+	TArray<uint8> KeysBytes;
+	TArray<uint8> EndBytes;
+	TArray<uint8> FileBytes;
+
+	FileString.Append("\r\n--" + Boundary + "\r\n");
+
+	FileString.Append("Content-Disposition: form-data; name=\"file\" ; filename=\""+FileName+"\"\r\n");
+	FileString.Append("Content-Type: application/octet-stream\r\n\r\n");
+
+
+	KeysBytes = FstringToBytes(KeyString);
+	FileBytes = FstringToBytes(FileString);
+	FileBytes.Append(UpFileRawData);
+
+	EndBytes = FstringToBytes("\r\n--" + Boundary + "--");
+
+	BodyData.Append(KeysBytes);
+	BodyData.Append(FileBytes);
+	BodyData.Append(EndBytes);
+
+
+	//FString AllString = BytesToString(BodyData.GetData(),BodyData.Num());
+	UE_LOG(LogTemp, Warning, TEXT("%s"),*KeyString);
+
+
+	// content
+	//FileUploadRequest->SetContent(UpFileRawData);
+	 //useless ? cf doc SetHeader(): "Content-Length is the only header set for you"
+
+	// request processing
+	//BodyData = FstringToBytes(KeyString);
+
+	FileUploadRequest->SetContent(BodyData);
+
+
+
+
+	FileUploadRequest->SetHeader("Content-Length", FString::FromInt(BodyData.Num()));
+
+	Rbaby->ContentLength = BodyData.Num();
+	Rbaby->RequerstType = "post";
+
+	//TArray<uint8>PBytes = FileUploadRequest->GetContent();
+	TArray<FString>heads = FileUploadRequest->GetAllHeaders();
+
+	for (int i=0;i< heads.Num();i++)
+	{
+		UE_LOG(LogTemp, Warning, TEXT("Head :%s"), *heads[i]);
+	}
+
+	//UE_LOG(LogTemp, Warning, TEXT("Ceeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb : %s"),*(KeyString+FileString));
+
+	//UE_LOG(LogTemp, Warning, TEXT("%s"), *BytesToString(PBytes.GetData(), PBytes.Num()));
+
+	if (FileUploadRequest->ProcessRequest())
+	{
+		return Rbaby;
+	}
+	
+	FileUploadRequest->OnProcessRequestComplete().Unbind();
+
+
+	//UE_LOG(LogTemp, Warning, TEXT("Ceeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb : %s"));
+
+
+	return Rbaby;
+	
+}
+
+URequerstObj* UHTTP_UPLOADBPLibrary::UPloadFiles(FString URL, const TMap<FString, FString> Files, TMap<FString, FString> Keys)
+{
+
+	URequerstObj* Rbaby = NewObject<URequerstObj>();
+
+	TSharedRef<IHttpRequest> HttpRequest = FHttpModule::Get().CreateRequest();
+	HttpRequest->SetURL(URL);
+
+	// Request header
+	FString Boundary = "---------------------------" + FString::FromInt(FDateTime::Now().GetTicks());
+	HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("multipart/form-data;boundary =" + Boundary));
+	HttpRequest->SetVerb(TEXT("POST"));
+	
+
+
+	//files 
+	TArray<FString> FilesNames;
+	Files.GenerateKeyArray(FilesNames);
+
+	TArray<uint8> RequestContent;
+	for (const FString& FileName : FilesNames)
+	{
+		TArray<uint8> FileContent;
+		FString FilePath = *Files.Find(FileName);
+
+		if (FFileHelper::LoadFileToArray(FileContent, *FilePath))
+		{
+			FString BeginBoundry = "\r\n--" + Boundary + "\r\n";
+
+			RequestContent.Append(FstringToBytes(BeginBoundry));
+
+
+
+			FString FileHeader = "Content-Disposition: form-data;";//文件头
+			FileHeader.Append("name=\"" + FileName + "\";");
+			FileHeader.Append("filename=\"" + FPaths::GetCleanFilename(FilePath) + "\"");
+			FileHeader.Append("\r\nContent-Type: \r\n\r\n");
+			RequestContent.Append(FstringToBytes(FileHeader));
+
+			RequestContent.Append(FileContent);
+		}
+	}
+
+	//keys parameter
+	FString KeyString;
+	TArray<FString> KeyNames;
+	Keys.GenerateKeyArray(KeyNames);
+	if (Keys.Num())
+	{
+		for (int i = 0; i < Keys.Num(); i++)
+		{
+			KeyString.Append("\r\n--" + Boundary + "\r\n");
+			KeyString.Append("Content-Disposition: form-data; name=\"" + KeyNames[i] + "\"\r\n\r\n");
+			KeyString.Append(*Keys.Find(KeyNames[i])+"\r\n");
+
+		}
+	}
+
+	//TArray<uint8> KeyBytes = FstringToBytes(KeyString);
+	RequestContent.Append(FstringToBytes(KeyString));
+
+	FString EndBoundary = "\r\n--" + Boundary + "--\r\n";
+	RequestContent.Append(FstringToBytes(EndBoundary));
+
+	HttpRequest->SetContent(RequestContent);
+	
+
+	HttpRequest->OnProcessRequestComplete().BindUObject(Rbaby, &URequerstObj::GetRQ);
+	HttpRequest->ProcessRequest();
+
+
+	return Rbaby;
+
+}
+
+TArray<uint8> UHTTP_UPLOADBPLibrary::FstringToBytes(FString JsonStr)
+{
+	//FString JsonStr;
+	TArray<uint8> content;
+
+	FTCHARToUTF8 EchoStrUtf8(*JsonStr);
+
+	int32 DestLen = EchoStrUtf8.Length();
+
+
+	content.SetNum(DestLen);
+	memcpy(content.GetData(), TCHAR_TO_UTF8(*JsonStr), DestLen);
+
+
+
+	return content;
+}
+
+FString UHTTP_UPLOADBPLibrary::BytesToFstring(TArray<uint8> content)
+{
+
+	//const std::string cstr(reinterpret_cast<const char*>(content.GetData()), content.Num());
+
+	FString frameAsFString = "11";
+	//UE_LOG(LogTemp, Warning, TEXT("%s"), *frameAsFString);
+
+	return frameAsFString;
+}
+
+
+void URequerstObj:: GetRQ(FHttpRequestPtr request, FHttpResponsePtr response, bool bWasSuccessful)
+{
+	TArray<uint8> byteArray;
+
+	if (response)
+	{
+		FString content1, content2 = "";
+		int code;
+		response->GetHeader(content1);
+		
+		content2 = response->GetContentAsString();
+		code = response->GetResponseCode();
+		
+		request->GetResponse().Get();
+
+		
+
+		//UE_LOG(LogTemp, Warning, TEXT("Ceeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb : %s"), *content2);
+		//UE_LOG(LogTemp, Warning, TEXT("ccccccccccccccccccccccccccbbbbbbbbbbbbb : %d"), code);
+
+		content2.Append(content1);
+		byteArray = response->GetContent();
+
+		OnHttpCompletedReturn.Broadcast(bWasSuccessful, content2, byteArray,ID);
+
+		return;
+	};
+
+	//response->GetAllHeaders();
+	if (bWasSuccessful)
+	{
+		OnHttpCompletedReturn.Broadcast(bWasSuccessful, "OK_LocalSay", byteArray,ID);
+	}
+	else
+	{
+		OnHttpCompletedReturn.Broadcast(bWasSuccessful, "Failed", byteArray,ID);
+	}
+
+}
+
+void URequerstObj::HttpRequestProgress(FHttpRequestPtr HttpRequest, int32 BytesSend, int32 InBytesReceived)
+{
+	int32 ReceivedSize = InBytesReceived;
+	//int32 TotalSize = HttpRequest->GetContentLength(); // Incorrect, this is the size of the payload SENT in the REQUEST
+
+	FHttpResponsePtr HttpResponse = HttpRequest->GetResponse();
+
+	if (HttpResponse.IsValid())
+	{
+		if (RequerstType =="post")
+		{
+			float PercentDone = ((float)BytesSend / (float)ContentLength) * 100;
+
+
+			OnHttpProgressReturn.Broadcast(BytesSend,ContentLength,PercentDone, ID);
+
+			//UE_LOG(LogTemp, Warning, TEXT("Bytes Send = %d, PercentDone = %f ,ContentLength = %d"), BytesSend, PercentDone, ContentLength);
+		}
+		else
+		{
+			int32 TotalSize = HttpResponse->GetContentLength(); // Correct, this is the size of the payload RECEIVED in the RESPONSE
+
+			float PercentDone = ((float)ReceivedSize / (float)TotalSize) * 100;
+
+			OnHttpProgressReturn.Broadcast(ReceivedSize, TotalSize, PercentDone, ID);
+			
+			//UE_LOG(LogTemp, Warning, TEXT("Received Size = %d, TotalSize = %d, PercentDone = %.2f%"), ReceivedSize, TotalSize, PercentDone);
+
+		}
+		
+	}
+}
+
+ URequerstObj* UHTTP_UPLOADBPLibrary::PostJson(FString URL, FString JsonString, FString ID)
+{
+	 URequerstObj* Rbaby = NewObject<URequerstObj>();
+
+	 Rbaby->ID = ID;
+
+	 TSharedRef<IHttpRequest> HttpRequest = FHttpModule::Get().CreateRequest();
+
+	 HttpRequest->OnProcessRequestComplete().BindUObject(Rbaby, &URequerstObj::GetRQ);
+	 HttpRequest->SetVerb("POST");
+	 HttpRequest->SetHeader("Content-Type", "application/json");
+	 //HttpRequest->SetHeader("Content-Type", "charset=utf-8" );
+	 
+
+
+	 HttpRequest->SetURL(URL);
+
+	 //TArray<uint8> Bytes = FstringToBytes(JsonString);
+
+	
+
+	 HttpRequest->SetContentAsString(JsonString);
+
+	 HttpRequest->ProcessRequest();
+
+
+
+	 return Rbaby;
+}
+
+
+ URequerstObj* UHTTP_UPLOADBPLibrary::Get(FString URL,FString ID)
+ {
+	 URequerstObj* Rbaby = NewObject<URequerstObj>();
+
+	 Rbaby->ID = ID;
+
+	 TSharedRef<IHttpRequest> HttpRequest = FHttpModule::Get().CreateRequest();
+
+	 HttpRequest->OnProcessRequestComplete().BindUObject(Rbaby, &URequerstObj::GetRQ);
+
+	 HttpRequest->OnRequestProgress().BindUObject(Rbaby, &URequerstObj::HttpRequestProgress);
+
+	 HttpRequest->SetURL(URL);
+	 
+	 HttpRequest->SetVerb("GET");
+	 
+	 HttpRequest->ProcessRequest();
+
+
+	 return Rbaby;
+ }
+
+
+
+ URequerstObj* UHTTP_UPLOADBPLibrary::GetParameter(FString URL, TMap<FString, FString> Parameters, FString ID)
+ {
+	 URequerstObj* Rbaby = NewObject<URequerstObj>();
+
+	 Rbaby->ID = ID;
+
+	 TSharedRef<IHttpRequest> HttpRequest = FHttpModule::Get().CreateRequest();
+
+
+
+	 HttpRequest->OnProcessRequestComplete().BindUObject(Rbaby, &URequerstObj::GetRQ);
+
+
+	 FString AllURL;
+
+	 AllURL = URL +"?";
+
+
+	 
+
+	 HttpRequest->SetVerb("GET");
+
+
+	 TArray<FString>Keys;
+
+	 Parameters.GetKeys(Keys);
+
+
+	 for (size_t i = 0; i < Keys.Num(); i++)
+	 {
+		 if (i == 0)
+		 {
+			 AllURL += Keys[i] + "=" + *Parameters.Find(Keys[i]);
+		 }
+		 else
+		 {
+			 AllURL += "&" + Keys[i] + "=" + *Parameters.Find(Keys[i]);
+		 }
+
+
+	 }
+	 
+	 HttpRequest->SetURL(AllURL);
+	 HttpRequest->ProcessRequest();
+
+
+	 return Rbaby;
+
+ }
+
+
+ URequerstObj* UHTTP_UPLOADBPLibrary::PostParameter(FString URL, TMap<FString, FString> Parameters, FString ID)
+ {
+	 URequerstObj* Rbaby = NewObject<URequerstObj>();
+
+	 Rbaby->ID = ID;
+
+	 TSharedRef<IHttpRequest> HttpRequest = FHttpModule::Get().CreateRequest();
+
+	 HttpRequest->OnProcessRequestComplete().BindUObject(Rbaby, &URequerstObj::GetRQ);
+	 HttpRequest->SetVerb("POST");
+	 HttpRequest->SetHeader("Content-Type", "application/x-www-form-urlencoded");
+	 //HttpRequest->SetHeader("Content-Type", "charset=utf-8" );
+
+
+	 HttpRequest->SetURL(URL);
+
+	 //TArray<uint8> Bytes = FstringToBytes(JsonString);
+
+	 //HttpRequest->SetContent();
+
+	 FString ContentString;
+
+	 TArray<FString>Keys;
+
+	 Parameters.GetKeys(Keys);
+
+
+	 for (size_t i = 0; i < Keys.Num(); i++)
+	 {
+		 if (i==0)
+		 {
+			 ContentString += Keys[i] + "=" + *Parameters.Find(Keys[i]);
+		 }
+		 else
+		 {
+			 ContentString += "&"+ Keys[i] + "=" + *Parameters.Find(Keys[i]);
+		 }
+		 
+
+	 }
+
+
+	 HttpRequest->SetContentAsString(ContentString);
+
+	 HttpRequest->ProcessRequest();
+
+
+
+	 return Rbaby;
+ }

+ 16 - 0
Plugins/HTTP_UPLOAD/Source/HTTP_UPLOAD/Public/HTTP_UPLOAD.h

@@ -0,0 +1,16 @@
+// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include "Modules/ModuleManager.h"
+
+class FHTTP_UPLOADModule : public IModuleInterface
+{
+public:
+
+	/** IModuleInterface implementation */
+	virtual void StartupModule() override;
+	virtual void ShutdownModule() override;
+};
+
+

+ 77 - 0
Plugins/HTTP_UPLOAD/Source/HTTP_UPLOAD/Public/HTTP_UPLOADBPLibrary.h

@@ -0,0 +1,77 @@
+// Copyright ECG. All Rights Reserved.
+
+#pragma once
+
+#include "Http.h"
+
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "HTTP_UPLOADBPLibrary.generated.h"
+
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FHttpRequestR, const bool, bWasSuccessful,const FString, Message,const TArray<uint8>&, bytes,const FString ,ID);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FHttpProgressR, const int32, ReceivedSize, const int32, TotalSize,const float, Percent, const FString, ID);
+
+UCLASS()
+class UHTTP_UPLOADBPLibrary : public UBlueprintFunctionLibrary
+{
+	GENERATED_UCLASS_BODY()
+
+	UFUNCTION(BlueprintCallable, meta = (DisplayName = "Execute Sample function", Keywords = "HTTP_UPLOAD sample test testing"), Category = "HTTP_UPLOADTesting")
+	static float HTTP_UPLOADSampleFunction(float Param);
+
+	UFUNCTION(BlueprintCallable, Category = "HTTP_UPLOADTesting")
+	static URequerstObj* UPloadFile(FString ID,FString URL, FString FileName, FString FilePath, TMap<FString, FString> Keys);
+
+	UFUNCTION(BlueprintCallable, Category = "HTTP_UPLOADTesting")
+	static URequerstObj* UPloadFiles(FString URL, const TMap<FString, FString> Files, const TMap<FString, FString> Keys);
+
+	//DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FRequestHTTP, FString, IsOver);
+	UFUNCTION(BlueprintCallable, Category = "HTTP_UPLOADTesting")
+	static TArray<uint8> FstringToBytes(FString ChangeString);
+
+	UFUNCTION(BlueprintCallable, Category = "HTTP_UPLOADTesting")
+	static FString BytesToFstring(TArray<uint8> Content);
+
+	UFUNCTION(BlueprintCallable, Category = "HTTP_UPLOADTesting")
+	static URequerstObj* PostJson(FString URL, FString JsonString, FString ID);
+
+	UFUNCTION(BlueprintCallable, Category = "HTTP_UPLOADTesting")
+	static URequerstObj* PostParameter(FString URL, TMap<FString, FString> Parameters, FString ID);
+
+	UFUNCTION(BlueprintCallable, Category = "HTTP_UPLOADTesting")
+	static URequerstObj* Get(FString URL, FString ID);
+
+	UFUNCTION(BlueprintCallable, Category = "HTTP_UPLOADTesting")
+	static URequerstObj* GetParameter(FString URL, TMap<FString, FString> Parameters, FString ID);
+
+
+};
+
+UCLASS(Blueprintable, BlueprintType)
+class URequerstObj :public UObject
+{
+	GENERATED_BODY()
+
+public:
+
+	void GetRQ(FHttpRequestPtr request, FHttpResponsePtr response, bool bWasSuccessful);
+
+
+	void HttpRequestProgress(FHttpRequestPtr Request, int32 BytesSent, int32 BytesReceived);
+
+
+	UPROPERTY(BlueprintAssignable, Category = "HTTP_UPLOAD")
+	FHttpRequestR OnHttpCompletedReturn;
+
+
+	UPROPERTY(BlueprintAssignable, Category = "HTTP_UPLOAD")
+	FHttpProgressR OnHttpProgressReturn;
+
+
+	FString RequerstType;  // post  get 
+	
+	int32 ContentLength;
+
+	FString ID;
+
+};

BIN
Plugins/RuntimeMeshImportExport/Resources/Icon128.png


+ 33 - 0
Plugins/RuntimeMeshImportExport/RuntimeMeshImportExport.uplugin

@@ -0,0 +1,33 @@
+{
+	"FileVersion": 3,
+	"Version": 1,
+	"VersionName": "1.11",
+	"FriendlyName": "RuntimeMeshImportExport",
+	"Description": "Allows to import and export at runtime created meshes with help of Assimp library.",
+	"Category": "Other",
+	"CreatedBy": "Lucid Layers",
+	"CreatedByURL": "www.lucidlayers.de",
+	"DocsURL": "https://forums.unrealengine.com/unreal-engine/marketplace/1653161-runtimemeshimportexport-import-and-export-runtime-created-static-meshes-using-assimp-library",
+	"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/4a300859ba06426494dbaf22349273ba",
+	"SupportURL": "https://forums.unrealengine.com/unreal-engine/marketplace/1653161-runtimemeshimportexport-import-and-export-runtime-created-static-meshes-using-assimp-library",
+	"EngineVersion": "4.25.0",
+	"CanContainContent": false,
+	"Installed": true,
+	"Modules": [
+		{
+			"Name": "RuntimeMeshImportExport",
+			"Type": "Runtime",
+			"LoadingPhase": "Default",
+			"WhitelistPlatforms": [
+				"Win64",
+				"Win32"
+			]
+		}
+	],
+	"Plugins": [
+		{
+			"Name": "ProceduralMeshComponent",
+			"Enabled": true
+		}
+	]
+}

+ 646 - 0
Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Private/AssimpCustom.cpp

@@ -0,0 +1,646 @@
+// MIT License
+//
+// Copyright (c) 2019 Lucid Layers
+
+#include "AssimpCustom.h"
+#include "Async/Async.h"
+#include "RuntimeMeshImportExport.h"
+#include "RuntimeMeshImportExportLibrary.h"
+#include "RuntimeMeshImportExportTypes.h"
+#include "ProfilingDebugging/ScopedTimers.h"
+
+FAssimpScene::FAssimpScene()
+{
+    rootNode = new FAssimpNode(FName(), nullptr);
+}
+
+FAssimpScene::~FAssimpScene()
+{
+    ClearParentDataAndPtrs();
+    ClearMeshData();
+
+    delete rootNode;
+}
+
+void FAssimpScene::SetDataAndPtrsToParentClass_EntireScene(const FRuntimeMeshExportParam& param)
+{
+    mNumMeshes = meshes.Num();
+    mMeshes = (aiMesh**)meshes.GetData();
+    mNumMaterials = materials.Num();
+    mMaterials = materials.GetData();
+
+    check(rootNode);
+    mRootNode = (aiNode*)rootNode;
+    rootNode->SetDataAndPtrsToParentClass(param);
+
+    for (FAssimpMesh* mesh : meshes)
+    {
+        mesh->SetDataAndPtrsToParentClass(param);
+    }
+}
+
+FAssimpMesh::~FAssimpMesh()
+{
+    // Remove the parent references to the data
+    mNumVertices = 0;
+    mNumFaces = 0;
+    mVertices = nullptr;
+    mNormals = nullptr;
+    mTangents = nullptr;
+    mBitangents = nullptr;
+    for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) {
+        mColors[a] = nullptr;
+    }
+    for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++) {
+        mTextureCoords[a] = nullptr;
+    }
+    mFaces = nullptr;
+    mBones = nullptr;
+    mAnimMeshes = nullptr;
+
+    for (aiFace& face : faces)
+    {
+        delete[] face.mIndices;
+        face.mIndices = nullptr;
+        face.mNumIndices = 0;
+    }
+}
+
+void FAssimpMesh::SetDataAndPtrsToParentClass(const FRuntimeMeshExportParam& param)
+{
+    mNumVertices = vertices.Num();
+    mVertices = vertices.GetData();
+    mNormals = normals.GetData();
+    mTangents = tangents.GetData();
+    mBitangents = bitangents.GetData();
+    mColors[0] = vertexColors.GetData();
+    for (uint32 i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i)
+    {
+        mNumUVComponents[i] = numUVComponents[i];
+        mTextureCoords[i] = textureCoordinates[i].GetData();
+    }
+    mNumFaces = faces.Num();
+    mFaces = (aiFace*)faces.GetData();
+}
+
+FAssimpNode::~FAssimpNode()
+{
+    ClearParentDataAndPtrs();
+    ClearMeshData();
+
+    for (FAssimpNode* child : children)
+    {
+        delete child;
+    }
+    // Free our own data
+    parent = nullptr;
+}
+
+void FAssimpNode::SetDataAndPtrsToParentClass(const FRuntimeMeshExportParam& param)
+{
+    // Set the name of the node
+    mName.Set(TCHAR_TO_ANSI(*name.ToString()));
+
+    // Set the transform for the node
+    FTransform relativeTransform = worldTransform;
+    if (parent)
+    {
+        // If has a parent
+        relativeTransform = parent->worldTransform.Inverse() * worldTransform;
+    }
+    else
+    {
+        // Is parent
+
+        // Apply transform corrections
+        FVector scale = relativeTransform.GetScale3D();
+        scale *= param.correction.scaleFactor;
+        if (param.correction.bFlipX)
+        {
+            scale.X *= -1.f;
+        }
+        if (param.correction.bFlipY)
+        {
+            scale.Y *= -1.f;
+        }
+        if (param.correction.bFlipZ)
+        {
+            scale.Z *= -1.f;
+        }
+        relativeTransform.SetScale3D(scale);
+
+        FRotator deltaRot(URuntimeMeshImportExportLibrary::RotationCorrectionToValue(param.correction.PitchCorrection_Y)
+                          , URuntimeMeshImportExportLibrary::RotationCorrectionToValue(param.correction.YawCorrection_Z)
+                          , URuntimeMeshImportExportLibrary::RotationCorrectionToValue(param.correction.RollCorrection_X));
+        relativeTransform.ConcatenateRotation(deltaRot.Quaternion());
+    }
+    mTransformation = URuntimeMeshImportExportLibrary::FTransformToAiTransform(relativeTransform);
+
+    mNumMeshes = meshRefIndices.Num();
+    mMeshes = (unsigned int*)meshRefIndices.GetData();
+    mNumChildren = children.Num();
+    mChildren = (aiNode**)children.GetData();
+
+    for (FAssimpNode* child : children)
+    {
+        child->SetDataAndPtrsToParentClass(param);
+    }
+}
+
+
+void FAssimpNode::ClearParentDataAndPtrs()
+{
+    mNumChildren = 0;
+    mChildren = nullptr;
+    mNumMeshes = 0;
+    mMeshes = nullptr;
+    mMetaData = nullptr;
+}
+
+void FAssimpNode::ClearMeshData()
+{
+    indexGatherNext = 0;
+    gatheredExportables.Empty();
+    meshRefIndices.Empty();
+}
+
+FString FAssimpNode::GetHierarchicalName() const
+{
+    if (parent)
+    {
+        return parent->GetHierarchicalName() + TEXT(".") + name.ToString();
+    }
+    else
+    {
+        return FString(TEXT("root"));
+    }
+}
+
+FAssimpNode* FAssimpNode::FindOrCreateNode(TArray<FString>& nodePathRelative)
+{
+    if (nodePathRelative.Num() > 0)
+    {
+        FName nodeName = FName(*MoveTemp(nodePathRelative[0]));
+        nodePathRelative.RemoveAt(0);
+
+        FAssimpNode** foundNode = children.FindByPredicate([nodeName](FAssimpNode* child) -> bool {
+            return child->name == nodeName;
+        });
+
+        FAssimpNode* childNode = nullptr;
+        if (foundNode)
+        {
+            childNode = *foundNode;
+        }
+        else
+        {
+            childNode = children.Add_GetRef(new FAssimpNode(nodeName, this));
+        }
+
+        check(childNode);
+        return childNode->FindOrCreateNode(nodePathRelative);
+    }
+
+    return this;
+}
+
+void FAssimpNode::ClearExportData()
+{
+    ClearParentDataAndPtrs();
+    ClearMeshData();
+    for (FAssimpNode* child : children)
+    {
+        child->ClearExportData();
+    }
+}
+
+void FAssimpNode::GetNodesRecursive(TArray<FAssimpNode*>& outNodes)
+{
+    outNodes.Add(this);
+    for (FAssimpNode* child : children)
+    {
+        if (child)
+        {
+            child->GetNodesRecursive(outNodes);
+        }
+    }
+}
+
+int32 FAssimpNode::GatherMeshData(FAssimpScene& scene, const FRuntimeMeshExportParam& param, const bool bGatherAll, const int32 numToGather)
+{
+    check(IsInGameThread());
+    check(bGatherAll || numToGather > 0);
+
+    int32 numGathered = 0;
+    const int32 startIndex = bGatherAll ? 0 : indexGatherNext;
+    const int32 endIndex = FMath::Min(bGatherAll ? exportObjects.Num() : startIndex + numToGather, exportObjects.Num());
+    for (; indexGatherNext < endIndex; ++indexGatherNext)
+    {
+        ++numGathered;
+
+        TScriptInterface<IMeshExportable>& object = exportObjects[indexGatherNext];
+        TArray<FExportableMeshSection> sections;
+        if (!object->Execute_GetMeshData(object.GetObject(), param.lod, param.bSkipLodNotValid, sections))
+        {
+            scene.WriteToLogWithNewLine(FString::Printf(TEXT("Object %s refused to be part of export."), *object.GetObject()->GetName()));
+            ++scene.numObjectsSkipped;
+            continue;
+        }
+
+        if (sections.Num() == 0)
+        {
+            scene.WriteToLogWithNewLine(FString::Printf(TEXT("Object %s did not return any sections."), *object.GetObject()->GetName()));
+            ++scene.numObjectsSkipped;
+            continue;
+        }
+
+        // Validate all sections
+        {
+            bool bAllSectionsValid = true;
+            for (int32 sectionIndex = sections.Num() - 1; sectionIndex >= 0; --sectionIndex)
+            {
+                if (!ValidateMeshSection(scene, object, sections[sectionIndex]))
+                {
+                    scene.WriteToLogWithNewLine(FString::Printf(TEXT("Object %s: Section %d failed validation."), *object.GetObject()->GetName(), sectionIndex));
+                    bAllSectionsValid = false;
+                }
+            }
+
+            if (!bAllSectionsValid)
+            {
+                scene.WriteToLogWithNewLine(FString::Printf(TEXT("Object %s has invalid sections. Skipped."), *object.GetObject()->GetName()));
+                ++scene.numObjectsSkipped;
+                continue;
+            }
+        }
+
+        gatheredExportables.Add(sections);
+    }
+
+    return numGathered;
+}
+
+void FAssimpNode::ProcessGatheredData_Recursive(FAssimpScene& scene, const FRuntimeMeshExportParam& param)
+{
+    check(!parent); // should only be called on the root node
+    scene.WriteToLogWithNewLine(FString(TEXT("Begin processing gathered data.")));
+    double duration = 0.f;
+    {
+        FScopedDurationTimer timer(duration);
+        ProcessGatheredData_Internal(scene, param);
+    }
+    scene.WriteToLogWithNewLine(FString::Printf(TEXT("End processing gathered data. Duration: %.3fs"), duration));
+}
+
+void FAssimpNode::ProcessGatheredData_Internal(FAssimpScene& scene, const FRuntimeMeshExportParam& param)
+{
+    FString hierarchicalName = GetHierarchicalName();
+
+    // Create meshes
+    CreateAssimpMeshesFromMeshData(scene, param);
+    scene.WriteToLogWithNewLine(FString::Printf(TEXT("Node %s has %d meshes for export."), *hierarchicalName, meshRefIndices.Num()));
+
+    // Process children
+    for (int32 childIndex = children.Num() - 1; childIndex >= 0; --childIndex)
+    {
+        children[childIndex]->ProcessGatheredData_Internal(scene, param);
+    }
+
+    if (children.Num() == 0 && meshRefIndices.Num() == 0)
+    {
+        scene.WriteToLogWithNewLine(FString::Printf(TEXT("Node %s has no children and no meshes."), *hierarchicalName));
+    }
+}
+
+void FAssimpNode::CreateAssimpMeshesFromMeshData(FAssimpScene& scene, const FRuntimeMeshExportParam& param)
+{
+    // Process the gathered mesh data
+    TMap<UMaterialInterface*, TArray<FExportableMeshSection>> mapMaterialSections;
+    for(TArray<FExportableMeshSection>& sections : gatheredExportables)
+    {
+        // Transform the data
+        for (FExportableMeshSection& section : sections)
+        {
+            FTransform objectSpaceToNodeSpace = section.meshToWorld * this->worldTransform.Inverse();
+            for (int32 i = section.vertices.Num() -1 ; i >= 0; --i)
+            {
+                section.vertices[i] = objectSpaceToNodeSpace.TransformPosition(section.vertices[i]);
+                section.normals[i] = objectSpaceToNodeSpace.TransformVector(section.normals[i]);
+                section.tangents[i] = objectSpaceToNodeSpace.TransformVector(section.tangents[i]);
+            }
+
+            TArray<FExportableMeshSection>& materialSections = mapMaterialSections.FindOrAdd(section.material);
+
+            // Combine data of the same material if wanted
+            if (param.bCombineSameMaterial && materialSections.IsValidIndex(0))
+            {
+                materialSections[0].Append(MoveTemp(section));
+            }
+            else
+            {
+                materialSections.Add(MoveTemp(section));
+            }
+        }
+    }
+    gatheredExportables.Empty();
+
+    // Create aiMeshes from the gathered data
+    for (auto& element : mapMaterialSections)
+    {
+        for (FExportableMeshSection& section : element.Value)
+        {
+            // Create the aiMesh
+            FAssimpMesh* mesh = new FAssimpMesh();
+            meshRefIndices.Add(scene.meshes.Add(mesh));
+
+            // mesh->mName = TODO do we need a name for the meshes?! Problem with merged meshes
+            mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+            // Add the material to the mesh
+            const int32 foundMaterialIndex = scene.uniqueMaterials.Find(section.material);
+            if (foundMaterialIndex == INDEX_NONE)
+            {
+                mesh->mMaterialIndex = scene.uniqueMaterials.Add(section.material);
+                aiMaterial* material = new aiMaterial();
+                scene.materials.Add(material);
+                check(scene.uniqueMaterials.Num() == scene.materials.Num())
+                // Set the material name
+                {
+                    aiString materialName;
+                    if (section.material)
+                    {
+                        materialName = TCHAR_TO_ANSI(*section.material->GetName());
+                    }
+                    else
+                    {
+                        materialName = "Unknown";
+                    }
+                    material->AddProperty(&materialName, AI_MATKEY_NAME);
+                }
+                // Set the material to be two sided
+                {
+                    const int bTwoSided = true;
+                    material->AddProperty(&bTwoSided, 1, AI_MATKEY_TWOSIDED);
+                }
+                // Shininess (only a FIX for gltf.v1 crash cause shininess not available)
+                {
+                    const float shininess = 0.f;
+                    material->AddProperty(&shininess, 1, AI_MATKEY_SHININESS);
+                }
+            }
+            else
+            {
+                mesh->mMaterialIndex = foundMaterialIndex;
+            }
+
+            // Vertices
+            {
+                // Do some checks to make sure we move the data
+                check(sizeof(aiVector3D) == sizeof(FVector));
+                check(sizeof(aiColor4D) == sizeof(FLinearColor));
+
+                int32 numVertices = section.vertices.Num();
+
+                // Positions
+                mesh->vertices = MoveTemp(*reinterpret_cast<TArray<aiVector3D>*>(&section.vertices));
+                check(section.vertices.Num() == 0); // just to check that the move worked
+
+                // Normals
+                mesh->normals = MoveTemp(*reinterpret_cast<TArray<aiVector3D>*>(&section.normals));
+
+                // Tangents
+                mesh->tangents = MoveTemp(*reinterpret_cast<TArray<aiVector3D>*>(&section.tangents));
+
+                // Bitangents
+                // Seems that Assimp requires bitangents, though we do not supply values for now. See how it works out.
+                mesh->bitangents.AddZeroed(numVertices);
+
+                // Colors
+                TArray<FLinearColor> linearColors;
+                linearColors.Init(FLinearColor::White, numVertices);
+                if (section.vertexColors.Num() > 0)
+                {
+                    for (int32 colorIndex = 0; colorIndex < numVertices; ++colorIndex)
+                    {
+                        linearColors[colorIndex] = (section.vertexColors[colorIndex].ReinterpretAsLinear());
+                    }
+                }
+                mesh->vertexColors = MoveTemp(*reinterpret_cast<TArray<aiColor4D>*>(&linearColors));
+
+                // TextureCoordinates
+                mesh->numUVComponents[0] = 2;
+                mesh->textureCoordinates->Reserve(numVertices);
+                for (int32 texIndex = 0; texIndex < numVertices; ++texIndex)
+                {
+                    FVector2D& coord = section.textureCoordinates[texIndex];
+                    mesh->textureCoordinates[0].Add(aiVector3D(coord.X, coord.Y, 0.f));
+                }
+            }
+
+            // Faces
+            {
+                const int32 numIndicesPerFace = 3;
+                check((section.triangles.Num() % numIndicesPerFace) == 0);
+                const int32 numFaces = section.triangles.Num() / numIndicesPerFace;
+                mesh->faces.SetNum(numFaces);
+                for (int32 faceIndex = 0; faceIndex < numFaces; ++faceIndex)
+                {
+                    int32 triangleStart = faceIndex * numIndicesPerFace;
+                    aiFace& face = mesh->faces[faceIndex];
+                    face.mNumIndices = numIndicesPerFace;
+                    face.mIndices = new unsigned int[numIndicesPerFace];
+                    face.mIndices[0] = section.triangles[triangleStart];
+                    face.mIndices[1] = section.triangles[triangleStart + 1];
+                    face.mIndices[2] = section.triangles[triangleStart + 2];
+                }
+            }
+        }
+    }
+}
+
+bool FAssimpNode::ValidateMeshSection(FAssimpScene& scene, TScriptInterface<IMeshExportable>& exportable, FExportableMeshSection& section)
+{
+    bool bMeshValid = true;
+    int32 numVertices = section.vertices.Num();
+    if (section.normals.Num() != numVertices)
+    {
+        scene.WriteToLogWithNewLine(FString::Printf(TEXT("Object %: Number of normals not equal number of vertices!"), *exportable.GetObject()->GetName()));
+        bMeshValid = false;
+    }
+
+    if (section.tangents.Num() != numVertices)
+    {
+        scene.WriteToLogWithNewLine(FString::Printf(TEXT("Object %: Number of tangents not equal number of vertices!"), *exportable.GetObject()->GetName()));
+        bMeshValid = false;
+    }
+
+    if (section.vertexColors.Num() > 0 && section.vertexColors.Num() != numVertices)
+    {
+        scene.WriteToLogWithNewLine(FString::Printf(TEXT("Object %: Number of vertexColors not equal number of vertices!"), *exportable.GetObject()->GetName()));
+        bMeshValid = false;
+    }
+
+    if (section.textureCoordinates.Num() != numVertices)
+    {
+        scene.WriteToLogWithNewLine(FString::Printf(TEXT("Object %: Number of textureCoordinates not equal number of vertices!"), *exportable.GetObject()->GetName()));
+        bMeshValid = false;
+    }
+
+    if (section.triangles.Num() % 3 != 0)
+    {
+        scene.WriteToLogWithNewLine(FString::Printf(TEXT("Object %: Number of triangles is not dividable by 3!"), *exportable.GetObject()->GetName()));
+        bMeshValid = false;
+    }
+
+    return bMeshValid;
+}
+
+
+void FAssimpScene::WriteToLogWithNewLine(const FString& logText)
+{
+    if (exportLog)
+    {
+        URuntimeMeshImportExportLibrary::NewLineAndAppend(*exportLog, logText);
+    }
+
+    if (bLogToUnreal)
+    {
+        RMIE_LOG(Log, "%s", *logText);
+    }
+}
+
+void FAssimpScene::PrepareSceneForExport(const FRuntimeMeshExportParam& param)
+{
+    numObjectsSkipped = 0;
+    WriteToLogWithNewLine(FString(TEXT("Begin gather mesh data.")));
+    rootNode->GetNodesRecursive(allNodesHelper);
+    double duration = 0.f;
+    {
+        FScopedDurationTimer timer(duration);
+        for (FAssimpNode* node : allNodesHelper)
+        {
+            node->GatherMeshData(*this, param, true);
+        }
+    }
+    WriteToLogWithNewLine(FString::Printf(TEXT("End gather mesh data. Duration: %.3fs"), duration));
+
+    rootNode->ProcessGatheredData_Recursive(*this, param);
+    SetDataAndPtrsToParentClass_EntireScene(param);
+}
+
+void FAssimpScene::PrepareSceneForExport_Async_Start(const FRuntimeMeshExportAsyncParam& param, FRuntimeMeshImportExportProgressUpdate callbackProgress
+        , TFunction<void()> onPrepareFinished)
+{
+    numObjectsSkipped = 0;
+    gatheredMeshNum = 0;
+    currentNodeIndex = 0;
+    delegateProgress = callbackProgress;
+    onGameThreadPrepareFinished = onPrepareFinished;
+    numGatherPerTick = param.numGatherPerTick < 1 ? 1 : param.numGatherPerTick;
+    rootNode->GetNodesRecursive(allNodesHelper);
+    startTimeGatherMeshData = FPlatformTime::Seconds();
+    WriteToLogWithNewLine(FString(TEXT("Begin gather mesh data.")));
+    gatherMeshDataTicker = MakeUnique<FGatherMeshDataTicker>(this, param);
+}
+
+void FAssimpScene::PrepareSceneForExport_Update(const FRuntimeMeshExportParam& param)
+{
+    check(IsInGameThread());
+    check(numGatherPerTick > 0);
+    if (currentNodeIndex >= allNodesHelper.Num())
+    {
+        // Just a precaution in case the ticker is called another time, though the gathering is finished
+        checkNoEntry();
+    }
+
+    const int32 endIndex = currentNodeIndex + numGatherPerTick;
+    int32 numToGather = numGatherPerTick;
+    while (numToGather)
+    {
+        int32 numGathered = allNodesHelper[currentNodeIndex]->GatherMeshData(*this, param, false, numToGather);
+        gatheredMeshNum += numGathered;
+        if (numGathered == 0)
+        {
+            ++currentNodeIndex;
+            if (currentNodeIndex >= allNodesHelper.Num())
+            {
+                // We are inside the function that is ticked from the ticker, can't reset the ticker from here.
+                AsyncTask(ENamedThreads::GameThread, [this]() {
+                    if (this)
+                    {
+                        gatherMeshDataTicker.Reset();
+                    }
+                });
+
+                WriteToLogWithNewLine(FString::Printf(TEXT("End gather mesh data. Duration: %.3fs"), FPlatformTime::Seconds() - startTimeGatherMeshData));
+
+                onGameThreadPrepareFinished();
+                break;
+            }
+        }
+        numToGather -= numGathered;
+    }
+
+    //delegateStatus.ExecuteIfBound(FString::Printf(TEXT("Gathering exportables in %d/%d nodes. Meshes: %d "), currentNodeIndex + 1, allNodesHelper.Num(), gatheredMeshNum));
+    delegateProgress.ExecuteIfBound(FRuntimeMeshImportExportProgress(ERuntimeMeshImportExportProgressType::GatheringMeshs, currentNodeIndex, allNodesHelper.Num()));
+}
+
+void FAssimpScene::PrepareSceneForExport_Async_Finish(const FRuntimeMeshExportParam& param)
+{
+    check(!IsInGameThread());
+    //  AsyncTask(ENamedThreads::GameThread, [this]() {
+    //delegateStatus.ExecuteIfBound(FString::Printf(TEXT("Processing gathered data")));
+    //  });
+
+    rootNode->ProcessGatheredData_Recursive(*this, param);
+
+    //  AsyncTask(ENamedThreads::GameThread, [this]() {
+    //delegateStatus.ExecuteIfBound(FString::Printf(TEXT("Giving Assimp types data access")));
+    //  });
+    SetDataAndPtrsToParentClass_EntireScene(param);
+}
+
+void FAssimpScene::ClearSceneExportData()
+{
+    delegateProgress.Unbind();
+    currentNodeIndex = 0;
+    numGatherPerTick = -1;
+
+    ClearParentDataAndPtrs();
+    ClearMeshData();
+    rootNode->ClearExportData();
+}
+
+void FAssimpScene::ClearParentDataAndPtrs()
+{
+    mRootNode = nullptr;
+    mMeshes = nullptr;
+    mNumMeshes = 0;
+    mMaterials = nullptr;
+    mNumMaterials = 0;
+    mAnimations = nullptr;
+    mNumAnimations = 0;
+    mTextures = nullptr;
+    mNumTextures = 0;
+    mLights = nullptr;
+    mNumLights = 0;
+    mCameras = nullptr;
+    mNumCameras = 0;
+}
+
+void FAssimpScene::ClearMeshData()
+{
+    uniqueMaterials.Empty();
+
+    for (FAssimpMesh* mesh : meshes)
+    {
+        delete mesh;
+    }
+    meshes.Empty();
+
+    for (aiMaterial* material : materials)
+    {
+        delete material;
+    }
+    materials.Empty();
+}

+ 189 - 0
Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Private/AssimpCustom.h

@@ -0,0 +1,189 @@
+// MIT License
+//
+// Copyright (c) 2019 Lucid Layers
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "assimp/scene.h"
+#include "assimp/mesh.h"
+#include "RuntimeMeshImportExportTypes.h"
+#include "Tickable.h"
+#include "Interface/MeshExportable.h"
+
+struct FAssimpScene;
+struct FAssimpMesh;
+struct FRuntimeMeshExportParam;
+
+/**
+ *	This files provides wrappers for the Assimp types.
+ *	These types hold all the data in TArrays and on request fill the
+ *  pointers of the Assimp parent type that are needed for export.
+ *  The destructors of the types makes sure that the pointers in the parent
+ *  classes are removed before the parent destructor is called to prevent 
+ *  heap corruption.
+ */
+
+struct FAssimpMesh : public aiMesh
+{
+public:
+    ~FAssimpMesh();
+
+    TArray<aiVector3D> vertices;
+    TArray<aiVector3D> normals;
+    TArray<aiVector3D> tangents;
+    TArray<aiVector3D> bitangents;
+    TArray<aiVector3D> textureCoordinates[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+	uint32 numUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+    TArray<aiColor4D> vertexColors;
+
+	// Note:: We had a FAssimpFace before that had an array
+	// for the indices that are then copied to the parent
+	// to be consistent with the other types.
+	// That did not work out as the TArray makes the type 
+	// larger. We can't do that, so work with aiFaces directly.
+	// (If the parent would need a ptr to an array of aiFace*, it
+	// would be no problem)
+    TArray<aiFace> faces;
+
+private:
+    friend FAssimpScene;
+    void SetDataAndPtrsToParentClass(const FRuntimeMeshExportParam& param);
+};
+
+struct FAssimpNode : public aiNode
+{
+public:
+	FAssimpNode(const FName& inName, FAssimpNode* inParent) :  parent(inParent), name(inName)
+	{}
+    ~FAssimpNode();
+
+    TArray<FAssimpNode*> children;
+    TArray<uint32> meshRefIndices;
+
+    const FAssimpNode* parent = nullptr;
+    const FName name;
+    FTransform worldTransform;
+    TArray<TScriptInterface<IMeshExportable>> exportObjects;
+
+    FString GetHierarchicalName() const;
+	FAssimpNode* FindOrCreateNode(TArray<FString>& nodePathRelative);
+
+private:
+    friend struct FAssimpScene;
+
+	// Helper index for async export
+	int32 indexGatherNext = 0;
+	TArray<TArray<FExportableMeshSection>> gatheredExportables;
+	/**
+	 *	Gathers the data from the exportables. This function must be run on the game thread.
+	 *	Returns the number of exportables gathered.
+	 */ 
+	int32 GatherMeshData(FAssimpScene& scene, const FRuntimeMeshExportParam& param, const bool bGatherAll, const int32 numToGather = 0);
+
+	void ProcessGatheredData_Recursive(FAssimpScene& scene, const FRuntimeMeshExportParam& param);
+	void ProcessGatheredData_Internal(FAssimpScene& scene, const FRuntimeMeshExportParam& param);
+
+    void CreateAssimpMeshesFromMeshData(FAssimpScene& scene, const FRuntimeMeshExportParam& param);
+    bool ValidateMeshSection(FAssimpScene& scene, TScriptInterface<IMeshExportable>& exportable, FExportableMeshSection& section);
+
+    void SetDataAndPtrsToParentClass(const FRuntimeMeshExportParam& param);
+	void ClearParentDataAndPtrs();
+	void ClearMeshData();
+	void ClearExportData();
+
+	void GetNodesRecursive(TArray<FAssimpNode*>& outNodes);
+};
+
+struct FAssimpScene : public aiScene
+{
+public:
+	FAssimpScene();
+    ~FAssimpScene();
+
+    FAssimpNode* rootNode = nullptr;
+    TArray<FAssimpMesh*> meshes;
+    TArray<aiMaterial*> materials;
+	// Helper array to find unique materials
+	TArray<UMaterialInterface*> uniqueMaterials;
+
+	bool bLogToUnreal = false;
+	int32 numObjectsSkipped = 0;
+
+	// Writes to 'exportLog' if available and adds a new line at the end.
+	void WriteToLogWithNewLine(const FString& logText);
+	FString* exportLog = nullptr;
+	
+	void PrepareSceneForExport(const FRuntimeMeshExportParam& param);
+	// Must be called on GameThread to gather mesh data.
+	void PrepareSceneForExport_Async_Start(const FRuntimeMeshExportAsyncParam& param, FRuntimeMeshImportExportProgressUpdate callbackProgress
+		, TFunction<void()> onPrepareFinished);
+
+	// Call on NONE GameThread to finish processing the data
+	void PrepareSceneForExport_Async_Finish(const FRuntimeMeshExportParam& param);
+	void ClearSceneExportData();
+
+private:
+	/**
+	 *	The whole data of the scene is stored in TArrays. The parent ai-Classes
+	 *	only get ptrs to the data.
+	 */
+    void SetDataAndPtrsToParentClass_EntireScene(const FRuntimeMeshExportParam& param);
+	void ClearParentDataAndPtrs();
+	void ClearMeshData();
+
+	TArray<FAssimpNode*> allNodesHelper;
+
+	// Called from ticker
+	void PrepareSceneForExport_Update(const FRuntimeMeshExportParam& param);
+
+#pragma region Async
+	int32 currentNodeIndex = 0;
+	int32 numGatherPerTick = -1;
+	int32 gatheredMeshNum = 0;
+	double startTimeGatherMeshData = 0.f;
+	FRuntimeMeshImportExportProgressUpdate delegateProgress;
+	TFunction<void()> onGameThreadPrepareFinished;
+
+	class FGatherMeshDataTicker : public FTickableGameObject
+	{
+	public:
+		FGatherMeshDataTicker(FAssimpScene* inScene, const FRuntimeMeshExportAsyncParam& inParam)
+			: scene(inScene), param(inParam) 
+		{}
+		virtual ~FGatherMeshDataTicker() {}
+
+		virtual bool IsTickableWhenPaused() const override
+		{
+			return true;
+		}
+
+		virtual bool IsTickableInEditor() const override
+		{
+			return true;
+		}
+
+		virtual UWorld* GetTickableGameObjectWorld() const override
+		{
+			return nullptr;
+		}
+
+		virtual TStatId GetStatId() const override
+		{
+			return TStatId(); // Creation of a valid stat id is confusing, just pass an empty for now.
+		}
+
+		virtual void Tick(float DeltaTime) override
+		{
+			scene->PrepareSceneForExport_Update(param.param);
+		}
+
+	private:
+		FAssimpScene* scene;
+		const FRuntimeMeshExportAsyncParam param;
+	};
+	TUniquePtr<FGatherMeshDataTicker> gatherMeshDataTicker;
+#pragma endregion Async
+
+};
+

+ 8 - 0
Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Private/Interface/MeshExportable.cpp

@@ -0,0 +1,8 @@
+// MIT License
+//
+// Copyright (c) 2019 Lucid Layers
+
+#include "MeshExportable.h"
+
+
+// Add default functionality here for any IMeshExportable functions that are not pure virtual.

+ 413 - 0
Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Private/RuntimeMeshExporter.cpp

@@ -0,0 +1,413 @@
+// MIT License
+//
+// Copyright (c) 2019 Lucid Layers
+
+#include "RuntimeMeshExporter.h"
+#include "RuntimeMeshImportExport.h"
+#include "assimp/scene.h"
+#include "assimp/Exporter.hpp"
+#include "assimp/DefaultLogger.hpp"
+#include "assimp/postprocess.h"
+#include "Engine/Engine.h"
+#include "assimp/LogStream.hpp"
+#include "RuntimeMeshImportExportLibrary.h"
+#include "HAL/PlatformFileManager.h"
+#include "HAL/PlatformFile.h"
+#include "Vector"
+#include "ProfilingDebugging/ScopedTimers.h"
+#include "Async/Async.h"
+#include "Misc/Paths.h"
+#include "AssimpProgressHandler.h"
+
+const unsigned int exportFlags = aiPostProcessSteps::aiProcess_MakeLeftHanded;
+
+URuntimeMeshExporter::URuntimeMeshExporter()
+{
+    // Make sure Assimp does not use double precision
+    check(sizeof(ai_real) == sizeof(float));
+    check(sizeof(ai_int) == sizeof(int32));
+
+    if (!(GetFlags() & EObjectFlags::RF_ClassDefaultObject))
+    {
+        scene = new FAssimpScene();
+    }
+}
+
+URuntimeMeshExporter::~URuntimeMeshExporter()
+{
+    delete scene;
+}
+
+void URuntimeMeshExporter::AddNode(const FString& hierarchicalName, const FTransform& nodeTransformWS)
+{
+    if (bIsExporting)
+    {
+        RMIE_LOG(Warning, "Currently exporting, you should not call functions on the exporter!");
+        return;
+    }
+
+    check(scene && scene->rootNode);
+    TArray<FString> nodeList;
+    hierarchicalName.ParseIntoArray(nodeList, TEXT("."), true);
+
+    FAssimpNode* foundNode = scene->rootNode->FindOrCreateNode(nodeList);
+
+    foundNode->worldTransform = nodeTransformWS;
+}
+
+void URuntimeMeshExporter::AddExportObject(const TScriptInterface<IMeshExportable>& exportable, const bool bOverrideNode, const FString& hierarchicalNodeName)
+{
+    if (bIsExporting)
+    {
+        RMIE_LOG(Warning, "Currently exporting, you should not call functions on the exporter!");
+        return;
+    }
+
+    check(scene && scene->rootNode);
+    TArray<FString> nodeList;
+    (bOverrideNode ? hierarchicalNodeName : exportable->Execute_GetHierarchicalNodeName(exportable.GetObject())).ParseIntoArray(nodeList, TEXT("."), true);
+    scene->rootNode->FindOrCreateNode(nodeList)->exportObjects.Add(exportable);
+}
+
+void URuntimeMeshExporter::AddExportObjects(const TArray<TScriptInterface<IMeshExportable>>& exportables, const bool bOverrideNode, const FString& hierarchicalNodeName)
+{
+    if (bIsExporting)
+    {
+        RMIE_LOG(Warning, "Currently exporting, you should not call functions on the exporter!");
+        return;
+    }
+
+    for (const TScriptInterface<IMeshExportable>& object : exportables)
+    {
+        AddExportObject(object, bOverrideNode, hierarchicalNodeName);
+    }
+}
+
+bool URuntimeMeshExporter::AddObjectIfExportable(UObject* object, const bool bOverrideNode, const FString& hierarchicalNodeName)
+{
+    if (bIsExporting)
+    {
+        RMIE_LOG(Warning, "Currently exporting, you should not call functions on the exporter!");
+        return false;
+    }
+
+    if (!object->GetClass()->ImplementsInterface(UMeshExportable::StaticClass()))
+    {
+        return false;
+    }
+
+    TScriptInterface<IMeshExportable> interfaceObject(object);
+    AddExportObject(interfaceObject, bOverrideNode, hierarchicalNodeName);
+    return true;
+}
+
+void URuntimeMeshExporter::AddObjectsIfExportable(UPARAM(ref) TArray<UObject*>& objects, const bool bOverrideNode, const FString& hierarchicalNodeName, TArray<UObject*>& notExportable)
+{
+    if (bIsExporting)
+    {
+        RMIE_LOG(Warning, "Currently exporting, you should not call functions on the exporter!");
+        return;
+    }
+
+    for (UObject* object : objects)
+    {
+        if (!AddObjectIfExportable(object, bOverrideNode, hierarchicalNodeName))
+        {
+            notExportable.Add(object);
+        }
+    }
+}
+
+void URuntimeMeshExporter::Export(const FRuntimeMeshExportParam& param, FRuntimeMeshExportResult& result)
+{
+    if (!PreExportWork(param, result))
+    {
+        result.bSuccess = false;
+        return;
+    }
+
+    FAssimpScene& sceneRef = *scene;
+
+    // Prepare the scene
+    sceneRef.PrepareSceneForExport(param);
+    sceneRef.WriteToLogWithNewLine(FString::Printf(TEXT("Scene does contain %d meshes."), sceneRef.mNumMeshes));
+    sceneRef.WriteToLogWithNewLine(FString::Printf(TEXT("Scene does contain %d materials."), sceneRef.mNumMaterials));
+
+    // Do the export
+    Assimp::Exporter exporter;
+    //try
+    //{
+    sceneRef.WriteToLogWithNewLine(FString(TEXT("Begin export scene.")));
+    double duration = 0.f;
+    {
+        FScopedDurationTimer timer(duration);
+        aiExporterReturn = exporter.Export(&sceneRef, TCHAR_TO_UTF8(*param.formatId), TCHAR_TO_UTF8(*param.file), exportFlags);
+    }
+    sceneRef.WriteToLogWithNewLine(FString::Printf(TEXT("End export scene. Duration: %.3fs"), duration));
+    //}
+    //catch (const std::exception& e)
+    //{
+    //	FString exceptionString = FString::Printf(TEXT("Exception thrown during export: %s"), ANSI_TO_TCHAR(e.what()));
+    //	sceneRef.WriteToLogWithNewLine(exceptionString);
+    //	URuntimeMeshImportExportLibrary::NewLineAndAppend(result.error, exceptionString);
+    //	RMIE_LOG(Error, "%s", *exceptionString);
+    //}
+    aiExporterError = FString(exporter.GetErrorString());
+
+    result.bSuccess = PostExportWork(result);
+    return;
+}
+
+void URuntimeMeshExporter::Export_Async_Cpp(const FRuntimeMeshExportAsyncParam param, FRuntimeMeshImportExportProgressUpdate callbackProgress
+        , FRuntimeImportExportGameThreadDone callbackGatherDone
+        , FRuntimeExportFinished callbackFinished)
+{
+    if (!PreExportWork(param.param, asyncResult))
+    {
+        asyncResult.bSuccess = false;
+        callbackFinished.ExecuteIfBound(asyncResult);
+        return;
+    }
+
+    delegateProgress = callbackProgress;
+    delegateGatherDone = callbackGatherDone;
+    delegateFinished = callbackFinished;
+
+    scene->PrepareSceneForExport_Async_Start(param, callbackProgress, [this, param]() {
+        delegateGatherDone.ExecuteIfBound();
+        AsyncTask(ENamedThreads::AnyThread, [this, param]() {
+            this->Export_Async_AnyThread(param.param);
+        });
+    });
+}
+
+
+class FExportAsyncAction : public FPendingLatentAction
+{
+public:
+    const FLatentActionInfo latentInfo;
+
+    FExportAsyncAction(const FLatentActionInfo& inLatentInfo, URuntimeMeshExporter* inExporter
+                       , const FRuntimeMeshExportAsyncParam& param, FRuntimeMeshImportExportProgressUpdateDyn inProgressDelegate
+                       , FRuntimeImportExportGameThreadDoneDyn gatherDoneDelegate
+                       , FRuntimeMeshExportResult& inResult)
+        : latentInfo(inLatentInfo), exporter(inExporter), result(inResult)
+    {
+        FRuntimeMeshImportExportProgressUpdate progressDelegateRaw;
+        progressDelegateRaw.BindLambda([inProgressDelegate](const FRuntimeMeshImportExportProgress& progress) {
+            check(IsInGameThread());
+			inProgressDelegate.ExecuteIfBound(progress);
+        });
+
+        FRuntimeImportExportGameThreadDone gatherDoneDelegateRaw;
+        gatherDoneDelegateRaw.BindLambda([gatherDoneDelegate]() {
+            check(IsInGameThread());
+            gatherDoneDelegate.ExecuteIfBound();
+        });
+
+        FRuntimeExportFinished finishedDelegateRaw;
+        finishedDelegateRaw.BindLambda([this](FRuntimeMeshExportResult delegateResult) {
+            check(IsInGameThread());
+            result = MoveTemp(delegateResult);
+            bExportFinished = true;
+        });
+
+        exporter->Export_Async_Cpp(param, progressDelegateRaw, gatherDoneDelegateRaw, finishedDelegateRaw);
+    }
+
+    virtual void UpdateOperation(FLatentResponse& response) override
+    {
+        if (bExportFinished)
+        {
+            if (!exporter->GetIsExporting())
+            {
+                response.FinishAndTriggerIf(true, latentInfo.ExecutionFunction, latentInfo.Linkage, latentInfo.CallbackTarget);
+            }
+        }
+    }
+
+#if WITH_EDITOR
+    // Returns a human readable description of the latent operation's current state
+    virtual FString GetDescription() const override
+    {
+        return FString::Printf(TEXT("LatentAction for URuntimeMeshExporter export task."));
+    }
+#endif
+
+private:
+    bool bExportFinished = false;
+    URuntimeMeshExporter* exporter = nullptr;
+    FRuntimeMeshExportResult& result;
+    //FRuntimeMeshExportDelegateStatusDyn statusDelegate;
+};
+
+void URuntimeMeshExporter::Export_Async(UObject* worldContextObject, FLatentActionInfo latentInfo, const FRuntimeMeshExportAsyncParam& param
+                                        , FRuntimeMeshImportExportProgressUpdateDyn progressDelegate, FRuntimeImportExportGameThreadDoneDyn gatherDoneDelegate
+                                        , FRuntimeMeshExportResult& result)
+{
+    if (UWorld* world = GEngine->GetWorldFromContextObject(worldContextObject, EGetWorldErrorMode::LogAndReturnNull))
+    {
+        FLatentActionManager& latentActionManager = world->GetLatentActionManager();
+        if (latentActionManager.FindExistingAction<FExportAsyncAction>(latentInfo.CallbackTarget, latentInfo.UUID) == NULL)
+        {
+            latentActionManager.AddNewAction(latentInfo.CallbackTarget, latentInfo.UUID, new FExportAsyncAction(latentInfo, this, param, progressDelegate, gatherDoneDelegate, result));
+        }
+    }
+}
+
+bool URuntimeMeshExporter::GetIsExporting()
+{
+    return bIsExporting;
+}
+
+void URuntimeMeshExporter::Export_Async_AnyThread(const FRuntimeMeshExportParam param)
+{
+    check(!IsInGameThread());
+
+    FAssimpScene& sceneRef = *scene;
+    sceneRef.PrepareSceneForExport_Async_Finish(param);
+    sceneRef.WriteToLogWithNewLine(FString::Printf(TEXT("Scene does contain %d meshes."), sceneRef.mNumMeshes));
+    sceneRef.WriteToLogWithNewLine(FString::Printf(TEXT("Scene does contain %d materials."), sceneRef.mNumMaterials));
+
+    Assimp::Exporter exporter;
+	FAssimpProgressHandler progressHandler(delegateProgress);
+    exporter.SetProgressHandler(&progressHandler);
+
+    //AsyncTask(ENamedThreads::GameThread, [this]() {
+    //    delegateStatus.ExecuteIfBound(FString(TEXT("Exporting scene with Assimp.")));
+    //});
+
+    sceneRef.WriteToLogWithNewLine(FString(TEXT("Begin export scene.")));
+    double duration = 0.f;
+    {
+        FScopedDurationTimer timer(duration);
+        aiExporterReturn = exporter.Export(scene, TCHAR_TO_UTF8(*param.formatId), TCHAR_TO_UTF8(*param.file), exportFlags);
+    }
+    exporter.SetProgressHandler(nullptr);
+    sceneRef.WriteToLogWithNewLine(FString::Printf(TEXT("End export scene. Duration: %.3fs"), duration));
+
+    aiExporterError = FString(exporter.GetErrorString());
+
+    /*AsyncTask(ENamedThreads::GameThread, [this]() {
+        delegateProgress.ExecuteIfBound(FString(TEXT("Clearing export data.")));
+    });*/
+    sceneRef.ClearSceneExportData();
+
+    // Done, continue in GameThread
+    AsyncTask(ENamedThreads::GameThread, [this]() {
+        Export_Async_Finish();
+    });
+}
+
+void URuntimeMeshExporter::Export_Async_Finish()
+{
+    check(IsInGameThread());
+    asyncResult.bSuccess = PostExportWork(asyncResult);
+
+    delegateFinished.ExecuteIfBound(asyncResult);
+    delegateProgress.Unbind();
+    delegateGatherDone.Unbind();
+    delegateFinished.Unbind();
+    asyncResult.error.Empty();
+    asyncResult.exportLog.Empty();
+    asyncResult.numObjectsSkipped = 0;
+}
+
+bool URuntimeMeshExporter::PreExportWork(const FRuntimeMeshExportParam& param, FRuntimeMeshExportResult& result)
+{
+    check(IsInGameThread());
+
+    if (bIsExporting)
+    {
+        URuntimeMeshImportExportLibrary::NewLineAndAppend(result.error, FString::Printf(TEXT("Already exporting!")));
+        return false;
+    }
+
+    result.error.Empty();
+    result.exportLog.Empty();
+    aiExporterReturn = aiReturn_FAILURE;
+    aiExporterError.Empty();
+
+    IPlatformFile& platformFile = FPlatformFileManager::Get().GetPlatformFile();
+
+    if (!param.bOverrideExisting)
+    {
+        if (platformFile.FileExists(*param.file))
+        {
+            URuntimeMeshImportExportLibrary::NewLineAndAppend(result.error, FString::Printf(TEXT("File %s does already exist!"), *param.file));
+            return false;
+        }
+    }
+
+    // Assimp can only write to a directory that exists.
+    // Make sure it does.
+    if (!platformFile.CreateDirectoryTree(*FPaths::GetPath(param.file)))
+    {
+        URuntimeMeshImportExportLibrary::NewLineAndAppend(result.error, FString::Printf(TEXT("Could not create directory: %s"), *FPaths::GetPath(param.file)));
+        return false;
+    }
+
+    // Create log stuff
+    scene->bLogToUnreal = param.bLogToUnreal;
+    scene->exportLog = &result.exportLog;
+    assimpLogger = Assimp::DefaultLogger::create("", Assimp::Logger::NORMAL);
+    exportLogger = new FExportLogger(*scene);
+    assimpLogger->attachStream(exportLogger);
+
+    bIsExporting = true;
+
+    return true;
+}
+
+bool URuntimeMeshExporter::PostExportWork(FRuntimeMeshExportResult& result)
+{
+    check(IsInGameThread());
+    bool bExportSuccessful = false;
+
+    // Check for errors
+    if (!aiExporterError.IsEmpty())
+    {
+        URuntimeMeshImportExportLibrary::NewLineAndAppend(result.error, aiExporterError);
+        RMIE_LOG(Error, "Error during export: %s", *aiExporterError);
+    }
+
+    // Evaluate result
+    if (aiExporterReturn == aiReturn::aiReturn_OUTOFMEMORY)
+    {
+        URuntimeMeshImportExportLibrary::NewLineAndAppend(result.error, FString::Printf(TEXT("Export failed: out of memory!")));
+        RMIE_LOG(Error, "Export failed: out of memory");
+        bExportSuccessful = false;
+    }
+    else if (aiExporterReturn == aiReturn::aiReturn_FAILURE)
+    {
+        URuntimeMeshImportExportLibrary::NewLineAndAppend(result.error, FString::Printf(TEXT("Export failed!")));
+        RMIE_LOG(Error, "Export failed!");
+        bExportSuccessful = false;
+    }
+    else if (!result.error.IsEmpty())
+    {
+        bExportSuccessful = false;
+    }
+    else if (aiExporterReturn == aiReturn::aiReturn_SUCCESS)
+    {
+        bExportSuccessful = true;
+    }
+
+    // Get rid of log stuff
+    scene->exportLog = nullptr;
+    assimpLogger->detatchStream(exportLogger);
+    delete exportLogger;
+    exportLogger = nullptr;
+    assimpLogger = nullptr;
+    Assimp::DefaultLogger::kill(); // kill assimpLogger instance.
+
+    // Cleanup
+    result.numObjectsSkipped = scene->numObjectsSkipped;
+    scene->ClearSceneExportData();
+    aiExporterError.Empty();
+    aiExporterReturn = aiReturn_FAILURE;
+
+    bIsExporting = false;
+
+    return bExportSuccessful;
+}

+ 54 - 0
Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Private/RuntimeMeshImportExport.cpp

@@ -0,0 +1,54 @@
+// MIT License
+//
+// Copyright (c) 2019 Lucid Layers
+
+#include "RuntimeMeshImportExport.h"
+#include "Interfaces/IPluginManager.h"
+#include "Misc/Paths.h"
+#include "HAL/PlatformFilemanager.h"
+#include "HAL/PlatformFile.h"
+#include "HAL/PlatformProcess.h"
+
+#define LOCTEXT_NAMESPACE "FRuntimeMeshImportExportModule"
+
+void FRuntimeMeshImportExportModule::StartupModule()
+{
+	// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
+	FString PluginBaseDir = IPluginManager::Get().FindPlugin("RuntimeMeshImportExport")->GetBaseDir();
+	FString configString;
+#if UE_BUILD_SHIPPING
+	configString = "Release";
+#else 
+	configString = "Debug";
+#endif
+
+#if PLATFORM_WINDOWS
+#if PLATFORM_32BITS
+	FString platformString = "Win32";
+#elif PLATFORM_64BITS
+	FString platformString = "x64";
+#endif
+#elif PLATFORM_MAC
+	FString platformString = "Mac";
+#endif
+
+	FString dllFileName = FString(TEXT("assimp-vc141-mt")) + (UE_BUILD_SHIPPING ? TEXT("") : TEXT("d")) + TEXT(".dll");
+	FString dllFile = FPaths::Combine(PluginBaseDir, FString("Source/ThirdParty/assimp/bin"), platformString, configString, dllFileName);
+	if (!FPlatformFileManager::Get().GetPlatformFile().FileExists(*dllFile))
+	{
+		RMIE_LOG(Fatal, "Missing file: %s", *dllFile);
+	}
+		
+	dllHandle_assimp = FPlatformProcess::GetDllHandle(*dllFile);
+}
+
+void FRuntimeMeshImportExportModule::ShutdownModule()
+{
+	// This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
+	// we call this function before unloading the module.
+	FPlatformProcess::FreeDllHandle(dllHandle_assimp);
+}
+
+#undef LOCTEXT_NAMESPACE
+	
+IMPLEMENT_MODULE(FRuntimeMeshImportExportModule, RuntimeMeshImportExport)

+ 1083 - 0
Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Private/RuntimeMeshImportExportLibrary.cpp

@@ -0,0 +1,1083 @@
+// MIT License
+//
+// Copyright (c) 2017 Eric Liu
+// Copyright (c) 2019 Lucid Layers
+
+#include "RuntimeMeshImportExportLibrary.h"
+#include "RuntimeMeshImportExport.h"
+#include "Engine/Engine.h"
+#include "Engine/LatentActionManager.h"
+#include "Misc/FileHelper.h"
+#include "Misc/Paths.h"
+#include "Async/Async.h"
+#include <assimp/Importer.hpp>  // C++ importer interface
+#include <assimp/Exporter.hpp>  // C++ exporter interface
+#include <assimp/IOSystem.hpp>
+#include <assimp/ProgressHandler.hpp>
+#include <assimp/scene.h>       // Output data structure
+#include <assimp/postprocess.h> // Post processing flags
+#include "ImageUtils.h"
+#include "Kismet/KismetMaterialLibrary.h"
+#include "Materials/MaterialInstanceDynamic.h"
+#include "AssimpProgressHandler.h"
+
+class FLoadMeshAsyncAction : public FPendingLatentAction
+{
+public:
+    FName executionFunction;
+    int32 outputLink;
+    FWeakObjectPtr callbackTarget;
+    FString file;
+
+    FLoadMeshAsyncAction(const FLatentActionInfo& latentInfo, const FRuntimeMeshImportParameter& params
+                         , FRuntimeMeshImportExportProgressUpdateDyn progressDelegate
+                         , FRuntimeMeshImportResult& result);
+
+    virtual void UpdateOperation(FLatentResponse& response) override
+    {
+        if (bTaskDone)
+        {
+            response.FinishAndTriggerIf(bTaskDone, executionFunction, outputLink, callbackTarget);
+        }
+    }
+
+#if WITH_EDITOR
+    // Returns a human readable description of the latent operation's current state
+    virtual FString GetDescription() const override
+    {
+        return FString::Printf(TEXT("FLoadMeshAsyncAction, loading file: %s"), *file);
+    }
+#endif
+
+private:
+    // Reference to the outer result of the user
+    FRuntimeMeshImportResult& resultRef;
+    bool bTaskDone = false;
+};
+
+void ImportMeshesOfNode(const aiScene* scene, aiNode* node, FRuntimeMeshImportResult& result, const FTransform& nodeTransform)
+{
+    if (node->mNumMeshes == 0)
+    {
+        RMIE_LOG(Log, "Mesh has no sections, not adding it as mesh to the result. Node: %s", *FString(node->mName.C_Str()));
+        return;
+    }
+
+    RMIE_LOG(Log, "Importing %d sections for mesh: %s", node->mNumMeshes, *FString(node->mName.C_Str()));
+
+    FRuntimeMeshImportMeshInfo& meshInfoRef = result.meshInfos.Add_GetRef(FRuntimeMeshImportMeshInfo());
+    meshInfoRef.meshName = FName(node->mName.C_Str());
+    meshInfoRef.sections.SetNum(node->mNumMeshes);
+
+    for (uint32 nodeMeshIndex = 0; nodeMeshIndex < node->mNumMeshes; nodeMeshIndex++)
+    {
+        int sceneMeshIndex = node->mMeshes[nodeMeshIndex];
+        aiMesh *mesh = scene->mMeshes[sceneMeshIndex];
+
+        FRuntimeMeshImportSectionInfo &sectionInfoRef = meshInfoRef.sections[nodeMeshIndex];
+
+        // Transform
+        //FTransform transform = URuntimeMeshImportExportLibrary::AiTransformToFTransform(node->mTransformation);
+        FTransform transform = nodeTransform;
+        /*       transform = transform * composedParentTransform;*/
+
+        sectionInfoRef.materialName = FName(scene->mMaterials[mesh->mMaterialIndex]->GetName().C_Str());
+        sectionInfoRef.materialIndex = mesh->mMaterialIndex;
+
+        // Vertices
+        sectionInfoRef.vertices.Reserve(mesh->mNumVertices);
+        sectionInfoRef.normals.Reserve(mesh->mNumVertices);
+        sectionInfoRef.uv0.Reserve(mesh->mNumVertices);
+        sectionInfoRef.tangents.Reserve(mesh->mNumVertices);
+        sectionInfoRef.vertexColors.Reserve(mesh->mNumVertices);
+        for (uint32 vertexIndex = 0; vertexIndex < mesh->mNumVertices; ++vertexIndex)
+        {
+            sectionInfoRef.vertices.Push(transform.TransformPosition(FVector(
+                                             mesh->mVertices[vertexIndex].x,
+                                             mesh->mVertices[vertexIndex].y,
+                                             mesh->mVertices[vertexIndex].z)));
+        }
+
+        //https://www.scratchapixel.com/lessons/mathematics-physics-for-computer-graphics/geometry/transforming-normals
+        FTransform transformForNormal = FTransform(transform.ToMatrixWithScale().Inverse().GetTransposed());
+
+        // Normal
+        if (mesh->HasNormals())
+        {
+            FVector normal;
+            for (uint32 normalIndex = 0; normalIndex < mesh->mNumVertices; ++normalIndex)
+            {
+                sectionInfoRef.normals.Push(transformForNormal.TransformVector(FVector(
+                                                mesh->mNormals[normalIndex].x,
+                                                mesh->mNormals[normalIndex].y,
+                                                mesh->mNormals[normalIndex].z)).GetSafeNormal());
+            }
+        }
+        else
+        {
+            sectionInfoRef.normals.SetNumZeroed(mesh->mNumVertices);
+        }
+
+        // UV Coordinates
+        if (mesh->HasTextureCoords(0))
+        {
+            for (uint32 textureCoordinateIndex = 0; textureCoordinateIndex < mesh->mNumVertices; ++textureCoordinateIndex)
+            {
+                sectionInfoRef.uv0.Add(FVector2D(mesh->mTextureCoords[0][textureCoordinateIndex].x
+                                                 , -mesh->mTextureCoords[0][textureCoordinateIndex].y));
+            }
+        }
+
+        // Tangent
+        if (mesh->HasTangentsAndBitangents())
+        {
+            for (uint32 tangentIndex = 0; tangentIndex < mesh->mNumVertices; ++tangentIndex)
+            {
+                sectionInfoRef.tangents.Push(transform.TransformVectorNoScale(FVector(
+                                                 mesh->mTangents[tangentIndex].x,
+                                                 mesh->mTangents[tangentIndex].y,
+                                                 mesh->mTangents[tangentIndex].z
+                                             )).GetSafeNormal());
+            }
+        }
+
+        // Vertex color
+        if (mesh->HasVertexColors(0))
+        {
+            for (uint32 vertexColorIndex = 0; vertexColorIndex < mesh->mNumVertices; ++vertexColorIndex)
+            {
+                sectionInfoRef.vertexColors.Push(FLinearColor(
+                                                     mesh->mColors[0][vertexColorIndex].r,
+                                                     mesh->mColors[0][vertexColorIndex].g,
+                                                     mesh->mColors[0][vertexColorIndex].b,
+                                                     mesh->mColors[0][vertexColorIndex].a
+                                                 ));
+            }
+        }
+
+
+        // Triangles
+        // When the mesh is inside out cause of the scale, flip the winding order of the triangles
+        sectionInfoRef.triangles.Reserve(mesh->mNumFaces * 3);
+        const bool bFlipTriangleWindingOrder = (transform.GetScale3D().X * transform.GetScale3D().Y * transform.GetScale3D().Z) < 0;
+        const int32 numFaces = mesh->mNumFaces;
+        bool bWarnOnce = false;
+        if (bFlipTriangleWindingOrder)
+        {
+            for (int32 faceIndex = 0; faceIndex < numFaces; ++faceIndex)
+            {
+                aiFace& face = mesh->mFaces[faceIndex];
+                if (face.mNumIndices != 3)
+                {
+                    if (!bWarnOnce)
+                    {
+                        RMIE_LOG(Warning, "Mesh %s section %d face %d has %d indices, but must be 3! Warning only once per mesh section", *meshInfoRef.meshName.ToString(), nodeMeshIndex, faceIndex, face.mNumIndices);
+                        bWarnOnce = true;
+                    }
+                    continue;
+                }
+                sectionInfoRef.triangles.Push(face.mIndices[0]);
+                sectionInfoRef.triangles.Push(face.mIndices[2]);
+                sectionInfoRef.triangles.Push(face.mIndices[1]);
+            }
+        }
+        else
+        {
+            for (int32 faceIndex = 0; faceIndex < numFaces; ++faceIndex)
+            {
+                aiFace& face = mesh->mFaces[faceIndex];
+                if (face.mNumIndices != 3)
+                {
+                    if (!bWarnOnce)
+                    {
+                        RMIE_LOG(Warning, "Mesh %s section %d face %d has %d indices, but must be 3! Warning only once per mesh section", *meshInfoRef.meshName.ToString(), nodeMeshIndex, faceIndex, face.mNumIndices);
+                        bWarnOnce = true;
+                    }
+                    continue;
+                }
+                sectionInfoRef.triangles.Push(face.mIndices[0]);
+                sectionInfoRef.triangles.Push(face.mIndices[1]);
+                sectionInfoRef.triangles.Push(face.mIndices[2]);
+            }
+        }
+    }
+}
+
+template<typename Predicate>
+void IterateSceneNodes(aiNode* node, Predicate predicate)
+{
+    predicate(node);
+
+    for (uint32 m = 0; m < node->mNumChildren; ++m)
+    {
+        IterateSceneNodes(node->mChildren[m], predicate);
+    }
+}
+
+void BuildComposedNodeTransform(aiNode* node, FTransform& transform)
+{
+    if (node->mParent)
+    {
+        BuildComposedNodeTransform(node->mParent, transform);
+    }
+
+    transform = FTransform(URuntimeMeshImportExportLibrary::AiTransformToFTransform(node->mTransformation)) * transform;
+}
+
+/**
+ * Assumes that path starts with '*'
+ */
+bool ReadTextureFromSceneByMaterialParamPath(const aiScene* scene, FString path, FRuntimeMeshImportExportMaterialParamTexture& texture)
+{
+    if (!path.StartsWith(TEXT("*")))
+    {
+        RMIE_LOG(Error, "Variable path does not start with *!");
+        return false;
+    }
+
+    path.RemoveFromStart(TEXT("*"), ESearchCase::CaseSensitive);
+    uint32 sceneTextureIndex = FCString::Atoi(*path);
+
+    if (sceneTextureIndex >= 0 && sceneTextureIndex < scene->mNumTextures)
+    {
+        aiTexture* sceneTexture = scene->mTextures[sceneTextureIndex];
+
+        texture.width = sceneTexture->mWidth;
+        texture.height = sceneTexture->mHeight;
+        texture.byteDescription = FString(UTF8_TO_TCHAR(sceneTexture->achFormatHint));
+
+        int32 numBytes = sceneTexture->mHeight == 0 ? sceneTexture->mWidth : sceneTexture->mWidth * sceneTexture->mHeight * sizeof(aiTexel);
+        texture.byteData.SetNumUninitialized(numBytes);
+        FMemory::Memcpy(texture.byteData.GetData(), (uint8*)sceneTexture->pcData, numBytes);
+    }
+    else
+    {
+        RMIE_LOG(Error, "Texture index %d is not part of the Assimp scene", sceneTextureIndex);
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @param importFile					Scene description file that is being imported
+ * @param texturefileRelativePath		The path to the texture file relative to the imported file.
+ */
+bool ImportTextureFromFile(const FString& importFile, const FString& relativeTexturePath, FRuntimeMeshImportExportMaterialParamTexture& texture)
+{
+    if (importFile.IsEmpty())
+    {
+        RMIE_LOG(Error, "Parameter importFile is empty!");
+        return false;
+    }
+
+    if (relativeTexturePath.IsEmpty())
+    {
+        RMIE_LOG(Error, "Parameter relativeTexturePath is empty!");
+        return false;
+    }
+
+    // SANITIZE
+    // OBJ files for example can contain a parameter "bump mybump.jpg -bm 1". -bm is a bump multiplier and is added to the path read from Assimp. That breaks our code!
+    FString relativeTexturePathSanitized = relativeTexturePath;
+    int32 dotIndex = relativeTexturePath.Find(TEXT("."), ESearchCase::CaseSensitive, ESearchDir::FromEnd);
+    if (dotIndex >= 0)
+    {
+        int32 spaceIndex = relativeTexturePath.Find(TEXT(" "), ESearchCase::CaseSensitive, ESearchDir::FromStart, dotIndex);
+        if (spaceIndex >= 0)
+        {
+            relativeTexturePathSanitized = relativeTexturePath.Left(spaceIndex);
+        }
+    }
+    if (!relativeTexturePathSanitized.Equals(relativeTexturePath, ESearchCase::CaseSensitive))
+    {
+        RMIE_LOG(Warning, "While importing %s Sanitized relative texture path from \"%s\" to \"%s\"", *importFile, *relativeTexturePath, *relativeTexturePathSanitized)
+    }
+
+    FString absolutTextureFilePath = FPaths::Combine(FPaths::GetPath(importFile), relativeTexturePathSanitized);
+
+    if (absolutTextureFilePath.IsEmpty())
+    {
+        RMIE_LOG(Error, "Combined file path is empty!");
+        return false;
+    }
+    else
+    {
+        texture.byteDescription = FPaths::GetExtension(absolutTextureFilePath).ToLower();
+        // To stay in sync with Assimp, byteDescription should only be 3 characters long when it contains a file format!
+        if (texture.byteDescription.Equals(TEXT("jpeg"), ESearchCase::IgnoreCase))
+        {
+            texture.byteDescription = FString(TEXT("jpg"));
+        }
+        check(texture.byteDescription.Len() <= 3 && "Only file formats with 3 characters are allowed");
+        FFileHelper::LoadFileToArray(texture.byteData, *absolutTextureFilePath);
+        texture.width = texture.byteData.Num();
+        return true;
+    }
+
+    return false;
+}
+
+bool ImportTextureStackFromMaterial(const FString& importFile, const aiScene *const scene, const aiMaterial *const material, aiTextureType textureType, const FName stackName, FRuntimeMeshImportMaterialInfo& materialInfo)
+{
+    uint32 textureStackSize = material->GetTextureCount(textureType);
+    if (textureStackSize == 0)
+    {
+        return true; // No texture of that type, so we declare it as success
+    }
+
+    bool bKillTexture = false;
+
+    int32 materialInfoTextureIndex = materialInfo.textures.Add(FRuntimeMeshImportExportMaterialParamTexture(stackName));
+    for (uint32 textureIndex = 0; textureIndex < textureStackSize; ++textureIndex)
+    {
+        if (textureIndex > 0)
+        {
+            RMIE_LOG(Warning, "During Import we noticed that Texture %s is a stack of more than one Texture. But we only import the first Texture of the stack! Stacksize: %d", *stackName.ToString(), textureStackSize);
+            break;
+        }
+
+        aiString texturePath;
+        if (material->GetTexture(textureType, textureIndex, &texturePath) == AI_SUCCESS)
+        {
+            FString path(UTF8_TO_TCHAR(texturePath.C_Str()));
+            // If the path starts with '*', the texture is part of the file. After the '*' the texture index is specified.
+            // If not, path should point to a file on disk
+            if (path.StartsWith(TEXT("*")))
+            {
+                bKillTexture |= !ReadTextureFromSceneByMaterialParamPath(scene, path, materialInfo.textures[materialInfoTextureIndex]);
+            }
+            else
+            {
+                bKillTexture |= !ImportTextureFromFile(importFile, path, materialInfo.textures[materialInfoTextureIndex]);
+            }
+
+        }
+        else
+        {
+            bKillTexture = true;
+            RMIE_LOG(Error, "That was an issue that we can't further define when importing Texture %s!", *stackName.ToString());
+        }
+    }
+
+    if (bKillTexture)
+    {
+        materialInfo.textures.RemoveAt(materialInfoTextureIndex);
+        RMIE_LOG(Error, "Failed to import Texture %s for Material %s", *stackName.ToString(), *materialInfo.name.ToString());
+        return false;
+    }
+
+    return true;
+}
+
+void ImportSceneMaterials(const FString& importFile, const aiScene* scene, FRuntimeMeshImportResult& result, FRuntimeMeshImportExportProgressUpdate callbackProgress)
+{
+    if (!scene || !scene->HasMaterials())
+    {
+        return;
+    }
+
+    for (uint32 sceneMaterialIndex = 0; sceneMaterialIndex < scene->mNumMaterials; ++sceneMaterialIndex)
+    {
+        aiMaterial* aiMaterial = scene->mMaterials[sceneMaterialIndex];
+        FRuntimeMeshImportMaterialInfo& materialInfo = result.materialInfos.Add_GetRef(FRuntimeMeshImportMaterialInfo());
+
+        materialInfo.name = FName(aiMaterial->GetName().C_Str());
+
+        // Params that are reused
+        int intParam;
+        float floatParam;
+        aiColor3D vectorParam;
+        auto IntParamToBool = [&intParam]() -> bool { return intParam != 0 ? true : false; };
+        auto VectorParamToLinearColor = [&vectorParam]() -> FLinearColor { return FLinearColor(vectorParam.r, vectorParam.g, vectorParam.b); };
+
+        if (aiMaterial->Get(AI_MATKEY_TWOSIDED, intParam) == AI_SUCCESS)
+        {
+            materialInfo.bTwoSided = IntParamToBool();
+        }
+
+        if (aiMaterial->Get(AI_MATKEY_ENABLE_WIREFRAME, intParam) == AI_SUCCESS)
+        {
+            materialInfo.bWireFrame = IntParamToBool();
+        }
+
+        if (aiMaterial->Get(AI_MATKEY_SHADING_MODEL, intParam) == AI_SUCCESS)
+        {
+            materialInfo.shadingMode = MaterialShadingModeFromInt(intParam);
+            materialInfo.shadingModeInt = intParam;
+        }
+        else
+        {
+            materialInfo.shadingMode = ERuntimeMeshImportExportMaterialShadingMode::Unknown;
+            materialInfo.shadingModeInt = -1;
+        }
+
+        if (aiMaterial->Get(AI_MATKEY_BLEND_FUNC, intParam) == AI_SUCCESS)
+        {
+            materialInfo.blendMode = MaterialBlendModeFromInt(intParam);
+            materialInfo.blendModeInt = intParam;
+        }
+        else
+        {
+            materialInfo.blendMode = ERuntimeMeshImportExportMaterialBlendMode::Unknown;
+            materialInfo.blendModeInt = -1;
+        }
+
+        if (aiMaterial->Get(AI_MATKEY_COLOR_DIFFUSE, vectorParam) == AI_SUCCESS)
+        {
+            materialInfo.vectors.Add(FRuntimeMeshImportExportMaterialParamVector(TEXT("Diffuse"), VectorParamToLinearColor()));
+        }
+
+        //if (aiMaterial->Get(AI_MATKEY_COLOR_AMBIENT, vectorParam) == AI_SUCCESS) // Removed as it seems to be same as diffuse
+        //{
+        //    materialInfo.vectors.Add(FRuntimeMeshImportExportMaterialParamVector(TEXT("Ambient"), VectorParamToLinearColor()));
+        //}
+
+        if (aiMaterial->Get(AI_MATKEY_COLOR_SPECULAR, vectorParam) == AI_SUCCESS)
+        {
+            materialInfo.vectors.Add(FRuntimeMeshImportExportMaterialParamVector(TEXT("Specular"), VectorParamToLinearColor()));
+        }
+
+        if (aiMaterial->Get(AI_MATKEY_COLOR_EMISSIVE, vectorParam) == AI_SUCCESS)
+        {
+            materialInfo.vectors.Add(FRuntimeMeshImportExportMaterialParamVector(TEXT("Emissive"), VectorParamToLinearColor()));
+        }
+
+        if (aiMaterial->Get(AI_MATKEY_COLOR_TRANSPARENT, vectorParam) == AI_SUCCESS)
+        {
+            materialInfo.vectors.Add(FRuntimeMeshImportExportMaterialParamVector(TEXT("Transparent"), VectorParamToLinearColor()));
+        }
+
+        if (aiMaterial->Get(AI_MATKEY_COLOR_REFLECTIVE, vectorParam) == AI_SUCCESS)
+        {
+            materialInfo.vectors.Add(FRuntimeMeshImportExportMaterialParamVector(TEXT("Reflective"), VectorParamToLinearColor()));
+        }
+
+        if (aiMaterial->Get(AI_MATKEY_OPACITY, floatParam) == AI_SUCCESS)
+        {
+            materialInfo.scalars.Add(FRuntimeMeshImportExportMaterialParamScalar(TEXT("Opacity"), floatParam));
+        }
+
+        if (aiMaterial->Get(AI_MATKEY_TRANSPARENCYFACTOR, floatParam) == AI_SUCCESS)
+        {
+            materialInfo.scalars.Add(FRuntimeMeshImportExportMaterialParamScalar(TEXT("Transparency"), floatParam));
+        }
+
+        if (aiMaterial->Get(AI_MATKEY_BUMPSCALING, floatParam) == AI_SUCCESS)
+        {
+            materialInfo.scalars.Add(FRuntimeMeshImportExportMaterialParamScalar(TEXT("BumpScaling"), floatParam));
+        }
+
+        if (aiMaterial->Get(AI_MATKEY_SHININESS, floatParam) == AI_SUCCESS)
+        {
+            materialInfo.scalars.Add(FRuntimeMeshImportExportMaterialParamScalar(TEXT("Shininess"), floatParam));
+        }
+
+        if (aiMaterial->Get(AI_MATKEY_SHININESS_STRENGTH, floatParam) == AI_SUCCESS)
+        {
+            materialInfo.scalars.Add(FRuntimeMeshImportExportMaterialParamScalar(TEXT("ShininessStrength"), floatParam));
+        }
+
+        if (aiMaterial->Get(AI_MATKEY_REFLECTIVITY, floatParam) == AI_SUCCESS)
+        {
+            materialInfo.scalars.Add(FRuntimeMeshImportExportMaterialParamScalar(TEXT("Reflectivity"), floatParam));
+        }
+
+        if (aiMaterial->Get(AI_MATKEY_REFRACTI, floatParam) == AI_SUCCESS)
+        {
+            materialInfo.scalars.Add(FRuntimeMeshImportExportMaterialParamScalar(TEXT("Refraction"), floatParam));
+        }
+
+        ImportTextureStackFromMaterial(importFile, scene, aiMaterial, aiTextureType_DIFFUSE, TEXT("TexDiffuse"), materialInfo);
+        ImportTextureStackFromMaterial(importFile, scene, aiMaterial, aiTextureType_SPECULAR, TEXT("TexSpecular"), materialInfo);
+        //ImportTextureStackFromMaterial(importFile, scene, aiMaterial, aiTextureType_AMBIENT, TEXT("TexAmbient"), materialInfo); // Removed as it seems to be same as diffuse
+        ImportTextureStackFromMaterial(importFile, scene, aiMaterial, aiTextureType_EMISSIVE, TEXT("TexEmissive"), materialInfo);
+        ImportTextureStackFromMaterial(importFile, scene, aiMaterial, aiTextureType_HEIGHT, TEXT("TexHeight"), materialInfo);
+        ImportTextureStackFromMaterial(importFile, scene, aiMaterial, aiTextureType_NORMALS, TEXT("TexNormal"), materialInfo);
+        ImportTextureStackFromMaterial(importFile, scene, aiMaterial, aiTextureType_SHININESS, TEXT("TexShininess"), materialInfo);
+        ImportTextureStackFromMaterial(importFile, scene, aiMaterial, aiTextureType_OPACITY, TEXT("TexOpacity"), materialInfo);
+        ImportTextureStackFromMaterial(importFile, scene, aiMaterial, aiTextureType_DISPLACEMENT, TEXT("TexDisplacement"), materialInfo);
+        ImportTextureStackFromMaterial(importFile, scene, aiMaterial, aiTextureType_LIGHTMAP, TEXT("TexLightmap"), materialInfo);
+        ImportTextureStackFromMaterial(importFile, scene, aiMaterial, aiTextureType_REFLECTION, TEXT("TexReflection"), materialInfo);
+        ImportTextureStackFromMaterial(importFile, scene, aiMaterial, aiTextureType_BASE_COLOR, TEXT("TexBaseColor"), materialInfo);
+        ImportTextureStackFromMaterial(importFile, scene, aiMaterial, aiTextureType_NORMAL_CAMERA, TEXT("TexNormalCamera"), materialInfo);
+        ImportTextureStackFromMaterial(importFile, scene, aiMaterial, aiTextureType_EMISSION_COLOR, TEXT("TexEmissive"), materialInfo);
+        ImportTextureStackFromMaterial(importFile, scene, aiMaterial, aiTextureType_METALNESS, TEXT("TexMetallic"), materialInfo);
+        ImportTextureStackFromMaterial(importFile, scene, aiMaterial, aiTextureType_DIFFUSE_ROUGHNESS, TEXT("TexRoughness"), materialInfo);
+        ImportTextureStackFromMaterial(importFile, scene, aiMaterial, aiTextureType_AMBIENT_OCCLUSION, TEXT("TexAmbientOcclusion"), materialInfo);
+
+        URuntimeMeshImportExportLibrary::SendProgress_AnyThread(callbackProgress, FRuntimeMeshImportExportProgress(ERuntimeMeshImportExportProgressType::ImportingMaterials, sceneMaterialIndex+1, scene->mNumMaterials));
+    }
+}
+
+void MergeMeshes(TArray<FRuntimeMeshImportMeshInfo>& meshInfos)
+{
+    if (meshInfos.Num() < 2) return;
+
+    // Merging different named meshes and keeping a name is useless.
+    // Set it to None.
+    meshInfos[0].meshName = FName();
+
+    // Merge
+    for (int32 meshIndex = 1; meshIndex < meshInfos.Num(); ++meshIndex)
+    {
+        meshInfos[0].sections.Append(MoveTemp(meshInfos[meshIndex].sections));
+    }
+
+    // Get rid of merged meshes
+    meshInfos.SetNum(1);
+}
+
+void MergeAllSections(TArray<FRuntimeMeshImportSectionInfo>& sectionInfos)
+{
+    if (sectionInfos.Num() < 2) return;
+
+    // Merge
+    for (int32 sectionIndex = 1; sectionIndex < sectionInfos.Num(); ++sectionIndex)
+    {
+        sectionInfos[0].Append_Move(MoveTemp(sectionInfos[sectionIndex]));
+    }
+
+    // Get rid of merged sections
+    sectionInfos.SetNum(1);
+}
+
+void MergeSectionsSameMaterial(TArray<FRuntimeMeshImportSectionInfo>& sectionInfos)
+{
+    TMap<FName, FRuntimeMeshImportSectionInfo> map_MatName_Mesh;
+    for (int32 i = sectionInfos.Num() - 1; i>=0; --i)
+    {
+        FRuntimeMeshImportSectionInfo& meshInfoRef = map_MatName_Mesh.FindOrAdd(sectionInfos[i].materialName);
+        if (meshInfoRef.vertices.Num() == 0)
+        {
+            // Key was just added
+            meshInfoRef = MoveTemp(sectionInfos[i]);
+        }
+        else
+        {
+            // Already existing data on that key
+            meshInfoRef.Append_Move(MoveTemp(sectionInfos[i]));
+        }
+    }
+
+    // Copy over the combined meshes from the map into the result
+    sectionInfos.Empty();
+    map_MatName_Mesh.GenerateValueArray(sectionInfos);
+}
+
+void URuntimeMeshImportExportLibrary::ImportScene(const FRuntimeMeshImportParameter& params, FRuntimeMeshImportResult& result)
+{
+    FRuntimeMeshImportExportProgressUpdate progDelegate;
+    ImportScene_AnyThread(params, progDelegate, result);
+}
+
+FLoadMeshAsyncAction::FLoadMeshAsyncAction(const FLatentActionInfo& latentInfo, const FRuntimeMeshImportParameter& params
+        , FRuntimeMeshImportExportProgressUpdateDyn progressDelegate
+        , FRuntimeMeshImportResult& result)
+    : executionFunction(latentInfo.ExecutionFunction)
+    , outputLink(latentInfo.Linkage)
+    , callbackTarget(latentInfo.CallbackTarget)
+    , file(params.file)
+    , resultRef(result)
+{
+    FRuntimeImportFinished callbackFinishedRaw;
+    callbackFinishedRaw.BindLambda([this](FRuntimeMeshImportResult result) ->void {
+        resultRef = MoveTemp(result);
+        bTaskDone = true;
+    });
+
+    FRuntimeMeshImportExportProgressUpdate callbackProgressRaw;
+    callbackProgressRaw.BindLambda([progressDelegate](FRuntimeMeshImportExportProgress progress) {
+        progressDelegate.ExecuteIfBound(progress);
+    });
+
+    URuntimeMeshImportExportLibrary::ImportScene_Async_Cpp(params, callbackFinishedRaw, callbackProgressRaw);
+}
+
+void URuntimeMeshImportExportLibrary::ImportScene_Async(UObject* worldContextObject, FLatentActionInfo latentInfo
+        , const FRuntimeMeshImportParameter& params
+        , FRuntimeMeshImportExportProgressUpdateDyn progressDelegate
+        , FRuntimeMeshImportResult& result)
+{
+    if (UWorld* world = GEngine->GetWorldFromContextObject(worldContextObject, EGetWorldErrorMode::LogAndReturnNull))
+    {
+        FLatentActionManager& latentActionManager = world->GetLatentActionManager();
+        if (latentActionManager.FindExistingAction<FLoadMeshAsyncAction>(latentInfo.CallbackTarget, latentInfo.UUID) == NULL)
+        {
+            latentActionManager.AddNewAction(latentInfo.CallbackTarget, latentInfo.UUID
+                                             , new FLoadMeshAsyncAction(latentInfo, params, progressDelegate, result));
+        }
+    }
+}
+
+void URuntimeMeshImportExportLibrary::ImportScene_Async_Cpp(const FRuntimeMeshImportParameter& params
+        , FRuntimeImportFinished callbackFinished
+        , FRuntimeMeshImportExportProgressUpdate callbackProgress)
+{
+    AsyncTask(ENamedThreads::AnyThread, [=]()-> void
+    {
+        FRuntimeMeshImportResult* result = new FRuntimeMeshImportResult();
+        URuntimeMeshImportExportLibrary::ImportScene_AnyThread(params, callbackProgress, *result);
+        AsyncTask(ENamedThreads::GameThread, [=]() -> void
+        {
+            callbackFinished.ExecuteIfBound(MoveTemp(*result));
+            check(result->meshInfos.Num() == 0);
+            delete result;
+        });
+    });
+}
+
+bool URuntimeMeshImportExportLibrary::GetIsExtensionSupportedImport(FString extension)
+{
+    if(!extension.StartsWith(TEXT(".")))
+    {
+        extension = FString(TEXT(".")).Append(extension);
+    }
+    Assimp::Importer importer;
+    return importer.IsExtensionSupported(TCHAR_TO_UTF8(*extension));
+}
+
+void URuntimeMeshImportExportLibrary::GetSupportedExtensionsImport(TArray<FString>& extensions)
+{
+    std::string string;
+    Assimp::Importer importer;
+    importer.GetExtensionList(string);
+    FString extensionsString(string.c_str());
+    extensionsString = extensionsString.Replace(TEXT("*"), TEXT(""));
+    extensionsString.ParseIntoArray(extensions, TEXT(";"), true);
+}
+
+bool URuntimeMeshImportExportLibrary::GetIsExtensionSupportedExport(FString extension)
+{
+    extension.Replace(TEXT("."), TEXT("")); // Don't want a dot
+    TArray<FAssimpExportFormat> formats;
+    GetSupportedExtensionsExport(formats);
+    FAssimpExportFormat* foundFormat = formats.FindByPredicate([extension](const FAssimpExportFormat& format) -> bool {
+        return format.fileExtension == extension;
+    });
+    return foundFormat ? true : false;
+}
+
+void URuntimeMeshImportExportLibrary::GetSupportedExtensionsExport(TArray<FAssimpExportFormat>& formats)
+{
+    formats.Reset();
+    Assimp::Exporter exporter;
+
+    int32 formatCount = exporter.GetExportFormatCount();
+    for (int32 i = 0; i < formatCount; ++i)
+    {
+        formats.Add(FAssimpExportFormat(exporter.GetExportFormatDescription(i)));
+    }
+}
+
+void URuntimeMeshImportExportLibrary::GetTransformCorrectionPresetsExport(TMap<FString, FTransformCorrection>& corrections)
+{
+    corrections.Empty(1);
+
+    // Blender
+    FTransformCorrection blender;
+    blender.RollCorrection_X = ERotationCorrection::Plus_90;
+    blender.scaleFactor = 0.01f;
+    corrections.Add(TEXT("Blender"), blender);
+}
+
+FTransform URuntimeMeshImportExportLibrary::TransformCorrectionToTransform(const FTransformCorrection& correction)
+{
+    FRotator rotation;
+    rotation.Roll = RotationCorrectionToValue(correction.RollCorrection_X);
+    rotation.Pitch = RotationCorrectionToValue(correction.PitchCorrection_Y);
+    rotation.Yaw = RotationCorrectionToValue(correction.YawCorrection_Z);
+
+    FVector translation(0.f);
+
+    FVector scale(correction.scaleFactor);
+    scale.X *= correction.bFlipX ? -1.f : 1.f;
+    scale.Y *= correction.bFlipY ? -1.f : 1.f;
+    scale.Z *= correction.bFlipZ ? -1.f : 1.f;
+
+    return FTransform(rotation, translation, scale);
+}
+
+void URuntimeMeshImportExportLibrary::ConvertVectorToProceduralMeshTangent(const TArray<FVector>& tangents, const bool bFlipTangentY, TArray<FProcMeshTangent>& procTangents)
+{
+    procTangents.Reset(tangents.Num());
+    for (const FVector& tan : tangents)
+    {
+        procTangents.Add(FProcMeshTangent(tan, bFlipTangentY));
+    }
+}
+
+void URuntimeMeshImportExportLibrary::ConvertProceduralMeshTangentToVector(const TArray<FProcMeshTangent>& procTangents, TArray<FVector>& tangents)
+{
+    tangents.Reset(procTangents.Num());
+    for (const FProcMeshTangent& tan : procTangents)
+    {
+        tangents.Add(tan.TangentX);
+    }
+}
+
+FString URuntimeMeshImportExportLibrary::MaterialInfoToLogString(const FRuntimeMeshImportMaterialInfo& materialInfo)
+{
+    FString out;
+    UEnum* myEnum = nullptr;
+    FString enumValueAsString;
+
+    out.Append(FString::Printf(TEXT("-------------------- MaterialInfo -------------------"), *materialInfo.name.ToString()));
+    NewLineAndAppend(out, FString::Printf(TEXT("Material Name: %s"), *materialInfo.name.ToString()));
+    NewLineAndAppend(out, FString::Printf(TEXT("\tbWireframe: %s"), materialInfo.bWireFrame ? TEXT("true") : TEXT("false")));
+    NewLineAndAppend(out, FString::Printf(TEXT("\tbTwoSided: %s"), materialInfo.bTwoSided ? TEXT("true") : TEXT("false")));
+
+    myEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("ERuntimeMeshImportExportMaterialShadingMode"), true);
+    if (myEnum)
+    {
+        enumValueAsString = myEnum->GetNameStringByIndex((int32)materialInfo.shadingMode);
+    }
+    else
+    {
+        enumValueAsString = FString(TEXT("Failed to read ShadingMode Enum"));
+    }
+    NewLineAndAppend(out, FString::Printf(TEXT("\tShadingModel: %s, as int: %d"), *enumValueAsString, materialInfo.shadingModeInt));
+
+    myEnum = nullptr;
+    myEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("ERuntimeMeshImportExportMaterialBlendMode"), true);
+    if (myEnum)
+    {
+        enumValueAsString = myEnum->GetNameStringByIndex((int32)materialInfo.blendMode);
+    }
+    else
+    {
+        enumValueAsString = FString(TEXT("Failed to read BlendMode Enum"));
+    }
+    NewLineAndAppend(out, FString::Printf(TEXT("\tBlendMode: %s, as int: %d"), *enumValueAsString, materialInfo.blendModeInt));
+
+    // Scalar parameter
+    NewLineAndAppend(out, FString::Printf(TEXT("\tScalarParameter")));
+    for (FRuntimeMeshImportExportMaterialParamScalar scalar : materialInfo.scalars)
+    {
+        NewLineAndAppend(out, FString::Printf(TEXT("\t\t%s: %f"), *scalar.name.ToString(), scalar.value));
+    }
+
+    // Vector parameter
+    NewLineAndAppend(out, FString::Printf(TEXT("\tVectorParameter")));
+    for (FRuntimeMeshImportExportMaterialParamVector vector : materialInfo.vectors)
+    {
+        NewLineAndAppend(out, FString::Printf(TEXT("\t\t%s: %s"), *vector.name.ToString(), *vector.value.ToString()));
+    }
+
+    // Texture parameter
+    NewLineAndAppend(out, FString::Printf(TEXT("\tTextureParameter")));
+    for (FRuntimeMeshImportExportMaterialParamTexture texture : materialInfo.textures)
+    {
+        NewLineAndAppend(out, FString::Printf(TEXT("\t\t%s: width: %d, height: %d, byteDescription: %s, byteCount: %d")
+                                              , *texture.name.ToString(), texture.width, texture.height, *texture.byteDescription, texture.byteData.Num()));
+    }
+
+    NewLineAndAppend(out, FString::Printf(TEXT("-----------------------------------------------------"), *materialInfo.name.ToString()));
+
+    return out;
+}
+
+UTexture2D* URuntimeMeshImportExportLibrary::MaterialParamTextureToTexture2D(const FRuntimeMeshImportExportMaterialParamTexture& textureParam)
+{
+    if (textureParam.height == 0)
+    {
+        // This is the easy case. The byte data is a image file. Just pass the data to the ImageWrapper.
+        return FImageUtils::ImportBufferAsTexture2D(textureParam.byteData);
+    }
+    else
+    {
+        ensureAlwaysMsgf(0, TEXT("raw pixel data of Assimp Texture is not yet supported"));
+    }
+    return nullptr;
+}
+
+UMaterialInstanceDynamic* URuntimeMeshImportExportLibrary::MaterialInfoToDynamicMaterial(UObject* worldContextObject, const FRuntimeMeshImportMaterialInfo& materialInfo, UMaterialInterface* sourceMaterial)
+{
+    if (!sourceMaterial)
+    {
+        RMIE_LOG(Error, "A source material must be specified!");
+        return nullptr;
+    }
+
+    // !!! THE NAME GIVEN TO THE MATERIAL MUST BE NONE, OTHERWISE WHEN CALLED 2x AND THE MATERIAL IS SET TO A UMG IMAGE, IT WILL CRASH !!!
+    UMaterialInstanceDynamic* dynamic = UKismetMaterialLibrary::CreateDynamicMaterialInstance(worldContextObject, sourceMaterial, FName());
+
+    for (const FRuntimeMeshImportExportMaterialParamScalar& scalarParam : materialInfo.scalars)
+    {
+        dynamic->SetScalarParameterValue(scalarParam.name, scalarParam.value);
+    }
+
+    for (const FRuntimeMeshImportExportMaterialParamVector& vectorParam : materialInfo.vectors)
+    {
+        dynamic->SetVectorParameterValue(vectorParam.name, vectorParam.value);
+    }
+
+    for (const FRuntimeMeshImportExportMaterialParamTexture& textureParam : materialInfo.textures)
+    {
+        UTexture* existingTexture = NULL;
+        // Only convert the textureParam to a Texture2D if the parameter is present in the material!
+        if (dynamic->GetTextureParameterValue(FMaterialParameterInfo(textureParam.name), existingTexture))
+        {
+            UTexture2D* texture = MaterialParamTextureToTexture2D(textureParam);
+            if (texture)
+            {
+                dynamic->SetTextureParameterValue(textureParam.name, texture);
+            }
+            else
+            {
+                RMIE_LOG(Error, "Could not convert TextureParam %s from to UTexture2D for MaterialInfo %s", *textureParam.name.ToString(), *materialInfo.name.ToString());
+            }
+        }
+    }
+
+    return dynamic;
+}
+
+float URuntimeMeshImportExportLibrary::RotationCorrectionToValue(const ERotationCorrection correction)
+{
+    switch (correction)
+    {
+    case ERotationCorrection::Minus_90:
+        return -90.f;
+    case ERotationCorrection::Zero:
+        return 0.f;
+    case ERotationCorrection::Plus_90:
+        return +90.f;
+    default:
+        checkNoEntry(); // Every case must be handled
+    }
+    return 0.f;
+}
+
+void URuntimeMeshImportExportLibrary::NewLineAndAppend(FString& appendTo, const FString& append)
+{
+    if (!appendTo.IsEmpty() && appendTo[appendTo.Len() - 1] != *TEXT("\n"))
+    {
+        appendTo += TEXT("\n");
+    }
+    appendTo.Append(append);
+}
+
+void URuntimeMeshImportExportLibrary::OffsetTriangleArray(int32 offset, TArray<int32>& triangles)
+{
+    for (int32& index : triangles)
+    {
+        index += offset;
+    }
+}
+
+FTransform URuntimeMeshImportExportLibrary::AiTransformToFTransform(const aiMatrix4x4& transform)
+{
+    FMatrix tempMatrix;
+    tempMatrix.M[0][0] = transform.a1;
+    tempMatrix.M[0][1] = transform.b1;
+    tempMatrix.M[0][2] = transform.c1;
+    tempMatrix.M[0][3] = transform.d1;
+    tempMatrix.M[1][0] = transform.a2;
+    tempMatrix.M[1][1] = transform.b2;
+    tempMatrix.M[1][2] = transform.c2;
+    tempMatrix.M[1][3] = transform.d2;
+    tempMatrix.M[2][0] = transform.a3;
+    tempMatrix.M[2][1] = transform.b3;
+    tempMatrix.M[2][2] = transform.c3;
+    tempMatrix.M[2][3] = transform.d3;
+    tempMatrix.M[3][0] = transform.a4;
+    tempMatrix.M[3][1] = transform.b4;
+    tempMatrix.M[3][2] = transform.c4;
+    tempMatrix.M[3][3] = transform.d4;
+    return FTransform(tempMatrix);
+}
+
+aiMatrix4x4 URuntimeMeshImportExportLibrary::FTransformToAiTransform(const FTransform& transform)
+{
+    FMatrix uMatrix = transform.ToMatrixWithScale();
+    aiMatrix4x4 outMatrix;
+    outMatrix.a1 = uMatrix.M[0][0];
+    outMatrix.b1 = uMatrix.M[0][1];
+    outMatrix.c1 = uMatrix.M[0][2];
+    outMatrix.d1 = uMatrix.M[0][3];
+    outMatrix.a2 = uMatrix.M[1][0];
+    outMatrix.b2 = uMatrix.M[1][1];
+    outMatrix.c2 = uMatrix.M[1][2];
+    outMatrix.d2 = uMatrix.M[1][3];
+    outMatrix.a3 = uMatrix.M[2][0];
+    outMatrix.b3 = uMatrix.M[2][1];
+    outMatrix.c3 = uMatrix.M[2][2];
+    outMatrix.d3 = uMatrix.M[2][3];
+    outMatrix.a4 = uMatrix.M[3][0];
+    outMatrix.b4 = uMatrix.M[3][1];
+    outMatrix.c4 = uMatrix.M[3][2];
+    outMatrix.d4 = uMatrix.M[3][3];
+    return outMatrix;
+}
+
+void URuntimeMeshImportExportLibrary::SendProgress_AnyThread(FRuntimeMeshImportExportProgressUpdate delegateProgress, FRuntimeMeshImportExportProgress progress)
+{
+    if (IsInGameThread())
+    {
+        delegateProgress.ExecuteIfBound(progress);
+    }
+    else
+    {
+        AsyncTask(ENamedThreads::GameThread, [delegateProgress, progress]() {
+            delegateProgress.ExecuteIfBound(progress);
+        });
+    }
+}
+
+void URuntimeMeshImportExportLibrary::ImportScene_AnyThread(const FRuntimeMeshImportParameter& params
+        , FRuntimeMeshImportExportProgressUpdate callbackProgress
+        , FRuntimeMeshImportResult& result)
+{
+    result.bSuccess = false;
+    result.meshInfos.Empty();
+    result.materialInfos.Empty();
+
+    if (params.file.IsEmpty())
+    {
+        RMIE_LOG(Warning, "No file specified.");
+        return;
+    }
+
+    FString fileFinal;
+    switch (params.pathType)
+    {
+    case EPathType::Absolute:
+        fileFinal = params.file;
+        break;
+    case EPathType::ProjectRelative:
+        fileFinal = FPaths::Combine(FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()), params.file);
+        break;
+    case EPathType::ContentRelative:
+        fileFinal = FPaths::Combine(FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir()), params.file);
+        break;
+    }
+
+    FAssimpProgressHandler progressHandler(callbackProgress);
+    Assimp::Importer importer;
+    importer.SetProgressHandler(&progressHandler);
+    const aiScene* scene = importer.ReadFile(TCHAR_TO_UTF8(*fileFinal), aiProcess_Triangulate | aiProcess_MakeLeftHanded | params.assimpPostProcessFlags.GetAsInt());
+    importer.SetProgressHandler(nullptr);
+    FString importError = FString(importer.GetErrorString());
+    if (importError.Len() > 0)
+    {
+        RMIE_LOG(Error, "Assimp failed to import file. File: %s, Error: %s", *fileFinal, *importError);
+        return;
+    }
+
+    if (scene == nullptr)
+    {
+        RMIE_LOG(Error, "File was imported but scene pointer not valid. File: %s", *fileFinal);
+        return;
+    }
+
+    bool bMeshImportSucces = false;
+    if (scene->HasMeshes())
+    {
+        // Count scene nodes for progress update
+        int32 numNodes = 0;
+        IterateSceneNodes(scene->mRootNode, [&numNodes](aiNode* node) {
+            numNodes++;
+        });
+
+        // Import mesh data
+        int32 nodeCounter = 0;
+        IterateSceneNodes(scene->mRootNode, [scene, &result, &params, &nodeCounter, &numNodes, &callbackProgress](aiNode* node)
+        {
+            FTransform composedNodeTransform;
+            BuildComposedNodeTransform(node, composedNodeTransform);
+            composedNodeTransform = composedNodeTransform * params.transform;
+            ImportMeshesOfNode(scene, node, result, composedNodeTransform);
+            ++nodeCounter;
+            SendProgress_AnyThread(callbackProgress, FRuntimeMeshImportExportProgress(ERuntimeMeshImportExportProgressType::ImportingMeshes, nodeCounter, numNodes));
+        });
+
+        if (result.meshInfos.Num() > 0)
+        {
+            // Handle Mesh Import Methode
+            switch (params.importMethodMesh)
+            {
+            case EImportMethodMesh::Keep:
+                // Do Nothing
+                break;
+            case EImportMethodMesh::Merge:
+                MergeMeshes(result.meshInfos);
+                break;
+
+            default:
+                checkNoEntry();
+            }
+
+            // Handle Section Import Methode
+            switch (params.importMethodSection)
+            {
+            case EImportMethodSection::Keep:
+                // Do Nothing
+                break;
+            case EImportMethodSection::Merge:
+                for (FRuntimeMeshImportMeshInfo& mesh : result.meshInfos)
+                {
+                    MergeAllSections(mesh.sections);
+                }
+                break;
+            case EImportMethodSection::MergeSameMaterial:
+                for (FRuntimeMeshImportMeshInfo& mesh : result.meshInfos)
+                {
+                    MergeSectionsSameMaterial(mesh.sections);
+                }
+                break;
+            default:
+                checkNoEntry();
+            }
+        }
+
+        if (params.bNormalizeScene)
+        {
+            // Get the total bounds of all mesh info
+            FBox totalBounds;
+            totalBounds.Min = FVector(0.f); // Should be initialized already to 0, but had trouble
+            totalBounds.Max = FVector(0.f); // Should be initialized already to 0, but had trouble
+            for (FRuntimeMeshImportMeshInfo& meshInfo : result.meshInfos)
+            {
+                for (FRuntimeMeshImportSectionInfo& sectionInfo : meshInfo.sections)
+                {
+                    FBox currentBounds(sectionInfo.vertices);
+                    totalBounds += currentBounds;
+                }
+            }
+
+            // Use the bounds to transform the scene
+            float scaleFactor = 50.f / totalBounds.GetExtent().GetMax();
+            FVector offset = -FBoxSphereBounds(totalBounds).Origin;
+            for (FRuntimeMeshImportMeshInfo& meshInfo : result.meshInfos)
+            {
+                for (FRuntimeMeshImportSectionInfo& sectionInfo : meshInfo.sections)
+                {
+                    for (FVector& vertex : sectionInfo.vertices)
+                    {
+                        vertex += offset;
+                        vertex *= scaleFactor;
+                    }
+                }
+            }
+        }
+
+        bMeshImportSucces = true;
+    }
+
+    bool bMaterialImportSuccess = false;
+    if (params.importMethodSection != EImportMethodSection::Merge && scene->HasMaterials())
+    {
+        ImportSceneMaterials(fileFinal, scene, result, callbackProgress);
+        bMaterialImportSuccess = true;
+    }
+    else
+    {
+        bMaterialImportSuccess = true;
+    }
+
+
+    result.bSuccess = bMeshImportSucces && bMaterialImportSuccess;
+    return;
+}
+

+ 99 - 0
Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Private/RuntimeMeshImportExportTypes.cpp

@@ -0,0 +1,99 @@
+// MIT License
+//
+// Copyright (c) 2019 Lucid Layers
+
+#include "RuntimeMeshImportExportTypes.h"
+#include "RuntimeMeshImportExportLibrary.h"
+#include "assimp/cexport.h"
+
+void FExportableMeshSection::Append(FExportableMeshSection&& other)
+{
+    check(material == other.material);
+
+
+    int32 triangleOffset = vertices.Num();
+
+    vertices.Append(other.vertices);
+    normals.Append(other.normals);
+    tangents.Append(other.tangents);
+    textureCoordinates.Append(other.textureCoordinates);
+    vertexColors.Append(other.vertexColors);
+
+    triangles.Reserve(triangles.Num() + other.triangles.Num());
+
+    for (int32& tri : other.triangles)
+    {
+        triangles.Add(tri + triangleOffset);
+    }
+}
+
+void FRuntimeMeshImportSectionInfo::Append_Move(FRuntimeMeshImportSectionInfo&& other)
+{
+    URuntimeMeshImportExportLibrary::OffsetTriangleArray(vertices.Num(), other.triangles);
+    vertices.Append(MoveTemp(other.vertices));
+    normals.Append(MoveTemp(other.normals));
+    tangents.Append(MoveTemp(other.tangents));
+    vertexColors.Append(MoveTemp(other.vertexColors));
+    uv0.Append(MoveTemp(other.uv0));
+    triangles.Append(MoveTemp(other.triangles));
+
+    // Retain the material data if it is the same
+    materialName = materialName == other.materialName ? materialName : FName();
+    materialIndex = materialIndex == other.materialIndex? materialIndex : INDEX_NONE;
+    // Clear the other material data
+    other.materialName = FName();
+    other.materialIndex = INDEX_NONE;
+}
+
+FAssimpExportFormat::FAssimpExportFormat(const aiExportFormatDesc* desc) : id(FString(desc->id)), description(FString(desc->description))
+, fileExtension(FString(desc->fileExtension))
+{
+
+}
+
+unsigned int FAssimpImportPostProcessFlags::GetAsInt() const
+{
+	unsigned int flags = 0;
+
+	if (bCalculateTangentSpace)
+	{
+		flags |= aiProcess_CalcTangentSpace;
+	}
+
+	if (bJoinIdenticalVertices)
+	{
+		flags |= aiProcess_JoinIdenticalVertices;
+	}
+
+	if (bGenerateSmoothNormals)
+	{
+		flags |= aiProcess_GenSmoothNormals;
+	}
+
+	if (bImproveCacheLocality)
+	{
+		flags |= aiProcess_ImproveCacheLocality;
+	}
+
+	if (bFixInfacingNormals)
+	{
+		flags |= aiProcess_FixInfacingNormals;
+	}
+
+	if (bFindInstances)
+	{
+		flags |= aiProcess_FindInstances;
+	}
+
+	if (bOptimizeMeshes)
+	{
+		flags |= aiProcess_OptimizeMeshes;
+	}
+
+	if (bDropNormals)
+	{
+		flags |= aiProcess_DropNormals;
+	}
+
+	return flags;
+}

+ 36 - 0
Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Public/AssimpProgressHandler.h

@@ -0,0 +1,36 @@
+
+#pragma once
+
+#include <Assimp/ProgressHandler.hpp>
+#include "RuntimeMeshImportExportTypes.h"
+#include "Async/Async.h"
+#include "RuntimeMeshImportExportLibrary.h"
+
+class FAssimpProgressHandler : public Assimp::ProgressHandler
+{
+public:
+	FAssimpProgressHandler(FRuntimeMeshImportExportProgressUpdate& inDelegateProgress) : delegateProgress(inDelegateProgress) {}
+	FRuntimeMeshImportExportProgressUpdate delegateProgress;
+
+	virtual bool Update(float percentage = -1.f) override
+	{
+		URuntimeMeshImportExportLibrary::SendProgress_AnyThread(delegateProgress, FRuntimeMeshImportExportProgress(ERuntimeMeshImportExportProgressType::Unknown, 100 * percentage, 100));
+		return true;
+	}
+
+	virtual void UpdateFileRead(int currentStep, int numberOfSteps) override
+	{
+		URuntimeMeshImportExportLibrary::SendProgress_AnyThread(delegateProgress, FRuntimeMeshImportExportProgress(ERuntimeMeshImportExportProgressType::AssimpFileRead, currentStep, numberOfSteps));
+	}
+
+	virtual void UpdatePostProcess(int currentStep, int numberOfSteps) override
+	{
+		URuntimeMeshImportExportLibrary::SendProgress_AnyThread(delegateProgress, FRuntimeMeshImportExportProgress(ERuntimeMeshImportExportProgressType::AssimpPostProcess, currentStep, numberOfSteps));
+	}
+
+	virtual void UpdateFileWrite(int currentStep, int numberOfSteps) override
+	{
+		URuntimeMeshImportExportLibrary::SendProgress_AnyThread(delegateProgress, FRuntimeMeshImportExportProgress(ERuntimeMeshImportExportProgressType::AssimpFileWrite, currentStep, numberOfSteps));
+	}
+
+};

+ 44 - 0
Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Public/Interface/MeshExportable.h

@@ -0,0 +1,44 @@
+// MIT License
+//
+// Copyright (c) 2019 Lucid Layers
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "UObject/Interface.h"
+#include "RuntimeMeshImportExportTypes.h"
+#include "MeshExportable.generated.h"
+
+// This class does not need to be modified.
+UINTERFACE(MinimalAPI)
+class UMeshExportable : public UInterface
+{
+    GENERATED_BODY()
+};
+
+/**
+ *
+ */
+class RUNTIMEMESHIMPORTEXPORT_API IMeshExportable 
+{
+    GENERATED_BODY()
+
+    // Add interface functions to this class. This is the class that will be inherited to implement this interface.
+public:
+    /**
+     *	Return the hierarchical name of the Node of this exportable. e.g.: Outer1.Outer2.Outer3.MyNode
+     */
+    UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "MeshExportable")
+    FString GetHierarchicalNodeName() const;
+
+    /**
+     *	@param forLod					The LOD of the mesh to return
+     *	@param bSkipLodNotValid			If true and 'forLod' is not available, do not return mesh data.
+     *									If false and 'forLod' is not available, return the next possible LOD.
+     *	@param outMeshToWorld			Return the transform that transforms the mesh data to WorldSpace
+     *  @param outSectionData			Return the mesh sections defined by 'forLod' and 'bSkipLodNotValid'
+     */
+    UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "MeshExportable")
+    bool GetMeshData(const int32 forLod, const bool bSkipLodNotValid, TArray<FExportableMeshSection>& outSectionData) const;
+
+};

+ 172 - 0
Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Public/RuntimeMeshExporter.h

@@ -0,0 +1,172 @@
+// MIT License
+//
+// Copyright (c) 2019 Lucid Layers
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Interface/MeshExportable.h"
+#include "UObject/NoExportTypes.h"
+#include "Assimp/LogStream.hpp"
+#include "Assimp/Logger.hpp"
+#include "AssimpCustom.h"
+#include "RuntimeMeshImportExportTypes.h"
+#include "Engine/LatentActionManager.h"
+#include "RuntimeMeshExporter.generated.h"
+
+struct aiScene;
+struct aiMesh;
+struct aiNode;
+struct aiMaterial;
+class Assimp::Logger;
+
+/**
+ *	Exporter that uses Assimp library http://www.assimp.org/
+ *
+ *	Objects are placed in Nodes. Nodes are simple transform objects like a SceneComponent in Unreal.
+ *	Nodes can contain Child Nodes to create a node tree (the scene).
+ */
+UCLASS(BlueprintType)
+class RUNTIMEMESHIMPORTEXPORT_API URuntimeMeshExporter : public UObject
+{
+    GENERATED_BODY()
+public:
+
+    URuntimeMeshExporter();
+    ~URuntimeMeshExporter();
+
+    /**
+     *	Adds a new node to the scene or overrides the existing node.
+     *
+     *	@param hierarchicalName			hierarchical name in the format: Outer1.Outer2.Outer3.MyNode
+     *									Outers nodes that do not exist yet, will be created.
+     *									Leave it empty to set the parameters for the root node.
+     *  @param nodeTransformWS			The transform of the node in worldSpace
+     */
+    UFUNCTION(BlueprintCallable, Category = "RuntimeMeshImportExport|Exporter")
+    void AddNode(const FString& hierarchicalName, const FTransform& nodeTransformWS);
+
+    /**
+     *	@param exportable				The object you want to get exported
+     *	@param bOverrideNode			false: the object is asked to return the node name.
+     *									true: 'hierarchicalNodeName' is used to place the object in the scene
+     *  @param hierarchicalNodeName		If bOverrideNode==true this is used to place the object in the scene. e.g.: Outer1.Outer2.Outer3.MyNode
+     */
+    UFUNCTION(BlueprintCallable, Category = "RuntimeMeshImportExport|Exporter")
+    void AddExportObject(const TScriptInterface<IMeshExportable>& exportable, const bool bOverrideNode, const FString& hierarchicalNodeName);
+
+    /**
+     *	@param exportables				The objects you want to get exported
+     *	@param bOverrideNode			false: the objects are asked to return the node name.
+     *									true: 'hierarchicalNodeName' is used to place the objects in the scene
+     *  @param hierarchicalNodeName		If bOverrideNode==true this is used to place the objects in the scene. e.g.: Outer1.Outer2.Outer3.MyNode
+     */
+    UFUNCTION(BlueprintCallable, Category = "RuntimeMeshImportExport|Exporter")
+    void AddExportObjects(const TArray<TScriptInterface<IMeshExportable>>& exportables, const bool bOverrideNode, const FString& hierarchicalNodeName);
+
+    /**
+     *	Try to add a object to the export. Object must inherit from MeshExportable interface.
+     *
+     *	@param object					The object you want to get exported
+     *	@param bOverrideNode			false: the objects are asked to return the node name.
+     *									true: 'hierarchicalNodeName' is used to place the objects in the scene
+     *  @param hierarchicalNodeName		If bOverrideNode==true this is used to place the objects in the scene. e.g.: Outer1.Outer2.Outer3.MyNode
+     *  @returns						true: The object inherits from MeshExportable and was added.
+     *									false: The object does not inherit from MeshExportable and was rejected.
+     */
+    UFUNCTION(BlueprintCallable, Category = "RuntimeMeshImportExport|Exporter")
+    bool AddObjectIfExportable(UObject* object, const bool bOverrideNode, const FString& hierarchicalNodeName);
+
+    /**
+     *	Tries to add an array of objects to the export. Only objects that inherit from MeshExportable interface are added.
+     *
+     *	@param objects					The objects you want to get exported
+     *	@param bOverrideNode			false: the objects are asked to return the node name.
+     *									true: 'hierarchicalNodeName' is used to place the objects in the scene
+     *  @param hierarchicalNodeName		If bOverrideNode==true this is used to place the objects in the scene. e.g.: Outer1.Outer2.Outer3.MyNode
+     *  @param notExportable			Objects that do not implement the MeshExportable interface
+     */
+    UFUNCTION(BlueprintCallable, Category = "RuntimeMeshImportExport|Exporter")
+    void AddObjectsIfExportable(UPARAM(ref) TArray<UObject*>& objects, const bool bOverrideNode, const FString& hierarchicalNodeName, TArray<UObject*>& notExportable);
+
+    /**
+     *	Exports the scene synchronous. This will block the game until export is finished.
+     * 
+     *	@param param		Export parameters
+     *	@param result		Will contain the results of the export operation.
+     */
+    UFUNCTION(BlueprintCallable, Category = "RuntimeMeshImportExport|Exporter")
+    void Export(const FRuntimeMeshExportParam& param, FRuntimeMeshExportResult& result);
+
+
+    /**
+     *	Export the scene asynchronous. Gathering of the mesh data is done in tick on the GameThread. During that time you should not modify the scene
+     *	to ensure consistency of the scene. As soon as the data gathering on the GameThread is done 'callbackGatherDone' is fired and the export process
+     *	is send to another thread where everything else is done.
+     *
+     *	@param param				The parameters for the export
+     *	@param callbackProgress		Callback for a progress update of the exporter
+     *	@param callbackGatherDone	This is fired as soon as the gathering of mesh data on the GameThread is done and it is save to modify the objects
+     *	@param callbackFinished		This is fired when the export finished.
+     */
+    void Export_Async_Cpp(const FRuntimeMeshExportAsyncParam param, FRuntimeMeshImportExportProgressUpdate callbackProgress
+                          , FRuntimeImportExportGameThreadDone callbackGatherDone
+                          , FRuntimeExportFinished callbackFinished);
+
+    /**
+	 *	Export the scene asynchronous. Gathering of the mesh data is done in tick on the GameThread. During that time you should not modify the scene
+	 *	to ensure consistency of the scene. As soon as the data gathering on the GameThread is done 'gatherDoneDelegate' is fired and the export process
+	 *	is send to another thread where everything else is done.
+	 *	
+	 *	@param param				The parameters for the export
+	 *	@param progressDelegate		Callback for a progress update of the exporter
+	 *	@param gatherDoneDelegate	This is fired as soon as the gathering of mesh data on the GameThread is done and it is save to modify the objects
+	 *	@param result				Is filled with the result of the export operation
+     */
+    UFUNCTION(BlueprintCallable, Category = "RuntimeMeshImportExport|Exporter", meta = (WorldContext = "WorldContextObject", Latent, LatentInfo = "latentInfo"))
+    void Export_Async(UObject* worldContextObject, FLatentActionInfo latentInfo, const FRuntimeMeshExportAsyncParam& param
+                      , FRuntimeMeshImportExportProgressUpdateDyn progressDelegate, FRuntimeImportExportGameThreadDoneDyn gatherDoneDelegate
+                      , FRuntimeMeshExportResult& result);
+
+    UFUNCTION(BlueprintPure, Category = "RuntimeMeshImportExport|Exporter")
+    bool GetIsExporting();
+
+private:
+
+    // This function does most of the export work
+    void Export_Async_AnyThread(const FRuntimeMeshExportParam param);
+    void Export_Async_Finish();
+
+    struct FExportLogger : public Assimp::LogStream
+    {
+        FExportLogger(FAssimpScene& inScene) : scene(inScene)
+        { }
+
+        virtual void write(const char* message) override
+        {
+            // log messages from Assimp should already contain a newline
+            scene.WriteToLogWithNewLine(message);
+        }
+
+    private:
+        FAssimpScene& scene;
+    };
+
+    bool bIsExporting = false;
+    FAssimpScene* scene = nullptr;
+
+    bool PreExportWork(const FRuntimeMeshExportParam& param, FRuntimeMeshExportResult& result);
+    bool PostExportWork(FRuntimeMeshExportResult& result);
+
+    Assimp::Logger* assimpLogger = nullptr;
+    FExportLogger* exportLogger = nullptr;
+    aiReturn aiExporterReturn;
+    FString aiExporterError;
+
+    #pragma region Async
+    FRuntimeMeshExportResult asyncResult;
+	FRuntimeMeshImportExportProgressUpdate delegateProgress;
+	FRuntimeImportExportGameThreadDone delegateGatherDone;
+	FRuntimeExportFinished delegateFinished;
+    #pragma endregion Async
+};

+ 22 - 0
Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Public/RuntimeMeshImportExport.h

@@ -0,0 +1,22 @@
+// MIT License
+//
+// Copyright (c) 2019 Lucid Layers
+
+#pragma once
+#include "Modules/ModuleManager.h"
+
+// RuntimeMeshImportExport Module Log
+DECLARE_LOG_CATEGORY_CLASS(LogRuntimeMeshImportExport, Log, All);
+#define RMIE_LOG(Verbosity, Format, ...) UE_LOG(LogRuntimeMeshImportExport, Verbosity, TEXT("%s(%s): %s"), *FString(__FUNCTION__), *FString::FromInt(__LINE__), *FString::Printf(TEXT(Format), ##__VA_ARGS__ ))
+
+class FRuntimeMeshImportExportModule : public IModuleInterface
+{
+public:
+
+	/** IModuleInterface implementation */
+	virtual void StartupModule() override;
+	virtual void ShutdownModule() override;
+
+private:
+	void* dllHandle_assimp = nullptr;
+};

+ 130 - 0
Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Public/RuntimeMeshImportExportLibrary.h

@@ -0,0 +1,130 @@
+// MIT License
+//
+// Copyright (c) 2017 Eric Liu
+// Copyright (c) 2019 Lucid Layers
+
+#pragma once
+
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "LatentActions.h"
+#include "Engine/LatentActionManager.h"
+#include "Interface/MeshExportable.h"
+#include "assimp/matrix4x4.h"
+#include "RuntimeMeshImportExportTypes.h"
+#include "ProceduralMeshComponent.h"
+#include "RuntimeMeshImportExportLibrary.generated.h"
+
+
+/**
+ * Library to import meshes from disk at runtime using Assimp library.
+ */
+UCLASS()
+class RUNTIMEMESHIMPORTEXPORT_API URuntimeMeshImportExportLibrary : public UBlueprintFunctionLibrary
+{
+    GENERATED_BODY()
+
+public:
+
+    /**
+     *	Import a mesh from various scene description files like fbx, gltf, obj, ... . @see GetSupportedExtensionsImport
+     *	Note: The hierarchy of the scene is not retained on import
+     *
+     *	@param params		Import parameter
+     *	@param result		Result of the import
+     */
+    UFUNCTION(BlueprintCallable, Category = "RuntimeMeshImportExport|Import")
+    static void ImportScene(const FRuntimeMeshImportParameter& params, FRuntimeMeshImportResult& result);
+
+    /**
+     *	Import a mesh from various scene description files like fbx, gltf, obj, ... . @see GetSupportedExtensionsImport
+     *	Note: The hierarchy of the scene is not retained on import
+     *
+     *  @param params				Import parameter
+     *	@param progressDelegate		Callback for a progress update of the import
+     */
+    UFUNCTION(BlueprintCallable, Category = "RuntimeMeshImportExport|Import", meta = (Latent, WorldContext = "WorldContextObject", LatentInfo = "latentInfo"))
+    static void	ImportScene_Async(UObject* worldContextObject, FLatentActionInfo latentInfo
+                                  , const FRuntimeMeshImportParameter& params
+                                  , FRuntimeMeshImportExportProgressUpdateDyn progressDelegate
+                                  , FRuntimeMeshImportResult& result);
+
+    /**
+     *	Import a mesh from various scene description files like fbx, gltf, obj, ... . @see GetSupportedExtensionsImpor
+     *	Note: The hierarchy of the scene is not retained on import
+     *
+     *	@param callbackFinished     Called when the Import is finished
+     *  @param progressDelegate		Callback for a progress update of the import
+     */
+    static void ImportScene_Async_Cpp(const FRuntimeMeshImportParameter& params
+                                      , FRuntimeImportFinished callbackFinished
+                                      , FRuntimeMeshImportExportProgressUpdate callbackProgress);
+
+    // Returns all supported extensions for import
+    UFUNCTION(BlueprintPure, Category = "RuntimeMeshImportExport|Import")
+    static bool GetIsExtensionSupportedImport(FString extension);
+
+    // Returns the extensions that are supported for Import
+    UFUNCTION(BlueprintCallable, Category = "RuntimeMeshImportExport|Import")
+    static void GetSupportedExtensionsImport(TArray<FString>& extensions);
+
+    // Returns all supported extensions for export
+    UFUNCTION(BlueprintPure, Category = "RuntimeMeshImportExport|Export")
+    static bool GetIsExtensionSupportedExport(FString extension);
+
+    // Returns the extensions that are supported for export
+    UFUNCTION(BlueprintCallable, Category = "RuntimeMeshImportExport|Export")
+    static void GetSupportedExtensionsExport(TArray<FAssimpExportFormat>& formats);
+
+    // Returns presets for transform corrections that can be used for export
+    UFUNCTION(BlueprintCallable, Category = "RuntimeMeshImportExport|Export")
+    static void GetTransformCorrectionPresetsExport(TMap<FString, FTransformCorrection>& corrections);
+
+    UFUNCTION(BlueprintPure, Category = "RuntimeMeshImportExport")
+    static FTransform TransformCorrectionToTransform(const FTransformCorrection& correction);
+
+    UFUNCTION(BlueprintPure, Category = "RuntimeMeshImportExport")
+    static float RotationCorrectionToValue(const ERotationCorrection correction);
+
+    UFUNCTION(BlueprintPure, Category = "RuntimeMeshImportExport")
+    static void ConvertVectorToProceduralMeshTangent(const TArray<FVector>& tangents, const bool bFlipTangentY, TArray<FProcMeshTangent>& procTangents);
+    UFUNCTION(BlueprintPure, Category = "RuntimeMeshImportExport")
+    static void ConvertProceduralMeshTangentToVector(const TArray<FProcMeshTangent>& procTangents, TArray<FVector>& tangents);
+
+    // Converts the whole MaterialInfo to a string for debugging purpose.
+    UFUNCTION(BlueprintPure, Category = "RuntimeMeshImportExport")
+    static FString MaterialInfoToLogString(const FRuntimeMeshImportMaterialInfo& materialInfo);
+
+    UFUNCTION(BlueprintCallable, Category = "RuntimeMeshImportExport")
+    static UTexture2D* MaterialParamTextureToTexture2D(const FRuntimeMeshImportExportMaterialParamTexture& textureParam);
+
+    /**
+     * Create a DynamicMaterialInstance from a given SourceMaterial and pass in parameters from MaterialInfo.
+     * Only parameters from MaterialInfo that also exist in SourceMaterial can be assigned.
+     * TextureParameter from MaterialInfo will only be loaded as UTexture2D if the parameter is present in SourceMaterial.
+     * The Dnamic material will be name: SOURCEMATERIALNAME_MATERIALINFONAME
+     */
+    UFUNCTION(BlueprintCallable, Category = "RuntimeMeshImportExport", meta = (WorldContext = "worldContextObject"))
+    static UMaterialInstanceDynamic* MaterialInfoToDynamicMaterial(UObject* worldContextObject, const FRuntimeMeshImportMaterialInfo& materialInfo, UMaterialInterface* sourceMaterial);
+
+    // Append 'append' to 'appendTo'. Add a newline before appending if last character of 'appendTo' is not already a newline
+    static void NewLineAndAppend(FString& appendTo, const FString& append);
+
+    static void OffsetTriangleArray(int32 offset, TArray<int32>& triangles);
+
+    static FTransform AiTransformToFTransform(const aiMatrix4x4& transform);
+    static aiMatrix4x4 FTransformToAiTransform(const FTransform& transform);
+
+    static void SendProgress_AnyThread(FRuntimeMeshImportExportProgressUpdate delegateProgress, FRuntimeMeshImportExportProgress progress);
+
+private:
+    /**
+    *	Import a mesh from various scene description files like fbx, gltf, obj, ... . @see GetSupportedExtensionsImport
+    *	Note: The hierarchy of the scene is not retained on import
+    *
+    *	@param params				Import parameter
+    *	@param callbackProgress     Callback for a progress update of the import
+    */
+	static void ImportScene_AnyThread(const FRuntimeMeshImportParameter& params
+                                      , FRuntimeMeshImportExportProgressUpdate callbackProgress
+                                      , FRuntimeMeshImportResult& result);
+};

+ 649 - 0
Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/Public/RuntimeMeshImportExportTypes.h

@@ -0,0 +1,649 @@
+// MIT License
+//
+// Copyright (c) 2019 Lucid Layers
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Materials/MaterialInterface.h"
+#include "RuntimeMeshImportExportTypes.generated.h"
+
+struct FRuntimeMeshExportResult;
+struct FRuntimeMeshImportResult;
+struct FRuntimeMeshImportExportProgress;
+struct aiExportFormatDesc;
+
+
+DECLARE_DELEGATE(FRuntimeImportExportGameThreadDone);
+DECLARE_DYNAMIC_DELEGATE(FRuntimeImportExportGameThreadDoneDyn);
+DECLARE_DELEGATE_OneParam(FRuntimeExportFinished, const FRuntimeMeshExportResult /*result*/);
+DECLARE_DELEGATE_OneParam(FRuntimeImportFinished, const FRuntimeMeshImportResult /*result*/);
+
+UENUM(BlueprintType)
+enum class ERuntimeMeshImportExportProgressType : uint8
+{
+    Nothing = 0,
+    // Only when not sure whats happening. More a precaution then a used type
+    Unknown,
+    // Assimp is reading the file
+    AssimpFileRead,
+    // Assimp is writing the data to a file
+    AssimpFileWrite,
+    // Assimp is processing imported data
+    AssimpPostProcess,
+    // Iterating scene nodes for mesh data to export
+    GatheringMeshs,
+    // Iterating scene nodes for meshes
+    ImportingMeshes,
+    // Importing material data from Assimp to Unreal
+    ImportingMaterials
+};
+
+USTRUCT(BlueprintType)
+struct FRuntimeMeshImportExportProgress
+{
+    GENERATED_BODY()
+    FRuntimeMeshImportExportProgress() {}
+    FRuntimeMeshImportExportProgress(ERuntimeMeshImportExportProgressType inType, int32 inCurrent, int32 inMax) : type(inType), current(inCurrent), max(inMax) {}
+    // The type of operation that is currently being done
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    ERuntimeMeshImportExportProgressType type = ERuntimeMeshImportExportProgressType::Nothing;
+    // The current operation that is done of type
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    int32 current = 0;
+    // The maximum of operations to be done for type
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    int32 max = 0;
+};
+
+DECLARE_DELEGATE_OneParam(FRuntimeMeshImportExportProgressUpdate, const FRuntimeMeshImportExportProgress& /*status*/);
+DECLARE_DYNAMIC_DELEGATE_OneParam(FRuntimeMeshImportExportProgressUpdateDyn, const FRuntimeMeshImportExportProgress&, progress);
+
+USTRUCT(BlueprintType)
+struct FRuntimeMeshExportResult
+{
+    GENERATED_BODY()
+
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bSuccess;
+
+    // 	The log created during export (independent of bLogToUnreal).
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    FString exportLog;
+
+    // Error that might have happened during export.
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    FString error;
+
+    // If this is > 0, you should check the exportLog for reasons.
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    int32 numObjectsSkipped;
+};
+
+UENUM(BlueprintType)
+enum class ERotationCorrection : uint8
+{
+    Minus_90,
+    Zero,
+    Plus_90,
+};
+
+USTRUCT(BlueprintType)
+struct FTransformCorrection
+{
+    GENERATED_BODY();
+
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bFlipX = false;
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bFlipY = false;
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bFlipZ = false;
+
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    ERotationCorrection RollCorrection_X = ERotationCorrection::Zero;
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    ERotationCorrection PitchCorrection_Y = ERotationCorrection::Zero;
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    ERotationCorrection YawCorrection_Z = ERotationCorrection::Zero;
+
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    float scaleFactor = 1;
+};
+
+USTRUCT(BlueprintType)
+struct FRuntimeMeshExportParam
+{
+    GENERATED_BODY()
+
+    // Set to true to combine mesh sections with the same material within the same node
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bCombineSameMaterial = false;
+
+    // The LOD that shall be exported
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    int32 lod = 0;
+
+    // True: Skip the mesh if 'lod' is not available.
+    // False: Mesh shall return the next possible LOD
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bSkipLodNotValid = false;
+
+    // Can be obtained with URuntimeMeshImportExportLibrary::GetSupportedExtensionsExport
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    FString formatId;
+
+    // Export file
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    FString file;
+
+    // Shall we overwrite an existing file
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bOverrideExisting;
+
+    // A correction that is applied to the transform of the RootNode
+    // to make the object display correctly in other software
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    FTransformCorrection correction;
+
+    // Write to Unreals log during export
+    // Note: Important stuff is logged nonetheless.
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bLogToUnreal;
+};
+
+
+USTRUCT(BlueprintType)
+struct FRuntimeMeshExportAsyncParam
+{
+    GENERATED_BODY()
+
+    // The number of mesh data to gather per tick from exportables
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    int32 numGatherPerTick;
+
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    FRuntimeMeshExportParam param;
+};
+
+
+USTRUCT(BlueprintType)
+struct FExportableMeshSection
+{
+    GENERATED_BODY();
+
+    // The transform the mesh has to the world. If the mesh is within a ProceduralMeshComponent,
+    // it is the world transform of the component.
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    FTransform meshToWorld;
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    UMaterialInterface* material;
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    TArray<FVector> vertices;
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    TArray<FVector> normals;
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    TArray<FVector> tangents;
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    TArray<FVector2D> textureCoordinates;
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    TArray<FColor> vertexColors;
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    TArray<int32> triangles;
+
+    // Append other data to this if it has the same material
+    void Append(FExportableMeshSection&& other);
+
+};
+
+UENUM(BlueprintType)
+enum class EPathType : uint8
+{
+    Absolute,
+    ProjectRelative,
+    ContentRelative,
+};
+
+UENUM(BlueprintType)
+enum class EImportMethodMesh : uint8
+{
+    // Keep the meshes separated
+    Keep,
+    // Merge all meshes together to a single mesh
+    Merge,
+};
+
+UENUM(BlueprintType)
+enum class EImportMethodSection : uint8
+{
+    // Keep the sections separated
+    Keep,
+    // Merge all sections of a mesh together
+    Merge,
+    // Merge only the sections of a mesh that have the same material
+    MergeSameMaterial,
+};
+
+USTRUCT(BlueprintType)
+struct FRuntimeMeshImportSectionInfo
+{
+    GENERATED_BODY()
+
+    // Name of the material for this section.
+    // Can be None when everything gets combined.
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    FName materialName;
+
+    // Index of the material in the material list.
+    // Can be -1 when everything gets combined.
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    int32 materialIndex;
+
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    TArray<FVector> vertices;
+
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    TArray<int32> triangles;
+
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    TArray<FVector> normals;
+
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    TArray<FVector2D> uv0;
+
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    TArray<FLinearColor> vertexColors;
+
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    TArray<FVector> tangents;
+
+    // Append other section data to this
+    void Append_Move(FRuntimeMeshImportSectionInfo&& other);
+};
+
+USTRUCT(BlueprintType)
+struct FRuntimeMeshImportMeshInfo
+{
+    GENERATED_BODY()
+
+    // Name of the imported mesh. Name None when merged
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    FName meshName;
+
+    // Mesh material sections
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    TArray<FRuntimeMeshImportSectionInfo> sections;
+};
+
+USTRUCT(BlueprintType)
+struct FRuntimeMeshImportExportMaterialParam
+{
+    GENERATED_BODY()
+
+    FRuntimeMeshImportExportMaterialParam() {}
+    FRuntimeMeshImportExportMaterialParam(FName&& inName) : name(MoveTemp(inName)) {}
+
+    // Material parameter name
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    FName name;
+};
+
+USTRUCT(BlueprintType)
+struct FRuntimeMeshImportExportMaterialParamScalar : public FRuntimeMeshImportExportMaterialParam
+{
+    GENERATED_BODY()
+
+    FRuntimeMeshImportExportMaterialParamScalar() {}
+    FRuntimeMeshImportExportMaterialParamScalar(FName&& inName, const float& inValue)
+        : FRuntimeMeshImportExportMaterialParam(MoveTemp(inName)), value(inValue)
+    {}
+
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    float value = 0.f;
+};
+
+USTRUCT(BlueprintType)
+struct FRuntimeMeshImportExportMaterialParamVector : public FRuntimeMeshImportExportMaterialParam
+{
+    GENERATED_BODY()
+
+    FRuntimeMeshImportExportMaterialParamVector() {}
+    FRuntimeMeshImportExportMaterialParamVector(FName&& inName, const FLinearColor& inValue)
+        : FRuntimeMeshImportExportMaterialParam(MoveTemp(inName)), value(inValue)
+    {}
+
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    FLinearColor value = FLinearColor::Black;
+};
+
+USTRUCT(BlueprintType)
+struct FRuntimeMeshImportExportMaterialParamTexture : public FRuntimeMeshImportExportMaterialParam
+{
+    GENERATED_BODY()
+
+    FRuntimeMeshImportExportMaterialParamTexture() {}
+    FRuntimeMeshImportExportMaterialParamTexture(FName inName) : FRuntimeMeshImportExportMaterialParam(MoveTemp(inName)) {}
+
+    // CONSULT THE ASSIMP DOCUMENTATION ABOUT THE MEANING IN aiTexture
+    int32 width = 0;
+    // CONSULT THE ASSIMP DOCUMENTATION ABOUT THE MEANING IN aiTexture
+    int32 height = 0;
+    // CONSULT THE ASSIMP DOCUMENTATION ABOUT THE MEANING IN aiTexture
+    FString byteDescription;
+
+    // Each texture is imported as byte array.
+    // NOTE: For each texture type e.g. Diffuse, Assimp has a texture stack. Though the plugin for now only imports the first texture within the stack.
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    TArray<uint8> byteData;
+};
+
+
+// Mirror of Assimp material.h::aiShadingMode
+UENUM(BlueprintType)
+enum class ERuntimeMeshImportExportMaterialShadingMode : uint8
+{
+    // Zero entry. Needed for compilation. No valid shading mode
+    REQUIRED = 0x0,
+
+    // Flat shading. Shading is done on per-face base, diffuse only. Also known as 'faceted shading'.
+    Flat = 0x1,
+
+    // Simple Gouraud shading.
+    Gouraud = 0x2,
+
+    // Phong-Shading -
+    Phong = 0x3,
+
+    // Phong-Blinn-Shading
+    Blinn = 0x4,
+
+    // Toon-Shading per pixel. Also known as 'comic' shader.
+    Toon = 0x5,
+
+    // OrenNayar-Shading per pixel. Extension to standard Lambertian shading, taking the roughness of the material into account
+    OrenNayar = 0x6,
+
+    // Minnaert-Shading per pixel. Extension to standard Lambertian shading, taking the "darkness" of the material into account
+    Minnaert = 0x7,
+
+    // CookTorrance-Shading per pixel. Special shader for metallic surfaces.
+    CookTorrance = 0x8,
+
+    // No shading at all. Constant light influence of 1.0.
+    NoShading = 0x9,
+
+    // Fresnel shading
+    Fresnel = 0xa,
+
+    // Only used to determine type
+    PRIVATE_Max,
+
+    Unknown = 0xFF,
+};
+#define MaterialShadingModeFromInt(mode) \
+ (mode == 0 || (uint8)mode > (uint8)ERuntimeMeshImportExportMaterialShadingMode::PRIVATE_Max) \
+        ? ERuntimeMeshImportExportMaterialShadingMode::Unknown \
+        : ERuntimeMeshImportExportMaterialShadingMode((uint8)mode);
+
+// Mirror of Assimp material.h::aiBlendMode
+UENUM(BlueprintType)
+enum class ERuntimeMeshImportExportMaterialBlendMode : uint8
+{
+    Default = 0x0,
+    Additive = 0x1,
+    // Only used to determine type
+    PRIVATE_Max,
+    Unknown = 0xFF,
+};
+#define MaterialBlendModeFromInt(mode) \
+(mode == 0 || (uint8)mode > (uint8)ERuntimeMeshImportExportMaterialBlendMode::PRIVATE_Max) \
+? ERuntimeMeshImportExportMaterialBlendMode::Unknown \
+: ERuntimeMeshImportExportMaterialBlendMode((uint8)mode);
+
+USTRUCT(BlueprintType)
+struct FRuntimeMeshImportMaterialInfo
+{
+    GENERATED_BODY()
+
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    FName name;
+
+    // TwoSided must be specified by the material defaults
+    bool bTwoSided = false;
+    // Wireframe must be specified by the material defaults
+    bool bWireFrame = false;
+
+    // ShadingMode must be specified by the material defaults
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    ERuntimeMeshImportExportMaterialShadingMode shadingMode;
+    // Value read from Assimp material data. Might come in handy if shadingMode == Unknown
+    // -1 if the shading mode was not written to the material
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    int32 shadingModeInt;
+
+    // BlendMode must be specified by the material defaults
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    ERuntimeMeshImportExportMaterialBlendMode blendMode;
+    // Value read from Assimp material data. Might come in handy if blendMode == Unknown
+    // -1 if the blend mode was not written to the material
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    int32 blendModeInt;
+
+    // Scalars that can be specified as parameters in the material graph
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    TArray<FRuntimeMeshImportExportMaterialParamScalar> scalars;
+
+    // Vectors that can be specified as parameters in the material graph
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    TArray<FRuntimeMeshImportExportMaterialParamVector> vectors;
+
+    // Textures that can be specified as parameters in the material graph
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    TArray<FRuntimeMeshImportExportMaterialParamTexture> textures;
+};
+
+USTRUCT(BlueprintType)
+struct FRuntimeMeshImportResult
+{
+    GENERATED_BODY()
+
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    bool bSuccess;
+
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    TArray<FRuntimeMeshImportMeshInfo> meshInfos;
+
+    // Materials will only get imported when sectionImportMethod != EImportMethodSection::Merge
+    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Default")
+    TArray<FRuntimeMeshImportMaterialInfo> materialInfos;
+};
+
+USTRUCT(BlueprintType)
+struct FAssimpExportFormat
+{
+    GENERATED_BODY()
+
+    FAssimpExportFormat() {}
+
+    FAssimpExportFormat(const aiExportFormatDesc* desc);
+
+    // a short string ID to uniquely identify the export format. Use this ID string to
+    // specify which file format you want to export to when calling #aiExportScene().
+    // Example: "dae" or "obj"
+    UPROPERTY(BlueprintReadOnly, Category = "Default")
+    FString id;
+
+    // A short description of the file format to present to users. Useful if you want
+    // to allow the user to select an export format.
+    UPROPERTY(BlueprintReadOnly, Category = "Default")
+    FString description;
+
+    // Recommended file extension for the exported file in lower case.
+    UPROPERTY(BlueprintReadOnly, Category = "Default")
+    FString fileExtension;
+};
+
+/**
+ *	PostProcess flags for the Assimp Importer.
+ *	This are not all possible flags, but the ones that seem useful to be exposed.
+ */
+USTRUCT(BlueprintType)
+struct FAssimpImportPostProcessFlags
+{
+    GENERATED_BODY()
+
+    unsigned int GetAsInt() const;
+
+    /**
+     * Calculates the tangents and bitangents for the imported meshes.
+     *
+     * Does nothing if a mesh does not have normals. You might want this post
+     * processing step to be executed if you plan to use tangent space calculations
+     * such as normal mapping applied to the meshes.
+     */
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bCalculateTangentSpace = true;
+
+    /**
+     * Identifies and joins identical vertex data sets within all
+     * imported meshes.
+     *
+     * After this step is run, each mesh contains unique vertices,
+     * so a vertex may be used by multiple faces. You usually want
+     * to use this post processing step. If your application deals with
+     * indexed geometry, this step is compulsory or you'll just waste rendering
+     * time. <b>If this flag is not specified</b>, no vertices are referenced by
+     * more than one face and <b>no index buffer is required</b> for rendering.
+     */
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bJoinIdenticalVertices = false;
+
+    /**
+     * Generates smooth normals for all vertices in the mesh.
+     *
+     * This is ignored if normals are already there at the time this flag
+     * is evaluated. Model importers try to load them from the source file, so
+     * they're usually already there.
+     */
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bGenerateSmoothNormals = true;
+
+    /**
+     * Reorders triangles for better vertex cache locality.
+     *
+     * The step tries to improve the ACMR (average post-transform vertex cache
+     * miss ratio) for all meshes. The implementation runs in O(n) and is
+     * roughly based on the 'tipsify' algorithm (see <a href="
+     * http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf">this
+     * paper</a>).
+     */
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bImproveCacheLocality = false;
+
+    /**
+     * This step tries to determine which meshes have normal vectors
+     * that are facing inwards and inverts them.
+     *
+     * The algorithm is simple but effective:
+     * the bounding box of all vertices + their normals is compared against
+     * the volume of the bounding box of all vertices without their normals.
+     * This works well for most objects, problems might occur with planar
+     * surfaces. However, the step tries to filter such cases.
+     * The step inverts all in-facing normals. Generally it is recommended
+     * to enable this step, although the result is not always correct.
+     */
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bFixInfacingNormals = false;
+
+    /**
+     *  This step searches for duplicate meshes and replaces them
+     *  with references to the first mesh.
+     *
+     *  This step takes a while, so don't use it if speed is a concern.
+     *  Its main purpose is to workaround the fact that many export
+     *  file formats don't support instanced meshes, so exporters need to
+     *  duplicate meshes. This step removes the duplicates again. Please
+     *  note that Assimp does not currently support per-node material
+     *  assignment to meshes, which means that identical meshes with
+     *  different materials are currently *not* joined, although this is
+     *  planned for future versions.
+     */
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bFindInstances = false;
+
+    /**
+     *  A post-processing step to reduce the number of meshes.
+     *
+     *  This will, in fact, reduce the number of draw calls.
+     *
+     *  This is a very effective optimization and is recommended to be used
+     *  together with #aiProcess_OptimizeGraph, if possible.
+     */
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bOptimizeMeshes = true;
+
+
+    /**
+     *  A post-processing step to optimize the scene hierarchy.
+     *
+     *  Nodes without animations, bones, lights or cameras assigned are
+     *  collapsed and joined.
+     *
+     *  Use this flag with caution. Most simple files will be collapsed to a
+     *  single node, so complex hierarchies are usually completely lost. This is not
+     *  useful for editor environments, but probably a very effective
+     *  optimization if you just want to get the model data, convert it to your
+     *  own format, and render it as fast as possible.
+     *
+     *  This flag is designed to be used with #aiProcess_OptimizeMeshes for best
+     *  results.
+     *
+     *  @note 'Crappy' scenes with thousands of extremely small meshes packed
+     *  in deeply nested nodes exist for almost all file formats.
+     *  #aiProcess_OptimizeMeshes in combination with #aiProcess_OptimizeGraph
+     *  usually fixes them all and makes them renderable.
+     */
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bOptimizeGraph = false;
+
+    /**
+     * Drops normals for all faces of all meshes.
+     *
+     * This is ignored if no normals are present.
+     */
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bDropNormals = false;
+};
+
+USTRUCT(BlueprintType)
+struct FRuntimeMeshImportParameter
+{
+    GENERATED_BODY()
+
+    // The file to import. Depending on 'pathType'
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    FString file;
+
+    // Transform that is applied to the imported scene
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    FTransform transform;
+
+    // Choose whether the 'file' provided is absolute or relative
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    EPathType pathType = EPathType::Absolute;
+
+    // Choose how meshes shall be treated on import (applied before 'importMethodSection')
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    EImportMethodMesh importMethodMesh = EImportMethodMesh::Keep;
+
+    // Choose how mesh sections are treated on import (applied after 'importMethodMesh')
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    EImportMethodSection importMethodSection = EImportMethodSection::MergeSameMaterial;
+
+    // When checked, the scene is transformed to fit into a 100cm cube, objects placed around the center.
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    bool bNormalizeScene = false;
+
+    UPROPERTY(BlueprintReadWrite, Category = "Default")
+    FAssimpImportPostProcessFlags assimpPostProcessFlags;
+};

+ 126 - 0
Plugins/RuntimeMeshImportExport/Source/RuntimeMeshImportExport/RuntimeMeshImportExport.Build.cs

@@ -0,0 +1,126 @@
+// MIT License
+//
+// Copyright (c) 2017 Eric Liu
+// Copyright (c) 2019 Lucid Layers
+
+
+using UnrealBuildTool;
+using System;
+using System.IO;
+using System.Diagnostics;
+
+namespace UnrealBuildTool.Rules
+{
+    public class RuntimeMeshImportExport : ModuleRules
+    {
+        private string ModulePath
+        {
+            get { return ModuleDirectory; }
+        }
+
+        private string ThirdPartyPath
+        {
+            get { return Path.GetFullPath(Path.Combine(ModulePath, "../ThirdParty/")); }
+        }
+
+
+        public RuntimeMeshImportExport(ReadOnlyTargetRules Target) : base(Target)
+        {
+            //Log.TraceInformation("ModulePath: " + ModulePath);
+            //Log.TraceInformation("ThirdPartyPath: " + ThirdPartyPath);
+            PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
+            bEnableExceptions = true;
+
+            PublicDefinitions.AddRange(new string[] { "ASSIMP_DLL" });
+
+            PublicIncludePaths.AddRange(
+                new string[] {
+                Path.Combine(ModuleDirectory, "Public"),
+                Path.Combine(ThirdPartyPath, "assimp/include")
+                    // ... add public include paths required here ...
+                }
+            );
+
+            //foreach(string Pathad in PublicIncludePaths)
+            //{
+            //    Log.TraceInformation("PublicIncludePaths: " + Pathad);
+            //}
+
+            PrivateIncludePaths.AddRange(
+                new string[] {
+                "RuntimeMeshImportExport/Private",
+                "RuntimeMeshImportExport/Public/Interface",
+                "RuntimeMeshImportExport/Private/Interface",
+                    // ... add other private include paths required here ...
+                }
+                );
+
+
+            PublicDependencyModuleNames.AddRange(
+                new string[]
+                {
+                "Core",
+                "CoreUObject",
+                "Engine",
+                "ProceduralMeshComponent"
+               // "RHI",
+               // "RenderCore",
+                    // ... add other public dependencies that you statically link with here ...
+                }
+                );
+
+
+            PrivateDependencyModuleNames.AddRange(
+                new string[]
+                {
+                    //	"Slate",
+                    //	"SlateCore",
+                    // ... add private dependencies that you statically link with here ...	
+                    // 
+                    "Projects"
+                }
+                );
+
+
+            DynamicallyLoadedModuleNames.AddRange(
+                new string[]
+                {
+                }
+                );
+
+            if ((Target.Platform == UnrealTargetPlatform.Win64) || (Target.Platform == UnrealTargetPlatform.Win32))
+            {
+                string PlatformString = (Target.Platform == UnrealTargetPlatform.Win64) ? "x64" : "Win32";
+                string ConfigurationString = (Target.Configuration == UnrealTargetConfiguration.Shipping) ? "Release" : "Debug";
+                string FileNameNoExt = "assimp-vc141-mt" + (Target.Configuration == UnrealTargetConfiguration.Shipping ? "" : "d");
+
+                string libFile = Path.Combine(ThirdPartyPath, "assimp/lib", PlatformString, ConfigurationString, FileNameNoExt + ".lib");
+                if(!File.Exists(libFile))
+                {
+                    //Log.TraceInformation("Missing file: " + libFile);
+                    Console.Error.WriteLine("Missing file: " + libFile);
+                }
+                PublicAdditionalLibraries.Add(libFile);
+
+                string dllFileName = FileNameNoExt + ".dll";
+                string dllFile = Path.Combine(ThirdPartyPath, "assimp/bin", PlatformString, ConfigurationString, dllFileName);
+                if (!File.Exists(dllFile))
+                {
+                    //Log.TraceInformation("Missing file: " + dllFile);
+                    Console.Error.WriteLine("Missing file: " + dllFile);
+                }
+
+                // Does only declare the file as dependency and seems important for packaging. Though it does not load the file.
+                RuntimeDependencies.Add(dllFile);
+                // The .dll is loaded manually with the start of the module. See the AnswerHub entry.
+                // https://answers.unrealengine.com/questions/427772/adding-dll-path-for-plugin.html
+                PublicDelayLoadDLLs.Add(dllFileName);
+            }
+            //else if (Target.Platform == UnrealTargetPlatform.Mac)
+            //{
+            //    string PlatformString = "Mac";
+            //    PublicAdditionalLibraries.Add(Path.Combine(ThirdPartyPath, "assimp/lib", PlatformString, "libassimp.4.1.0.dylib"));
+            //}
+        }
+    }
+}

+ 10 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/LICENSE.txt

@@ -0,0 +1,10 @@
+Copyright (c) 2006-2015 assimp team
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    Neither the name of the assimp team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

BIN
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/bin/x64/Debug/assimp-vc141-mtd.pdb


+ 8 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/.editorconfig

@@ -0,0 +1,8 @@
+# See <http://EditorConfig.org> for details
+
+[*.{h,hpp,inl}]
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+indent_size = 4
+indent_style = space

+ 418 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/BaseImporter.h

@@ -0,0 +1,418 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Definition of the base class for all importer worker classes. */
+#ifndef INCLUDED_AI_BASEIMPORTER_H
+#define INCLUDED_AI_BASEIMPORTER_H
+
+#include "Exceptional.h"
+
+#include <vector>
+#include <set>
+#include <map>
+#include <assimp/types.h>
+#include <assimp/ProgressHandler.hpp>
+#include <assimp/ai_assert.h>
+
+struct aiScene;
+struct aiImporterDesc;
+
+namespace Assimp    {
+
+class Importer;
+class IOSystem;
+class BaseProcess;
+class SharedPostProcessInfo;
+class IOStream;
+
+// utility to do char4 to uint32 in a portable manner
+#define AI_MAKE_MAGIC(string) ((uint32_t)((string[0] << 24) + \
+    (string[1] << 16) + (string[2] << 8) + string[3]))
+
+
+// ---------------------------------------------------------------------------
+/** FOR IMPORTER PLUGINS ONLY: The BaseImporter defines a common interface
+ *  for all importer worker classes.
+ *
+ * The interface defines two functions: CanRead() is used to check if the
+ * importer can handle the format of the given file. If an implementation of
+ * this function returns true, the importer then calls ReadFile() which
+ * imports the given file. ReadFile is not overridable, it just calls
+ * InternReadFile() and catches any ImportErrorException that might occur.
+ */
+class ASSIMP_API BaseImporter {
+    friend class Importer;
+
+private:
+    /* Pushes state into importer for the importer scale */
+    virtual void UpdateImporterScale( Importer* pImp );
+
+public:
+
+    /** Constructor to be privately used by #Importer */
+    BaseImporter() AI_NO_EXCEPT;
+
+    /** Destructor, private as well */
+    virtual ~BaseImporter();
+
+    // -------------------------------------------------------------------
+    /** Returns whether the class can handle the format of the given file.
+     *
+     * The implementation should be as quick as possible. A check for
+     * the file extension is enough. If no suitable loader is found with
+     * this strategy, CanRead() is called again, the 'checkSig' parameter
+     * set to true this time. Now the implementation is expected to
+     * perform a full check of the file structure, possibly searching the
+     * first bytes of the file for magic identifiers or keywords.
+     *
+     * @param pFile Path and file name of the file to be examined.
+     * @param pIOHandler The IO handler to use for accessing any file.
+     * @param checkSig Set to true if this method is called a second time.
+     *   This time, the implementation may take more time to examine the
+     *   contents of the file to be loaded for magic bytes, keywords, etc
+     *   to be able to load files with unknown/not existent file extensions.
+     * @return true if the class can read this file, false if not.
+     */
+    virtual bool CanRead(
+        const std::string& pFile,
+        IOSystem* pIOHandler,
+        bool checkSig
+        ) const = 0;
+
+    // -------------------------------------------------------------------
+    /** Imports the given file and returns the imported data.
+     * If the import succeeds, ownership of the data is transferred to
+     * the caller. If the import fails, NULL is returned. The function
+     * takes care that any partially constructed data is destroyed
+     * beforehand.
+     *
+     * @param pImp #Importer object hosting this loader.
+     * @param pFile Path of the file to be imported.
+     * @param pIOHandler IO-Handler used to open this and possible other files.
+     * @return The imported data or NULL if failed. If it failed a
+     * human-readable error description can be retrieved by calling
+     * GetErrorText()
+     *
+     * @note This function is not intended to be overridden. Implement
+     * InternReadFile() to do the import. If an exception is thrown somewhere
+     * in InternReadFile(), this function will catch it and transform it into
+     *  a suitable response to the caller.
+     */
+    aiScene* ReadFile(
+        Importer* pImp,
+        const std::string& pFile,
+        IOSystem* pIOHandler
+        );
+
+    // -------------------------------------------------------------------
+    /** Returns the error description of the last error that occurred.
+     * @return A description of the last error that occurred. An empty
+     * string if there was no error.
+     */
+    const std::string& GetErrorText() const {
+        return m_ErrorText;
+    }
+
+    // -------------------------------------------------------------------
+    /** Called prior to ReadFile().
+     * The function is a request to the importer to update its configuration
+     * basing on the Importer's configuration property list.
+     * @param pImp Importer instance
+     */
+    virtual void SetupProperties(
+        const Importer* pImp
+        );
+
+    // -------------------------------------------------------------------
+    /** Called by #Importer::GetImporterInfo to get a description of
+     *  some loader features. Importers must provide this information. */
+    virtual const aiImporterDesc* GetInfo() const = 0;
+
+    /**
+     * Will be called only by scale process when scaling is requested.
+     */
+    virtual void SetFileScale(double scale)
+    {
+        fileScale = scale;
+    }
+
+    virtual double GetFileScale() const
+    {
+        return fileScale;
+    }
+
+    enum ImporterUnits {
+        M,
+        MM,
+        CM,
+        INCHES,
+        FEET
+    };
+
+    /**
+     * Assimp Importer
+     * unit conversions available 
+     * if you need another measurment unit add it below.
+     * it's currently defined in assimp that we prefer meters.
+     * */
+    std::map<ImporterUnits, double> importerUnits = {
+        {ImporterUnits::M, 1},
+        {ImporterUnits::CM, 0.01},
+        {ImporterUnits::MM, 0.001},
+        {ImporterUnits::INCHES, 0.0254},
+        {ImporterUnits::FEET, 0.3048}
+    };
+
+    virtual void SetApplicationUnits( const ImporterUnits& unit )
+    {
+        importerScale = importerUnits[unit];
+        applicationUnits = unit;
+    }
+
+    virtual const ImporterUnits& GetApplicationUnits()
+    {
+        return applicationUnits;
+    }
+
+    // -------------------------------------------------------------------
+    /** Called by #Importer::GetExtensionList for each loaded importer.
+     *  Take the extension list contained in the structure returned by
+     *  #GetInfo and insert all file extensions into the given set.
+     *  @param extension set to collect file extensions in*/
+    void GetExtensionList(std::set<std::string>& extensions);
+    
+protected:    
+    ImporterUnits applicationUnits = ImporterUnits::M;
+    double importerScale = 1.0;
+    double fileScale = 1.0;
+
+
+
+    // -------------------------------------------------------------------
+    /** Imports the given file into the given scene structure. The
+     * function is expected to throw an ImportErrorException if there is
+     * an error. If it terminates normally, the data in aiScene is
+     * expected to be correct. Override this function to implement the
+     * actual importing.
+     * <br>
+     *  The output scene must meet the following requirements:<br>
+     * <ul>
+     * <li>At least a root node must be there, even if its only purpose
+     *     is to reference one mesh.</li>
+     * <li>aiMesh::mPrimitiveTypes may be 0. The types of primitives
+     *   in the mesh are determined automatically in this case.</li>
+     * <li>the vertex data is stored in a pseudo-indexed "verbose" format.
+     *   In fact this means that every vertex that is referenced by
+     *   a face is unique. Or the other way round: a vertex index may
+     *   not occur twice in a single aiMesh.</li>
+     * <li>aiAnimation::mDuration may be -1. Assimp determines the length
+     *   of the animation automatically in this case as the length of
+     *   the longest animation channel.</li>
+     * <li>aiMesh::mBitangents may be NULL if tangents and normals are
+     *   given. In this case bitangents are computed as the cross product
+     *   between normal and tangent.</li>
+     * <li>There needn't be a material. If none is there a default material
+     *   is generated. However, it is recommended practice for loaders
+     *   to generate a default material for yourself that matches the
+     *   default material setting for the file format better than Assimp's
+     *   generic default material. Note that default materials *should*
+     *   be named AI_DEFAULT_MATERIAL_NAME if they're just color-shaded
+     *   or AI_DEFAULT_TEXTURED_MATERIAL_NAME if they define a (dummy)
+     *   texture. </li>
+     * </ul>
+     * If the AI_SCENE_FLAGS_INCOMPLETE-Flag is <b>not</b> set:<ul>
+     * <li> at least one mesh must be there</li>
+     * <li> there may be no meshes with 0 vertices or faces</li>
+     * </ul>
+     * This won't be checked (except by the validation step): Assimp will
+     * crash if one of the conditions is not met!
+     *
+     * @param pFile Path of the file to be imported.
+     * @param pScene The scene object to hold the imported data.
+     * NULL is not a valid parameter.
+     * @param pIOHandler The IO handler to use for any file access.
+     * NULL is not a valid parameter. */
+    virtual void InternReadFile(
+        const std::string& pFile,
+        aiScene* pScene,
+        IOSystem* pIOHandler
+        ) = 0;
+
+public: // static utilities
+
+    // -------------------------------------------------------------------
+    /** A utility for CanRead().
+     *
+     *  The function searches the header of a file for a specific token
+     *  and returns true if this token is found. This works for text
+     *  files only. There is a rudimentary handling of UNICODE files.
+     *  The comparison is case independent.
+     *
+     *  @param pIOSystem IO System to work with
+     *  @param file File name of the file
+     *  @param tokens List of tokens to search for
+     *  @param numTokens Size of the token array
+     *  @param searchBytes Number of bytes to be searched for the tokens.
+     */
+    static bool SearchFileHeaderForToken(
+        IOSystem* pIOSystem,
+        const std::string&  file,
+        const char** tokens,
+        unsigned int numTokens,
+        unsigned int searchBytes = 200,
+        bool tokensSol = false,
+        bool noAlphaBeforeTokens = false);
+
+    // -------------------------------------------------------------------
+    /** @brief Check whether a file has a specific file extension
+     *  @param pFile Input file
+     *  @param ext0 Extension to check for. Lowercase characters only, no dot!
+     *  @param ext1 Optional second extension
+     *  @param ext2 Optional third extension
+     *  @note Case-insensitive
+     */
+    static bool SimpleExtensionCheck (
+        const std::string& pFile,
+        const char* ext0,
+        const char* ext1 = NULL,
+        const char* ext2 = NULL);
+
+    // -------------------------------------------------------------------
+    /** @brief Extract file extension from a string
+     *  @param pFile Input file
+     *  @return Extension without trailing dot, all lowercase
+     */
+    static std::string GetExtension (
+        const std::string& pFile);
+
+    // -------------------------------------------------------------------
+    /** @brief Check whether a file starts with one or more magic tokens
+     *  @param pFile Input file
+     *  @param pIOHandler IO system to be used
+     *  @param magic n magic tokens
+     *  @params num Size of magic
+     *  @param offset Offset from file start where tokens are located
+     *  @param Size of one token, in bytes. Maximally 16 bytes.
+     *  @return true if one of the given tokens was found
+     *
+     *  @note For convenience, the check is also performed for the
+     *  byte-swapped variant of all tokens (big endian). Only for
+     *  tokens of size 2,4.
+     */
+    static bool CheckMagicToken(
+        IOSystem* pIOHandler,
+        const std::string& pFile,
+        const void* magic,
+        unsigned int num,
+        unsigned int offset = 0,
+        unsigned int size   = 4);
+
+    // -------------------------------------------------------------------
+    /** An utility for all text file loaders. It converts a file to our
+     *   UTF8 character set. Errors are reported, but ignored.
+     *
+     *  @param data File buffer to be converted to UTF8 data. The buffer
+     *  is resized as appropriate. */
+    static void ConvertToUTF8(
+        std::vector<char>& data);
+
+    // -------------------------------------------------------------------
+    /** An utility for all text file loaders. It converts a file from our
+     *   UTF8 character set back to ISO-8859-1. Errors are reported, but ignored.
+     *
+     *  @param data File buffer to be converted from UTF8 to ISO-8859-1. The buffer
+     *  is resized as appropriate. */
+    static void ConvertUTF8toISO8859_1(
+        std::string& data);
+
+    // -------------------------------------------------------------------
+    /// @brief  Enum to define, if empty files are ok or not.
+    enum TextFileMode { 
+        ALLOW_EMPTY,
+        FORBID_EMPTY 
+    };
+
+    // -------------------------------------------------------------------
+    /** Utility for text file loaders which copies the contents of the
+     *  file into a memory buffer and converts it to our UTF8
+     *  representation.
+     *  @param stream Stream to read from.
+     *  @param data Output buffer to be resized and filled with the
+     *   converted text file data. The buffer is terminated with
+     *   a binary 0.
+     *  @param mode Whether it is OK to load empty text files. */
+    static void TextFileToBuffer(
+        IOStream* stream,
+        std::vector<char>& data,
+        TextFileMode mode = FORBID_EMPTY);
+
+    // -------------------------------------------------------------------
+    /** Utility function to move a std::vector into a aiScene array
+    *  @param vec The vector to be moved
+    *  @param out The output pointer to the allocated array.
+    *  @param numOut The output count of elements copied. */
+    template<typename T>
+    AI_FORCE_INLINE
+    static void CopyVector(
+        std::vector<T>& vec,
+        T*& out,
+        unsigned int& outLength)
+    {
+        outLength = unsigned(vec.size());
+        if (outLength) {
+            out = new T[outLength];
+            std::swap_ranges(vec.begin(), vec.end(), out);
+        }
+    }
+
+protected:
+    /// Error description in case there was one.
+    std::string m_ErrorText;
+    /// Currently set progress handler.
+    ProgressHandler* m_progress;
+};
+
+
+
+} // end of namespace Assimp
+
+#endif // AI_BASEIMPORTER_H_INC

+ 125 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/Bitmap.h

@@ -0,0 +1,125 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Bitmap.h
+ *  @brief Defines bitmap format helper for textures
+ *
+ * Used for file formats which embed their textures into the model file.
+ */
+
+#ifndef AI_BITMAP_H_INC
+#define AI_BITMAP_H_INC
+
+#include "defs.h"
+#include <stdint.h>
+#include <cstddef>
+
+struct aiTexture;
+
+namespace Assimp {
+
+class IOStream;
+
+class ASSIMP_API Bitmap {
+protected:
+
+    struct Header {
+        uint16_t type;
+        uint32_t size;
+        uint16_t reserved1;
+        uint16_t reserved2;
+        uint32_t offset;
+
+        // We define the struct size because sizeof(Header) might return a wrong result because of structure padding.
+        // Moreover, we must use this ugly and error prone syntax because Visual Studio neither support constexpr or sizeof(name_of_field).
+        static const std::size_t header_size =
+            sizeof(uint16_t) + // type
+            sizeof(uint32_t) + // size
+            sizeof(uint16_t) + // reserved1
+            sizeof(uint16_t) + // reserved2
+            sizeof(uint32_t);  // offset
+    };
+
+    struct DIB {
+        uint32_t size;
+        int32_t width;
+        int32_t height;
+        uint16_t planes;
+        uint16_t bits_per_pixel;
+        uint32_t compression;
+        uint32_t image_size;
+        int32_t x_resolution;
+        int32_t y_resolution;
+        uint32_t nb_colors;
+        uint32_t nb_important_colors;
+
+        // We define the struct size because sizeof(DIB) might return a wrong result because of structure padding.
+        // Moreover, we must use this ugly and error prone syntax because Visual Studio neither support constexpr or sizeof(name_of_field).
+        static const std::size_t dib_size =
+            sizeof(uint32_t) + // size
+            sizeof(int32_t) +  // width
+            sizeof(int32_t) +  // height
+            sizeof(uint16_t) + // planes
+            sizeof(uint16_t) + // bits_per_pixel
+            sizeof(uint32_t) + // compression
+            sizeof(uint32_t) + // image_size
+            sizeof(int32_t) +  // x_resolution
+            sizeof(int32_t) +  // y_resolution
+            sizeof(uint32_t) + // nb_colors
+            sizeof(uint32_t);  // nb_important_colors
+    };
+
+    static const std::size_t mBytesPerPixel = 4;
+
+public:
+    static void Save(aiTexture* texture, IOStream* file);
+
+protected:
+    static void WriteHeader(Header& header, IOStream* file);
+    static void WriteDIB(DIB& dib, IOStream* file);
+    static void WriteData(aiTexture* texture, IOStream* file);
+};
+
+}
+
+#endif // AI_BITMAP_H_INC

+ 338 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/BlobIOSystem.h

@@ -0,0 +1,338 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Provides cheat implementations for IOSystem and IOStream to
+ *  redirect exporter output to a blob chain.*/
+
+#ifndef AI_BLOBIOSYSTEM_H_INCLUDED
+#define AI_BLOBIOSYSTEM_H_INCLUDED
+
+#include <assimp/IOStream.hpp>
+#include <assimp/cexport.h>
+#include <assimp/IOSystem.hpp>
+#include <assimp/DefaultLogger.hpp>
+#include <stdint.h>
+#include <set>
+#include <vector>
+
+namespace Assimp    {
+    class BlobIOSystem;
+
+// --------------------------------------------------------------------------------------------
+/** Redirect IOStream to a blob */
+// --------------------------------------------------------------------------------------------
+class BlobIOStream : public IOStream
+{
+public:
+
+    BlobIOStream(BlobIOSystem* creator, const std::string& file, size_t initial = 4096)
+        : buffer()
+        , cur_size()
+        , file_size()
+        , cursor()
+        , initial(initial)
+        , file(file)
+        , creator(creator)
+    {
+    }
+
+
+    virtual ~BlobIOStream();
+
+public:
+
+    // -------------------------------------------------------------------
+    aiExportDataBlob* GetBlob()
+    {
+        aiExportDataBlob* blob = new aiExportDataBlob();
+        blob->size = file_size;
+        blob->data = buffer;
+
+        buffer = NULL;
+
+        return blob;
+    }
+
+
+public:
+
+
+    // -------------------------------------------------------------------
+    virtual size_t Read( void *,
+        size_t,
+        size_t )
+    {
+        return 0;
+    }
+
+    // -------------------------------------------------------------------
+    virtual size_t Write(const void* pvBuffer,
+        size_t pSize,
+        size_t pCount)
+    {
+        pSize *= pCount;
+        if (cursor + pSize > cur_size) {
+            Grow(cursor + pSize);
+        }
+
+        memcpy(buffer+cursor, pvBuffer, pSize);
+        cursor += pSize;
+
+        file_size = std::max(file_size,cursor);
+        return pCount;
+    }
+
+    // -------------------------------------------------------------------
+    virtual aiReturn Seek(size_t pOffset,
+        aiOrigin pOrigin)
+    {
+        switch(pOrigin)
+        {
+        case aiOrigin_CUR:
+            cursor += pOffset;
+            break;
+
+        case aiOrigin_END:
+            cursor = file_size - pOffset;
+            break;
+
+        case aiOrigin_SET:
+            cursor = pOffset;
+            break;
+
+        default:
+            return AI_FAILURE;
+        }
+
+        if (cursor > file_size) {
+            Grow(cursor);
+        }
+
+        file_size = std::max(cursor,file_size);
+        return AI_SUCCESS;
+    }
+
+    // -------------------------------------------------------------------
+    virtual size_t Tell() const
+    {
+        return cursor;
+    }
+
+    // -------------------------------------------------------------------
+    virtual size_t FileSize() const
+    {
+        return file_size;
+    }
+
+    // -------------------------------------------------------------------
+    virtual void Flush()
+    {
+        // ignore
+    }
+
+
+
+private:
+
+    // -------------------------------------------------------------------
+    void Grow(size_t need = 0)
+    {
+        // 1.5 and phi are very heap-friendly growth factors (the first
+        // allows for frequent re-use of heap blocks, the second
+        // forms a fibonacci sequence with similar characteristics -
+        // since this heavily depends on the heap implementation
+        // and other factors as well, i'll just go with 1.5 since
+        // it is quicker to compute).
+        size_t new_size = std::max(initial, std::max( need, cur_size+(cur_size>>1) ));
+
+        const uint8_t* const old = buffer;
+        buffer = new uint8_t[new_size];
+
+        if (old) {
+            memcpy(buffer,old,cur_size);
+            delete[] old;
+        }
+
+        cur_size = new_size;
+    }
+
+private:
+
+    uint8_t* buffer;
+    size_t cur_size,file_size, cursor, initial;
+
+    const std::string file;
+    BlobIOSystem* const creator;
+};
+
+
+#define AI_BLOBIO_MAGIC "$blobfile"
+
+// --------------------------------------------------------------------------------------------
+/** Redirect IOSystem to a blob */
+// --------------------------------------------------------------------------------------------
+class BlobIOSystem : public IOSystem
+{
+
+    friend class BlobIOStream;
+    typedef std::pair<std::string, aiExportDataBlob*> BlobEntry;
+
+public:
+
+    BlobIOSystem()
+    {
+    }
+
+    virtual ~BlobIOSystem()
+    {
+        for(BlobEntry& blobby : blobs) {
+            delete blobby.second;
+        }
+    }
+
+public:
+
+    // -------------------------------------------------------------------
+    const char* GetMagicFileName() const
+    {
+        return AI_BLOBIO_MAGIC;
+    }
+
+
+    // -------------------------------------------------------------------
+    aiExportDataBlob* GetBlobChain()
+    {
+        // one must be the master
+        aiExportDataBlob* master = NULL, *cur;
+        for(const BlobEntry& blobby : blobs) {
+            if (blobby.first == AI_BLOBIO_MAGIC) {
+                master = blobby.second;
+                break;
+            }
+        }
+        if (!master) {
+            ASSIMP_LOG_ERROR("BlobIOSystem: no data written or master file was not closed properly.");
+            return NULL;
+        }
+
+        master->name.Set("");
+
+        cur = master;
+        for(const BlobEntry& blobby : blobs) {
+            if (blobby.second == master) {
+                continue;
+            }
+
+            cur->next = blobby.second;
+            cur = cur->next;
+
+            // extract the file extension from the file written
+            const std::string::size_type s = blobby.first.find_first_of('.');
+            cur->name.Set(s == std::string::npos ? blobby.first : blobby.first.substr(s+1));
+        }
+
+        // give up blob ownership
+        blobs.clear();
+        return master;
+    }
+
+public:
+
+    // -------------------------------------------------------------------
+    virtual bool Exists( const char* pFile) const {
+        return created.find(std::string(pFile)) != created.end();
+    }
+
+
+    // -------------------------------------------------------------------
+    virtual char getOsSeparator() const {
+        return '/';
+    }
+
+
+    // -------------------------------------------------------------------
+    virtual IOStream* Open(const char* pFile,
+        const char* pMode)
+    {
+        if (pMode[0] != 'w') {
+            return NULL;
+        }
+
+        created.insert(std::string(pFile));
+        return new BlobIOStream(this,std::string(pFile));
+    }
+
+    // -------------------------------------------------------------------
+    virtual void Close( IOStream* pFile)
+    {
+        delete pFile;
+    }
+
+private:
+
+    // -------------------------------------------------------------------
+    void OnDestruct(const std::string& filename, BlobIOStream* child)
+    {
+        // we don't know in which the files are closed, so we
+        // can't reliably say that the first must be the master
+        // file ...
+        blobs.push_back( BlobEntry(filename,child->GetBlob()) );
+    }
+
+private:
+    std::set<std::string> created;
+    std::vector< BlobEntry > blobs;
+};
+
+
+// --------------------------------------------------------------------------------------------
+BlobIOStream :: ~BlobIOStream()
+{
+    creator->OnDestruct(file,this);
+    delete[] buffer;
+}
+
+
+} // end Assimp
+
+#endif

+ 287 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/ByteSwapper.h

@@ -0,0 +1,287 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Helper class tp perform various byte oder swappings
+   (e.g. little to big endian) */
+#ifndef AI_BYTESWAPPER_H_INC
+#define AI_BYTESWAPPER_H_INC
+
+#include <assimp/ai_assert.h>
+#include <assimp/types.h>
+#include <stdint.h>
+
+#if _MSC_VER >= 1400
+#include <stdlib.h>
+#endif
+
+namespace Assimp    {
+// --------------------------------------------------------------------------------------
+/** Defines some useful byte order swap routines.
+ *
+ * This is required to read big-endian model formats on little-endian machines,
+ * and vice versa. Direct use of this class is DEPRECATED. Use #StreamReader instead. */
+// --------------------------------------------------------------------------------------
+class ByteSwap {
+    ByteSwap() AI_NO_EXCEPT {}
+
+public:
+
+    // ----------------------------------------------------------------------
+    /** Swap two bytes of data
+     *  @param[inout] _szOut A void* to save the reintcasts for the caller. */
+    static inline void Swap2(void* _szOut)
+    {
+        ai_assert(_szOut);
+
+#if _MSC_VER >= 1400
+        uint16_t* const szOut = reinterpret_cast<uint16_t*>(_szOut);
+        *szOut = _byteswap_ushort(*szOut);
+#else
+        uint8_t* const szOut = reinterpret_cast<uint8_t*>(_szOut);
+        std::swap(szOut[0],szOut[1]);
+#endif
+    }
+
+    // ----------------------------------------------------------------------
+    /** Swap four bytes of data
+     *  @param[inout] _szOut A void* to save the reintcasts for the caller. */
+    static inline void Swap4(void* _szOut)
+    {
+        ai_assert(_szOut);
+
+#if _MSC_VER >= 1400
+        uint32_t* const szOut = reinterpret_cast<uint32_t*>(_szOut);
+        *szOut = _byteswap_ulong(*szOut);
+#else
+        uint8_t* const szOut = reinterpret_cast<uint8_t*>(_szOut);
+        std::swap(szOut[0],szOut[3]);
+        std::swap(szOut[1],szOut[2]);
+#endif
+    }
+
+    // ----------------------------------------------------------------------
+    /** Swap eight bytes of data
+     *  @param[inout] _szOut A void* to save the reintcasts for the caller. */
+    static inline void Swap8(void* _szOut)
+    {
+    ai_assert(_szOut);
+
+#if _MSC_VER >= 1400
+        uint64_t* const szOut = reinterpret_cast<uint64_t*>(_szOut);
+        *szOut = _byteswap_uint64(*szOut);
+#else
+        uint8_t* const szOut = reinterpret_cast<uint8_t*>(_szOut);
+        std::swap(szOut[0],szOut[7]);
+        std::swap(szOut[1],szOut[6]);
+        std::swap(szOut[2],szOut[5]);
+        std::swap(szOut[3],szOut[4]);
+#endif
+    }
+
+    // ----------------------------------------------------------------------
+    /** ByteSwap a float. Not a joke.
+     *  @param[inout] fOut ehm. .. */
+    static inline void Swap(float* fOut) {
+        Swap4(fOut);
+    }
+
+    // ----------------------------------------------------------------------
+    /** ByteSwap a double. Not a joke.
+     *  @param[inout] fOut ehm. .. */
+    static inline void Swap(double* fOut) {
+        Swap8(fOut);
+    }
+
+
+    // ----------------------------------------------------------------------
+    /** ByteSwap an int16t. Not a joke.
+     *  @param[inout] fOut ehm. .. */
+    static inline void Swap(int16_t* fOut) {
+        Swap2(fOut);
+    }
+
+    static inline void Swap(uint16_t* fOut) {
+        Swap2(fOut);
+    }
+
+    // ----------------------------------------------------------------------
+    /** ByteSwap an int32t. Not a joke.
+     *  @param[inout] fOut ehm. .. */
+    static inline void Swap(int32_t* fOut){
+        Swap4(fOut);
+    }
+
+    static inline void Swap(uint32_t* fOut){
+        Swap4(fOut);
+    }
+
+    // ----------------------------------------------------------------------
+    /** ByteSwap an int64t. Not a joke.
+     *  @param[inout] fOut ehm. .. */
+    static inline void Swap(int64_t* fOut) {
+        Swap8(fOut);
+    }
+
+    static inline void Swap(uint64_t* fOut) {
+        Swap8(fOut);
+    }
+
+    // ----------------------------------------------------------------------
+    //! Templatized ByteSwap
+    //! \returns param tOut as swapped
+    template<typename Type>
+    static inline Type Swapped(Type tOut)
+    {
+        return _swapper<Type,sizeof(Type)>()(tOut);
+    }
+
+private:
+
+    template <typename T, size_t size> struct _swapper;
+};
+
+template <typename T> struct ByteSwap::_swapper<T,2> {
+    T operator() (T tOut) {
+        Swap2(&tOut);
+        return tOut;
+    }
+};
+
+template <typename T> struct ByteSwap::_swapper<T,4> {
+    T operator() (T tOut) {
+        Swap4(&tOut);
+        return tOut;
+    }
+};
+
+template <typename T> struct ByteSwap::_swapper<T,8> {
+    T operator() (T tOut) {
+        Swap8(&tOut);
+        return tOut;
+    }
+};
+
+
+// --------------------------------------------------------------------------------------
+// ByteSwap macros for BigEndian/LittleEndian support
+// --------------------------------------------------------------------------------------
+#if (defined AI_BUILD_BIG_ENDIAN)
+#   define AI_LE(t) (t)
+#   define AI_BE(t) ByteSwap::Swapped(t)
+#   define AI_LSWAP2(p)
+#   define AI_LSWAP4(p)
+#   define AI_LSWAP8(p)
+#   define AI_LSWAP2P(p)
+#   define AI_LSWAP4P(p)
+#   define AI_LSWAP8P(p)
+#   define LE_NCONST const
+#   define AI_SWAP2(p) ByteSwap::Swap2(&(p))
+#   define AI_SWAP4(p) ByteSwap::Swap4(&(p))
+#   define AI_SWAP8(p) ByteSwap::Swap8(&(p))
+#   define AI_SWAP2P(p) ByteSwap::Swap2((p))
+#   define AI_SWAP4P(p) ByteSwap::Swap4((p))
+#   define AI_SWAP8P(p) ByteSwap::Swap8((p))
+#   define BE_NCONST
+#else
+#   define AI_BE(t) (t)
+#   define AI_LE(t) ByteSwap::Swapped(t)
+#   define AI_SWAP2(p)
+#   define AI_SWAP4(p)
+#   define AI_SWAP8(p)
+#   define AI_SWAP2P(p)
+#   define AI_SWAP4P(p)
+#   define AI_SWAP8P(p)
+#   define BE_NCONST const
+#   define AI_LSWAP2(p)     ByteSwap::Swap2(&(p))
+#   define AI_LSWAP4(p)     ByteSwap::Swap4(&(p))
+#   define AI_LSWAP8(p)     ByteSwap::Swap8(&(p))
+#   define AI_LSWAP2P(p)    ByteSwap::Swap2((p))
+#   define AI_LSWAP4P(p)    ByteSwap::Swap4((p))
+#   define AI_LSWAP8P(p)    ByteSwap::Swap8((p))
+#   define LE_NCONST
+#endif
+
+
+namespace Intern {
+
+// --------------------------------------------------------------------------------------------
+template <typename T, bool doit>
+struct ByteSwapper  {
+    void operator() (T* inout) {
+        ByteSwap::Swap(inout);
+    }
+};
+
+template <typename T>
+struct ByteSwapper<T,false> {
+    void operator() (T*) {
+    }
+};
+
+// --------------------------------------------------------------------------------------------
+template <bool SwapEndianess, typename T, bool RuntimeSwitch>
+struct Getter {
+    void operator() (T* inout, bool le) {
+#ifdef AI_BUILD_BIG_ENDIAN
+        le =  le;
+#else
+        le =  !le;
+#endif
+        if (le) {
+            ByteSwapper<T,(sizeof(T)>1?true:false)> () (inout);
+        }
+        else ByteSwapper<T,false> () (inout);
+    }
+};
+
+template <bool SwapEndianess, typename T>
+struct Getter<SwapEndianess,T,false> {
+
+    void operator() (T* inout, bool /*le*/) {
+        // static branch
+        ByteSwapper<T,(SwapEndianess && sizeof(T)>1)> () (inout);
+    }
+};
+} // end Intern
+} // end Assimp
+
+#endif //!! AI_BYTESWAPPER_H_INC

+ 22 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/Compiler/poppack1.h

@@ -0,0 +1,22 @@
+
+// ===============================================================================
+// May be included multiple times - resets structure packing to the defaults 
+// for all supported compilers. Reverts the changes made by #include <pushpack1.h> 
+//
+// Currently this works on the following compilers:
+// MSVC 7,8,9
+// GCC
+// BORLAND (complains about 'pack state changed but not reverted', but works)
+// ===============================================================================
+
+#ifndef AI_PUSHPACK_IS_DEFINED
+#	error pushpack1.h must be included after poppack1.h
+#endif
+
+// reset packing to the original value
+#if defined(_MSC_VER) ||  defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
+#	pragma pack( pop )
+#endif
+#undef PACK_STRUCT
+
+#undef AI_PUSHPACK_IS_DEFINED

+ 912 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/Compiler/pstdint.h

@@ -0,0 +1,912 @@
+/*  A portable stdint.h
+ ****************************************************************************
+ *  BSD License:
+ ****************************************************************************
+ *
+ *  Copyright (c) 2005-2016 Paul Hsieh
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************
+ *
+ *  Version 0.1.15.4
+ *
+ *  The ANSI C standard committee, for the C99 standard, specified the
+ *  inclusion of a new standard include file called stdint.h.  This is
+ *  a very useful and long desired include file which contains several
+ *  very precise definitions for integer scalar types that is
+ *  critically important for making portable several classes of
+ *  applications including cryptography, hashing, variable length
+ *  integer libraries and so on.  But for most developers its likely
+ *  useful just for programming sanity.
+ *
+ *  The problem is that some compiler vendors chose to ignore the C99
+ *  standard and some older compilers have no opportunity to be updated.
+ *  Because of this situation, simply including stdint.h in your code
+ *  makes it unportable.
+ *
+ *  So that's what this file is all about.  Its an attempt to build a
+ *  single universal include file that works on as many platforms as
+ *  possible to deliver what stdint.h is supposed to.  Even compilers
+ *  that already come with stdint.h can use this file instead without
+ *  any loss of functionality.  A few things that should be noted about
+ *  this file:
+ *
+ *    1) It is not guaranteed to be portable and/or present an identical
+ *       interface on all platforms.  The extreme variability of the
+ *       ANSI C standard makes this an impossibility right from the
+ *       very get go. Its really only meant to be useful for the vast
+ *       majority of platforms that possess the capability of
+ *       implementing usefully and precisely defined, standard sized
+ *       integer scalars.  Systems which are not intrinsically 2s
+ *       complement may produce invalid constants.
+ *
+ *    2) There is an unavoidable use of non-reserved symbols.
+ *
+ *    3) Other standard include files are invoked.
+ *
+ *    4) This file may come in conflict with future platforms that do
+ *       include stdint.h.  The hope is that one or the other can be
+ *       used with no real difference.
+ *
+ *    5) In the current version, if your platform can't represent
+ *       int32_t, int16_t and int8_t, it just dumps out with a compiler
+ *       error.
+ *
+ *    6) 64 bit integers may or may not be defined.  Test for their
+ *       presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX.
+ *       Note that this is different from the C99 specification which
+ *       requires the existence of 64 bit support in the compiler.  If
+ *       this is not defined for your platform, yet it is capable of
+ *       dealing with 64 bits then it is because this file has not yet
+ *       been extended to cover all of your system's capabilities.
+ *
+ *    7) (u)intptr_t may or may not be defined.  Test for its presence
+ *       with the test: #ifdef PTRDIFF_MAX.  If this is not defined
+ *       for your platform, then it is because this file has not yet
+ *       been extended to cover all of your system's capabilities, not
+ *       because its optional.
+ *
+ *    8) The following might not been defined even if your platform is
+ *       capable of defining it:
+ *
+ *       WCHAR_MIN
+ *       WCHAR_MAX
+ *       (u)int64_t
+ *       PTRDIFF_MIN
+ *       PTRDIFF_MAX
+ *       (u)intptr_t
+ *
+ *    9) The following have not been defined:
+ *
+ *       WINT_MIN
+ *       WINT_MAX
+ *
+ *   10) The criteria for defining (u)int_least(*)_t isn't clear,
+ *       except for systems which don't have a type that precisely
+ *       defined 8, 16, or 32 bit types (which this include file does
+ *       not support anyways). Default definitions have been given.
+ *
+ *   11) The criteria for defining (u)int_fast(*)_t isn't something I
+ *       would trust to any particular compiler vendor or the ANSI C
+ *       committee.  It is well known that "compatible systems" are
+ *       commonly created that have very different performance
+ *       characteristics from the systems they are compatible with,
+ *       especially those whose vendors make both the compiler and the
+ *       system.  Default definitions have been given, but its strongly
+ *       recommended that users never use these definitions for any
+ *       reason (they do *NOT* deliver any serious guarantee of
+ *       improved performance -- not in this file, nor any vendor's
+ *       stdint.h).
+ *
+ *   12) The following macros:
+ *
+ *       PRINTF_INTMAX_MODIFIER
+ *       PRINTF_INT64_MODIFIER
+ *       PRINTF_INT32_MODIFIER
+ *       PRINTF_INT16_MODIFIER
+ *       PRINTF_LEAST64_MODIFIER
+ *       PRINTF_LEAST32_MODIFIER
+ *       PRINTF_LEAST16_MODIFIER
+ *       PRINTF_INTPTR_MODIFIER
+ *
+ *       are strings which have been defined as the modifiers required
+ *       for the "d", "u" and "x" printf formats to correctly output
+ *       (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t,
+ *       (u)least32_t, (u)least16_t and (u)intptr_t types respectively.
+ *       PRINTF_INTPTR_MODIFIER is not defined for some systems which
+ *       provide their own stdint.h.  PRINTF_INT64_MODIFIER is not
+ *       defined if INT64_MAX is not defined.  These are an extension
+ *       beyond what C99 specifies must be in stdint.h.
+ *
+ *       In addition, the following macros are defined:
+ *
+ *       PRINTF_INTMAX_HEX_WIDTH
+ *       PRINTF_INT64_HEX_WIDTH
+ *       PRINTF_INT32_HEX_WIDTH
+ *       PRINTF_INT16_HEX_WIDTH
+ *       PRINTF_INT8_HEX_WIDTH
+ *       PRINTF_INTMAX_DEC_WIDTH
+ *       PRINTF_INT64_DEC_WIDTH
+ *       PRINTF_INT32_DEC_WIDTH
+ *       PRINTF_INT16_DEC_WIDTH
+ *       PRINTF_UINT8_DEC_WIDTH
+ *       PRINTF_UINTMAX_DEC_WIDTH
+ *       PRINTF_UINT64_DEC_WIDTH
+ *       PRINTF_UINT32_DEC_WIDTH
+ *       PRINTF_UINT16_DEC_WIDTH
+ *       PRINTF_UINT8_DEC_WIDTH
+ *
+ *       Which specifies the maximum number of characters required to
+ *       print the number of that type in either hexadecimal or decimal.
+ *       These are an extension beyond what C99 specifies must be in
+ *       stdint.h.
+ *
+ *  Compilers tested (all with 0 warnings at their highest respective
+ *  settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32
+ *  bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio
+ *  .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3
+ *
+ *  This file should be considered a work in progress.  Suggestions for
+ *  improvements, especially those which increase coverage are strongly
+ *  encouraged.
+ *
+ *  Acknowledgements
+ *
+ *  The following people have made significant contributions to the
+ *  development and testing of this file:
+ *
+ *  Chris Howie
+ *  John Steele Scott
+ *  Dave Thorup
+ *  John Dill
+ *  Florian Wobbe
+ *  Christopher Sean Morrison
+ *  Mikkel Fahnoe Jorgensen
+ *
+ */
+
+#include <stddef.h>
+#include <limits.h>
+#include <signal.h>
+
+/*
+ *  For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and
+ *  do nothing else.  On the Mac OS X version of gcc this is _STDINT_H_.
+ */
+
+#if ((defined(__SUNPRO_C) && __SUNPRO_C >= 0x570) || (defined(_MSC_VER) && _MSC_VER >= 1600) || (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (__GNUC__ > 3 || defined(_STDINT_H) || defined(_STDINT_H_) || defined (__UINT_FAST64_TYPE__)) )) && !defined (_PSTDINT_H_INCLUDED)
+#include <stdint.h>
+#define _PSTDINT_H_INCLUDED
+# if defined(__GNUC__) && (defined(__x86_64__) || defined(__ppc64__)) && !(defined(__APPLE__) && defined(__MACH__))
+#  ifndef PRINTF_INT64_MODIFIER
+#   define PRINTF_INT64_MODIFIER "l"
+#  endif
+#  ifndef PRINTF_INT32_MODIFIER
+#   define PRINTF_INT32_MODIFIER ""
+#  endif
+# else
+#  ifndef PRINTF_INT64_MODIFIER
+#   define PRINTF_INT64_MODIFIER "ll"
+#  endif
+#  ifndef PRINTF_INT32_MODIFIER
+#   if (UINT_MAX == UINT32_MAX)
+#    define PRINTF_INT32_MODIFIER ""
+#   else
+#    define PRINTF_INT32_MODIFIER "l"
+#   endif
+#  endif
+# endif
+# ifndef PRINTF_INT16_MODIFIER
+#  define PRINTF_INT16_MODIFIER "h"
+# endif
+# ifndef PRINTF_INTMAX_MODIFIER
+#  define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER
+# endif
+# ifndef PRINTF_INT64_HEX_WIDTH
+#  define PRINTF_INT64_HEX_WIDTH "16"
+# endif
+# ifndef PRINTF_UINT64_HEX_WIDTH
+#  define PRINTF_UINT64_HEX_WIDTH "16"
+# endif
+# ifndef PRINTF_INT32_HEX_WIDTH
+#  define PRINTF_INT32_HEX_WIDTH "8"
+# endif
+# ifndef PRINTF_UINT32_HEX_WIDTH
+#  define PRINTF_UINT32_HEX_WIDTH "8"
+# endif
+# ifndef PRINTF_INT16_HEX_WIDTH
+#  define PRINTF_INT16_HEX_WIDTH "4"
+# endif
+# ifndef PRINTF_UINT16_HEX_WIDTH
+#  define PRINTF_UINT16_HEX_WIDTH "4"
+# endif
+# ifndef PRINTF_INT8_HEX_WIDTH
+#  define PRINTF_INT8_HEX_WIDTH "2"
+# endif
+# ifndef PRINTF_UINT8_HEX_WIDTH
+#  define PRINTF_UINT8_HEX_WIDTH "2"
+# endif
+# ifndef PRINTF_INT64_DEC_WIDTH
+#  define PRINTF_INT64_DEC_WIDTH "19"
+# endif
+# ifndef PRINTF_UINT64_DEC_WIDTH
+#  define PRINTF_UINT64_DEC_WIDTH "20"
+# endif
+# ifndef PRINTF_INT32_DEC_WIDTH
+#  define PRINTF_INT32_DEC_WIDTH "10"
+# endif
+# ifndef PRINTF_UINT32_DEC_WIDTH
+#  define PRINTF_UINT32_DEC_WIDTH "10"
+# endif
+# ifndef PRINTF_INT16_DEC_WIDTH
+#  define PRINTF_INT16_DEC_WIDTH "5"
+# endif
+# ifndef PRINTF_UINT16_DEC_WIDTH
+#  define PRINTF_UINT16_DEC_WIDTH "5"
+# endif
+# ifndef PRINTF_INT8_DEC_WIDTH
+#  define PRINTF_INT8_DEC_WIDTH "3"
+# endif
+# ifndef PRINTF_UINT8_DEC_WIDTH
+#  define PRINTF_UINT8_DEC_WIDTH "3"
+# endif
+# ifndef PRINTF_INTMAX_HEX_WIDTH
+#  define PRINTF_INTMAX_HEX_WIDTH PRINTF_UINT64_HEX_WIDTH
+# endif
+# ifndef PRINTF_UINTMAX_HEX_WIDTH
+#  define PRINTF_UINTMAX_HEX_WIDTH PRINTF_UINT64_HEX_WIDTH
+# endif
+# ifndef PRINTF_INTMAX_DEC_WIDTH
+#  define PRINTF_INTMAX_DEC_WIDTH PRINTF_UINT64_DEC_WIDTH
+# endif
+# ifndef PRINTF_UINTMAX_DEC_WIDTH
+#  define PRINTF_UINTMAX_DEC_WIDTH PRINTF_UINT64_DEC_WIDTH
+# endif
+
+/*
+ *  Something really weird is going on with Open Watcom.  Just pull some of
+ *  these duplicated definitions from Open Watcom's stdint.h file for now.
+ */
+
+# if defined (__WATCOMC__) && __WATCOMC__ >= 1250
+#  if !defined (INT64_C)
+#   define INT64_C(x)   (x + (INT64_MAX - INT64_MAX))
+#  endif
+#  if !defined (UINT64_C)
+#   define UINT64_C(x)  (x + (UINT64_MAX - UINT64_MAX))
+#  endif
+#  if !defined (INT32_C)
+#   define INT32_C(x)   (x + (INT32_MAX - INT32_MAX))
+#  endif
+#  if !defined (UINT32_C)
+#   define UINT32_C(x)  (x + (UINT32_MAX - UINT32_MAX))
+#  endif
+#  if !defined (INT16_C)
+#   define INT16_C(x)   (x)
+#  endif
+#  if !defined (UINT16_C)
+#   define UINT16_C(x)  (x)
+#  endif
+#  if !defined (INT8_C)
+#   define INT8_C(x)   (x)
+#  endif
+#  if !defined (UINT8_C)
+#   define UINT8_C(x)  (x)
+#  endif
+#  if !defined (UINT64_MAX)
+#   define UINT64_MAX  18446744073709551615ULL
+#  endif
+#  if !defined (INT64_MAX)
+#   define INT64_MAX  9223372036854775807LL
+#  endif
+#  if !defined (UINT32_MAX)
+#   define UINT32_MAX  4294967295UL
+#  endif
+#  if !defined (INT32_MAX)
+#   define INT32_MAX  2147483647L
+#  endif
+#  if !defined (INTMAX_MAX)
+#   define INTMAX_MAX INT64_MAX
+#  endif
+#  if !defined (INTMAX_MIN)
+#   define INTMAX_MIN INT64_MIN
+#  endif
+# endif
+#endif
+
+/*
+ *  I have no idea what is the truly correct thing to do on older Solaris.
+ *  From some online discussions, this seems to be what is being
+ *  recommended.  For people who actually are developing on older Solaris,
+ *  what I would like to know is, does this define all of the relevant
+ *  macros of a complete stdint.h?  Remember, in pstdint.h 64 bit is
+ *  considered optional.
+ */
+
+#if (defined(__SUNPRO_C) && __SUNPRO_C >= 0x420) && !defined(_PSTDINT_H_INCLUDED)
+#include <sys/inttypes.h>
+#define _PSTDINT_H_INCLUDED
+#endif
+
+#ifndef _PSTDINT_H_INCLUDED
+#define _PSTDINT_H_INCLUDED
+
+#ifndef SIZE_MAX
+# define SIZE_MAX (~(size_t)0)
+#endif
+
+/*
+ *  Deduce the type assignments from limits.h under the assumption that
+ *  integer sizes in bits are powers of 2, and follow the ANSI
+ *  definitions.
+ */
+
+#ifndef UINT8_MAX
+# define UINT8_MAX 0xff
+#endif
+#if !defined(uint8_t) && !defined(_UINT8_T) && !defined(vxWorks)
+# if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S)
+    typedef unsigned char uint8_t;
+#   define UINT8_C(v) ((uint8_t) v)
+# else
+#   error "Platform not supported"
+# endif
+#endif
+
+#ifndef INT8_MAX
+# define INT8_MAX 0x7f
+#endif
+#ifndef INT8_MIN
+# define INT8_MIN INT8_C(0x80)
+#endif
+#if !defined(int8_t) && !defined(_INT8_T) && !defined(vxWorks)
+# if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S)
+    typedef signed char int8_t;
+#   define INT8_C(v) ((int8_t) v)
+# else
+#   error "Platform not supported"
+# endif
+#endif
+
+#ifndef UINT16_MAX
+# define UINT16_MAX 0xffff
+#endif
+#if !defined(uint16_t) && !defined(_UINT16_T) && !defined(vxWorks)
+#if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S)
+  typedef unsigned int uint16_t;
+# ifndef PRINTF_INT16_MODIFIER
+#  define PRINTF_INT16_MODIFIER ""
+# endif
+# define UINT16_C(v) ((uint16_t) (v))
+#elif (USHRT_MAX == UINT16_MAX)
+  typedef unsigned short uint16_t;
+# define UINT16_C(v) ((uint16_t) (v))
+# ifndef PRINTF_INT16_MODIFIER
+#  define PRINTF_INT16_MODIFIER "h"
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+#ifndef INT16_MAX
+# define INT16_MAX 0x7fff
+#endif
+#ifndef INT16_MIN
+# define INT16_MIN INT16_C(0x8000)
+#endif
+#if !defined(int16_t) && !defined(_INT16_T) && !defined(vxWorks)
+#if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S)
+  typedef signed int int16_t;
+# define INT16_C(v) ((int16_t) (v))
+# ifndef PRINTF_INT16_MODIFIER
+#  define PRINTF_INT16_MODIFIER ""
+# endif
+#elif (SHRT_MAX == INT16_MAX)
+  typedef signed short int16_t;
+# define INT16_C(v) ((int16_t) (v))
+# ifndef PRINTF_INT16_MODIFIER
+#  define PRINTF_INT16_MODIFIER "h"
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+#ifndef UINT32_MAX
+# define UINT32_MAX (0xffffffffUL)
+#endif
+#if !defined(uint32_t) && !defined(_UINT32_T) && !defined(vxWorks)
+#if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S)
+  typedef unsigned long uint32_t;
+# define UINT32_C(v) v ## UL
+# ifndef PRINTF_INT32_MODIFIER
+#  define PRINTF_INT32_MODIFIER "l"
+# endif
+#elif (UINT_MAX == UINT32_MAX)
+  typedef unsigned int uint32_t;
+# ifndef PRINTF_INT32_MODIFIER
+#  define PRINTF_INT32_MODIFIER ""
+# endif
+# define UINT32_C(v) v ## U
+#elif (USHRT_MAX == UINT32_MAX)
+  typedef unsigned short uint32_t;
+# define UINT32_C(v) ((unsigned short) (v))
+# ifndef PRINTF_INT32_MODIFIER
+#  define PRINTF_INT32_MODIFIER ""
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+#ifndef INT32_MAX
+# define INT32_MAX (0x7fffffffL)
+#endif
+#ifndef INT32_MIN
+# define INT32_MIN INT32_C(0x80000000)
+#endif
+#if !defined(int32_t) && !defined(_INT32_T) && !defined(vxWorks)
+#if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S)
+  typedef signed long int32_t;
+# define INT32_C(v) v ## L
+# ifndef PRINTF_INT32_MODIFIER
+#  define PRINTF_INT32_MODIFIER "l"
+# endif
+#elif (INT_MAX == INT32_MAX)
+  typedef signed int int32_t;
+# define INT32_C(v) v
+# ifndef PRINTF_INT32_MODIFIER
+#  define PRINTF_INT32_MODIFIER ""
+# endif
+#elif (SHRT_MAX == INT32_MAX)
+  typedef signed short int32_t;
+# define INT32_C(v) ((short) (v))
+# ifndef PRINTF_INT32_MODIFIER
+#  define PRINTF_INT32_MODIFIER ""
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+/*
+ *  The macro stdint_int64_defined is temporarily used to record
+ *  whether or not 64 integer support is available.  It must be
+ *  defined for any 64 integer extensions for new platforms that are
+ *  added.
+ */
+
+#undef stdint_int64_defined
+#if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S)
+# if (__STDC__ && __STDC_VERSION__ >= 199901L) || defined (S_SPLINT_S)
+#  define stdint_int64_defined
+   typedef long long int64_t;
+   typedef unsigned long long uint64_t;
+#  define UINT64_C(v) v ## ULL
+#  define  INT64_C(v) v ## LL
+#  ifndef PRINTF_INT64_MODIFIER
+#   define PRINTF_INT64_MODIFIER "ll"
+#  endif
+# endif
+#endif
+
+#if !defined (stdint_int64_defined)
+# if defined(__GNUC__) && !defined(vxWorks)
+#  define stdint_int64_defined
+   __extension__ typedef long long int64_t;
+   __extension__ typedef unsigned long long uint64_t;
+#  define UINT64_C(v) v ## ULL
+#  define  INT64_C(v) v ## LL
+#  ifndef PRINTF_INT64_MODIFIER
+#   define PRINTF_INT64_MODIFIER "ll"
+#  endif
+# elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S)
+#  define stdint_int64_defined
+   typedef long long int64_t;
+   typedef unsigned long long uint64_t;
+#  define UINT64_C(v) v ## ULL
+#  define  INT64_C(v) v ## LL
+#  ifndef PRINTF_INT64_MODIFIER
+#   define PRINTF_INT64_MODIFIER "ll"
+#  endif
+# elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC)
+#  define stdint_int64_defined
+   typedef __int64 int64_t;
+   typedef unsigned __int64 uint64_t;
+#  define UINT64_C(v) v ## UI64
+#  define  INT64_C(v) v ## I64
+#  ifndef PRINTF_INT64_MODIFIER
+#   define PRINTF_INT64_MODIFIER "I64"
+#  endif
+# endif
+#endif
+
+#if !defined (LONG_LONG_MAX) && defined (INT64_C)
+# define LONG_LONG_MAX INT64_C (9223372036854775807)
+#endif
+#ifndef ULONG_LONG_MAX
+# define ULONG_LONG_MAX UINT64_C (18446744073709551615)
+#endif
+
+#if !defined (INT64_MAX) && defined (INT64_C)
+# define INT64_MAX INT64_C (9223372036854775807)
+#endif
+#if !defined (INT64_MIN) && defined (INT64_C)
+# define INT64_MIN INT64_C (-9223372036854775808)
+#endif
+#if !defined (UINT64_MAX) && defined (INT64_C)
+# define UINT64_MAX UINT64_C (18446744073709551615)
+#endif
+
+/*
+ *  Width of hexadecimal for number field.
+ */
+
+#ifndef PRINTF_INT64_HEX_WIDTH
+# define PRINTF_INT64_HEX_WIDTH "16"
+#endif
+#ifndef PRINTF_INT32_HEX_WIDTH
+# define PRINTF_INT32_HEX_WIDTH "8"
+#endif
+#ifndef PRINTF_INT16_HEX_WIDTH
+# define PRINTF_INT16_HEX_WIDTH "4"
+#endif
+#ifndef PRINTF_INT8_HEX_WIDTH
+# define PRINTF_INT8_HEX_WIDTH "2"
+#endif
+#ifndef PRINTF_INT64_DEC_WIDTH
+# define PRINTF_INT64_DEC_WIDTH "19"
+#endif
+#ifndef PRINTF_INT32_DEC_WIDTH
+# define PRINTF_INT32_DEC_WIDTH "10"
+#endif
+#ifndef PRINTF_INT16_DEC_WIDTH
+# define PRINTF_INT16_DEC_WIDTH "5"
+#endif
+#ifndef PRINTF_INT8_DEC_WIDTH
+# define PRINTF_INT8_DEC_WIDTH "3"
+#endif
+#ifndef PRINTF_UINT64_DEC_WIDTH
+# define PRINTF_UINT64_DEC_WIDTH "20"
+#endif
+#ifndef PRINTF_UINT32_DEC_WIDTH
+# define PRINTF_UINT32_DEC_WIDTH "10"
+#endif
+#ifndef PRINTF_UINT16_DEC_WIDTH
+# define PRINTF_UINT16_DEC_WIDTH "5"
+#endif
+#ifndef PRINTF_UINT8_DEC_WIDTH
+# define PRINTF_UINT8_DEC_WIDTH "3"
+#endif
+
+/*
+ *  Ok, lets not worry about 128 bit integers for now.  Moore's law says
+ *  we don't need to worry about that until about 2040 at which point
+ *  we'll have bigger things to worry about.
+ */
+
+#ifdef stdint_int64_defined
+  typedef int64_t intmax_t;
+  typedef uint64_t uintmax_t;
+# define  INTMAX_MAX   INT64_MAX
+# define  INTMAX_MIN   INT64_MIN
+# define UINTMAX_MAX  UINT64_MAX
+# define UINTMAX_C(v) UINT64_C(v)
+# define  INTMAX_C(v)  INT64_C(v)
+# ifndef PRINTF_INTMAX_MODIFIER
+#   define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER
+# endif
+# ifndef PRINTF_INTMAX_HEX_WIDTH
+#  define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH
+# endif
+# ifndef PRINTF_INTMAX_DEC_WIDTH
+#  define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH
+# endif
+#else
+  typedef int32_t intmax_t;
+  typedef uint32_t uintmax_t;
+# define  INTMAX_MAX   INT32_MAX
+# define UINTMAX_MAX  UINT32_MAX
+# define UINTMAX_C(v) UINT32_C(v)
+# define  INTMAX_C(v)  INT32_C(v)
+# ifndef PRINTF_INTMAX_MODIFIER
+#   define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER
+# endif
+# ifndef PRINTF_INTMAX_HEX_WIDTH
+#  define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH
+# endif
+# ifndef PRINTF_INTMAX_DEC_WIDTH
+#  define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH
+# endif
+#endif
+
+/*
+ *  Because this file currently only supports platforms which have
+ *  precise powers of 2 as bit sizes for the default integers, the
+ *  least definitions are all trivial.  Its possible that a future
+ *  version of this file could have different definitions.
+ */
+
+#ifndef stdint_least_defined
+  typedef   int8_t   int_least8_t;
+  typedef  uint8_t  uint_least8_t;
+  typedef  int16_t  int_least16_t;
+  typedef uint16_t uint_least16_t;
+  typedef  int32_t  int_least32_t;
+  typedef uint32_t uint_least32_t;
+# define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER
+# define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER
+# define  UINT_LEAST8_MAX  UINT8_MAX
+# define   INT_LEAST8_MAX   INT8_MAX
+# define UINT_LEAST16_MAX UINT16_MAX
+# define  INT_LEAST16_MAX  INT16_MAX
+# define UINT_LEAST32_MAX UINT32_MAX
+# define  INT_LEAST32_MAX  INT32_MAX
+# define   INT_LEAST8_MIN   INT8_MIN
+# define  INT_LEAST16_MIN  INT16_MIN
+# define  INT_LEAST32_MIN  INT32_MIN
+# ifdef stdint_int64_defined
+    typedef  int64_t  int_least64_t;
+    typedef uint64_t uint_least64_t;
+#   define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER
+#   define UINT_LEAST64_MAX UINT64_MAX
+#   define  INT_LEAST64_MAX  INT64_MAX
+#   define  INT_LEAST64_MIN  INT64_MIN
+# endif
+#endif
+#undef stdint_least_defined
+
+/*
+ *  The ANSI C committee pretending to know or specify anything about
+ *  performance is the epitome of misguided arrogance.  The mandate of
+ *  this file is to *ONLY* ever support that absolute minimum
+ *  definition of the fast integer types, for compatibility purposes.
+ *  No extensions, and no attempt to suggest what may or may not be a
+ *  faster integer type will ever be made in this file.  Developers are
+ *  warned to stay away from these types when using this or any other
+ *  stdint.h.
+ */
+
+typedef   int_least8_t   int_fast8_t;
+typedef  uint_least8_t  uint_fast8_t;
+typedef  int_least16_t  int_fast16_t;
+typedef uint_least16_t uint_fast16_t;
+typedef  int_least32_t  int_fast32_t;
+typedef uint_least32_t uint_fast32_t;
+#define  UINT_FAST8_MAX  UINT_LEAST8_MAX
+#define   INT_FAST8_MAX   INT_LEAST8_MAX
+#define UINT_FAST16_MAX UINT_LEAST16_MAX
+#define  INT_FAST16_MAX  INT_LEAST16_MAX
+#define UINT_FAST32_MAX UINT_LEAST32_MAX
+#define  INT_FAST32_MAX  INT_LEAST32_MAX
+#define   INT_FAST8_MIN   INT_LEAST8_MIN
+#define  INT_FAST16_MIN  INT_LEAST16_MIN
+#define  INT_FAST32_MIN  INT_LEAST32_MIN
+#ifdef stdint_int64_defined
+  typedef  int_least64_t  int_fast64_t;
+  typedef uint_least64_t uint_fast64_t;
+# define UINT_FAST64_MAX UINT_LEAST64_MAX
+# define  INT_FAST64_MAX  INT_LEAST64_MAX
+# define  INT_FAST64_MIN  INT_LEAST64_MIN
+#endif
+
+#undef stdint_int64_defined
+
+/*
+ *  Whatever piecemeal, per compiler thing we can do about the wchar_t
+ *  type limits.
+ */
+
+#if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__) && !defined(vxWorks)
+# include <wchar.h>
+# ifndef WCHAR_MIN
+#  define WCHAR_MIN 0
+# endif
+# ifndef WCHAR_MAX
+#  define WCHAR_MAX ((wchar_t)-1)
+# endif
+#endif
+
+/*
+ *  Whatever piecemeal, per compiler/platform thing we can do about the
+ *  (u)intptr_t types and limits.
+ */
+
+#if (defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED)) || defined (_UINTPTR_T)
+# define STDINT_H_UINTPTR_T_DEFINED
+#endif
+
+#ifndef STDINT_H_UINTPTR_T_DEFINED
+# if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64) || defined (__ppc64__)
+#  define stdint_intptr_bits 64
+# elif defined (__WATCOMC__) || defined (__TURBOC__)
+#  if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__)
+#    define stdint_intptr_bits 16
+#  else
+#    define stdint_intptr_bits 32
+#  endif
+# elif defined (__i386__) || defined (_WIN32) || defined (WIN32) || defined (__ppc64__)
+#  define stdint_intptr_bits 32
+# elif defined (__INTEL_COMPILER)
+/* TODO -- what did Intel do about x86-64? */
+# else
+/* #error "This platform might not be supported yet" */
+# endif
+
+# ifdef stdint_intptr_bits
+#  define stdint_intptr_glue3_i(a,b,c)  a##b##c
+#  define stdint_intptr_glue3(a,b,c)    stdint_intptr_glue3_i(a,b,c)
+#  ifndef PRINTF_INTPTR_MODIFIER
+#    define PRINTF_INTPTR_MODIFIER      stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER)
+#  endif
+#  ifndef PTRDIFF_MAX
+#    define PTRDIFF_MAX                 stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX)
+#  endif
+#  ifndef PTRDIFF_MIN
+#    define PTRDIFF_MIN                 stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN)
+#  endif
+#  ifndef UINTPTR_MAX
+#    define UINTPTR_MAX                 stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX)
+#  endif
+#  ifndef INTPTR_MAX
+#    define INTPTR_MAX                  stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX)
+#  endif
+#  ifndef INTPTR_MIN
+#    define INTPTR_MIN                  stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN)
+#  endif
+#  ifndef INTPTR_C
+#    define INTPTR_C(x)                 stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x)
+#  endif
+#  ifndef UINTPTR_C
+#    define UINTPTR_C(x)                stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x)
+#  endif
+  typedef stdint_intptr_glue3(uint,stdint_intptr_bits,_t) uintptr_t;
+  typedef stdint_intptr_glue3( int,stdint_intptr_bits,_t)  intptr_t;
+# else
+/* TODO -- This following is likely wrong for some platforms, and does
+   nothing for the definition of uintptr_t. */
+  typedef ptrdiff_t intptr_t;
+# endif
+# define STDINT_H_UINTPTR_T_DEFINED
+#endif
+
+/*
+ *  Assumes sig_atomic_t is signed and we have a 2s complement machine.
+ */
+
+#ifndef SIG_ATOMIC_MAX
+# define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1)
+#endif
+
+#endif
+
+#if defined (__TEST_PSTDINT_FOR_CORRECTNESS)
+
+/*
+ *  Please compile with the maximum warning settings to make sure macros are
+ *  not defined more than once.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define glue3_aux(x,y,z) x ## y ## z
+#define glue3(x,y,z) glue3_aux(x,y,z)
+
+#define DECLU(bits) glue3(uint,bits,_t) glue3(u,bits,) = glue3(UINT,bits,_C) (0);
+#define DECLI(bits) glue3(int,bits,_t) glue3(i,bits,) = glue3(INT,bits,_C) (0);
+
+#define DECL(us,bits) glue3(DECL,us,) (bits)
+
+#define TESTUMAX(bits) glue3(u,bits,) = ~glue3(u,bits,); if (glue3(UINT,bits,_MAX) != glue3(u,bits,)) printf ("Something wrong with UINT%d_MAX\n", bits)
+
+#define REPORTERROR(msg) { err_n++; if (err_first <= 0) err_first = __LINE__; printf msg; }
+
+int main () {
+	int err_n = 0;
+	int err_first = 0;
+	DECL(I,8)
+	DECL(U,8)
+	DECL(I,16)
+	DECL(U,16)
+	DECL(I,32)
+	DECL(U,32)
+#ifdef INT64_MAX
+	DECL(I,64)
+	DECL(U,64)
+#endif
+	intmax_t imax = INTMAX_C(0);
+	uintmax_t umax = UINTMAX_C(0);
+	char str0[256], str1[256];
+
+	sprintf (str0, "%" PRINTF_INT32_MODIFIER "d", INT32_C(2147483647));
+	if (0 != strcmp (str0, "2147483647")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0));
+	if (atoi(PRINTF_INT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_INT32_DEC_WIDTH : %s\n", PRINTF_INT32_DEC_WIDTH));
+	sprintf (str0, "%" PRINTF_INT32_MODIFIER "u", UINT32_C(4294967295));
+	if (0 != strcmp (str0, "4294967295")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0));
+	if (atoi(PRINTF_UINT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_UINT32_DEC_WIDTH : %s\n", PRINTF_UINT32_DEC_WIDTH));
+#ifdef INT64_MAX
+	sprintf (str1, "%" PRINTF_INT64_MODIFIER "d", INT64_C(9223372036854775807));
+	if (0 != strcmp (str1, "9223372036854775807")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1));
+	if (atoi(PRINTF_INT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_INT64_DEC_WIDTH : %s, %d\n", PRINTF_INT64_DEC_WIDTH, (int) strlen(str1)));
+	sprintf (str1, "%" PRINTF_INT64_MODIFIER "u", UINT64_C(18446744073709550591));
+	if (0 != strcmp (str1, "18446744073709550591")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1));
+	if (atoi(PRINTF_UINT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_UINT64_DEC_WIDTH : %s, %d\n", PRINTF_UINT64_DEC_WIDTH, (int) strlen(str1)));
+#endif
+
+	sprintf (str0, "%d %x\n", 0, ~0);
+
+	sprintf (str1, "%d %x\n",  i8, ~0);
+	if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i8 : %s\n", str1));
+	sprintf (str1, "%u %x\n",  u8, ~0);
+	if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u8 : %s\n", str1));
+	sprintf (str1, "%d %x\n",  i16, ~0);
+	if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i16 : %s\n", str1));
+	sprintf (str1, "%u %x\n",  u16, ~0);
+	if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u16 : %s\n", str1));
+	sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n",  i32, ~0);
+	if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i32 : %s\n", str1));
+	sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n",  u32, ~0);
+	if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u32 : %s\n", str1));
+#ifdef INT64_MAX
+	sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n",  i64, ~0);
+	if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i64 : %s\n", str1));
+#endif
+	sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n",  imax, ~0);
+	if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with imax : %s\n", str1));
+	sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n",  umax, ~0);
+	if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with umax : %s\n", str1));
+
+	TESTUMAX(8);
+	TESTUMAX(16);
+	TESTUMAX(32);
+#ifdef INT64_MAX
+	TESTUMAX(64);
+#endif
+
+#define STR(v) #v
+#define Q(v) printf ("sizeof " STR(v) " = %u\n", (unsigned) sizeof (v));
+	if (err_n) {
+		printf ("pstdint.h is not correct.  Please use sizes below to correct it:\n");
+	}
+
+	Q(int)
+	Q(unsigned)
+	Q(long int)
+	Q(short int)
+	Q(int8_t)
+	Q(int16_t)
+	Q(int32_t)
+#ifdef INT64_MAX
+	Q(int64_t)
+#endif
+
+	return EXIT_SUCCESS;
+}
+
+#endif

+ 43 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/Compiler/pushpack1.h

@@ -0,0 +1,43 @@
+
+
+// ===============================================================================
+// May be included multiple times - sets structure packing to 1 
+// for all supported compilers. #include <poppack1.h> reverts the changes.
+//
+// Currently this works on the following compilers:
+// MSVC 7,8,9
+// GCC
+// BORLAND (complains about 'pack state changed but not reverted', but works)
+// Clang
+//
+//
+// USAGE:
+//
+// struct StructToBePacked {
+// } PACK_STRUCT;
+//
+// ===============================================================================
+
+#ifdef AI_PUSHPACK_IS_DEFINED
+#	error poppack1.h must be included after pushpack1.h
+#endif
+
+#if defined(_MSC_VER) ||  defined(__BORLANDC__) ||	defined (__BCPLUSPLUS__)
+#	pragma pack(push,1)
+#	define PACK_STRUCT
+#elif defined( __GNUC__ ) || defined(__clang__)
+#	if !defined(HOST_MINGW)
+#		define PACK_STRUCT	__attribute__((__packed__))
+#	else
+#		define PACK_STRUCT	__attribute__((gcc_struct, __packed__))
+#	endif
+#else
+#	error Compiler not supported
+#endif
+
+#if defined(_MSC_VER)
+// C4103: Packing was changed after the inclusion of the header, probably missing #pragma pop
+#	pragma warning (disable : 4103) 
+#endif
+
+#define AI_PUSHPACK_IS_DEFINED

+ 58 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/CreateAnimMesh.h

@@ -0,0 +1,58 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file CreateAnimMesh.h
+ *  Create AnimMesh from Mesh
+ */
+#ifndef INCLUDED_AI_CREATE_ANIM_MESH_H
+#define INCLUDED_AI_CREATE_ANIM_MESH_H
+
+#include <assimp/mesh.h>
+
+namespace Assimp    {
+
+/** Create aiAnimMesh from aiMesh. */
+ASSIMP_API aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh);
+
+} // end of namespace Assimp
+#endif // INCLUDED_AI_CREATE_ANIM_MESH_H
+

+ 140 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/DefaultIOStream.h

@@ -0,0 +1,140 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Default file I/O using fXXX()-family of functions */
+#ifndef AI_DEFAULTIOSTREAM_H_INC
+#define AI_DEFAULTIOSTREAM_H_INC
+
+#include <stdio.h>
+#include <assimp/IOStream.hpp>
+#include <assimp/importerdesc.h>
+#include <assimp/Defines.h>
+
+namespace Assimp    {
+
+// ----------------------------------------------------------------------------------
+//! @class  DefaultIOStream
+//! @brief  Default IO implementation, use standard IO operations
+//! @note   An instance of this class can exist without a valid file handle
+//!         attached to it. All calls fail, but the instance can nevertheless be
+//!         used with no restrictions.
+class ASSIMP_API DefaultIOStream : public IOStream
+{
+    friend class DefaultIOSystem;
+#if __ANDROID__
+# if __ANDROID_API__ > 9
+#  if defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT)
+    friend class AndroidJNIIOSystem;
+#  endif // defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT)
+# endif // __ANDROID_API__ > 9
+#endif // __ANDROID__
+
+protected:
+    DefaultIOStream() AI_NO_EXCEPT;
+    DefaultIOStream(FILE* pFile, const std::string &strFilename);
+
+public:
+    /** Destructor public to allow simple deletion to close the file. */
+    ~DefaultIOStream ();
+
+    // -------------------------------------------------------------------
+    /// Read from stream
+    size_t Read(void* pvBuffer,
+        size_t pSize,
+        size_t pCount);
+
+
+    // -------------------------------------------------------------------
+    /// Write to stream
+    size_t Write(const void* pvBuffer,
+        size_t pSize,
+        size_t pCount);
+
+    // -------------------------------------------------------------------
+    /// Seek specific position
+    aiReturn Seek(size_t pOffset,
+        aiOrigin pOrigin);
+
+    // -------------------------------------------------------------------
+    /// Get current seek position
+    size_t Tell() const;
+
+    // -------------------------------------------------------------------
+    /// Get size of file
+    size_t FileSize() const;
+
+    // -------------------------------------------------------------------
+    /// Flush file contents
+    void Flush();
+
+private:
+    //  File data-structure, using clib
+    FILE* mFile;
+    //  Filename
+    std::string mFilename;
+    // Cached file size
+    mutable size_t mCachedSize;
+};
+
+// ----------------------------------------------------------------------------------
+inline
+DefaultIOStream::DefaultIOStream() AI_NO_EXCEPT
+: mFile(nullptr)
+, mFilename("")
+, mCachedSize(SIZE_MAX) {
+    // empty
+}
+
+// ----------------------------------------------------------------------------------
+inline
+DefaultIOStream::DefaultIOStream (FILE* pFile, const std::string &strFilename)
+: mFile(pFile)
+, mFilename(strFilename)
+, mCachedSize(SIZE_MAX) {
+    // empty
+}
+// ----------------------------------------------------------------------------------
+
+} // ns assimp
+
+#endif //!!AI_DEFAULTIOSTREAM_H_INC
+

+ 93 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/DefaultIOSystem.h

@@ -0,0 +1,93 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Default implementation of IOSystem using the standard C file functions */
+#ifndef AI_DEFAULTIOSYSTEM_H_INC
+#define AI_DEFAULTIOSYSTEM_H_INC
+
+#include <assimp/IOSystem.hpp>
+
+namespace Assimp    {
+
+// ---------------------------------------------------------------------------
+/** Default implementation of IOSystem using the standard C file functions */
+class ASSIMP_API DefaultIOSystem : public IOSystem {
+public:
+    // -------------------------------------------------------------------
+    /** Tests for the existence of a file at the given path. */
+    bool Exists( const char* pFile) const;
+
+    // -------------------------------------------------------------------
+    /** Returns the directory separator. */
+    char getOsSeparator() const;
+
+    // -------------------------------------------------------------------
+    /** Open a new file with a given path. */
+    IOStream* Open( const char* pFile, const char* pMode = "rb");
+
+    // -------------------------------------------------------------------
+    /** Closes the given file and releases all resources associated with it. */
+    void Close( IOStream* pFile);
+
+    // -------------------------------------------------------------------
+    /** Compare two paths */
+    bool ComparePaths (const char* one, const char* second) const;
+
+    /** @brief get the file name of a full filepath
+     * example: /tmp/archive.tar.gz -> archive.tar.gz
+     */
+    static std::string fileName( const std::string &path );
+
+    /** @brief get the complete base name of a full filepath
+     * example: /tmp/archive.tar.gz -> archive.tar
+     */
+    static std::string completeBaseName( const std::string &path);
+
+    /** @brief get the path of a full filepath
+     * example: /tmp/archive.tar.gz -> /tmp/
+     */
+    static std::string absolutePath( const std::string &path);
+};
+
+} //!ns Assimp
+
+#endif //AI_DEFAULTIOSYSTEM_H_INC

+ 188 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/DefaultLogger.hpp

@@ -0,0 +1,188 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/** @file DefaultLogger.hpp
+*/
+
+#ifndef INCLUDED_AI_DEFAULTLOGGER
+#define INCLUDED_AI_DEFAULTLOGGER
+
+#include "Logger.hpp"
+#include "LogStream.hpp"
+#include "NullLogger.hpp"
+#include <vector>
+
+namespace Assimp    {
+// ------------------------------------------------------------------------------------
+class IOStream;
+struct LogStreamInfo;
+
+/** default name of logfile */
+#define ASSIMP_DEFAULT_LOG_NAME "AssimpLog.txt"
+
+// ------------------------------------------------------------------------------------
+/** @brief CPP-API: Primary logging facility of Assimp.
+ *
+ *  The library stores its primary #Logger as a static member of this class.
+ *  #get() returns this primary logger. By default the underlying implementation is
+ *  just a #NullLogger which rejects all log messages. By calling #create(), logging
+ *  is turned on. To capture the log output multiple log streams (#LogStream) can be
+ *  attach to the logger. Some default streams for common streaming locations (such as
+ *  a file, std::cout, OutputDebugString()) are also provided.
+ *
+ *  If you wish to customize the logging at an even deeper level supply your own
+ *  implementation of #Logger to #set().
+ *  @note The whole logging stuff causes a small extra overhead for all imports. */
+class ASSIMP_API DefaultLogger :
+    public Logger   {
+
+public:
+
+    // ----------------------------------------------------------------------
+    /** @brief Creates a logging instance.
+     *  @param name Name for log file. Only valid in combination
+     *    with the aiDefaultLogStream_FILE flag.
+     *  @param severity Log severity, VERBOSE turns on debug messages
+     *  @param defStreams  Default log streams to be attached. Any bitwise
+     *    combination of the aiDefaultLogStream enumerated values.
+     *    If #aiDefaultLogStream_FILE is specified but an empty string is
+     *    passed for 'name', no log file is created at all.
+     *  @param  io IOSystem to be used to open external files (such as the
+     *   log file). Pass NULL to rely on the default implementation.
+     *  This replaces the default #NullLogger with a #DefaultLogger instance. */
+    static Logger *create(const char* name = ASSIMP_DEFAULT_LOG_NAME,
+        LogSeverity severity    = NORMAL,
+        unsigned int defStreams = aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE,
+        IOSystem* io            = NULL);
+
+    // ----------------------------------------------------------------------
+    /** @brief Setup a custom #Logger implementation.
+     *
+     *  Use this if the provided #DefaultLogger class doesn't fit into
+     *  your needs. If the provided message formatting is OK for you,
+     *  it's much easier to use #create() and to attach your own custom
+     *  output streams to it.
+     *  @param logger Pass NULL to setup a default NullLogger*/
+    static void set (Logger *logger);
+
+    // ----------------------------------------------------------------------
+    /** @brief  Getter for singleton instance
+     *   @return Only instance. This is never null, but it could be a
+     *  NullLogger. Use isNullLogger to check this.*/
+    static Logger *get();
+
+    // ----------------------------------------------------------------------
+    /** @brief  Return whether a #NullLogger is currently active
+     *  @return true if the current logger is a #NullLogger.
+     *  Use create() or set() to setup a logger that does actually do
+     *  something else than just rejecting all log messages. */
+    static bool isNullLogger();
+
+    // ----------------------------------------------------------------------
+    /** @brief  Kills the current singleton logger and replaces it with a
+     *  #NullLogger instance. */
+    static void kill();
+
+    // ----------------------------------------------------------------------
+    /** @copydoc Logger::attachStream   */
+    bool attachStream(LogStream *pStream,
+        unsigned int severity);
+
+    // ----------------------------------------------------------------------
+    /** @copydoc Logger::detatchStream */
+    bool detatchStream(LogStream *pStream,
+        unsigned int severity);
+
+private:
+    // ----------------------------------------------------------------------
+    /** @briefPrivate construction for internal use by create().
+     *  @param severity Logging granularity  */
+    explicit DefaultLogger(LogSeverity severity);
+
+    // ----------------------------------------------------------------------
+    /** @briefDestructor    */
+    ~DefaultLogger();
+
+    /** @brief  Logs debug infos, only been written when severity level VERBOSE is set */
+    void OnDebug(const char* message);
+
+    /** @brief  Logs an info message */
+    void OnInfo(const char*  message);
+
+    /** @brief  Logs a warning message */
+    void OnWarn(const char*  message);
+
+    /** @brief  Logs an error message */
+    void OnError(const char* message);
+
+    // ----------------------------------------------------------------------
+    /** @brief Writes a message to all streams */
+    void WriteToStreams(const char* message, ErrorSeverity ErrorSev );
+
+    // ----------------------------------------------------------------------
+    /** @brief Returns the thread id.
+     *  @note This is an OS specific feature, if not supported, a
+     *    zero will be returned.
+     */
+    unsigned int GetThreadID();
+
+private:
+    //  Aliases for stream container
+    typedef std::vector<LogStreamInfo*> StreamArray;
+    typedef std::vector<LogStreamInfo*>::iterator StreamIt;
+    typedef std::vector<LogStreamInfo*>::const_iterator ConstStreamIt;
+
+    //! only logging instance
+    static Logger *m_pLogger;
+    static NullLogger s_pNullLogger;
+
+    //! Attached streams
+    StreamArray m_StreamArray;
+
+    bool noRepeatMsg;
+    char lastMsg[MAX_LOG_MESSAGE_LENGTH*2];
+    size_t lastLen;
+};
+// ------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // !! INCLUDED_AI_DEFAULTLOGGER

+ 49 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/Defines.h

@@ -0,0 +1,49 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+// We need those constants, workaround for any platforms where nobody defined them yet
+#if (!defined SIZE_MAX)
+#   define SIZE_MAX (~((size_t)0))
+#endif
+
+#if (!defined UINT_MAX)
+#   define UINT_MAX (~((unsigned int)0))
+#endif
+

+ 125 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/Exceptional.h

@@ -0,0 +1,125 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef INCLUDED_EXCEPTIONAL_H
+#define INCLUDED_EXCEPTIONAL_H
+
+#include <stdexcept>
+#include <assimp/DefaultIOStream.h>
+using std::runtime_error;
+
+#ifdef _MSC_VER
+#   pragma warning(disable : 4275)
+#endif
+
+// ---------------------------------------------------------------------------
+/** FOR IMPORTER PLUGINS ONLY: Simple exception class to be thrown if an
+ *  unrecoverable error occurs while importing. Loading APIs return
+ *  NULL instead of a valid aiScene then.  */
+class DeadlyImportError
+    : public runtime_error
+{
+public:
+    /** Constructor with arguments */
+    explicit DeadlyImportError( const std::string& errorText)
+        : runtime_error(errorText)
+    {
+    }
+
+private:
+};
+
+typedef DeadlyImportError DeadlyExportError;
+
+#ifdef _MSC_VER
+#   pragma warning(default : 4275)
+#endif
+
+// ---------------------------------------------------------------------------
+template <typename T>
+struct ExceptionSwallower   {
+    T operator ()() const {
+        return T();
+    }
+};
+
+// ---------------------------------------------------------------------------
+template <typename T>
+struct ExceptionSwallower<T*>   {
+    T* operator ()() const {
+        return NULL;
+    }
+};
+
+// ---------------------------------------------------------------------------
+template <>
+struct ExceptionSwallower<aiReturn> {
+    aiReturn operator ()() const {
+        try {
+            throw;
+        }
+        catch (std::bad_alloc&) {
+            return aiReturn_OUTOFMEMORY;
+        }
+        catch (...) {
+            return aiReturn_FAILURE;
+        }
+    }
+};
+
+// ---------------------------------------------------------------------------
+template <>
+struct ExceptionSwallower<void> {
+    void operator ()() const {
+        return;
+    }
+};
+
+#define ASSIMP_BEGIN_EXCEPTION_REGION()\
+{\
+    try {
+
+#define ASSIMP_END_EXCEPTION_REGION(type)\
+    } catch(...) {\
+        return ExceptionSwallower<type>()();\
+    }\
+}
+
+#endif // INCLUDED_EXCEPTIONAL_H

+ 505 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/Exporter.hpp

@@ -0,0 +1,505 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file  Exporter.hpp
+*  @brief Defines the CPP-API for the Assimp export interface
+*/
+#pragma once
+#ifndef AI_EXPORT_HPP_INC
+#define AI_EXPORT_HPP_INC
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+
+#include "cexport.h"
+#include <map>
+
+namespace Assimp {
+    
+class ExporterPimpl;
+class IOSystem;
+class ProgressHandler;
+
+// ----------------------------------------------------------------------------------
+/** CPP-API: The Exporter class forms an C++ interface to the export functionality
+ * of the Open Asset Import Library. Note that the export interface is available
+ * only if Assimp has been built with ASSIMP_BUILD_NO_EXPORT not defined.
+ *
+ * The interface is modeled after the importer interface and mostly
+ * symmetric. The same rules for threading etc. apply.
+ *
+ * In a nutshell, there are two export interfaces: #Export, which writes the
+ * output file(s) either to the regular file system or to a user-supplied
+ * #IOSystem, and #ExportToBlob which returns a linked list of memory
+ * buffers (blob), each referring to one output file (in most cases
+ * there will be only one output file of course, but this extra complexity is
+ * needed since Assimp aims at supporting a wide range of file formats).
+ *
+ * #ExportToBlob is especially useful if you intend to work
+ * with the data in-memory.
+*/
+class ASSIMP_API ExportProperties;
+
+class ASSIMP_API Exporter {
+public:
+    /** Function pointer type of a Export worker function */
+    typedef void (*fpExportFunc)(const char*, IOSystem*, const aiScene*, const ExportProperties*);
+
+    /** Internal description of an Assimp export format option */
+    struct ExportFormatEntry {
+        /// Public description structure to be returned by aiGetExportFormatDescription()
+        aiExportFormatDesc mDescription;
+
+        // Worker function to do the actual exporting
+        fpExportFunc mExportFunction;
+
+        // Post-processing steps to be executed PRIOR to invoking mExportFunction
+        unsigned int mEnforcePP;
+
+        // Constructor to fill all entries
+        ExportFormatEntry( const char* pId, const char* pDesc, const char* pExtension, fpExportFunc pFunction, unsigned int pEnforcePP = 0u)
+        {
+            mDescription.id = pId;
+            mDescription.description = pDesc;
+            mDescription.fileExtension = pExtension;
+            mExportFunction = pFunction;
+            mEnforcePP = pEnforcePP;
+        }
+
+        ExportFormatEntry() :
+            mExportFunction()
+          , mEnforcePP()
+        {
+            mDescription.id = NULL;
+            mDescription.description = NULL;
+            mDescription.fileExtension = NULL;
+        }
+    };
+
+    /**
+     *  @brief  The class constructor.
+     */
+    Exporter();
+
+    /**
+    *  @brief  The class destructor.
+    */
+    ~Exporter();
+
+    // -------------------------------------------------------------------
+    /** Supplies a custom IO handler to the exporter to use to open and
+     * access files.
+     *
+     * If you need #Export to use custom IO logic to access the files,
+     * you need to supply a custom implementation of IOSystem and
+     * IOFile to the exporter.
+     *
+     * #Exporter takes ownership of the object and will destroy it
+     * afterwards. The previously assigned handler will be deleted.
+     * Pass NULL to take again ownership of your IOSystem and reset Assimp
+     * to use its default implementation, which uses plain file IO.
+     *
+     * @param pIOHandler The IO handler to be used in all file accesses
+     *   of the Importer. */
+    void SetIOHandler( IOSystem* pIOHandler);
+
+    // -------------------------------------------------------------------
+    /** Retrieves the IO handler that is currently set.
+     * You can use #IsDefaultIOHandler() to check whether the returned
+     * interface is the default IO handler provided by ASSIMP. The default
+     * handler is active as long the application doesn't supply its own
+     * custom IO handler via #SetIOHandler().
+     * @return A valid IOSystem interface, never NULL. */
+    IOSystem* GetIOHandler() const;
+
+    // -------------------------------------------------------------------
+    /** Checks whether a default IO handler is active
+     * A default handler is active as long the application doesn't
+     * supply its own custom IO handler via #SetIOHandler().
+     * @return true by default */
+    bool IsDefaultIOHandler() const;
+
+    // -------------------------------------------------------------------
+    /** Supplies a custom progress handler to the exporter. This
+     *  interface exposes an #Update() callback, which is called
+     *  more or less periodically (please don't sue us if it
+     *  isn't as periodically as you'd like it to have ...).
+     *  This can be used to implement progress bars and loading
+     *  timeouts.
+     *  @param pHandler Progress callback interface. Pass nullptr to
+     *    disable progress reporting.
+     *  @note Progress handlers can be used to abort the loading
+     *    at almost any time.*/
+    void SetProgressHandler(ProgressHandler* pHandler);
+
+    // -------------------------------------------------------------------
+    /** Exports the given scene to a chosen file format. Returns the exported
+    * data as a binary blob which you can write into a file or something.
+    * When you're done with the data, simply let the #Exporter instance go
+    * out of scope to have it released automatically.
+    * @param pScene The scene to export. Stays in possession of the caller,
+    *   is not changed by the function.
+    * @param pFormatId ID string to specify to which format you want to
+    *   export to. Use
+    * #GetExportFormatCount / #GetExportFormatDescription to learn which
+    *   export formats are available.
+    * @param pPreprocessing See the documentation for #Export
+    * @return the exported data or NULL in case of error.
+    * @note If the Exporter instance did already hold a blob from
+    *   a previous call to #ExportToBlob, it will be disposed.
+    *   Any IO handlers set via #SetIOHandler are ignored here.
+    * @note Use aiCopyScene() to get a modifiable copy of a previously
+    *   imported scene. */
+    const aiExportDataBlob* ExportToBlob(const aiScene* pScene, const char* pFormatId,
+        unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr);
+    const aiExportDataBlob* ExportToBlob(  const aiScene* pScene, const std::string& pFormatId,
+        unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr);
+
+    // -------------------------------------------------------------------
+    /** Convenience function to export directly to a file. Use
+     *  #SetIOSystem to supply a custom IOSystem to gain fine-grained control
+     *  about the output data flow of the export process.
+     * @param pBlob A data blob obtained from a previous call to #aiExportScene. Must not be NULL.
+     * @param pPath Full target file name. Target must be accessible.
+     * @param pPreprocessing Accepts any choice of the #aiPostProcessSteps enumerated
+     *   flags, but in reality only a subset of them makes sense here. Specifying
+     *   'preprocessing' flags is useful if the input scene does not conform to
+     *   Assimp's default conventions as specified in the @link data Data Structures Page @endlink.
+     *   In short, this means the geometry data should use a right-handed coordinate systems, face
+     *   winding should be counter-clockwise and the UV coordinate origin is assumed to be in
+     *   the upper left. The #aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and
+     *   #aiProcess_FlipWindingOrder flags are used in the import side to allow users
+     *   to have those defaults automatically adapted to their conventions. Specifying those flags
+     *   for exporting has the opposite effect, respectively. Some other of the
+     *   #aiPostProcessSteps enumerated values may be useful as well, but you'll need
+     *   to try out what their effect on the exported file is. Many formats impose
+     *   their own restrictions on the structure of the geometry stored therein,
+     *   so some preprocessing may have little or no effect at all, or may be
+     *   redundant as exporters would apply them anyhow. A good example
+     *   is triangulation - whilst you can enforce it by specifying
+     *   the #aiProcess_Triangulate flag, most export formats support only
+     *   triangulate data so they would run the step even if it wasn't requested.
+     *
+     *   If assimp detects that the input scene was directly taken from the importer side of
+     *   the library (i.e. not copied using aiCopyScene and potentially modified afterwards),
+     *   any post-processing steps already applied to the scene will not be applied again, unless
+     *   they show non-idempotent behavior (#aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and
+     *   #aiProcess_FlipWindingOrder).
+     * @return AI_SUCCESS if everything was fine.
+     * @note Use aiCopyScene() to get a modifiable copy of a previously
+     *   imported scene.*/
+    aiReturn Export( const aiScene* pScene, const char* pFormatId, const char* pPath,
+        unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr);
+    aiReturn Export( const aiScene* pScene, const std::string& pFormatId, const std::string& pPath,
+        unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr);
+
+    // -------------------------------------------------------------------
+    /** Returns an error description of an error that occurred in #Export
+     *    or #ExportToBlob
+     *
+     * Returns an empty string if no error occurred.
+     * @return A description of the last error, an empty string if no
+     *   error occurred. The string is never NULL.
+     *
+     * @note The returned function remains valid until one of the
+     * following methods is called: #Export, #ExportToBlob, #FreeBlob */
+    const char* GetErrorString() const;
+
+    // -------------------------------------------------------------------
+    /** Return the blob obtained from the last call to #ExportToBlob */
+    const aiExportDataBlob* GetBlob() const;
+
+    // -------------------------------------------------------------------
+    /** Orphan the blob from the last call to #ExportToBlob. This means
+     *  the caller takes ownership and is thus responsible for calling
+     *  the C API function #aiReleaseExportBlob to release it. */
+    const aiExportDataBlob* GetOrphanedBlob() const;
+
+    // -------------------------------------------------------------------
+    /** Frees the current blob.
+     *
+     *  The function does nothing if no blob has previously been
+     *  previously produced via #ExportToBlob. #FreeBlob is called
+     *  automatically by the destructor. The only reason to call
+     *  it manually would be to reclaim as much storage as possible
+     *  without giving up the #Exporter instance yet. */
+    void FreeBlob( );
+
+    // -------------------------------------------------------------------
+    /** Returns the number of export file formats available in the current
+     *  Assimp build. Use #Exporter::GetExportFormatDescription to
+     *  retrieve infos of a specific export format.
+     *
+     *  This includes built-in exporters as well as exporters registered
+     *  using #RegisterExporter.
+     **/
+    size_t GetExportFormatCount() const;
+
+    // -------------------------------------------------------------------
+    /** Returns a description of the nth export file format. Use #
+     *  #Exporter::GetExportFormatCount to learn how many export
+     *  formats are supported.
+     *
+     * The returned pointer is of static storage duration if the
+     * pIndex pertains to a built-in exporter (i.e. one not registered
+     * via #RegistrerExporter). It is restricted to the life-time of the
+     * #Exporter instance otherwise.
+     *
+     * @param pIndex Index of the export format to retrieve information
+     *  for. Valid range is 0 to #Exporter::GetExportFormatCount
+     * @return A description of that specific export format.
+     *  NULL if pIndex is out of range. */
+    const aiExportFormatDesc* GetExportFormatDescription( size_t pIndex ) const;
+
+    // -------------------------------------------------------------------
+    /** Register a custom exporter. Custom export formats are limited to
+     *    to the current #Exporter instance and do not affect the
+     *    library globally. The indexes under which the format's
+     *    export format description can be queried are assigned
+     *    monotonously.
+     *  @param desc Exporter description.
+     *  @return aiReturn_SUCCESS if the export format was successfully
+     *    registered. A common cause that would prevent an exporter
+     *    from being registered is that its format id is already
+     *    occupied by another format. */
+    aiReturn RegisterExporter(const ExportFormatEntry& desc);
+
+    // -------------------------------------------------------------------
+    /** Remove an export format previously registered with #RegisterExporter
+     *  from the #Exporter instance (this can also be used to drop
+     *  built-in exporters because those are implicitly registered
+     *  using #RegisterExporter).
+     *  @param id Format id to be unregistered, this refers to the
+     *    'id' field of #aiExportFormatDesc.
+     *  @note Calling this method on a format description not yet registered
+     *    has no effect.*/
+    void UnregisterExporter(const char* id);
+
+protected:
+    // Just because we don't want you to know how we're hacking around.
+    ExporterPimpl* pimpl;
+};
+
+class ASSIMP_API ExportProperties {
+public:
+    // Data type to store the key hash
+    typedef unsigned int KeyType;
+
+    // typedefs for our four configuration maps.
+    // We don't need more, so there is no need for a generic solution
+    typedef std::map<KeyType, int> IntPropertyMap;
+    typedef std::map<KeyType, ai_real> FloatPropertyMap;
+    typedef std::map<KeyType, std::string> StringPropertyMap;
+    typedef std::map<KeyType, aiMatrix4x4> MatrixPropertyMap;
+
+public:
+    /** Standard constructor
+    * @see ExportProperties()
+    */
+    ExportProperties();
+
+    // -------------------------------------------------------------------
+    /** Copy constructor.
+     *
+     * This copies the configuration properties of another ExportProperties.
+     * @see ExportProperties(const ExportProperties& other)
+     */
+    ExportProperties(const ExportProperties& other);
+
+    // -------------------------------------------------------------------
+    /** Set an integer configuration property.
+     * @param szName Name of the property. All supported properties
+     *   are defined in the aiConfig.g header (all constants share the
+     *   prefix AI_CONFIG_XXX and are simple strings).
+     * @param iValue New value of the property
+     * @return true if the property was set before. The new value replaces
+     *   the previous value in this case.
+     * @note Property of different types (float, int, string ..) are kept
+     *   on different stacks, so calling SetPropertyInteger() for a
+     *   floating-point property has no effect - the loader will call
+     *   GetPropertyFloat() to read the property, but it won't be there.
+     */
+    bool SetPropertyInteger(const char* szName, int iValue);
+
+    // -------------------------------------------------------------------
+    /** Set a boolean configuration property. Boolean properties
+     *  are stored on the integer stack internally so it's possible
+     *  to set them via #SetPropertyBool and query them with
+     *  #GetPropertyBool and vice versa.
+     * @see SetPropertyInteger()
+     */
+    bool SetPropertyBool(const char* szName, bool value)    {
+        return SetPropertyInteger(szName,value);
+    }
+
+    // -------------------------------------------------------------------
+    /** Set a floating-point configuration property.
+     * @see SetPropertyInteger()
+     */
+    bool SetPropertyFloat(const char* szName, ai_real fValue);
+
+    // -------------------------------------------------------------------
+    /** Set a string configuration property.
+     * @see SetPropertyInteger()
+     */
+    bool SetPropertyString(const char* szName, const std::string& sValue);
+
+    // -------------------------------------------------------------------
+    /** Set a matrix configuration property.
+     * @see SetPropertyInteger()
+     */
+    bool SetPropertyMatrix(const char* szName, const aiMatrix4x4& sValue);
+
+    // -------------------------------------------------------------------
+    /** Get a configuration property.
+     * @param szName Name of the property. All supported properties
+     *   are defined in the aiConfig.g header (all constants share the
+     *   prefix AI_CONFIG_XXX).
+     * @param iErrorReturn Value that is returned if the property
+     *   is not found.
+     * @return Current value of the property
+     * @note Property of different types (float, int, string ..) are kept
+     *   on different lists, so calling SetPropertyInteger() for a
+     *   floating-point property has no effect - the loader will call
+     *   GetPropertyFloat() to read the property, but it won't be there.
+     */
+    int GetPropertyInteger(const char* szName,
+        int iErrorReturn = 0xffffffff) const;
+
+    // -------------------------------------------------------------------
+    /** Get a boolean configuration property. Boolean properties
+     *  are stored on the integer stack internally so it's possible
+     *  to set them via #SetPropertyBool and query them with
+     *  #GetPropertyBool and vice versa.
+     * @see GetPropertyInteger()
+     */
+    bool GetPropertyBool(const char* szName, bool bErrorReturn = false) const {
+        return GetPropertyInteger(szName,bErrorReturn)!=0;
+    }
+
+    // -------------------------------------------------------------------
+    /** Get a floating-point configuration property
+     * @see GetPropertyInteger()
+     */
+    ai_real GetPropertyFloat(const char* szName,
+        ai_real fErrorReturn = 10e10f) const;
+
+    // -------------------------------------------------------------------
+    /** Get a string configuration property
+     *
+     *  The return value remains valid until the property is modified.
+     * @see GetPropertyInteger()
+     */
+    const std::string GetPropertyString(const char* szName,
+        const std::string& sErrorReturn = "") const;
+
+    // -------------------------------------------------------------------
+    /** Get a matrix configuration property
+     *
+     *  The return value remains valid until the property is modified.
+     * @see GetPropertyInteger()
+     */
+    const aiMatrix4x4 GetPropertyMatrix(const char* szName,
+        const aiMatrix4x4& sErrorReturn = aiMatrix4x4()) const;
+
+    // -------------------------------------------------------------------
+    /** Determine a integer configuration property has been set.
+    * @see HasPropertyInteger()
+     */
+    bool HasPropertyInteger(const char* szName) const;
+
+    /** Determine a boolean configuration property has been set.
+    * @see HasPropertyBool()
+     */
+    bool HasPropertyBool(const char* szName) const;
+
+    /** Determine a boolean configuration property has been set.
+    * @see HasPropertyFloat()
+     */
+    bool HasPropertyFloat(const char* szName) const;
+
+    /** Determine a String configuration property has been set.
+    * @see HasPropertyString()
+     */
+    bool HasPropertyString(const char* szName) const;
+
+    /** Determine a Matrix configuration property has been set.
+    * @see HasPropertyMatrix()
+     */
+    bool HasPropertyMatrix(const char* szName) const;
+
+protected:
+
+    /** List of integer properties */
+    IntPropertyMap mIntProperties;
+
+    /** List of floating-point properties */
+    FloatPropertyMap mFloatProperties;
+
+    /** List of string properties */
+    StringPropertyMap mStringProperties;
+
+    /** List of Matrix properties */
+    MatrixPropertyMap mMatrixProperties;
+};
+
+// ----------------------------------------------------------------------------------
+inline 
+const aiExportDataBlob* Exporter::ExportToBlob(  const aiScene* pScene, const std::string& pFormatId,
+                                                 unsigned int pPreprocessing, const ExportProperties* pProperties)
+{
+    return ExportToBlob(pScene,pFormatId.c_str(),pPreprocessing, pProperties);
+}
+
+// ----------------------------------------------------------------------------------
+inline
+aiReturn Exporter :: Export( const aiScene* pScene, const std::string& pFormatId, 
+                                    const std::string& pPath, unsigned int pPreprocessing, 
+                                    const ExportProperties* pProperties)
+{
+    return Export(pScene,pFormatId.c_str(),pPath.c_str(),pPreprocessing, pProperties);
+}
+
+} // namespace Assimp
+
+#endif // ASSIMP_BUILD_NO_EXPORT
+#endif // AI_EXPORT_HPP_INC

+ 133 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/GenericProperty.h

@@ -0,0 +1,133 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef AI_GENERIC_PROPERTY_H_INCLUDED
+#define AI_GENERIC_PROPERTY_H_INCLUDED
+
+#include <assimp/Importer.hpp>
+#include <assimp/ai_assert.h>
+#include "Hash.h"
+
+#include <map>
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline
+bool SetGenericProperty(std::map< unsigned int, T >& list,
+        const char* szName, const T& value) {
+    ai_assert(nullptr != szName);
+    const uint32_t hash = SuperFastHash(szName);
+
+    typename std::map<unsigned int, T>::iterator it = list.find(hash);
+    if (it == list.end())   {
+        list.insert(std::pair<unsigned int, T>( hash, value ));
+        return false;
+    }
+    (*it).second = value;
+
+    return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline
+const T& GetGenericProperty(const std::map< unsigned int, T >& list,
+        const char* szName, const T& errorReturn) {
+    ai_assert(nullptr != szName);
+    const uint32_t hash = SuperFastHash(szName);
+
+    typename std::map<unsigned int, T>::const_iterator it = list.find(hash);
+    if (it == list.end()) {
+        return errorReturn;
+    }
+
+    return (*it).second;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Special version for pointer types - they will be deleted when replaced with another value
+// passing NULL removes the whole property
+template <class T>
+inline
+void SetGenericPropertyPtr(std::map< unsigned int, T* >& list,
+        const char* szName, T* value, bool* bWasExisting = nullptr ) {
+    ai_assert(nullptr != szName);
+    const uint32_t hash = SuperFastHash(szName);
+
+    typename std::map<unsigned int, T*>::iterator it = list.find(hash);
+    if (it == list.end())   {
+        if (bWasExisting) {
+            *bWasExisting = false;
+        }
+
+        list.insert(std::pair<unsigned int,T*>( hash, value ));
+        return;
+    }
+    if ((*it).second != value)  {
+        delete (*it).second;
+        (*it).second = value;
+    }
+    if (!value) {
+        list.erase(it);
+    }
+    if (bWasExisting) {
+        *bWasExisting = true;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline
+bool HasGenericProperty(const std::map< unsigned int, T >& list,
+        const char* szName) {
+    ai_assert(nullptr != szName);
+    const uint32_t hash = SuperFastHash(szName);
+
+    typename std::map<unsigned int, T>::const_iterator it = list.find(hash);
+    if (it == list.end()) {
+        return false;
+    }
+
+    return true;
+}
+
+#endif // !! AI_GENERIC_PROPERTY_H_INCLUDED

+ 118 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/Hash.h

@@ -0,0 +1,118 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef AI_HASH_H_INCLUDED
+#define AI_HASH_H_INCLUDED
+
+#include <stdint.h>
+#include <string.h>
+
+// ------------------------------------------------------------------------------------------------
+// Hashing function taken from
+// http://www.azillionmonkeys.com/qed/hash.html
+// (incremental version)
+//
+// This code is Copyright 2004-2008 by Paul Hsieh. It is used here in the belief that
+// Assimp's license is considered compatible with Pauls's derivative license as specified
+// on his web page.
+//
+// (stdint.h should have been been included here)
+// ------------------------------------------------------------------------------------------------
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
+  || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+                       +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+
+// ------------------------------------------------------------------------------------------------
+inline uint32_t SuperFastHash (const char * data, uint32_t len = 0, uint32_t hash = 0) {
+uint32_t tmp;
+int rem;
+
+    if (!data) return 0;
+    if (!len)len = (uint32_t)::strlen(data);
+
+    rem = len & 3;
+    len >>= 2;
+
+    /* Main loop */
+    for (;len > 0; len--) {
+        hash  += get16bits (data);
+        tmp    = (get16bits (data+2) << 11) ^ hash;
+        hash   = (hash << 16) ^ tmp;
+        data  += 2*sizeof (uint16_t);
+        hash  += hash >> 11;
+    }
+
+    /* Handle end cases */
+    switch (rem) {
+        case 3: hash += get16bits (data);
+                hash ^= hash << 16;
+                hash ^= data[sizeof (uint16_t)] << 18;
+                hash += hash >> 11;
+                break;
+        case 2: hash += get16bits (data);
+                hash ^= hash << 11;
+                hash += hash >> 17;
+                break;
+        case 1: hash += *data;
+                hash ^= hash << 10;
+                hash += hash >> 1;
+    }
+
+    /* Force "avalanching" of final 127 bits */
+    hash ^= hash << 3;
+    hash += hash >> 5;
+    hash ^= hash << 4;
+    hash += hash >> 17;
+    hash ^= hash << 25;
+    hash += hash >> 6;
+
+    return hash;
+}
+
+#endif // !! AI_HASH_H_INCLUDED

+ 142 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/IOStream.hpp

@@ -0,0 +1,142 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+/** @file IOStream.hpp
+ *  @brief File I/O wrappers for C++.
+ */
+
+#pragma once
+#ifndef AI_IOSTREAM_H_INC
+#define AI_IOSTREAM_H_INC
+
+#include "types.h"
+
+#ifndef __cplusplus
+#   error This header requires C++ to be used. aiFileIO.h is the \
+    corresponding C interface.
+#endif
+
+namespace Assimp    {
+
+// ----------------------------------------------------------------------------------
+/** @brief CPP-API: Class to handle file I/O for C++
+ *
+ *  Derive an own implementation from this interface to provide custom IO handling
+ *  to the Importer. If you implement this interface, be sure to also provide an
+ *  implementation for IOSystem that creates instances of your custom IO class.
+*/
+class ASSIMP_API IOStream
+#ifndef SWIG
+    : public Intern::AllocateFromAssimpHeap
+#endif
+{
+protected:
+    /** Constructor protected, use IOSystem::Open() to create an instance. */
+    IOStream() AI_NO_EXCEPT;
+
+public:
+    // -------------------------------------------------------------------
+    /** @brief Destructor. Deleting the object closes the underlying file,
+     * alternatively you may use IOSystem::Close() to release the file.
+     */
+    virtual ~IOStream();
+
+    // -------------------------------------------------------------------
+    /** @brief Read from the file
+     *
+     * See fread() for more details
+     * This fails for write-only files */
+    virtual size_t Read(void* pvBuffer,
+        size_t pSize,
+        size_t pCount) = 0;
+
+    // -------------------------------------------------------------------
+    /** @brief Write to the file
+    *
+    * See fwrite() for more details
+    * This fails for read-only files */
+    virtual size_t Write(const void* pvBuffer,
+        size_t pSize,
+        size_t pCount) = 0;
+
+    // -------------------------------------------------------------------
+    /** @brief Set the read/write cursor of the file
+     *
+     * Note that the offset is _negative_ for aiOrigin_END.
+     * See fseek() for more details */
+    virtual aiReturn Seek(size_t pOffset,
+        aiOrigin pOrigin) = 0;
+
+    // -------------------------------------------------------------------
+    /** @brief Get the current position of the read/write cursor
+     *
+     * See ftell() for more details */
+    virtual size_t Tell() const = 0;
+
+    // -------------------------------------------------------------------
+    /** @brief Returns filesize
+     *  Returns the filesize. */
+    virtual size_t FileSize() const = 0;
+
+    // -------------------------------------------------------------------
+    /** @brief Flush the contents of the file buffer (for writers)
+     *  See fflush() for more details.
+     */
+    virtual void Flush() = 0;
+}; //! class IOStream
+
+// ----------------------------------------------------------------------------------
+inline
+IOStream::IOStream() AI_NO_EXCEPT {
+    // empty
+}
+
+// ----------------------------------------------------------------------------------
+inline
+IOStream::~IOStream() {
+    // empty
+}
+// ----------------------------------------------------------------------------------
+
+} //!namespace Assimp
+
+#endif //!!AI_IOSTREAM_H_INC

+ 355 - 0
Plugins/RuntimeMeshImportExport/Source/ThirdParty/assimp/include/assimp/IOStreamBuffer.h

@@ -0,0 +1,355 @@
+#pragma once
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#include <assimp/types.h>
+#include <assimp/IOStream.hpp>
+
+#include "ParsingUtils.h"
+
+#include <vector>
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/**
+ *  Implementation of a cached stream buffer.
+ */
+template<class T>
+class IOStreamBuffer {
+public:
+    /// @brief  The class constructor.
+    IOStreamBuffer( size_t cache = 4096 * 4096 );
+
+    /// @brief  The class destructor.
+    ~IOStreamBuffer();
+
+    /// @brief  Will open the cached access for a given stream.
+    /// @param  stream      The stream to cache.
+    /// @return true if successful.
+    bool open( IOStream *stream );
+
+    /// @brief  Will close the cached access.
+    /// @return true if successful.
+    bool close();
+
+    /// @brief  Returns the file-size.
+    /// @return The file-size.
+    size_t size() const;
+    
+    /// @brief  Returns the cache size.
+    /// @return The cache size.
+    size_t cacheSize() const;
+
+    /// @brief  Will read the next block.
+    /// @return true if successful.
+    bool readNextBlock();
+
+    /// @brief  Returns the number of blocks to read.
+    /// @return The number of blocks.
+    size_t getNumBlocks() const;
+
+    /// @brief  Returns the current block index.
+    /// @return The current block index.
+    size_t getCurrentBlockIndex() const;
+
+    /// @brief  Returns the current file pos.
+    /// @return The current file pos.
+    size_t getFilePos() const;
+
+    /// @brief  Will read the next line.
+    /// @param  buffer      The buffer for the next line.
+    /// @return true if successful.
+    bool getNextDataLine( std::vector<T> &buffer, T continuationToken );
+
+    /// @brief  Will read the next line ascii or binary end line char.
+    /// @param  buffer      The buffer for the next line.
+    /// @return true if successful.
+    bool getNextLine(std::vector<T> &buffer);
+
+    /// @brief  Will read the next block.
+    /// @param  buffer      The buffer for the next block.
+    /// @return true if successful.
+    bool getNextBlock( std::vector<T> &buffer );
+
+private:
+    IOStream *m_stream;
+    size_t m_filesize;
+    size_t m_cacheSize;
+    size_t m_numBlocks;
+    size_t m_blockIdx;
+    std::vector<T> m_cache;
+    size_t m_cachePos;
+    size_t m_filePos;
+};
+
+template<class T>
+inline
+IOStreamBuffer<T>::IOStreamBuffer( size_t cache )
+: m_stream( nullptr )
+, m_filesize( 0 )
+, m_cacheSize( cache )
+, m_numBlocks( 0 )
+, m_blockIdx( 0 )
+, m_cachePos( 0 )
+, m_filePos( 0 ) {
+    m_cache.resize( cache );
+    std::fill( m_cache.begin(), m_cache.end(), '\n' );
+}
+
+template<class T>
+inline
+IOStreamBuffer<T>::~IOStreamBuffer() {
+    // empty
+}
+
+template<class T>
+inline
+bool IOStreamBuffer<T>::open( IOStream *stream ) {
+    //  file still opened!
+    if ( nullptr != m_stream ) {
+        return false;
+    }
+
+    //  Invalid stream pointer
+    if ( nullptr == stream ) {
+        return false;
+    }
+
+    m_stream = stream;
+    m_filesize = m_stream->FileSize();
+    if ( m_filesize == 0 ) {
+        return false;
+    }
+    if ( m_filesize < m_cacheSize ) {
+        m_cacheSize = m_filesize;
+    }
+
+    m_numBlocks = m_filesize / m_cacheSize;
+    if ( ( m_filesize % m_cacheSize ) > 0 ) {
+        m_numBlocks++;
+    }
+
+    return true;
+}
+
+template<class T>
+inline
+bool IOStreamBuffer<T>::close() {
+    if ( nullptr == m_stream ) {
+        return false;
+    }
+
+    // init counters and state vars
+    m_stream    = nullptr;
+    m_filesize  = 0;
+    m_numBlocks = 0;
+    m_blockIdx  = 0;
+    m_cachePos  = 0;
+    m_filePos   = 0;
+
+    return true;
+}
+
+template<class T>
+inline
+size_t IOStreamBuffer<T>::size() const {
+    return m_filesize;
+}
+
+template<class T>
+inline
+size_t IOStreamBuffer<T>::cacheSize() const {
+    return m_cacheSize;
+}
+
+template<class T>
+inline
+bool IOStreamBuffer<T>::readNextBlock() {
+    m_stream->Seek( m_filePos, aiOrigin_SET );
+    size_t readLen = m_stream->Read( &m_cache[ 0 ], sizeof( T ), m_cacheSize );
+    if ( readLen == 0 ) {
+        return false;
+    }
+    if ( readLen < m_cacheSize ) {
+        m_cacheSize = readLen;
+    }
+    m_filePos += m_cacheSize;
+    m_cachePos = 0;
+    m_blockIdx++;
+
+    return true;
+}
+
+template<class T>
+inline
+size_t IOStreamBuffer<T>::getNumBlocks() const {
+    return m_numBlocks;
+}
+
+template<class T>
+inline
+size_t IOStreamBuffer<T>::getCurrentBlockIndex() const {
+    return m_blockIdx;
+}
+
+template<class T>
+inline
+size_t IOStreamBuffer<T>::getFilePos() const {
+    return m_filePos;
+}
+
+template<class T>
+inline
+bool IOStreamBuffer<T>::getNextDataLine( std::vector<T> &buffer, T continuationToken ) {
+    buffer.resize( m_cacheSize );
+    if ( m_cachePos >= m_cacheSize || 0 == m_filePos ) {
+        if ( !readNextBlock() ) {
+            return false;
+        }
+    }
+
+    bool continuationFound( false );
+    size_t i = 0;
+    for( ;; ) {
+        if ( continuationToken == m_cache[ m_cachePos ] ) {
+            continuationFound = true;
+            ++m_cachePos;
+        }
+        if ( IsLineEnd( m_cache[ m_cachePos ] ) ) {
+            if ( !continuationFound ) {
+                // the end of the data line
+                break;
+            } else {
+                // skip line end
+                while ( m_cache[m_cachePos] != '\n') {
+                    ++m_cachePos;
+                }
+                ++m_cachePos;
+                continuationFound = false;
+            }
+        }
+
+        buffer[ i ] = m_cache[ m_cachePos ];
+        ++m_cachePos;
+        ++i;
+        if (m_cachePos >= size()) {
+            break;
+        }
+        if ( m_cachePos >= m_cacheSize ) {
+            if ( !readNextBlock() ) {
+                return false;
+            }
+        }
+    }
+    
+    buffer[ i ] = '\n';
+    ++m_cachePos;
+
+    return true;
+}
+
+static inline
+bool isEndOfCache( size_t pos, size_t cacheSize ) {
+    return ( pos == cacheSize );
+}
+
+template<class T>
+inline
+bool IOStreamBuffer<T>::getNextLine(std::vector<T> &buffer) {
+    buffer.resize(m_cacheSize);
+    if ( isEndOfCache( m_cachePos, m_cacheSize ) || 0 == m_filePos) {
+       if (!readNextBlock()) {
+          return false;
+       }
+      }
+
+    if (IsLineEnd(m_cache[m_cachePos])) {
+        // skip line end
+        while (m_cache[m_cachePos] != '\n') {
+            ++m_cachePos;
+        }
+        ++m_cachePos;
+        if ( isEndOfCache( m_cachePos, m_cacheSize ) ) {
+            if ( !readNextBlock() ) {
+                return false;
+            }
+        }
+    }
+
+    size_t i( 0 );
+    while (!IsLineEnd(m_cache[ m_cachePos ])) {
+        buffer[i] = m_cache[ m_cachePos ];
+        ++m_cachePos;
+        ++i;
+        if (m_cachePos >= m_cacheSize) {
+            if (!readNextBlock()) {
+                return false;
+            }
+        }
+    }
+    buffer[i] = '\n';
+    ++m_cachePos;
+
+    return true;
+}
+
+template<class T>
+inline
+bool IOStreamBuffer<T>::getNextBlock( std::vector<T> &buffer) {
+    // Return the last block-value if getNextLine was used before
+    if ( 0 != m_cachePos ) {      
+        buffer = std::vector<T>( m_cache.begin() + m_cachePos, m_cache.end() );
+        m_cachePos = 0;
+    } else {
+        if ( !readNextBlock() ) {
+            return false;
+        }
+
+        buffer = std::vector<T>(m_cache.begin(), m_cache.end());
+    }
+
+    return true;
+}
+
+} // !ns Assimp

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor