How to create assets programmatically in the editor?

I’ve been programming a tool for generating static cubemap textures in editor and I’ve almost got it working. My only remaining problem is that I can’t get the created .uasset files to show up in the content browser.
The code does as follows:

First it gets the path to the level folder and then creates a new folder (package?) inside it that is the name of the level and _TOD suffix. After that I call the ConstructTextureCube function on the capture component which should(?) construct a static texture cube with mip maps and what not. Then the cube and the package it’s in is marked dirty and added to an array for pending save. Finally I call PromptForCheckoutAndSave which correctly opens the prompt and also lists all the created texturecubes. Saving them makes the files appear on my HDD in the correct path, but they don’t show up in the content browser. The _TOD folder doesn’t show up either unless I restart the editor. Then it shows up. But the individual cube uassets don’t show up even though they are in that folder in explorer.

Below is a print of the full path of a cube.


LogTemp: cube full path:TextureCube /Game/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_0_altitude_-20./Game/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_0_altitude_-20



TArray<UPackage*> PackagesToSave;
const FString todAssetPath = this->GetOutermost()->GetName() + TEXT("_TOD/");
FString assetPath = FString::Printf(TEXT("%ssky_cubemap_azimuth_%i_altitude_%i"), *todAssetPath, azimuth, elevation);
UPackage* package = CreatePackage(NULL, *assetPath);
if (package)
{
	const FString name = FString::Printf(TEXT("sky_cubemap_azimuth_%i_altitude_%i"), azimuth, elevation);
	UTextureCube* cube = captureComponent->TextureTarget->ConstructTextureCube(package, assetPath, EObjectFlags::RF_Standalone);

	if (cube)
	{
		cube->MarkPackageDirty();
		cube->GetOuter()->MarkPackageDirty();
		FAssetRegistryModule::AssetCreated(cube);

		UE_LOG(LogTemp, Log, TEXT("cube full path:%s"), *(cube->GetFullName()));
		PackagesToSave.Add(package);
	}
}
bool bCheckDirty = true;
bool bPromptToSave = true;

FEditorFileUtils::PromptForCheckoutAndSave(PackagesToSave, bCheckDirty, bPromptToSave);


So my question is, how can I do what I want: Save the texture cubes onto HDD as an asset so that they show up in the content browser.

@jonimake , Factories are responsible of creating ,saving… assets in Content Browser.
If you are dealing with a child class of UObject , UFactory is what are you looking for.

MyObject.h


UCLASS()
class UMyObject : public UObject
{
	GENERATED_BODY()
//.....
};

MyFactory.h


#pragma once
#include "MyObject.h"
#include "Factories/Factory.h"
#include "MyFactory.generated.h"

/**
 * 
 */
UCLASS()
class MYPROJECT15_API UMyFactory : public UFactory
{
	GENERATED_BODY()

public:
	UMyFactory(const FObjectInitializer& ObjectInitializer);
	virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;

	// Helps us to know if we are create a new Object or just saving an existing one.
	UMyObject* CreatedObjectAsset;

};

MyFactory.cpp


#include "MyFactory.h"
#include "UnrealEd.h"

UMyFactory::UMyFactory(const FObjectInitializer& ObjectInitializer) :Super(ObjectInitializer)
{
	bCreateNew = true;
	bEditAfterNew = true;
	SupportedClass = UMyObject::StaticClass();
}

UObject* UMyFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
{

	if(CreatedObjectAsset != nullptr)
	{
		CreatedObjectAsset->SetFlags(Flags | RF_Transactional);
		CreatedObjectAsset->Modify();
		CreatedObjectAsset->Rename(*Name.ToString(), InParent);
	}
	else
	{
		CreatedObjectAsset = NewObject<UMyObject>(InParent, Class, Name, Flags | RF_Transactional);
	}
	return CreatedObjectAsset;
}

Now , in Content Browser , if you RightClick–Miscellaneous , you can create an asset of your object from there.

Here is needed code to create an asset from an existing instance of MyObject :


#include "MyObject.h"
#include "Engine/Engine.h"
#include "MyFactory.h"
#include "IAssetTools.h"
#include "AssetToolsModule.h"
#include "UnrealEd.h"



if(ExistingObject != nullptr)
	{

		UMyFactory* NewFactory = NewObject<UMyFactory>();
		NewFactory->CreatedObjectAsset = ExistingObject;

		FAssetToolsModule& AssetToolsModule = FAssetToolsModule::GetModule();
		UObject* NewAsset = AssetToolsModule.Get().CreateAsset(NewFactory->GetSupportedClass(), NewFactory);
		TArray<UObject*> ObjectsToSync;
		ObjectsToSync.Add(NewAsset);
		GEditor->SyncBrowserToObjects(ObjectsToSync);
	}


Now I’m confused. I thought this is how the editor creates the asset when you right click a cube render target and click “create static texture”: https://github.com/EpicGames/UnrealEngine/blob/4.15/Engine/Source/Developer/AssetTools/Private/AssetTypeActions/AssetTypeActions_TextureRenderTarget.cpp#L31

So If I create a TextureCube the way you posted, how would I copy the data from the CubeRenderTarget into the static texture cube?

Bump. Still trying to figure this one out. Here’s what log shows after I save the uassets with the code in my original post.



LogSavePackage: Save=5.19ms
LogSavePackage: Moving '../../../../Unreal Projects/MyGame 4.15/Saved/sky_cubemap_azimuth_0_altitude_-20B6BB6B014A421B748BA75CBCB62E5810.tmp' to '../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_0_altitude_-20.uasset'
LogSavePackage:Display: Finished SavePackage ../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_0_altitude_-20.uasset
LogSavePackage: Save=6.15ms
LogSavePackage: Moving '../../../../Unreal Projects/MyGame 4.15/Saved/sky_cubemap_azimuth_0_altitude_20C3E8FB1345A24AD99362B6B72D41680F.tmp' to '../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_0_altitude_20.uasset'
LogSavePackage:Display: Finished SavePackage ../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_0_altitude_20.uasset
LogSavePackage: Save=6.67ms
LogSavePackage: Moving '../../../../Unreal Projects/MyGame 4.15/Saved/sky_cubemap_azimuth_0_altitude_60FCDD7F97455060135823929E7939F17E.tmp' to '../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_0_altitude_60.uasset'
LogSavePackage:Display: Finished SavePackage ../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_0_altitude_60.uasset
LogSavePackage: Save=4.68ms
LogSavePackage: Moving '../../../../Unreal Projects/MyGame 4.15/Saved/sky_cubemap_azimuth_180_altitude_-202C89A91540AA6414E26935A372333548.tmp' to '../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_180_altitude_-20.uasset'
LogSavePackage:Display: Finished SavePackage ../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_180_altitude_-20.uasset
LogSavePackage: Save=6.28ms
LogSavePackage: Moving '../../../../Unreal Projects/MyGame 4.15/Saved/sky_cubemap_azimuth_180_altitude_209D54DFBA4DAC5A1D438C0590F0218EE5.tmp' to '../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_180_altitude_20.uasset'
LogSavePackage:Display: Finished SavePackage ../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_180_altitude_20.uasset
LogSavePackage: Save=6.96ms
LogSavePackage: Moving '../../../../Unreal Projects/MyGame 4.15/Saved/sky_cubemap_azimuth_180_altitude_602DF4E16843EAFBF9BD63B488A45DC99A.tmp' to '../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_180_altitude_60.uasset'
LogSavePackage:Display: Finished SavePackage ../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_180_altitude_60.uasset
LogSavePackage: Save=5.55ms
LogSavePackage: Moving '../../../../Unreal Projects/MyGame 4.15/Saved/sky_cubemap_azimuth_270_altitude_-20F313B18B456C5094D6E843B1997486DB.tmp' to '../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_270_altitude_-20.uasset'
LogSavePackage:Display: Finished SavePackage ../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_270_altitude_-20.uasset
LogSavePackage: Save=6.11ms
LogSavePackage: Moving '../../../../Unreal Projects/MyGame 4.15/Saved/sky_cubemap_azimuth_270_altitude_20CD0661CF444D23574D75668AC636E278.tmp' to '../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_270_altitude_20.uasset'
LogSavePackage:Display: Finished SavePackage ../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_270_altitude_20.uasset
LogSavePackage: Save=6.12ms
LogSavePackage: Moving '../../../../Unreal Projects/MyGame 4.15/Saved/sky_cubemap_azimuth_270_altitude_609268E7B342E165C748ABEE99EED03608.tmp' to '../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_270_altitude_60.uasset'
LogSavePackage:Display: Finished SavePackage ../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_270_altitude_60.uasset
LogSavePackage: Save=4.67ms
LogSavePackage: Moving '../../../../Unreal Projects/MyGame 4.15/Saved/sky_cubemap_azimuth_90_altitude_-20DCD3A79745B49AC9D41B3EA0083F8329.tmp' to '../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_90_altitude_-20.uasset'
LogSavePackage:Display: Finished SavePackage ../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_90_altitude_-20.uasset
LogSavePackage: Save=6.66ms
LogSavePackage: Moving '../../../../Unreal Projects/MyGame 4.15/Saved/sky_cubemap_azimuth_90_altitude_2037B13527443C9A357289B1A80E9B1D7A.tmp' to '../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_90_altitude_20.uasset'
LogSavePackage:Display: Finished SavePackage ../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_90_altitude_20.uasset
LogSavePackage: Save=6.12ms
LogSavePackage: Moving '../../../../Unreal Projects/MyGame 4.15/Saved/sky_cubemap_azimuth_90_altitude_602C37D3BD4415C0CDB162F5B38608E092.tmp' to '../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_90_altitude_60.uasset'
LogSavePackage:Display: Finished SavePackage ../../../../Unreal Projects/MyGame 4.15/Content/Maps/Map1/Desert_TOD/sky_cubemap_azimuth_90_altitude_60.uasset


I think I just figured it out… you have to use EObjectFlags::RF_Public when calling ConstructTextureCube.