├── PutThisInYourContentFolder └── StereoscopicRender │ ├── RTC_LeftEye.uasset │ ├── StereoExample.umap │ ├── RTC_RightEye.uasset │ └── BP_GearVRCameraRig.uasset ├── PutThisInYourEnginePluginsDeveloperFolder └── ExportObjectPlugin │ ├── Resources │ └── Icon128.png │ ├── Source │ └── ExportObjectPlugin │ │ ├── Private │ │ ├── ExportObjectPluginPrivatePCH.h │ │ ├── ExportObjectPlugin.cpp │ │ └── StereoscopicRenderBlueprintLibrary.cpp │ │ ├── Classes │ │ └── StereoscopicRenderBlueprintLibrary.h │ │ ├── Public │ │ └── IExportObjectPlugin.h │ │ └── ExportObjectPlugin.Build.cs │ └── ExportObjectPlugin.uplugin ├── .gitignore ├── LICENSE └── README.md /PutThisInYourContentFolder/StereoscopicRender/RTC_LeftEye.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Allar/ExportObjectPlugin/HEAD/PutThisInYourContentFolder/StereoscopicRender/RTC_LeftEye.uasset -------------------------------------------------------------------------------- /PutThisInYourContentFolder/StereoscopicRender/StereoExample.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Allar/ExportObjectPlugin/HEAD/PutThisInYourContentFolder/StereoscopicRender/StereoExample.umap -------------------------------------------------------------------------------- /PutThisInYourContentFolder/StereoscopicRender/RTC_RightEye.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Allar/ExportObjectPlugin/HEAD/PutThisInYourContentFolder/StereoscopicRender/RTC_RightEye.uasset -------------------------------------------------------------------------------- /PutThisInYourContentFolder/StereoscopicRender/BP_GearVRCameraRig.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Allar/ExportObjectPlugin/HEAD/PutThisInYourContentFolder/StereoscopicRender/BP_GearVRCameraRig.uasset -------------------------------------------------------------------------------- /PutThisInYourEnginePluginsDeveloperFolder/ExportObjectPlugin/Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Allar/ExportObjectPlugin/HEAD/PutThisInYourEnginePluginsDeveloperFolder/ExportObjectPlugin/Resources/Icon128.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | -------------------------------------------------------------------------------- /PutThisInYourEnginePluginsDeveloperFolder/ExportObjectPlugin/Source/ExportObjectPlugin/Private/ExportObjectPluginPrivatePCH.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "IExportObjectPlugin.h" 4 | 5 | // You should place include statements to your module's private header files here. You only need to 6 | // add includes for headers that are used in most of your module's source files though. 7 | 8 | #include "Engine.h" 9 | #include "ExportObjectPluginClasses.h" -------------------------------------------------------------------------------- /PutThisInYourEnginePluginsDeveloperFolder/ExportObjectPlugin/Source/ExportObjectPlugin/Classes/StereoscopicRenderBlueprintLibrary.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Michael Allar. All Rights Reserved. 2 | 3 | #pragma once 4 | #include "StereoscopicRenderBlueprintLibrary.Generated.h" 5 | 6 | // Library of helper functions for UMGEx Widgets 7 | UCLASS() 8 | class EXPORTOBJECTPLUGIN_API UStereoscopicRenderBlueprintLibrary : public UBlueprintFunctionLibrary 9 | { 10 | GENERATED_BODY() 11 | 12 | public: 13 | 14 | 15 | UFUNCTION(BlueprintCallable, Category = "Online") 16 | static bool ExportObjectToPath(class UObject* ObjectToExport, FString SaveFileName, FString FileNameAppendage); 17 | 18 | 19 | }; 20 | -------------------------------------------------------------------------------- /PutThisInYourEnginePluginsDeveloperFolder/ExportObjectPlugin/ExportObjectPlugin.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "Export Object Plugin", 6 | "Description": "A plug-in that exposes an 'Export Object to Path' node in blueprint for editor builds.", 7 | "Category": "Experimental", 8 | "CreatedBy": "Michael Allar", 9 | "CreatedByURL": "http://www.allarsblog.com", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "Modules": [ 13 | { 14 | "Name": "ExportObjectPlugin", 15 | "Type": "Developer", 16 | "LoadingPhase": "Default" 17 | } 18 | ], 19 | "EnabledByDefault": true, 20 | "CanContainContent": false, 21 | "IsBetaVersion": false, 22 | "Installed": false 23 | } -------------------------------------------------------------------------------- /PutThisInYourEnginePluginsDeveloperFolder/ExportObjectPlugin/Source/ExportObjectPlugin/Private/ExportObjectPlugin.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "ExportObjectPluginPrivatePCH.h" 4 | 5 | 6 | class FExportObjectPlugin : public IExportObjectPlugin 7 | { 8 | /** IModuleInterface implementation */ 9 | virtual void StartupModule() override; 10 | virtual void ShutdownModule() override; 11 | }; 12 | 13 | IMPLEMENT_MODULE( FExportObjectPlugin, ExportObjectPlugin ) 14 | 15 | 16 | 17 | void FExportObjectPlugin::StartupModule() 18 | { 19 | // This code will execute after your module is loaded into memory (but after global variables are initialized, of course.) 20 | } 21 | 22 | 23 | void FExportObjectPlugin::ShutdownModule() 24 | { 25 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 26 | // we call this function before unloading the module. 27 | } 28 | 29 | DEFINE_LOG_CATEGORY(LogExportObjectPlugin) 30 | 31 | 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2010-2016 Google, Inc. http://angularjs.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /PutThisInYourEnginePluginsDeveloperFolder/ExportObjectPlugin/Source/ExportObjectPlugin/Public/IExportObjectPlugin.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "ModuleManager.h" 6 | 7 | 8 | /** 9 | * The public interface to this module. In most cases, this interface is only public to sibling modules 10 | * within this plugin. 11 | */ 12 | class IExportObjectPlugin : public IModuleInterface 13 | { 14 | 15 | public: 16 | 17 | /** 18 | * Singleton-like access to this module's interface. This is just for convenience! 19 | * Beware of calling this during the shutdown phase, though. Your module might have been unloaded already. 20 | * 21 | * @return Returns singleton instance, loading the module on demand if needed 22 | */ 23 | static inline IExportObjectPlugin& Get() 24 | { 25 | return FModuleManager::LoadModuleChecked< IExportObjectPlugin >( "ExportObjectPlugin" ); 26 | } 27 | 28 | /** 29 | * Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true. 30 | * 31 | * @return True if the module is loaded and ready to use 32 | */ 33 | static inline bool IsAvailable() 34 | { 35 | return FModuleManager::Get().IsModuleLoaded( "ExportObjectPlugin" ); 36 | } 37 | }; 38 | 39 | DECLARE_LOG_CATEGORY_EXTERN(LogExportObjectPlugin, VeryVerbose, All); -------------------------------------------------------------------------------- /PutThisInYourEnginePluginsDeveloperFolder/ExportObjectPlugin/Source/ExportObjectPlugin/ExportObjectPlugin.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. 2 | 3 | namespace UnrealBuildTool.Rules 4 | { 5 | public class ExportObjectPlugin : ModuleRules 6 | { 7 | public ExportObjectPlugin(TargetInfo Target) 8 | { 9 | PublicIncludePaths.AddRange( 10 | new string[] { 11 | // ... add public include paths required here ... 12 | } 13 | ); 14 | 15 | PrivateIncludePaths.AddRange( 16 | new string[] { 17 | "Developer/ExportObjectPlugin/Private", 18 | // ... add other private include paths required here ... 19 | } 20 | ); 21 | 22 | PublicDependencyModuleNames.AddRange( 23 | new string[] 24 | { 25 | "Core", 26 | "CoreUObject", 27 | "Engine", 28 | "UnrealEd" 29 | // ... add other public dependencies that you statically link with here ... 30 | } 31 | ); 32 | 33 | PrivateDependencyModuleNames.AddRange( 34 | new string[] 35 | { 36 | // ... add private dependencies that you statically link with here ... 37 | } 38 | ); 39 | 40 | DynamicallyLoadedModuleNames.AddRange( 41 | new string[] 42 | { 43 | // ... add any modules that your module loads dynamically here ... 44 | } 45 | ); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | 3 | This thing is deprecated. Is only here for reference. 4 | 5 | Please use https://www.unrealengine.com/marketplace/simple-panoramic-exporter instead. 6 | 7 | # ExportObjectPlugin 8 | Allows you to export arbitrary objects via a blueprint node in editor builds 9 | 10 | Built to facilitate 360 stereoscopic panoramic video rendering out of UE4 11 | 12 | # Requirements 13 | 14 | This requires 4.7.6 or later, and requires your engine to be built from source code. 15 | 16 | # Installation 17 | 18 | 1. Copy the contents of PutThisInYourContentFolder to your project's content folder 19 | 1. Copy the contents of PutThisInYourEnginePluginsDeveloperFolder to your Engine\Plugins\Developer folder 20 | 1. Run your GenerateProjectFiles batch file 21 | 1. Recompile your UE4 engine 22 | 23 | # Usage 24 | 25 | See YouTube explanation: [Quick rough badly done getting started video](https://www.youtube.com/watch?v=3m85QBjyFGE) 26 | 27 | # Resources 28 | 29 | 1. [YouTube 360 video guidelines and Metadata tool](https://support.google.com/youtube/answer/6178631?hl=en) 30 | 1. [UE4 Forum Post](https://forums.unrealengine.com/showthread.php?71489-360-Panoramic-Stereoscopic-Export-Pipeline) 31 | 32 | # Demos 33 | 34 | 1. [Urban City Marketplace Asset with a custom matinee (Stereoscopic available in description!)](https://www.youtube.com/watch?v=AY5vQ0Pm1WE) 35 | 1. [10 Second Kite Asset demo](https://www.youtube.com/watch?v=TiCL93jVLyo) 36 | 1. [Provided scene render](https://www.youtube.com/watch?v=hWeozTO6KQ4) 37 | 38 | -------------------------------------------------------------------------------- /PutThisInYourEnginePluginsDeveloperFolder/ExportObjectPlugin/Source/ExportObjectPlugin/Private/StereoscopicRenderBlueprintLibrary.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Michael Allar. All Rights Reserved. 2 | 3 | #include "ExportObjectPluginPrivatePCH.h" 4 | #include "StereoscopicRenderBlueprintLibrary.h" 5 | #include "ObjectTools.h" 6 | 7 | 8 | 9 | bool UStereoscopicRenderBlueprintLibrary::ExportObjectToPath(class UObject* ObjectToExport, FString SaveFileName, FString FileNameAppendage) 10 | { 11 | if (ObjectToExport == nullptr) 12 | { 13 | UE_LOG(LogExportObjectPlugin, Warning, TEXT("Export Object to Path called without a valid object!")); 14 | return false; 15 | } 16 | 17 | 18 | // Create an array of all available exporters. 19 | TArray Exporters; 20 | ObjectTools::AssembleListOfExporters(Exporters); 21 | 22 | // Find all the exporters that can export this type of object and construct an export file dialog. 23 | TArray AllFileTypes; 24 | TArray AllExtensions; 25 | TArray PreferredExtensions; 26 | 27 | // Iterate in reverse so the most relevant file formats are considered first. 28 | for (int32 ExporterIndex = Exporters.Num() - 1; ExporterIndex >= 0; --ExporterIndex) 29 | { 30 | UExporter* Exporter = Exporters[ExporterIndex]; 31 | if (Exporter->SupportedClass) 32 | { 33 | const bool bObjectIsSupported = Exporter->SupportsObject(ObjectToExport); 34 | if (bObjectIsSupported) 35 | { 36 | // Get a string representing of the exportable types. 37 | check(Exporter->FormatExtension.Num() == Exporter->FormatDescription.Num()); 38 | check(Exporter->FormatExtension.IsValidIndex(Exporter->PreferredFormatIndex)); 39 | for (int32 FormatIndex = Exporter->FormatExtension.Num() - 1; FormatIndex >= 0; --FormatIndex) 40 | { 41 | const FString& FormatExtension = Exporter->FormatExtension[FormatIndex]; 42 | const FString& FormatDescription = Exporter->FormatDescription[FormatIndex]; 43 | 44 | if (FormatIndex == Exporter->PreferredFormatIndex) 45 | { 46 | PreferredExtensions.Add(FormatExtension); 47 | } 48 | AllFileTypes.Add(FString::Printf(TEXT("%s (*.%s)|*.%s"), *FormatDescription, *FormatExtension, *FormatExtension)); 49 | AllExtensions.Add(FString::Printf(TEXT("*.%s"), *FormatExtension)); 50 | } 51 | } 52 | } 53 | } 54 | 55 | // Skip this object if no exporter found for this resource type. 56 | if (PreferredExtensions.Num() == 0) 57 | { 58 | UE_LOG(LogExportObjectPlugin, Warning, TEXT("Export Object to Path unable to find exporter for %s!"), *ObjectToExport->GetName()); 59 | return false; 60 | } 61 | 62 | if (!FPackageName::IsShortPackageName(ObjectToExport->GetOutermost()->GetFName())) 63 | { 64 | // Determine the save file name from the long package name 65 | FString PackageName = ObjectToExport->GetOutermost()->GetName(); 66 | if (PackageName.Left(1) == TEXT("/")) 67 | { 68 | // Trim the leading slash so the file manager doesn't get confused 69 | PackageName = PackageName.Mid(1); 70 | } 71 | 72 | FPaths::NormalizeFilename(PackageName); 73 | SaveFileName /= PackageName; 74 | } 75 | else 76 | { 77 | // Assemble the path from the package name. 78 | SaveFileName /= ObjectToExport->GetOutermost()->GetName(); 79 | SaveFileName /= ObjectToExport->GetName(); 80 | } 81 | 82 | FString FirstExtension = PreferredExtensions[0]; 83 | SaveFileName += FileNameAppendage + FString::Printf(TEXT(".%s"), *FirstExtension); 84 | 85 | UE_LOG(LogExportObjectPlugin, Log, TEXT("ExportObjectToPath Exporting \"%s\" to \"%s\""), *ObjectToExport->GetPathName(), *SaveFileName); 86 | 87 | // Create the path, then make sure the target file is not read-only. 88 | const FString ObjectExportPath(FPaths::GetPath(SaveFileName)); 89 | const bool bFileInSubdirectory = ObjectExportPath.Contains(TEXT("/")); 90 | if (bFileInSubdirectory && (!IFileManager::Get().MakeDirectory(*ObjectExportPath, true))) 91 | { 92 | UE_LOG(LogExportObjectPlugin, Warning, TEXT("ExportObjectToPath Failed to make directory %s"), *ObjectExportPath); 93 | return false; 94 | } 95 | else if (IFileManager::Get().IsReadOnly(*SaveFileName)) 96 | { 97 | UE_LOG(LogExportObjectPlugin, Warning, TEXT("ExportObjectToPath Couldn't write to file '%s'. Maybe file is read-only?"), *ObjectExportPath); 98 | return false; 99 | } 100 | else 101 | { 102 | // We have a writeable file. Now go through that list of exporters again and find the right exporter and use it. 103 | TArray ValidExporters; 104 | 105 | for (int32 ExporterIndex = 0; ExporterIndex < Exporters.Num(); ++ExporterIndex) 106 | { 107 | UExporter* Exporter = Exporters[ExporterIndex]; 108 | if (Exporter->SupportsObject(ObjectToExport)) 109 | { 110 | check(Exporter->FormatExtension.Num() == Exporter->FormatDescription.Num()); 111 | for (int32 FormatIndex = 0; FormatIndex < Exporter->FormatExtension.Num(); ++FormatIndex) 112 | { 113 | const FString& FormatExtension = Exporter->FormatExtension[FormatIndex]; 114 | if (FCString::Stricmp(*FormatExtension, *FPaths::GetExtension(SaveFileName)) == 0 || 115 | FCString::Stricmp(*FormatExtension, TEXT("*")) == 0) 116 | { 117 | ValidExporters.Add(Exporter); 118 | break; 119 | } 120 | } 121 | } 122 | } 123 | 124 | // Handle the potential of multiple exporters being found 125 | UExporter* ExporterToUse = NULL; 126 | if (ValidExporters.Num() == 1) 127 | { 128 | ExporterToUse = ValidExporters[0]; 129 | } 130 | else if (ValidExporters.Num() > 1) 131 | { 132 | // Set up the first one as default 133 | ExporterToUse = ValidExporters[0]; 134 | 135 | // ...but search for a better match if available 136 | for (int32 ExporterIdx = 0; ExporterIdx < ValidExporters.Num(); ExporterIdx++) 137 | { 138 | if (ValidExporters[ExporterIdx]->GetClass()->GetFName() == ObjectToExport->GetExporterName()) 139 | { 140 | ExporterToUse = ValidExporters[ExporterIdx]; 141 | break; 142 | } 143 | } 144 | } 145 | 146 | // If an exporter was found, use it. 147 | if (ExporterToUse) 148 | { 149 | UExporter::FExportToFileParams Params; 150 | Params.Object = ObjectToExport; 151 | Params.Exporter = ExporterToUse; 152 | Params.Filename = *SaveFileName; 153 | Params.InSelectedOnly = false; 154 | Params.NoReplaceIdentical = false; 155 | Params.Prompt = false; 156 | Params.bUseFileArchive = ObjectToExport->IsA(UPackage::StaticClass()); 157 | Params.WriteEmptyFiles = false; 158 | UExporter::ExportToFileEx(Params); 159 | 160 | return true; 161 | } 162 | else 163 | { 164 | UE_LOG(LogExportObjectPlugin, Warning, TEXT("ExportObjectToPath Failed to find exporter (last stage) for object: %s"), *ObjectToExport->GetName()); 165 | } 166 | } 167 | 168 | 169 | UE_LOG(LogExportObjectPlugin, Warning, TEXT("ExportObjectToPath Failed for unknown reason for object: %s"), *ObjectToExport->GetName()); 170 | return false; 171 | 172 | 173 | } 174 | 175 | --------------------------------------------------------------------------------