Files
FlaxEngine/Source/Editor/Managed/ManagedEditor.Internal.cpp
Wojtek Figat 510fc443e8 Refactor CoreCLR runtime into explicit dotnet api instead of mocking mono api
Required by platforms that will use mono under the hood for .Net 7
New `USE_CSHARP` define for C# ability
Engine doesn't use `mono_*` apis directly but via MCore/MClass/MMethod/ apis
2023-03-27 17:29:42 +02:00

1142 lines
41 KiB
C++

// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#include "ManagedEditor.h"
#include "Editor/Editor.h"
#include "Editor/ProjectInfo.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Platform/WindowsManager.h"
#include "Engine/ContentImporters/AssetsImportingManager.h"
#include "Engine/ContentExporters/AssetsExportingManager.h"
#include "Editor/CustomEditors/CustomEditorsUtil.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/ManagedCLR/MUtils.h"
#include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Scripting/ManagedCLR/MAssembly.h"
#include "Engine/Scripting/Internal/InternalCalls.h"
#include "Engine/Scripting/Internal/StdTypesContainer.h"
#include "Engine/Graphics/Shaders/Cache/ShaderCacheManager.h"
#include "Engine/ShadowsOfMordor/Builder.h"
#include "Engine/Physics/CollisionData.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Assets/Material.h"
#include "Engine/Content/Assets/VisualScript.h"
#include "Engine/ContentImporters/ImportTexture.h"
#include "Engine/ContentImporters/ImportModel.h"
#include "Engine/ContentImporters/ImportAudio.h"
#include "Engine/ContentImporters/CreateCollisionData.h"
#include "Engine/ContentImporters/CreateJson.h"
#include "Engine/Level/Level.h"
#include "Engine/Level/Actor.h"
#include "Engine/Level/Prefabs/Prefab.h"
#include "Engine/Core/Config/GameSettings.h"
#include "Engine/Core/Config/GraphicsSettings.h"
#include "Engine/Core/Cache.h"
#include "Engine/CSG/CSGBuilder.h"
#include "Engine/Debug/DebugLog.h"
#include "Engine/Debug/Exceptions/JsonParseException.h"
#include "Engine/Audio/AudioClip.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Utilities/Encryption.h"
#include "Engine/Navigation/Navigation.h"
#include "Engine/Particles/ParticleEmitter.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Input/Input.h"
#include "Engine/Input/Mouse.h"
#include "Engine/Input/Keyboard.h"
#include "Engine/Threading/Threading.h"
#include "FlaxEngine.Gen.h"
#include "Engine/Level/Actors/AnimatedModel.h"
#include "Engine/Serialization/JsonTools.h"
Guid ManagedEditor::ObjectID(0x91970b4e, 0x99634f61, 0x84723632, 0x54c776af);
// Disable warning C4800: 'const byte': forcing value to bool 'true' or 'false' (performance warning)
#if defined(_MSC_VER)
#pragma warning( push )
#pragma warning( disable : 4800)
#endif
struct InternalTextureOptions
{
TextureFormatType Type;
byte IsAtlas;
byte NeverStream;
byte Compress;
byte IndependentChannels;
byte sRGB;
byte GenerateMipMaps;
byte FlipY;
byte Resize;
byte PreserveAlphaCoverage;
float PreserveAlphaCoverageReference;
float Scale;
int32 MaxSize;
int32 TextureGroup;
int32 SizeX;
int32 SizeY;
MArray* SpriteAreas;
MArray* SpriteNames;
static void Convert(InternalTextureOptions* from, ImportTexture::Options* to)
{
to->Type = from->Type;
to->IsAtlas = from->IsAtlas;
to->NeverStream = from->NeverStream;
to->Compress = from->Compress;
to->IndependentChannels = from->IndependentChannels;
to->sRGB = from->sRGB;
to->GenerateMipMaps = from->GenerateMipMaps;
to->FlipY = from->FlipY;
to->Scale = from->Scale;
to->Resize = from->Resize;
to->PreserveAlphaCoverage = from->PreserveAlphaCoverage;
to->PreserveAlphaCoverageReference = from->PreserveAlphaCoverageReference;
to->MaxSize = from->MaxSize;
to->TextureGroup = from->TextureGroup;
to->SizeX = from->SizeX;
to->SizeY = from->SizeY;
to->Sprites.Clear();
if (from->SpriteNames != nullptr)
{
int32 count = MCore::Array::GetLength(from->SpriteNames);
ASSERT(count == MCore::Array::GetLength(from->SpriteAreas));
to->Sprites.Resize(count);
MString** spriteNamesPtr = MCore::Array::GetAddress<MString*>(from->SpriteNames);
Rectangle* spriteAreasPtr = MCore::Array::GetAddress<Rectangle>(from->SpriteAreas);
for (int32 i = 0; i < count; i++)
{
Sprite& sprite = to->Sprites[i];
sprite.Area = spriteAreasPtr[i];
sprite.Name = MUtils::ToString(spriteNamesPtr[i]);
}
}
}
static void Convert(ImportTexture::Options* from, InternalTextureOptions* to)
{
to->Type = from->Type;
to->IsAtlas = from->IsAtlas;
to->NeverStream = from->NeverStream;
to->Compress = from->Compress;
to->IndependentChannels = from->IndependentChannels;
to->sRGB = from->sRGB;
to->GenerateMipMaps = from->GenerateMipMaps;
to->FlipY = from->FlipY;
to->Resize = from->Resize;
to->PreserveAlphaCoverage = from->PreserveAlphaCoverage;
to->PreserveAlphaCoverageReference = from->PreserveAlphaCoverageReference;
to->Scale = from->Scale;
to->MaxSize = from->MaxSize;
to->TextureGroup = from->TextureGroup;
to->SizeX = from->SizeX;
to->SizeY = from->SizeY;
if (from->Sprites.HasItems())
{
const int32 count = from->Sprites.Count();
to->SpriteAreas = MCore::Array::New(Rectangle::TypeInitializer.GetClass(), count);
to->SpriteNames = MCore::Array::New( MCore::TypeCache::String, count);
Rectangle* spriteAreasPtr = MCore::Array::GetAddress<Rectangle>(to->SpriteAreas);
for (int32 i = 0; i < count; i++)
{
spriteAreasPtr[i] = from->Sprites[i].Area;
MCore::GC::WriteArrayRef(to->SpriteNames, (MObject*)MUtils::ToString(from->Sprites[i].Name), i);
}
}
else
{
to->SpriteAreas = nullptr;
to->SpriteNames = nullptr;
}
}
};
struct InternalModelOptions
{
ModelTool::ModelType Type;
// Geometry
byte CalculateNormals;
float SmoothingNormalsAngle;
byte FlipNormals;
float SmoothingTangentsAngle;
byte CalculateTangents;
byte OptimizeMeshes;
byte MergeMeshes;
byte ImportLODs;
byte ImportVertexColors;
byte ImportBlendShapes;
ModelLightmapUVsSource LightmapUVsSource;
MString* CollisionMeshesPrefix;
// Transform
float Scale;
Quaternion Rotation;
Float3 Translation;
byte CenterGeometry;
// Animation
ModelTool::AnimationDuration Duration;
float FramesRangeStart;
float FramesRangeEnd;
float DefaultFrameRate;
float SamplingRate;
byte SkipEmptyCurves;
byte OptimizeKeyframes;
byte ImportScaleTracks;
byte EnableRootMotion;
MString* RootNodeName;
// Level Of Detail
byte GenerateLODs;
int32 BaseLOD;
int32 LODCount;
float TriangleReduction;
// Misc
byte ImportMaterials;
byte ImportTextures;
byte RestoreMaterialsOnReimport;
// SDF
byte GenerateSDF;
float SDFResolution;
// Splitting
byte SplitObjects;
int32 ObjectIndex;
static void Convert(InternalModelOptions* from, ImportModelFile::Options* to)
{
to->Type = from->Type;
to->CalculateNormals = from->CalculateNormals;
to->SmoothingNormalsAngle = from->SmoothingNormalsAngle;
to->FlipNormals = from->FlipNormals;
to->SmoothingTangentsAngle = from->SmoothingTangentsAngle;
to->CalculateTangents = from->CalculateTangents;
to->OptimizeMeshes = from->OptimizeMeshes;
to->MergeMeshes = from->MergeMeshes;
to->ImportLODs = from->ImportLODs;
to->ImportVertexColors = from->ImportVertexColors;
to->ImportBlendShapes = from->ImportBlendShapes;
to->LightmapUVsSource = from->LightmapUVsSource;
to->CollisionMeshesPrefix = MUtils::ToString(from->CollisionMeshesPrefix);
to->Scale = from->Scale;
to->Rotation = from->Rotation;
to->Translation = from->Translation;
to->CenterGeometry = from->CenterGeometry;
to->Duration = from->Duration;
to->FramesRange.X = from->FramesRangeStart;
to->FramesRange.Y = from->FramesRangeEnd;
to->DefaultFrameRate = from->DefaultFrameRate;
to->SamplingRate = from->SamplingRate;
to->SkipEmptyCurves = from->SkipEmptyCurves;
to->OptimizeKeyframes = from->OptimizeKeyframes;
to->ImportScaleTracks = from->ImportScaleTracks;
to->EnableRootMotion = from->EnableRootMotion;
to->RootNodeName = MUtils::ToString(from->RootNodeName);
to->GenerateLODs = from->GenerateLODs;
to->BaseLOD = from->BaseLOD;
to->LODCount = from->LODCount;
to->TriangleReduction = from->TriangleReduction;
to->ImportMaterials = from->ImportMaterials;
to->ImportTextures = from->ImportTextures;
to->RestoreMaterialsOnReimport = from->RestoreMaterialsOnReimport;
to->GenerateSDF = from->GenerateSDF;
to->SDFResolution = from->SDFResolution;
to->SplitObjects = from->SplitObjects;
to->ObjectIndex = from->ObjectIndex;
}
static void Convert(ImportModelFile::Options* from, InternalModelOptions* to)
{
to->Type = from->Type;
to->CalculateNormals = from->CalculateNormals;
to->SmoothingNormalsAngle = from->SmoothingNormalsAngle;
to->FlipNormals = from->FlipNormals;
to->SmoothingTangentsAngle = from->SmoothingTangentsAngle;
to->CalculateTangents = from->CalculateTangents;
to->OptimizeMeshes = from->OptimizeMeshes;
to->MergeMeshes = from->MergeMeshes;
to->ImportLODs = from->ImportLODs;
to->ImportVertexColors = from->ImportVertexColors;
to->ImportBlendShapes = from->ImportBlendShapes;
to->LightmapUVsSource = from->LightmapUVsSource;
to->CollisionMeshesPrefix = MUtils::ToString(from->CollisionMeshesPrefix);
to->Scale = from->Scale;
to->Rotation = from->Rotation;
to->Translation = from->Translation;
to->CenterGeometry = from->CenterGeometry;
to->Duration = from->Duration;
to->FramesRangeStart = from->FramesRange.X;
to->FramesRangeEnd = from->FramesRange.Y;
to->DefaultFrameRate = from->DefaultFrameRate;
to->SamplingRate = from->SamplingRate;
to->SkipEmptyCurves = from->SkipEmptyCurves;
to->OptimizeKeyframes = from->OptimizeKeyframes;
to->ImportScaleTracks = from->ImportScaleTracks;
to->EnableRootMotion = from->EnableRootMotion;
to->RootNodeName = MUtils::ToString(from->RootNodeName);
to->GenerateLODs = from->GenerateLODs;
to->BaseLOD = from->BaseLOD;
to->LODCount = from->LODCount;
to->TriangleReduction = from->TriangleReduction;
to->ImportMaterials = from->ImportMaterials;
to->ImportTextures = from->ImportTextures;
to->RestoreMaterialsOnReimport = from->RestoreMaterialsOnReimport;
to->GenerateSDF = from->GenerateSDF;
to->SDFResolution = from->SDFResolution;
to->SplitObjects = from->SplitObjects;
to->ObjectIndex = from->ObjectIndex;
}
};
struct InternalAudioOptions
{
AudioFormat Format;
byte DisableStreaming;
byte Is3D;
int32 BitDepth;
float Quality;
static void Convert(InternalAudioOptions* from, ImportAudio::Options* to)
{
to->Format = from->Format;
to->DisableStreaming = from->DisableStreaming;
to->Is3D = from->Is3D;
to->BitDepth = from->BitDepth;
to->Quality = from->Quality;
}
static void Convert(ImportAudio::Options* from, InternalAudioOptions* to)
{
to->Format = from->Format;
to->DisableStreaming = from->DisableStreaming;
to->Is3D = from->Is3D;
to->BitDepth = from->BitDepth;
to->Quality = from->Quality;
}
};
#if defined(_MSC_VER)
#pragma warning( pop )
#endif
// Pack log messages into a single scratch buffer to reduce dynamic memory allocations
CriticalSection CachedLogDataLocker;
Array<byte> CachedLogData;
void OnLogMessage(LogType type, const StringView& msg)
{
ScopeLock lock(CachedLogDataLocker);
CachedLogData.EnsureCapacity(4 + 8 + 4 + msg.Length() * 2);
// Log Type
int32 buf = (int32)type;
CachedLogData.Add((byte*)&buf, 4);
// Time
auto time = DateTime::Now();
CachedLogData.Add((byte*)&time.Ticks, 8);
// Message Length
buf = msg.Length();
CachedLogData.Add((byte*)&buf, 4);
// Message
CachedLogData.Add((byte*)msg.Get(), msg.Length() * 2);
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_IsDevInstance()
{
#if COMPILE_WITH_DEV_ENV
return true;
#else
return false;
#endif
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_IsOfficialBuild()
{
#if OFFICIAL_BUILD
return true;
#else
return false;
#endif
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_IsPlayMode()
{
return Editor::IsPlayMode;
}
DEFINE_INTERNAL_CALL(int32) EditorInternal_ReadOutputLogs(MArray** outMessages, MArray** outLogTypes, MArray** outLogTimes, int outArraySize)
{
ScopeLock lock(CachedLogDataLocker);
if (CachedLogData.IsEmpty() || CachedLogData.Get() == nullptr)
return 0;
int32 count = 0;
const int32 maxCount = outArraySize;
byte* ptr = CachedLogData.Get();
byte* end = ptr + CachedLogData.Count();
byte* outLogTypesPtr = MCore::Array::GetAddress<byte>(*outLogTypes);
int64* outLogTimesPtr = MCore::Array::GetAddress<int64>(*outLogTimes);
while (count < maxCount && ptr != end)
{
auto type = (byte)*(int32*)ptr;
ptr += 4;
auto time = *(int64*)ptr;
ptr += 8;
auto length = *(int32*)ptr;
ptr += 4;
auto msg = (Char*)ptr;
ptr += length * 2;
auto msgObj = MUtils::ToString(StringView(msg, length));
MCore::GC::WriteArrayRef(*outMessages, (MObject*)msgObj, count);
outLogTypesPtr[count] = type;
outLogTimesPtr[count] = time;
count++;
}
const int32 dataLeft = (int32)(end - ptr);
Platform::MemoryCopy(CachedLogData.Get(), ptr, dataLeft);
CachedLogData.Resize(dataLeft);
return count;
}
DEFINE_INTERNAL_CALL(void) EditorInternal_SetPlayMode(bool value)
{
Editor::IsPlayMode = value;
}
DEFINE_INTERNAL_CALL(MString*) EditorInternal_GetProjectPath()
{
return MUtils::ToString(Editor::Project->ProjectPath);
}
DEFINE_INTERNAL_CALL(void) EditorInternal_CloseSplashScreen()
{
Editor::CloseSplashScreen();
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_CloneAssetFile(MString* dstPathObj, MString* srcPathObj, Guid* dstId)
{
// Get normalized paths
String dstPath, srcPath;
MUtils::ToString(dstPathObj, dstPath);
MUtils::ToString(srcPathObj, srcPath);
FileSystem::NormalizePath(dstPath);
FileSystem::NormalizePath(srcPath);
// Call util function
return Content::CloneAssetFile(dstPath, srcPath, *dstId);
}
enum class NewAssetType
{
Material = 0,
MaterialInstance = 1,
CollisionData = 2,
AnimationGraph = 3,
SkeletonMask = 4,
ParticleEmitter = 5,
ParticleSystem = 6,
SceneAnimation = 7,
MaterialFunction = 8,
ParticleEmitterFunction = 9,
AnimationGraphFunction = 10,
Animation = 11,
};
DEFINE_INTERNAL_CALL(bool) EditorInternal_CreateAsset(NewAssetType type, MString* outputPathObj)
{
String tag;
switch (type)
{
case NewAssetType::Material:
tag = AssetsImportingManager::CreateMaterialTag;
break;
case NewAssetType::MaterialInstance:
tag = AssetsImportingManager::CreateMaterialInstanceTag;
break;
case NewAssetType::CollisionData:
tag = AssetsImportingManager::CreateCollisionDataTag;
break;
case NewAssetType::AnimationGraph:
tag = AssetsImportingManager::CreateAnimationGraphTag;
break;
case NewAssetType::SkeletonMask:
tag = AssetsImportingManager::CreateSkeletonMaskTag;
break;
case NewAssetType::ParticleEmitter:
tag = AssetsImportingManager::CreateParticleEmitterTag;
break;
case NewAssetType::ParticleSystem:
tag = AssetsImportingManager::CreateParticleSystemTag;
break;
case NewAssetType::SceneAnimation:
tag = AssetsImportingManager::CreateSceneAnimationTag;
break;
case NewAssetType::MaterialFunction:
tag = AssetsImportingManager::CreateMaterialFunctionTag;
break;
case NewAssetType::ParticleEmitterFunction:
tag = AssetsImportingManager::CreateParticleEmitterFunctionTag;
break;
case NewAssetType::AnimationGraphFunction:
tag = AssetsImportingManager::CreateAnimationGraphFunctionTag;
break;
case NewAssetType::Animation:
tag = AssetsImportingManager::CreateAnimationTag;
break;
default:
return true;
}
String outputPath;
MUtils::ToString(outputPathObj, outputPath);
FileSystem::NormalizePath(outputPath);
return AssetsImportingManager::Create(tag, outputPath);
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_CreateVisualScript(MString* outputPathObj, MString* baseTypenameObj)
{
String outputPath;
MUtils::ToString(outputPathObj, outputPath);
FileSystem::NormalizePath(outputPath);
String baseTypename;
MUtils::ToString(baseTypenameObj, baseTypename);
return AssetsImportingManager::Create(AssetsImportingManager::CreateVisualScriptTag, outputPath, &baseTypename);
}
DEFINE_INTERNAL_CALL(MString*) EditorInternal_CanImport(MString* extensionObj)
{
String extension;
MUtils::ToString(extensionObj, extension);
if (extension.Length() > 0 && extension[0] == '.')
extension.Remove(0, 1);
const AssetImporter* importer = AssetsImportingManager::GetImporter(extension);
return importer ? MUtils::ToString(importer->ResultExtension) : nullptr;
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_Import(MString* inputPathObj, MString* outputPathObj, void* arg)
{
String inputPath, outputPath;
MUtils::ToString(inputPathObj, inputPath);
MUtils::ToString(outputPathObj, outputPath);
FileSystem::NormalizePath(inputPath);
FileSystem::NormalizePath(outputPath);
return AssetsImportingManager::Import(inputPath, outputPath, arg);
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_ImportTexture(MString* inputPathObj, MString* outputPathObj, InternalTextureOptions* optionsObj)
{
ImportTexture::Options options;
InternalTextureOptions::Convert(optionsObj, &options);
return EditorInternal_Import(inputPathObj, outputPathObj, &options);
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_ImportModel(MString* inputPathObj, MString* outputPathObj, InternalModelOptions* optionsObj)
{
ImportModelFile::Options options;
InternalModelOptions::Convert(optionsObj, &options);
return EditorInternal_Import(inputPathObj, outputPathObj, &options);
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_ImportAudio(MString* inputPathObj, MString* outputPathObj, InternalAudioOptions* optionsObj)
{
ImportAudio::Options options;
InternalAudioOptions::Convert(optionsObj, &options);
return EditorInternal_Import(inputPathObj, outputPathObj, &options);
}
DEFINE_INTERNAL_CALL(void) EditorInternal_GetAudioClipMetadata(AudioClip* clip, int32* originalSize, int32* importedSize)
{
INTERNAL_CALL_CHECK(clip);
*originalSize = clip->AudioHeader.OriginalSize;
*importedSize = clip->AudioHeader.ImportedSize;
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_SaveJsonAsset(MString* outputPathObj, MString* dataObj, MString* dataTypeNameObj)
{
String outputPath;
MUtils::ToString(outputPathObj, outputPath);
FileSystem::NormalizePath(outputPath);
const StringView dataObjChars = MCore::String::GetChars(dataObj);
const StringAsANSI<> data(dataObjChars.Get(), dataObjChars.Length());
const StringAnsiView dataAnsi(data.Get(), data.Length());
const StringView dataTypeNameObjChars = MCore::String::GetChars(dataTypeNameObj);
const StringAsANSI<> dataTypeName(dataTypeNameObjChars.Get(), dataTypeNameObjChars.Length());
const StringAnsiView dataTypeNameAnsi(dataTypeName.Get(), dataTypeName.Length());
return CreateJson::Create(outputPath, dataAnsi, dataTypeNameAnsi);
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_CanExport(MString* pathObj)
{
#if COMPILE_WITH_ASSETS_EXPORTER
String path;
MUtils::ToString(pathObj, path);
FileSystem::NormalizePath(path);
return AssetsExportingManager::CanExport(path);
#else
return false;
#endif
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_Export(MString* inputPathObj, MString* outputFolderObj)
{
#if COMPILE_WITH_ASSETS_EXPORTER
String inputPath;
MUtils::ToString(inputPathObj, inputPath);
FileSystem::NormalizePath(inputPath);
String outputFolder;
MUtils::ToString(outputFolderObj, outputFolder);
FileSystem::NormalizePath(outputFolder);
return AssetsExportingManager::Export(inputPath, outputFolder);
#else
return false;
#endif
}
DEFINE_INTERNAL_CALL(void) EditorInternal_CopyCache(Guid* dstId, Guid* srcId)
{
ShaderCacheManager::CopyCache(*dstId, *srcId);
}
DEFINE_INTERNAL_CALL(void) EditorInternal_BakeLightmaps(bool cancel)
{
auto builder = ShadowsOfMordor::Builder::Instance();
if (cancel)
builder->CancelBuild();
else
builder->Build();
}
DEFINE_INTERNAL_CALL(MString*) EditorInternal_GetShaderAssetSourceCode(BinaryAsset* obj)
{
INTERNAL_CALL_CHECK_RETURN(obj, nullptr);
if (obj->WaitForLoaded())
DebugLog::ThrowNullReference();
auto lock = obj->Storage->Lock();
if (obj->LoadChunk(SHADER_FILE_CHUNK_SOURCE))
return nullptr;
BytesContainer data;
obj->GetChunkData(SHADER_FILE_CHUNK_SOURCE, data);
Encryption::DecryptBytes((byte*)data.Get(), data.Length());
const StringAnsiView srcData((const char*)data.Get(), data.Length());
const String source(srcData);
const auto str = MUtils::ToString(source);
Encryption::EncryptBytes((byte*)data.Get(), data.Length());
return str;
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_CookMeshCollision(MString* pathObj, CollisionDataType type, ModelBase* modelObj, int32 modelLodIndex, uint32 materialSlotsMask, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
{
#if COMPILE_WITH_PHYSICS_COOKING
CollisionCooking::Argument arg;
String path;
MUtils::ToString(pathObj, path);
FileSystem::NormalizePath(path);
arg.Type = type;
arg.Model = modelObj;
arg.ModelLodIndex = modelLodIndex;
arg.MaterialSlotsMask = materialSlotsMask;
arg.ConvexFlags = convexFlags;
arg.ConvexVertexLimit = convexVertexLimit;
return CreateCollisionData::CookMeshCollision(path, arg);
#else
LOG(Warning, "Collision cooking is disabled.");
return true;
#endif
}
DEFINE_INTERNAL_CALL(void) EditorInternal_GetCollisionWires(CollisionData* collisionData, MArray** triangles, MArray** indices, int* trianglesCount, int* indicesCount)
{
if (!collisionData || collisionData->WaitForLoaded() || collisionData->GetOptions().Type == CollisionDataType::None)
return;
const auto& debugLines = collisionData->GetDebugLines();
const int32 linesCount = debugLines.Count() / 2;
MCore::GC::WriteRef(triangles, (MObject*)MCore::Array::New(Float3::TypeInitializer.GetClass(), debugLines.Count()));
MCore::GC::WriteRef(indices, (MObject*)MCore::Array::New( MCore::TypeCache::Int32, linesCount * 3));
// Use one triangle per debug line
Platform::MemoryCopy(MCore::Array::GetAddress<Float3>(*triangles), debugLines.Get(), debugLines.Count() * sizeof(Float3));
int32 iI = 0;
int32* indicesPtr = MCore::Array::GetAddress<int32>(*indices);
for (int32 i = 0; i < debugLines.Count(); i += 2)
{
indicesPtr[iI++] = i;
indicesPtr[iI++] = i + 1;
indicesPtr[iI++] = i;
}
*trianglesCount = debugLines.Count();
*indicesCount = linesCount * 3;
}
DEFINE_INTERNAL_CALL(void) EditorInternal_GetEditorBoxWithChildren(Actor* obj, BoundingBox* result)
{
INTERNAL_CALL_CHECK(obj);
*result = obj->GetEditorBoxChildren();
}
DEFINE_INTERNAL_CALL(void) EditorInternal_SetOptions(ManagedEditor::InternalOptions* options)
{
ManagedEditor::ManagedEditorOptions = *options;
// Apply options
AssetsImportingManager::UseImportPathRelative = ManagedEditor::ManagedEditorOptions.UseAssetImportPathRelative != 0;
}
DEFINE_INTERNAL_CALL(void) EditorInternal_DrawNavMesh()
{
Navigation::DrawNavMesh();
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_GetIsEveryAssemblyLoaded()
{
return Scripting::IsEveryAssemblyLoaded();
}
DEFINE_INTERNAL_CALL(int32) EditorInternal_GetLastProjectOpenedEngineBuild()
{
return Editor::LastProjectOpenedEngineBuild;
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_GetIsCSGActive()
{
#if COMPILE_WITH_CSG_BUILDER
return CSG::Builder::IsActive();
#else
return false;
#endif
}
DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(float deltaTime)
{
// Update
Platform::Tick();
Engine::HasFocus = (Engine::MainWindow && Engine::MainWindow->IsFocused()) || Platform::GetHasFocus();
if (Engine::HasFocus)
{
InputDevice::EventQueue inputEvents;
if (Input::Mouse)
{
if (Input::Mouse->Update(inputEvents))
{
Input::Mouse->DeleteObject();
Input::Mouse = nullptr;
}
}
if (Input::Keyboard)
{
if (Input::Keyboard->Update(inputEvents))
{
Input::Keyboard->DeleteObject();
Input::Keyboard = nullptr;
}
}
WindowsManager::WindowsLocker.Lock();
Window* defaultWindow = nullptr;
for (auto window : WindowsManager::Windows)
{
if (window->IsFocused() && window->GetSettings().AllowInput)
{
defaultWindow = window;
break;
}
}
for (const auto& e : inputEvents)
{
auto window = e.Target ? e.Target : defaultWindow;
if (!window)
continue;
switch (e.Type)
{
// Keyboard events
case InputDevice::EventType::Char:
window->OnCharInput(e.CharData.Char);
break;
case InputDevice::EventType::KeyDown:
window->OnKeyDown(e.KeyData.Key);
break;
case InputDevice::EventType::KeyUp:
window->OnKeyUp(e.KeyData.Key);
break;
// Mouse events
case InputDevice::EventType::MouseDown:
window->OnMouseDown(window->ScreenToClient(e.MouseData.Position), e.MouseData.Button);
break;
case InputDevice::EventType::MouseUp:
window->OnMouseUp(window->ScreenToClient(e.MouseData.Position), e.MouseData.Button);
break;
case InputDevice::EventType::MouseDoubleClick:
window->OnMouseDoubleClick(window->ScreenToClient(e.MouseData.Position), e.MouseData.Button);
break;
case InputDevice::EventType::MouseWheel:
window->OnMouseWheel(window->ScreenToClient(e.MouseWheelData.Position), e.MouseWheelData.WheelDelta);
break;
case InputDevice::EventType::MouseMove:
window->OnMouseMove(window->ScreenToClient(e.MouseData.Position));
break;
case InputDevice::EventType::MouseLeave:
window->OnMouseLeave();
break;
}
}
WindowsManager::WindowsLocker.Unlock();
}
WindowsManager::WindowsLocker.Lock();
for (auto& win : WindowsManager::Windows)
{
if (win->IsVisible())
win->OnUpdate(deltaTime);
}
WindowsManager::WindowsLocker.Unlock();
// Draw
Engine::OnDraw();
}
struct VisualScriptLocalManaged
{
MString* Value;
MString* ValueTypeName;
uint32 NodeId;
int32 BoxId;
};
DEFINE_INTERNAL_CALL(MArray*) EditorInternal_GetVisualScriptLocals(int* localsCount)
{
MArray* result = nullptr;
*localsCount = 0;
const auto stack = VisualScripting::GetThreadStackTop();
if (stack && stack->Scope)
{
const int32 count = stack->Scope->Parameters.Length() + stack->Scope->ReturnedValues.Count();
const MClass* mclass = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->GetClass("FlaxEditor.Editor+VisualScriptLocal");
ASSERT(mclass);
result = MCore::Array::New( mclass, count);
VisualScriptLocalManaged local;
local.NodeId = MAX_uint32;
if (stack->Scope->Parameters.Length() != 0)
{
auto s = stack;
while (s->PreviousFrame && s->PreviousFrame->Scope == stack->Scope)
s = s->PreviousFrame;
if (s)
local.NodeId = s->Node->ID;
}
VisualScriptLocalManaged* resultPtr = MCore::Array::GetAddress<VisualScriptLocalManaged>(result);
for (int32 i = 0; i < stack->Scope->Parameters.Length(); i++)
{
auto& v = stack->Scope->Parameters[i];
local.BoxId = i + 1;
local.Value = MUtils::ToString(v.ToString());
local.ValueTypeName = MUtils::ToString(v.Type.GetTypeName());
resultPtr[i] = local;
}
for (int32 i = 0; i < stack->Scope->ReturnedValues.Count(); i++)
{
auto& v = stack->Scope->ReturnedValues[i];
local.NodeId = v.NodeId;
local.BoxId = v.BoxId;
local.Value = MUtils::ToString(v.Value.ToString());
local.ValueTypeName = MUtils::ToString(v.Value.Type.GetTypeName());
resultPtr[stack->Scope->Parameters.Length() + i] = local;
}
*localsCount = count;
}
return result;
}
struct VisualScriptStackFrameManaged
{
MObject* Script;
uint32 NodeId;
int32 BoxId;
};
DEFINE_INTERNAL_CALL(MArray*) EditorInternal_GetVisualScriptStackFrames(int* stackFramesCount)
{
MArray* result = nullptr;
*stackFramesCount = 0;
const auto stack = VisualScripting::GetThreadStackTop();
if (stack)
{
int32 count = 0;
auto s = stack;
while (s)
{
s = s->PreviousFrame;
count++;
}
const MClass* mclass = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->GetClass("FlaxEditor.Editor+VisualScriptStackFrame");
ASSERT(mclass);
result = MCore::Array::New( mclass, count);
VisualScriptStackFrameManaged* resultPtr = MCore::Array::GetAddress<VisualScriptStackFrameManaged>(result);
s = stack;
count = 0;
while (s)
{
VisualScriptStackFrameManaged frame;
frame.Script = s->Script->GetOrCreateManagedInstance();
frame.NodeId = s->Node->ID;
frame.BoxId = s->Box ? s->Box->ID : MAX_uint32;
resultPtr[count] = frame;
s = s->PreviousFrame;
count++;
}
*stackFramesCount = count;
}
return result;
}
DEFINE_INTERNAL_CALL(VisualScriptStackFrameManaged) EditorInternal_GetVisualScriptPreviousScopeFrame()
{
VisualScriptStackFrameManaged frame;
Platform::MemoryClear(&frame, sizeof(frame));
const auto stack = VisualScripting::GetThreadStackTop();
if (stack)
{
auto s = stack;
while (s->PreviousFrame && s->PreviousFrame->Scope == stack->Scope)
s = s->PreviousFrame;
if (s && s->PreviousFrame)
{
s = s->PreviousFrame;
frame.Script = s->Script->GetOrCreateManagedInstance();
frame.NodeId = s->Node->ID;
frame.BoxId = s->Box ? s->Box->ID : MAX_uint32;
}
}
return frame;
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_EvaluateVisualScriptLocal(VisualScript* script, VisualScriptLocalManaged* local)
{
Variant v;
if (VisualScripting::Evaluate(script, VisualScripting::GetThreadStackTop()->Instance, local->NodeId, local->BoxId, v))
{
local->Value = MUtils::ToString(v.ToString());
local->ValueTypeName = MUtils::ToString(v.Type.GetTypeName());
return true;
}
return false;
}
DEFINE_INTERNAL_CALL(void) EditorInternal_DeserializeSceneObject(SceneObject* sceneObject, MString* jsonObj)
{
PROFILE_CPU_NAMED("DeserializeSceneObject");
StringAnsi json;
MUtils::ToString(jsonObj, json);
rapidjson_flax::Document document;
{
PROFILE_CPU_NAMED("Json.Parse");
document.Parse(json.Get(), json.Length());
}
if (document.HasParseError())
{
Log::JsonParseException(document.GetParseError(), document.GetErrorOffset());
DebugLog::ThrowException("Failed to parse Json.");
}
auto modifier = Cache::ISerializeModifier.Get();
modifier->EngineBuild = FLAXENGINE_VERSION_BUILD;
Scripting::ObjectsLookupIdMapping.Set(&modifier.Value->IdsMapping);
{
PROFILE_CPU_NAMED("Deserialize");
sceneObject->Deserialize(document, modifier.Value);
}
}
DEFINE_INTERNAL_CALL(void) EditorInternal_LoadAsset(Guid* id)
{
Content::LoadAsync<Asset>(*id);
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_CanSetToRoot(Prefab* prefab, Actor* targetActor)
{
// Reference: Prefab::ApplyAll(Actor* targetActor)
if (targetActor->GetPrefabID() != prefab->GetID())
return false;
if (targetActor->GetPrefabObjectID() != prefab->GetRootObjectId())
{
const ISerializable::DeserializeStream** newRootDataPtr = prefab->ObjectsDataCache.TryGet(targetActor->GetPrefabObjectID());
if (!newRootDataPtr || !*newRootDataPtr)
return false;
const ISerializable::DeserializeStream& newRootData = **newRootDataPtr;
Guid prefabId, prefabObjectID;
if (JsonTools::GetGuidIfValid(prefabId, newRootData, "PrefabID") && JsonTools::GetGuidIfValid(prefabObjectID, newRootData, "PrefabObjectID"))
{
const auto nestedPrefab = Content::Load<Prefab>(prefabId);
if (nestedPrefab && nestedPrefab->GetRootObjectId() != prefabObjectID)
return false;
}
}
return true;
}
DEFINE_INTERNAL_CALL(float) EditorInternal_GetAnimationTime(AnimatedModel* animatedModel)
{
return animatedModel && animatedModel->GraphInstance.State.Count() == 1 ? animatedModel->GraphInstance.State[0].Animation.TimePosition : 0.0f;
}
DEFINE_INTERNAL_CALL(void) EditorInternal_SetAnimationTime(AnimatedModel* animatedModel, float time)
{
if (animatedModel && animatedModel->GraphInstance.State.Count() == 1)
animatedModel->GraphInstance.State[0].Animation.TimePosition = time;
}
DEFINE_INTERNAL_CALL(MTypeObject*) CustomEditorsUtilInternal_GetCustomEditor(MTypeObject* targetType)
{
return CustomEditorsUtil::GetCustomEditor(targetType);
}
DEFINE_INTERNAL_CALL(bool) TextureImportEntryInternal_GetTextureImportOptions(MString* pathObj, InternalTextureOptions* result)
{
String path;
MUtils::ToString(pathObj, path);
FileSystem::NormalizePath(path);
ImportTexture::Options options;
if (ImportTexture::TryGetImportOptions(path, options))
{
// Convert into managed storage
InternalTextureOptions::Convert(&options, result);
return true;
}
return false;
}
DEFINE_INTERNAL_CALL(void) ModelImportEntryInternal_GetModelImportOptions(MString* pathObj, InternalModelOptions* result)
{
// Initialize defaults
ImportModelFile::Options options;
if (const auto* graphicsSettings = GraphicsSettings::Get())
{
options.GenerateSDF = graphicsSettings->GenerateSDFOnModelImport;
}
// Get options from model
String path;
MUtils::ToString(pathObj, path);
FileSystem::NormalizePath(path);
ImportModelFile::TryGetImportOptions(path, options);
// Convert into managed storage
InternalModelOptions::Convert(&options, result);
}
DEFINE_INTERNAL_CALL(bool) AudioImportEntryInternal_GetAudioImportOptions(MString* pathObj, InternalAudioOptions* result)
{
String path;
MUtils::ToString(pathObj, path);
FileSystem::NormalizePath(path);
ImportAudio::Options options;
if (ImportAudio::TryGetImportOptions(path, options))
{
// Convert into managed storage
InternalAudioOptions::Convert(&options, result);
return true;
}
return false;
}
DEFINE_INTERNAL_CALL(MArray*) LayersAndTagsSettingsInternal_GetCurrentLayers(int* layersCount)
{
*layersCount = Math::Max(1, Level::GetNonEmptyLayerNamesCount());
return MUtils::ToArray(Span<String>(Level::Layers, *layersCount));
}
DEFINE_INTERNAL_CALL(void) GameSettingsInternal_Apply()
{
LOG(Info, "Apply game settings");
GameSettings::Load();
}
class ManagedEditorInternal
{
public:
static void InitRuntime()
{
ADD_INTERNAL_CALL("FlaxEditor.Editor::IsDevInstance", &EditorInternal_IsDevInstance);
ADD_INTERNAL_CALL("FlaxEditor.Editor::IsOfficialBuild", &EditorInternal_IsOfficialBuild);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_IsPlayMode", &EditorInternal_IsPlayMode);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_ReadOutputLogs", &EditorInternal_ReadOutputLogs);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_SetPlayMode", &EditorInternal_SetPlayMode);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_GetProjectPath", &EditorInternal_GetProjectPath);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_Import", &EditorInternal_Import);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_ImportTexture", &EditorInternal_ImportTexture);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_ImportModel", &EditorInternal_ImportModel);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_ImportAudio", &EditorInternal_ImportAudio);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_GetAudioClipMetadata", &EditorInternal_GetAudioClipMetadata);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_SaveJsonAsset", &EditorInternal_SaveJsonAsset);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_CopyCache", &EditorInternal_CopyCache);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_CloneAssetFile", &EditorInternal_CloneAssetFile);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_BakeLightmaps", &EditorInternal_BakeLightmaps);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_GetShaderAssetSourceCode", &EditorInternal_GetShaderAssetSourceCode);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_CookMeshCollision", &EditorInternal_CookMeshCollision);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_GetCollisionWires", &EditorInternal_GetCollisionWires);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_GetEditorBoxWithChildren", &EditorInternal_GetEditorBoxWithChildren);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_SetOptions", &EditorInternal_SetOptions);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_DrawNavMesh", &EditorInternal_DrawNavMesh);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_CloseSplashScreen", &EditorInternal_CloseSplashScreen);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_CreateAsset", &EditorInternal_CreateAsset);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_CreateVisualScript", &EditorInternal_CreateVisualScript);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_CanImport", &EditorInternal_CanImport);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_CanExport", &EditorInternal_CanExport);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_Export", &EditorInternal_Export);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_GetIsEveryAssemblyLoaded", &EditorInternal_GetIsEveryAssemblyLoaded);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_GetLastProjectOpenedEngineBuild", &EditorInternal_GetLastProjectOpenedEngineBuild);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_GetIsCSGActive", &EditorInternal_GetIsCSGActive);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_RunVisualScriptBreakpointLoopTick", &EditorInternal_RunVisualScriptBreakpointLoopTick);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_GetVisualScriptLocals", &EditorInternal_GetVisualScriptLocals);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_GetVisualScriptStackFrames", &EditorInternal_GetVisualScriptStackFrames);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_GetVisualScriptPreviousScopeFrame", &EditorInternal_GetVisualScriptPreviousScopeFrame);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_EvaluateVisualScriptLocal", &EditorInternal_EvaluateVisualScriptLocal);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_DeserializeSceneObject", &EditorInternal_DeserializeSceneObject);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_LoadAsset", &EditorInternal_LoadAsset);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_CanSetToRoot", &EditorInternal_CanSetToRoot);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_GetAnimationTime", &EditorInternal_GetAnimationTime);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_SetAnimationTime", &EditorInternal_SetAnimationTime);
ADD_INTERNAL_CALL("FlaxEditor.CustomEditors.CustomEditorsUtil::Internal_GetCustomEditor", &CustomEditorsUtilInternal_GetCustomEditor);
ADD_INTERNAL_CALL("FlaxEditor.Content.Import.TextureImportEntry::Internal_GetTextureImportOptions", &TextureImportEntryInternal_GetTextureImportOptions);
ADD_INTERNAL_CALL("FlaxEditor.Content.Import.ModelImportEntry::Internal_GetModelImportOptions", &ModelImportEntryInternal_GetModelImportOptions);
ADD_INTERNAL_CALL("FlaxEditor.Content.Import.AudioImportEntry::Internal_GetAudioImportOptions", &AudioImportEntryInternal_GetAudioImportOptions);
ADD_INTERNAL_CALL("FlaxEditor.Content.Settings.LayersAndTagsSettings::GetCurrentLayers", &LayersAndTagsSettingsInternal_GetCurrentLayers);
ADD_INTERNAL_CALL("FlaxEditor.Content.Settings.GameSettings::Apply", &GameSettingsInternal_Apply);
}
};
IMPLEMENT_SCRIPTING_TYPE_NO_SPAWN_WITH_BASE(ManagedEditor, ScriptingObject, FlaxEngine, "FlaxEditor.Editor", nullptr, nullptr);