You're breathtaking!
This commit is contained in:
34
Source/.editorconfig
Normal file
34
Source/.editorconfig
Normal file
@@ -0,0 +1,34 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = crlf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
|
||||
[*.ttinclude]
|
||||
insert_final_newline = false
|
||||
|
||||
# C++ files
|
||||
[*.{cpp,c,h,hpp}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# C# files
|
||||
[*.cs]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# XAML files
|
||||
[*.xaml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# MSBuild
|
||||
[*.{csproj,proj,projitems,shproj,fsproj,target,props}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# Python files
|
||||
[*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
270
Source/Editor/Analytics/EditorAnalytics.cpp
Normal file
270
Source/Editor/Analytics/EditorAnalytics.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "EditorAnalytics.h"
|
||||
#include "EditorAnalyticsController.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Math/Vector2.h"
|
||||
#include "Engine/Core/Types/DateTime.h"
|
||||
#include "Editor/Editor.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Utilities/StringConverter.h"
|
||||
#include "FlaxEngine.Gen.h"
|
||||
#include <ThirdParty/UniversalAnalytics/universal-analytics.h>
|
||||
|
||||
#define FLAX_EDITOR_GOOGLE_ID "UA-88357703-3"
|
||||
|
||||
// Helper doc: https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters
|
||||
|
||||
namespace EditorAnalyticsImpl
|
||||
{
|
||||
UATracker Tracker = nullptr;
|
||||
|
||||
StringAnsi ClientId;
|
||||
StringAnsi ProjectName;
|
||||
StringAnsi ScreenResolution;
|
||||
StringAnsi UserLanguage;
|
||||
StringAnsi GPU;
|
||||
DateTime SessionStartTime;
|
||||
|
||||
CriticalSection Locker;
|
||||
bool IsSessionActive = false;
|
||||
EditorAnalyticsController Controller;
|
||||
Array<char> TmpBuffer;
|
||||
}
|
||||
|
||||
using namespace EditorAnalyticsImpl;
|
||||
|
||||
class EditorAnalyticsService : public EngineService
|
||||
{
|
||||
public:
|
||||
|
||||
EditorAnalyticsService()
|
||||
: EngineService(TEXT("Editor Analytics"))
|
||||
{
|
||||
}
|
||||
|
||||
bool Init() override;
|
||||
void Dispose() override;
|
||||
};
|
||||
|
||||
EditorAnalyticsService EditorAnalyticsServiceInstance;
|
||||
|
||||
bool EditorAnalytics::IsSessionActive()
|
||||
{
|
||||
return EditorAnalyticsImpl::IsSessionActive;
|
||||
}
|
||||
|
||||
void EditorAnalytics::StartSession()
|
||||
{
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
if (EditorAnalyticsImpl::IsSessionActive)
|
||||
return;
|
||||
|
||||
// Prepare client metadata
|
||||
if (ClientId.IsEmpty())
|
||||
{
|
||||
ClientId = Platform::GetUniqueDeviceId().ToString(Guid::FormatType::N).ToStringAnsi();
|
||||
}
|
||||
if (ScreenResolution.IsEmpty())
|
||||
{
|
||||
const Vector2 desktopSize = Platform::GetDesktopSize();
|
||||
ScreenResolution = StringAnsi(StringUtils::ToString((int32)desktopSize.X)) + "x" + StringAnsi(StringUtils::ToString((int32)desktopSize.Y));
|
||||
}
|
||||
if (UserLanguage.IsEmpty())
|
||||
{
|
||||
UserLanguage = Platform::GetUserLocaleName().ToStringAnsi();
|
||||
}
|
||||
if (GPU.IsEmpty())
|
||||
{
|
||||
const auto gpu = GPUDevice::Instance;
|
||||
if (gpu && gpu->GetState() == GPUDevice::DeviceState::Ready)
|
||||
GPU = StringAsANSI<>(gpu->GetAdapter()->GetDescription().GetText()).Get();
|
||||
}
|
||||
if (ProjectName.IsEmpty())
|
||||
{
|
||||
ProjectName = Editor::Project->Name.ToStringAnsi();
|
||||
}
|
||||
SessionStartTime = DateTime::Now();
|
||||
|
||||
// Initialize the analytics tracker
|
||||
Tracker = createTracker(FLAX_EDITOR_GOOGLE_ID, ClientId.Get(), nullptr);
|
||||
Tracker->user_agent = "Flax Editor";
|
||||
|
||||
// Store these options permanently (for the lifetime of the tracker)
|
||||
setTrackerOption(Tracker, UA_OPTION_QUEUE, 1);
|
||||
UASettings GlobalSettings =
|
||||
{
|
||||
{
|
||||
{ UA_DOCUMENT_PATH, 0, "Flax Editor" },
|
||||
{ UA_DOCUMENT_TITLE, 0, "Flax Editor" },
|
||||
#if PLATFORM_WINDOWS
|
||||
{ UA_USER_AGENT, 0, "Windows " FLAXENGINE_VERSION_TEXT },
|
||||
#else
|
||||
#error "Unknown platform"
|
||||
#endif
|
||||
{ UA_ANONYMIZE_IP, 0, "0" },
|
||||
{ UA_APP_ID, 0, "Flax Editor " FLAXENGINE_VERSION_TEXT },
|
||||
{ UA_APP_INSTALLER_ID, 0, "Flax Editor" },
|
||||
{ UA_APP_NAME, 0, "Flax Editor" },
|
||||
{ UA_APP_VERSION, 0, FLAXENGINE_VERSION_TEXT },
|
||||
{ UA_SCREEN_NAME, 0, "Flax Editor " FLAXENGINE_VERSION_TEXT },
|
||||
{ UA_SCREEN_RESOLUTION, 0, ScreenResolution.Get() },
|
||||
{ UA_USER_LANGUAGE, 0, UserLanguage.Get() },
|
||||
}
|
||||
};
|
||||
setParameters(Tracker, &GlobalSettings);
|
||||
|
||||
// Send the initial session event
|
||||
UAOptions sessionViewOptions =
|
||||
{
|
||||
{
|
||||
{ UA_EVENT_CATEGORY, 0, "Session" },
|
||||
{ UA_EVENT_ACTION, 0, "Start Editor" },
|
||||
{ UA_EVENT_LABEL, 0, "Start Editor" },
|
||||
{ UA_SESSION_CONTROL, 0, "start" },
|
||||
{ UA_DOCUMENT_TITLE, 0, ProjectName.Get() },
|
||||
}
|
||||
};
|
||||
sendTracking(Tracker, UA_SCREENVIEW, &sessionViewOptions);
|
||||
|
||||
EditorAnalyticsImpl::IsSessionActive = true;
|
||||
|
||||
Controller.Init();
|
||||
|
||||
// Report GPU model
|
||||
if (GPU.HasChars())
|
||||
{
|
||||
SendEvent("Telemetry", "GPU.Model", GPU.Get());
|
||||
}
|
||||
}
|
||||
|
||||
void EditorAnalytics::EndSession()
|
||||
{
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
if (!EditorAnalyticsImpl::IsSessionActive)
|
||||
return;
|
||||
|
||||
Controller.Cleanup();
|
||||
|
||||
StringAnsi sessionLength = StringAnsi::Format("{0}", (int32)(DateTime::Now() - SessionStartTime).GetTotalSeconds());
|
||||
|
||||
// Send the end session event
|
||||
UAOptions sessionEventOptions =
|
||||
{
|
||||
{
|
||||
{ UA_EVENT_CATEGORY, 0, "Session" },
|
||||
{ UA_EVENT_ACTION, 0, "Session Length" },
|
||||
{ UA_EVENT_LABEL, 0, "Session Length" },
|
||||
{ UA_EVENT_VALUE, 0, sessionLength.Get() },
|
||||
{ UA_CUSTOM_DIMENSION, 1, "Session Length" },
|
||||
{ UA_CUSTOM_METRIC, 1, sessionLength.Get() },
|
||||
}
|
||||
};
|
||||
sendTracking(Tracker, UA_EVENT, &sessionEventOptions);
|
||||
|
||||
// Send the end session event
|
||||
UAOptions sessionViewOptions =
|
||||
{
|
||||
{
|
||||
{ UA_EVENT_CATEGORY, 0, "Session" },
|
||||
{ UA_EVENT_ACTION, 0, "End Editor" },
|
||||
{ UA_EVENT_LABEL, 0, "End Editor" },
|
||||
{ UA_EVENT_VALUE, 0, sessionLength.Get() },
|
||||
{ UA_SESSION_CONTROL, 0, "end" },
|
||||
}
|
||||
};
|
||||
sendTracking(Tracker, UA_SCREENVIEW, &sessionViewOptions);
|
||||
|
||||
// Cleanup
|
||||
removeTracker(Tracker);
|
||||
Tracker = nullptr;
|
||||
|
||||
EditorAnalyticsImpl::IsSessionActive = false;
|
||||
}
|
||||
|
||||
void EditorAnalytics::SendEvent(const char* category, const char* name, const char* label)
|
||||
{
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
if (!EditorAnalyticsImpl::IsSessionActive)
|
||||
return;
|
||||
|
||||
UAOptions opts =
|
||||
{
|
||||
{
|
||||
{ UA_EVENT_CATEGORY, 0, (char*)category },
|
||||
{ UA_EVENT_ACTION, 0, (char*)name },
|
||||
{ UA_EVENT_LABEL, 0, (char*)label },
|
||||
}
|
||||
};
|
||||
|
||||
sendTracking(Tracker, UA_EVENT, &opts);
|
||||
}
|
||||
|
||||
void EditorAnalytics::SendEvent(const char* category, const char* name, const StringView& label)
|
||||
{
|
||||
SendEvent(category, name, label.Get());
|
||||
}
|
||||
|
||||
void EditorAnalytics::SendEvent(const char* category, const char* name, const Char* label)
|
||||
{
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
if (!EditorAnalyticsImpl::IsSessionActive)
|
||||
return;
|
||||
|
||||
ASSERT(category && name && label);
|
||||
|
||||
const int32 labelLength = StringUtils::Length(label);
|
||||
TmpBuffer.Clear();
|
||||
TmpBuffer.Resize(labelLength + 1);
|
||||
StringUtils::ConvertUTF162ANSI(label, TmpBuffer.Get(), labelLength);
|
||||
TmpBuffer[labelLength] = 0;
|
||||
|
||||
UAOptions opts =
|
||||
{
|
||||
{
|
||||
{ UA_EVENT_CATEGORY, 0, (char*)category },
|
||||
{ UA_EVENT_ACTION, 0, (char*)name },
|
||||
{ UA_EVENT_LABEL, 0, (char*)TmpBuffer.Get() },
|
||||
}
|
||||
};
|
||||
|
||||
sendTracking(Tracker, UA_EVENT, &opts);
|
||||
}
|
||||
|
||||
bool EditorAnalyticsService::Init()
|
||||
{
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
// Disable analytics from the dev (internal) builds
|
||||
LOG(Info, "Editor analytics service is disabled in dev builds.");
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// If file '<editor_install_root>/noTracking' exists then do not use analytics (per engine instance)
|
||||
// If file '%appdata/Flax/noTracking' exists then do not use analytics (globally)
|
||||
String appDataPath;
|
||||
FileSystem::GetSpecialFolderPath(SpecialFolder::AppData, appDataPath);
|
||||
if (FileSystem::FileExists(Globals::StartupFolder / TEXT("noTracking")) ||
|
||||
FileSystem::FileExists(appDataPath / TEXT("Flax/noTracking")))
|
||||
{
|
||||
LOG(Info, "Editor analytics service is disabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(Info, "Editor analytics service is enabled. Curl version: {0}", TEXT(LIBCURL_VERSION));
|
||||
|
||||
EditorAnalytics::StartSession();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void EditorAnalyticsService::Dispose()
|
||||
{
|
||||
EditorAnalytics::EndSession();
|
||||
}
|
||||
53
Source/Editor/Analytics/EditorAnalytics.h
Normal file
53
Source/Editor/Analytics/EditorAnalytics.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Types/BaseTypes.h"
|
||||
|
||||
/// <summary>
|
||||
/// Editor user analytics reporting and telemetry service.
|
||||
/// </summary>
|
||||
class EditorAnalytics
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether analytics session is active.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if there is active analytics session running; otherwise, <c>false</c>.</returns>
|
||||
static bool IsSessionActive();
|
||||
|
||||
/// <summary>
|
||||
/// Starts the session.
|
||||
/// </summary>
|
||||
static void StartSession();
|
||||
|
||||
/// <summary>
|
||||
/// Ends the session.
|
||||
/// </summary>
|
||||
static void EndSession();
|
||||
|
||||
/// <summary>
|
||||
/// Sends the custom event.
|
||||
/// </summary>
|
||||
/// <param name="category">The event category name.</param>
|
||||
/// <param name="name">The event name.</param>
|
||||
/// <param name="label">The event label.</param>
|
||||
static void SendEvent(const char* category, const char* name, const char* label = nullptr);
|
||||
|
||||
/// <summary>
|
||||
/// Sends the custom event.
|
||||
/// </summary>
|
||||
/// <param name="category">The event category name.</param>
|
||||
/// <param name="name">The event name.</param>
|
||||
/// <param name="label">The event label.</param>
|
||||
static void SendEvent(const char* category, const char* name, const StringView& label);
|
||||
|
||||
/// <summary>
|
||||
/// Sends the custom event.
|
||||
/// </summary>
|
||||
/// <param name="category">The event category name.</param>
|
||||
/// <param name="name">The event name.</param>
|
||||
/// <param name="label">The event label.</param>
|
||||
static void SendEvent(const char* category, const char* name, const Char* label);
|
||||
};
|
||||
68
Source/Editor/Analytics/EditorAnalyticsController.cpp
Normal file
68
Source/Editor/Analytics/EditorAnalyticsController.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "EditorAnalyticsController.h"
|
||||
#include "Editor/Cooker/GameCooker.h"
|
||||
#include "EditorAnalytics.h"
|
||||
#include "Engine/ShadowsOfMordor/Builder.h"
|
||||
|
||||
void RegisterGameCookingStart(GameCooker::EventType type)
|
||||
{
|
||||
auto platform = ToString(GameCooker::GetCurrentData().Platform);
|
||||
if (type == GameCooker::EventType::BuildStarted)
|
||||
{
|
||||
EditorAnalytics::SendEvent("Actions", "GameCooker.Start", platform);
|
||||
}
|
||||
else if (type == GameCooker::EventType::BuildFailed)
|
||||
{
|
||||
EditorAnalytics::SendEvent("Actions", "GameCooker.Failed", platform);
|
||||
}
|
||||
else if (type == GameCooker::EventType::BuildDone)
|
||||
{
|
||||
EditorAnalytics::SendEvent("Actions", "GameCooker.End", platform);
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterLightmapsBuildingStart()
|
||||
{
|
||||
EditorAnalytics::SendEvent("Actions", "ShadowsOfMordor.Build", "ShadowsOfMordor.Build");
|
||||
}
|
||||
|
||||
void RegisterError(LogType type, const StringView& msg)
|
||||
{
|
||||
if (type == LogType::Error && false)
|
||||
{
|
||||
String value = msg.ToString();
|
||||
const int32 MaxLength = 300;
|
||||
if (msg.Length() > MaxLength)
|
||||
value = value.Substring(0, MaxLength);
|
||||
value.Replace('\n', ' ');
|
||||
value.Replace('\r', ' ');
|
||||
|
||||
EditorAnalytics::SendEvent("Errors", "Log.Error", value);
|
||||
}
|
||||
else if (type == LogType::Fatal)
|
||||
{
|
||||
String value = msg.ToString();
|
||||
const int32 MaxLength = 300;
|
||||
if (msg.Length() > MaxLength)
|
||||
value = value.Substring(0, MaxLength);
|
||||
value.Replace('\n', ' ');
|
||||
value.Replace('\r', ' ');
|
||||
|
||||
EditorAnalytics::SendEvent("Errors", "Log.Fatal", value);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorAnalyticsController::Init()
|
||||
{
|
||||
GameCooker::OnEvent.Bind<RegisterGameCookingStart>();
|
||||
ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Bind<RegisterLightmapsBuildingStart>();
|
||||
Log::Logger::OnError.Bind<RegisterError>();
|
||||
}
|
||||
|
||||
void EditorAnalyticsController::Cleanup()
|
||||
{
|
||||
GameCooker::OnEvent.Unbind<RegisterGameCookingStart>();
|
||||
ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Unbind<RegisterLightmapsBuildingStart>();
|
||||
Log::Logger::OnError.Unbind<RegisterError>();
|
||||
}
|
||||
21
Source/Editor/Analytics/EditorAnalyticsController.h
Normal file
21
Source/Editor/Analytics/EditorAnalyticsController.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
/// <summary>
|
||||
/// The controller object for the tracking events for the editor analytics.
|
||||
/// </summary>
|
||||
class EditorAnalyticsController
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Starts the service (registers to event handlers).
|
||||
/// </summary>
|
||||
void Init();
|
||||
|
||||
/// <summary>
|
||||
/// Ends the service (unregisters to event handlers).
|
||||
/// </summary>
|
||||
void Cleanup();
|
||||
};
|
||||
43
Source/Editor/Content/AssetItemConverter.cs
Normal file
43
Source/Editor/Content/AssetItemConverter.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Serialize references to the FlaxEngine.Object as Guid (format N).
|
||||
/// </summary>
|
||||
/// <seealso cref="Newtonsoft.Json.JsonConverter" />
|
||||
internal class AssetItemConverter : JsonConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
|
||||
{
|
||||
Guid id = Guid.Empty;
|
||||
if (value is AssetItem obj)
|
||||
id = obj.ID;
|
||||
|
||||
writer.WriteValue(FlaxEngine.Json.JsonSerializer.GetStringID(id));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType == JsonToken.String)
|
||||
{
|
||||
Guid id;
|
||||
FlaxEngine.Json.JsonSerializer.ParseID((string)reader.Value, out id);
|
||||
return Editor.Instance.ContentDatabase.Find(id);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(AssetItem).IsAssignableFrom(objectType);
|
||||
}
|
||||
}
|
||||
}
|
||||
49
Source/Editor/Content/Create/CreateFileEntry.cs
Normal file
49
Source/Editor/Content/Create/CreateFileEntry.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
namespace FlaxEditor.Content.Create
|
||||
{
|
||||
/// <summary>
|
||||
/// File create entry.
|
||||
/// </summary>
|
||||
public abstract class CreateFileEntry : IFileEntryAction
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string SourceUrl { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ResultUrl { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this entry has settings to modify.
|
||||
/// </summary>
|
||||
public virtual bool HasSettings => Settings != null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the settings object to modify.
|
||||
/// </summary>
|
||||
public virtual object Settings => null;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CreateFileEntry"/> class.
|
||||
/// </summary>
|
||||
/// <param name="outputType">The output file type.</param>
|
||||
/// <param name="resultUrl">The result file url.</param>
|
||||
protected CreateFileEntry(string outputType, string resultUrl)
|
||||
{
|
||||
SourceUrl = outputType;
|
||||
ResultUrl = resultUrl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the result file.
|
||||
/// </summary>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
public abstract bool Create();
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Execute()
|
||||
{
|
||||
return Create();
|
||||
}
|
||||
}
|
||||
}
|
||||
124
Source/Editor/Content/Create/CreateFilesDialog.cs
Normal file
124
Source/Editor/Content/Create/CreateFilesDialog.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.CustomEditors;
|
||||
using FlaxEditor.GUI.Dialogs;
|
||||
using FlaxEditor.GUI.Tree;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content.Create
|
||||
{
|
||||
/// <summary>
|
||||
/// Dialog used to edit new file settings.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.GUI.Dialogs.Dialog" />
|
||||
public class CreateFilesDialog : Dialog
|
||||
{
|
||||
private CreateFileEntry _entry;
|
||||
private CustomEditorPresenter _settingsEditor;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CreateFilesDialog"/> class.
|
||||
/// </summary>
|
||||
/// <param name="entry">The entry to edit it's settings.</param>
|
||||
public CreateFilesDialog(CreateFileEntry entry)
|
||||
: base("Create file settings")
|
||||
{
|
||||
_entry = entry ?? throw new ArgumentNullException();
|
||||
|
||||
const float TotalWidth = 520;
|
||||
const float EditorHeight = 250;
|
||||
Width = TotalWidth;
|
||||
|
||||
// Header and help description
|
||||
var headerLabel = new Label
|
||||
{
|
||||
Text = "Asset Options",
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Offsets = new Margin(0, 0, 0, 40),
|
||||
Parent = this,
|
||||
Font = new FontReference(Style.Current.FontTitle)
|
||||
};
|
||||
var infoLabel = new Label
|
||||
{
|
||||
Text = "Specify options for creating new asset",
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
Margin = new Margin(7),
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Offsets = new Margin(10, -20, 45, 70),
|
||||
Parent = this
|
||||
};
|
||||
|
||||
// Buttons
|
||||
const float ButtonsWidth = 60;
|
||||
const float ButtonsHeight = 24;
|
||||
const float ButtonsMargin = 8;
|
||||
var createButton = new Button
|
||||
{
|
||||
Text = "Create",
|
||||
AnchorPreset = AnchorPresets.BottomRight,
|
||||
Offsets = new Margin(-ButtonsWidth - ButtonsMargin, ButtonsWidth, -ButtonsHeight - ButtonsMargin, ButtonsHeight),
|
||||
Parent = this
|
||||
};
|
||||
createButton.Clicked += OnCreate;
|
||||
var cancelButton = new Button
|
||||
{
|
||||
Text = "Cancel",
|
||||
AnchorPreset = AnchorPresets.BottomRight,
|
||||
Offsets = new Margin(-ButtonsWidth - ButtonsMargin - ButtonsWidth - ButtonsMargin, ButtonsWidth, -ButtonsHeight - ButtonsMargin, ButtonsHeight),
|
||||
Parent = this
|
||||
};
|
||||
cancelButton.Clicked += OnCancel;
|
||||
|
||||
// Panel for settings editor
|
||||
var panel = new Panel(ScrollBars.Vertical)
|
||||
{
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Offsets = new Margin(2, 2, infoLabel.Bottom + 2, EditorHeight),
|
||||
Parent = this
|
||||
};
|
||||
|
||||
// Settings editor
|
||||
_settingsEditor = new CustomEditorPresenter(null);
|
||||
_settingsEditor.Panel.Parent = panel;
|
||||
|
||||
_dialogSize = new Vector2(TotalWidth, panel.Bottom);
|
||||
|
||||
_settingsEditor.Select(_entry.Settings);
|
||||
}
|
||||
|
||||
private void OnCreate()
|
||||
{
|
||||
Editor.Instance.ContentImporting.LetThemBeCreatedxD(_entry);
|
||||
Close(DialogResult.OK);
|
||||
}
|
||||
|
||||
private void OnCancel()
|
||||
{
|
||||
Close(DialogResult.Cancel);
|
||||
}
|
||||
|
||||
private void OnSelectedChanged(List<TreeNode> before, List<TreeNode> after)
|
||||
{
|
||||
var selection = new List<object>(after.Count);
|
||||
for (int i = 0; i < after.Count; i++)
|
||||
{
|
||||
if (after[i].Tag is CreateFileEntry fileEntry && fileEntry.HasSettings)
|
||||
selection.Add(fileEntry.Settings);
|
||||
}
|
||||
|
||||
_settingsEditor.Select(selection);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SetupWindowSettings(ref CreateWindowSettings settings)
|
||||
{
|
||||
base.SetupWindowSettings(ref settings);
|
||||
|
||||
settings.MinimumSize = new Vector2(300, 400);
|
||||
settings.HasSizingFrame = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
106
Source/Editor/Content/Create/ParticleEmitterCreateEntry.cs
Normal file
106
Source/Editor/Content/Create/ParticleEmitterCreateEntry.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Create
|
||||
{
|
||||
/// <summary>
|
||||
/// Particle emitter asset creating handler. Allows to specify asset template.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.Create.CreateFileEntry" />
|
||||
public class ParticleEmitterCreateEntry : CreateFileEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Types of the emitter templates that can be created.
|
||||
/// </summary>
|
||||
public enum Templates
|
||||
{
|
||||
/// <summary>
|
||||
/// The empty asset.
|
||||
/// </summary>
|
||||
Empty,
|
||||
|
||||
/// <summary>
|
||||
/// The simple particle system that uses constant emission rate.
|
||||
/// </summary>
|
||||
ConstantBurst,
|
||||
|
||||
/// <summary>
|
||||
/// The simple periodic burst particle system.
|
||||
/// </summary>
|
||||
PeriodicBurst,
|
||||
|
||||
/// <summary>
|
||||
/// The layers and tags settings.
|
||||
/// </summary>
|
||||
Smoke,
|
||||
|
||||
/// <summary>
|
||||
/// The GPU sparks with depth-buffer collisions.
|
||||
/// </summary>
|
||||
Sparks,
|
||||
|
||||
/// <summary>
|
||||
/// The ribbon spiral particles.
|
||||
/// </summary>
|
||||
RibbonSpiral,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The create options.
|
||||
/// </summary>
|
||||
public class Options
|
||||
{
|
||||
/// <summary>
|
||||
/// The template.
|
||||
/// </summary>
|
||||
[Tooltip("Particle emitter template.")]
|
||||
public Templates Template = Templates.ConstantBurst;
|
||||
}
|
||||
|
||||
private readonly Options _options = new Options();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object Settings => _options;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ParticleEmitterCreateEntry"/> class.
|
||||
/// </summary>
|
||||
/// <param name="resultUrl">The result file url.</param>
|
||||
public ParticleEmitterCreateEntry(string resultUrl)
|
||||
: base("Settings", resultUrl)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Create()
|
||||
{
|
||||
string templateName;
|
||||
switch (_options.Template)
|
||||
{
|
||||
case Templates.Empty:
|
||||
return Editor.CreateAsset(Editor.NewAssetType.ParticleEmitter, ResultUrl);
|
||||
case Templates.ConstantBurst:
|
||||
templateName = "Constant Burst";
|
||||
break;
|
||||
case Templates.PeriodicBurst:
|
||||
templateName = "Periodic Burst";
|
||||
break;
|
||||
case Templates.Smoke:
|
||||
templateName = "Smoke";
|
||||
break;
|
||||
case Templates.Sparks:
|
||||
templateName = "Sparks";
|
||||
break;
|
||||
case Templates.RibbonSpiral:
|
||||
templateName = "Ribbon Spiral";
|
||||
break;
|
||||
default: throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
var templatePath = Path.Combine(Globals.EngineContentFolder, "Editor/Particles", templateName + ".flax");
|
||||
return Editor.Instance.ContentEditing.CloneAssetFile(templatePath, ResultUrl, Guid.NewGuid());
|
||||
}
|
||||
}
|
||||
}
|
||||
156
Source/Editor/Content/Create/SettingsCreateEntry.cs
Normal file
156
Source/Editor/Content/Create/SettingsCreateEntry.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Create
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine settings asset creating handler. Allows to specify type of the settings to create (e.g. <see cref="GameSettings"/>, <see cref="TimeSettings"/>, etc.).
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.Create.CreateFileEntry" />
|
||||
public class SettingsCreateEntry : CreateFileEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Types of the settings assets that can be created.
|
||||
/// </summary>
|
||||
public enum SettingsTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// The game settings.
|
||||
/// </summary>
|
||||
GameSettings,
|
||||
|
||||
/// <summary>
|
||||
/// The audio settings.
|
||||
/// </summary>
|
||||
AudioSettings,
|
||||
|
||||
/// <summary>
|
||||
/// The time settings.
|
||||
/// </summary>
|
||||
TimeSettings,
|
||||
|
||||
/// <summary>
|
||||
/// The layers and tags settings.
|
||||
/// </summary>
|
||||
LayersAndTagsSettings,
|
||||
|
||||
/// <summary>
|
||||
/// The physics settings.
|
||||
/// </summary>
|
||||
PhysicsSettings,
|
||||
|
||||
/// <summary>
|
||||
/// The graphics settings.
|
||||
/// </summary>
|
||||
GraphicsSettings,
|
||||
|
||||
/// <summary>
|
||||
/// The navigation settings.
|
||||
/// </summary>
|
||||
NavigationSettings,
|
||||
|
||||
/// <summary>
|
||||
/// The build settings.
|
||||
/// </summary>
|
||||
BuildSettings,
|
||||
|
||||
/// <summary>
|
||||
/// The input settings.
|
||||
/// </summary>
|
||||
InputSettings,
|
||||
|
||||
/// <summary>
|
||||
/// The Windows settings.
|
||||
/// </summary>
|
||||
WindowsPlatformSettings,
|
||||
|
||||
/// <summary>
|
||||
/// The UWP settings.
|
||||
/// </summary>
|
||||
UWPPlatformSettings,
|
||||
|
||||
/// <summary>
|
||||
/// The Linux settings.
|
||||
/// </summary>
|
||||
LinuxPlatformSettings,
|
||||
|
||||
/// <summary>
|
||||
/// The PS4 settings
|
||||
/// </summary>
|
||||
PS4PlatformSettings,
|
||||
|
||||
/// <summary>
|
||||
/// The Xbox Scarlett settings
|
||||
/// </summary>
|
||||
XboxScarlettPlatformSettings,
|
||||
|
||||
/// <summary>
|
||||
/// The Android settings
|
||||
/// </summary>
|
||||
AndroidPlatformSettings,
|
||||
}
|
||||
|
||||
private static readonly Type[] _types =
|
||||
{
|
||||
typeof(GameSettings),
|
||||
typeof(AudioSettings),
|
||||
typeof(TimeSettings),
|
||||
typeof(LayersAndTagsSettings),
|
||||
typeof(PhysicsSettings),
|
||||
typeof(GraphicsSettings),
|
||||
typeof(NavigationSettings),
|
||||
typeof(BuildSettings),
|
||||
typeof(InputSettings),
|
||||
typeof(WindowsPlatformSettings),
|
||||
typeof(UWPPlatformSettings),
|
||||
typeof(LinuxPlatformSettings),
|
||||
TypeUtils.GetManagedType(GameSettings.PS4PlatformSettingsTypename),
|
||||
TypeUtils.GetManagedType(GameSettings.XboxScarlettPlatformSettingsTypename),
|
||||
typeof(AndroidPlatformSettings),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The create options.
|
||||
/// </summary>
|
||||
public class Options
|
||||
{
|
||||
/// <summary>
|
||||
/// The type.
|
||||
/// </summary>
|
||||
[Tooltip("Type of the settings asset to create")]
|
||||
public SettingsTypes Type = SettingsTypes.GameSettings;
|
||||
}
|
||||
|
||||
private readonly Options _options = new Options();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object Settings => _options;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SettingsCreateEntry"/> class.
|
||||
/// </summary>
|
||||
/// <param name="resultUrl">The result file url.</param>
|
||||
public SettingsCreateEntry(string resultUrl)
|
||||
: base("Settings", resultUrl)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Create()
|
||||
{
|
||||
// Create settings asset object and serialize it to pure json asset
|
||||
var type = _types[(int)_options.Type];
|
||||
if (type == null)
|
||||
{
|
||||
MessageBox.Show("Cannot create " + _options.Type + " settings. Platform not supported.");
|
||||
return true;
|
||||
}
|
||||
var data = Activator.CreateInstance(type);
|
||||
return Editor.SaveJsonAsset(ResultUrl, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Source/Editor/Content/Create/VisualScriptCreateEntry.cs
Normal file
52
Source/Editor/Content/Create/VisualScriptCreateEntry.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Create
|
||||
{
|
||||
/// <summary>
|
||||
/// Visual Script asset creating handler. Allows to specify base class to inherit from.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.Create.CreateFileEntry" />
|
||||
public class VisualScriptCreateEntry : CreateFileEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// The create options.
|
||||
/// </summary>
|
||||
public class Options
|
||||
{
|
||||
/// <summary>
|
||||
/// The template.
|
||||
/// </summary>
|
||||
[TypeReference(typeof(FlaxEngine.Object), nameof(IsValid))]
|
||||
[Tooltip("The base class of the new Visual Script to inherit from.")]
|
||||
public Type BaseClass = typeof(Script);
|
||||
|
||||
private static bool IsValid(Type type)
|
||||
{
|
||||
return type.IsPublic && !type.IsSealed && !type.IsGenericType;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Options _options = new Options();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object Settings => _options;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VisualScriptCreateEntry"/> class.
|
||||
/// </summary>
|
||||
/// <param name="resultUrl">The result file url.</param>
|
||||
public VisualScriptCreateEntry(string resultUrl)
|
||||
: base("Settings", resultUrl)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Create()
|
||||
{
|
||||
return Editor.CreateVisualScript(ResultUrl, _options.BaseClass?.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
125
Source/Editor/Content/GUI/ContentNavigationButton.cs
Normal file
125
Source/Editor/Content/GUI/ContentNavigationButton.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.Drag;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content.GUI
|
||||
{
|
||||
/// <summary>
|
||||
/// A navigation button for <see cref="Windows.ContentWindow"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.GUI.NavigationButton" />
|
||||
public class ContentNavigationButton : NavigationButton
|
||||
{
|
||||
private DragItems _dragOverItems;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target node.
|
||||
/// </summary>
|
||||
public ContentTreeNode TargetNode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentNavigationButton"/> class.
|
||||
/// </summary>
|
||||
/// <param name="targetNode">The target node.</param>
|
||||
/// <param name="x">The x position.</param>
|
||||
/// <param name="y">The y position.</param>
|
||||
/// <param name="height">The height.</param>
|
||||
public ContentNavigationButton(ContentTreeNode targetNode, float x, float y, float height)
|
||||
: base(x, y, height)
|
||||
{
|
||||
TargetNode = targetNode;
|
||||
Text = targetNode.NavButtonLabel + "/";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnClick()
|
||||
{
|
||||
// Navigate
|
||||
Editor.Instance.Windows.ContentWin.Navigate(TargetNode);
|
||||
|
||||
base.OnClick();
|
||||
}
|
||||
|
||||
private DragDropEffect GetDragEffect(DragData data)
|
||||
{
|
||||
if (data is DragDataFiles)
|
||||
{
|
||||
if (TargetNode.CanHaveAssets)
|
||||
return DragDropEffect.Copy;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_dragOverItems.HasValidDrag)
|
||||
return DragDropEffect.Move;
|
||||
}
|
||||
|
||||
return DragDropEffect.None;
|
||||
}
|
||||
|
||||
private bool ValidateDragItem(ContentItem item)
|
||||
{
|
||||
// Reject itself and any parent
|
||||
return item != TargetNode.Folder && !item.Find(TargetNode.Folder) && !TargetNode.IsRoot;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragEnter(ref Vector2 location, DragData data)
|
||||
{
|
||||
base.OnDragEnter(ref location, data);
|
||||
|
||||
if (_dragOverItems == null)
|
||||
_dragOverItems = new DragItems(ValidateDragItem);
|
||||
|
||||
_dragOverItems.OnDragEnter(data);
|
||||
var result = GetDragEffect(data);
|
||||
_validDragOver = result != DragDropEffect.None;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragMove(ref Vector2 location, DragData data)
|
||||
{
|
||||
base.OnDragMove(ref location, data);
|
||||
|
||||
return GetDragEffect(data);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
base.OnDragLeave();
|
||||
|
||||
_dragOverItems.OnDragLeave();
|
||||
_validDragOver = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragDrop(ref Vector2 location, DragData data)
|
||||
{
|
||||
var result = DragDropEffect.None;
|
||||
base.OnDragDrop(ref location, data);
|
||||
|
||||
// Check if drop element or files
|
||||
if (data is DragDataFiles files)
|
||||
{
|
||||
// Import files
|
||||
Editor.Instance.ContentImporting.Import(files.Files, TargetNode.Folder);
|
||||
result = DragDropEffect.Copy;
|
||||
}
|
||||
else if (_dragOverItems.HasValidDrag)
|
||||
{
|
||||
// Move items
|
||||
Editor.Instance.ContentDatabase.Move(_dragOverItems.Objects, TargetNode.Folder);
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
|
||||
_dragOverItems.OnDragDrop();
|
||||
_validDragOver = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
117
Source/Editor/Content/GUI/ContentView.DragDrop.cs
Normal file
117
Source/Editor/Content/GUI/ContentView.DragDrop.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.GUI.Drag;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content.GUI
|
||||
{
|
||||
public partial class ContentView
|
||||
{
|
||||
private bool _validDragOver;
|
||||
private DragActors _dragActors;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragEnter(ref Vector2 location, DragData data)
|
||||
{
|
||||
var result = base.OnDragEnter(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
return result;
|
||||
|
||||
// Check if drop file(s)
|
||||
if (data is DragDataFiles)
|
||||
{
|
||||
_validDragOver = true;
|
||||
return DragDropEffect.Copy;
|
||||
}
|
||||
|
||||
// Check if drop actor(s)
|
||||
if (_dragActors == null)
|
||||
_dragActors = new DragActors(ValidateDragActors);
|
||||
if (_dragActors.OnDragEnter(data))
|
||||
{
|
||||
_validDragOver = true;
|
||||
return DragDropEffect.Move;
|
||||
}
|
||||
|
||||
return DragDropEffect.None;
|
||||
}
|
||||
|
||||
private bool ValidateDragActors(ActorNode actor)
|
||||
{
|
||||
return actor.CanCreatePrefab && Editor.Instance.Windows.ContentWin.CurrentViewFolder.CanHaveAssets;
|
||||
}
|
||||
|
||||
private void ImportActors(DragActors actors, ContentFolder location)
|
||||
{
|
||||
// Use only the first actor
|
||||
Editor.Instance.Prefabs.CreatePrefab(actors.Objects[0].Actor);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragMove(ref Vector2 location, DragData data)
|
||||
{
|
||||
_validDragOver = false;
|
||||
var result = base.OnDragMove(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
return result;
|
||||
|
||||
if (data is DragDataFiles)
|
||||
{
|
||||
_validDragOver = true;
|
||||
result = DragDropEffect.Copy;
|
||||
}
|
||||
else if (_dragActors.HasValidDrag)
|
||||
{
|
||||
_validDragOver = true;
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragDrop(ref Vector2 location, DragData data)
|
||||
{
|
||||
var result = base.OnDragDrop(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
return result;
|
||||
|
||||
// Check if drop file(s)
|
||||
if (data is DragDataFiles files)
|
||||
{
|
||||
// Import files
|
||||
var currentFolder = Editor.Instance.Windows.ContentWin.CurrentViewFolder;
|
||||
if (currentFolder != null)
|
||||
Editor.Instance.ContentImporting.Import(files.Files, currentFolder);
|
||||
result = DragDropEffect.Copy;
|
||||
}
|
||||
// Check if drop actor(s)
|
||||
else if (_dragActors.HasValidDrag)
|
||||
{
|
||||
// Import actors
|
||||
var currentFolder = Editor.Instance.Windows.ContentWin.CurrentViewFolder;
|
||||
if (currentFolder != null)
|
||||
ImportActors(_dragActors, currentFolder);
|
||||
|
||||
_dragActors.OnDragDrop();
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
|
||||
// Clear cache
|
||||
_validDragOver = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
_validDragOver = false;
|
||||
_dragActors?.OnDragLeave();
|
||||
|
||||
base.OnDragLeave();
|
||||
}
|
||||
}
|
||||
}
|
||||
714
Source/Editor/Content/GUI/ContentView.cs
Normal file
714
Source/Editor/Content/GUI/ContentView.cs
Normal file
@@ -0,0 +1,714 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content.GUI
|
||||
{
|
||||
/// <summary>
|
||||
/// The content items view modes.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public enum ContentViewType
|
||||
{
|
||||
/// <summary>
|
||||
/// The uniform tiles.
|
||||
/// </summary>
|
||||
Tiles,
|
||||
|
||||
/// <summary>
|
||||
/// The vertical list.
|
||||
/// </summary>
|
||||
List,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main control for <see cref="ContentWindow"/> used to present collection of <see cref="ContentItem"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
/// <seealso cref="FlaxEditor.Content.IContentItemOwner" />
|
||||
[HideInEditor]
|
||||
public partial class ContentView : ContainerControl, IContentItemOwner
|
||||
{
|
||||
private readonly List<ContentItem> _items = new List<ContentItem>(256);
|
||||
private readonly List<ContentItem> _selection = new List<ContentItem>(16);
|
||||
|
||||
private float _viewScale = 1.0f;
|
||||
private ContentViewType _viewType = ContentViewType.Tiles;
|
||||
|
||||
#region External Events
|
||||
|
||||
/// <summary>
|
||||
/// Called when user wants to open the item.
|
||||
/// </summary>
|
||||
public event Action<ContentItem> OnOpen;
|
||||
|
||||
/// <summary>
|
||||
/// Called when user wants to rename the item.
|
||||
/// </summary>
|
||||
public event Action<ContentItem> OnRename;
|
||||
|
||||
/// <summary>
|
||||
/// Called when user wants to delete the item.
|
||||
/// </summary>
|
||||
public event Action<List<ContentItem>> OnDelete;
|
||||
|
||||
/// <summary>
|
||||
/// Called when user wants to paste the files/folders.
|
||||
/// </summary>
|
||||
public event Action<string[]> OnPaste;
|
||||
|
||||
/// <summary>
|
||||
/// Called when user wants to duplicate the item(s).
|
||||
/// </summary>
|
||||
public event Action<List<ContentItem>> OnDuplicate;
|
||||
|
||||
/// <summary>
|
||||
/// Called when user wants to navigate backward.
|
||||
/// </summary>
|
||||
public event Action OnNavigateBack;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when view scale gets changed.
|
||||
/// </summary>
|
||||
public event Action ViewScaleChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when view type gets changed.
|
||||
/// </summary>
|
||||
public event Action ViewTypeChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets the items.
|
||||
/// </summary>
|
||||
public List<ContentItem> Items => _items;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the items count.
|
||||
/// </summary>
|
||||
public int ItemsCount => _items.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected items.
|
||||
/// </summary>
|
||||
public List<ContentItem> Selection => _selection;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected count.
|
||||
/// </summary>
|
||||
public int SelectedCount => _selection.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether any item is selected.
|
||||
/// </summary>
|
||||
public bool HasSelection => _selection.Count > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the view scale.
|
||||
/// </summary>
|
||||
public float ViewScale
|
||||
{
|
||||
get => _viewScale;
|
||||
set
|
||||
{
|
||||
value = Mathf.Clamp(value, 0.3f, 3.0f);
|
||||
if (!Mathf.NearEqual(value, _viewScale))
|
||||
{
|
||||
_viewScale = value;
|
||||
ViewScaleChanged?.Invoke();
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the view.
|
||||
/// </summary>
|
||||
public ContentViewType ViewType
|
||||
{
|
||||
get => _viewType;
|
||||
set
|
||||
{
|
||||
if (_viewType != value)
|
||||
{
|
||||
_viewType = value;
|
||||
ViewTypeChanged?.Invoke();
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flag is used to indicate if user is searching for items. Used to show a proper message to the user.
|
||||
/// </summary>
|
||||
public bool IsSearching;
|
||||
|
||||
/// <summary>
|
||||
/// Flag used to indicate whenever show full file names including extensions.
|
||||
/// </summary>
|
||||
public bool ShowFileExtensions;
|
||||
|
||||
/// <summary>
|
||||
/// The input actions collection to processed during user input.
|
||||
/// </summary>
|
||||
public readonly InputActionsContainer InputActions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentView"/> class.
|
||||
/// </summary>
|
||||
public ContentView()
|
||||
{
|
||||
// Setup input actions
|
||||
InputActions = new InputActionsContainer(new[]
|
||||
{
|
||||
new InputActionsContainer.Binding(options => options.Delete, () =>
|
||||
{
|
||||
if (HasSelection)
|
||||
OnDelete?.Invoke(_selection);
|
||||
}),
|
||||
new InputActionsContainer.Binding(options => options.SelectAll, SelectAll),
|
||||
new InputActionsContainer.Binding(options => options.Rename, () =>
|
||||
{
|
||||
if (HasSelection && _selection[0].CanRename)
|
||||
{
|
||||
if (_selection.Count > 1)
|
||||
Select(_selection[0]);
|
||||
OnRename?.Invoke(_selection[0]);
|
||||
}
|
||||
}),
|
||||
new InputActionsContainer.Binding(options => options.Copy, Copy),
|
||||
new InputActionsContainer.Binding(options => options.Paste, Paste),
|
||||
new InputActionsContainer.Binding(options => options.Duplicate, Duplicate),
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the items in the view.
|
||||
/// </summary>
|
||||
public void ClearItems()
|
||||
{
|
||||
// Lock layout
|
||||
var wasLayoutLocked = IsLayoutLocked;
|
||||
IsLayoutLocked = true;
|
||||
|
||||
// Deselect items first
|
||||
ClearSelection();
|
||||
|
||||
// Remove references and unlink items
|
||||
for (int i = 0; i < _items.Count; i++)
|
||||
{
|
||||
_items[i].Parent = null;
|
||||
_items[i].RemoveReference(this);
|
||||
}
|
||||
_items.Clear();
|
||||
|
||||
// Unload and perform UI layout
|
||||
IsLayoutLocked = wasLayoutLocked;
|
||||
PerformLayout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the items collection in the view.
|
||||
/// </summary>
|
||||
/// <param name="items">The items to show.</param>
|
||||
/// <param name="additive">If set to <c>true</c> items will be added to the current selection. Otherwise selection will be cleared before.</param>
|
||||
public void ShowItems(List<ContentItem> items, bool additive = false)
|
||||
{
|
||||
if (items == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
// Check if show nothing or not change view
|
||||
if (items.Count == 0)
|
||||
{
|
||||
// Deselect items if need to
|
||||
if (!additive)
|
||||
ClearItems();
|
||||
return;
|
||||
}
|
||||
|
||||
// Lock layout
|
||||
var wasLayoutLocked = IsLayoutLocked;
|
||||
IsLayoutLocked = true;
|
||||
|
||||
// Deselect items if need to
|
||||
if (!additive)
|
||||
ClearItems();
|
||||
|
||||
// Add references and link items
|
||||
_items.AddRange(items);
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
items[i].Parent = this;
|
||||
items[i].AddReference(this);
|
||||
}
|
||||
|
||||
// Sort items
|
||||
_children.Sort();
|
||||
|
||||
// Unload and perform UI layout
|
||||
IsLayoutLocked = wasLayoutLocked;
|
||||
PerformLayout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified item is selected.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns><c>true</c> if the specified item is selected; otherwise, <c>false</c>.</returns>
|
||||
public bool IsSelected(ContentItem item)
|
||||
{
|
||||
return _selection.Contains(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the selected items collection.
|
||||
/// </summary>
|
||||
public void ClearSelection()
|
||||
{
|
||||
if (_selection.Count == 0)
|
||||
return;
|
||||
|
||||
_selection.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects the specified items.
|
||||
/// </summary>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="additive">If set to <c>true</c> items will be added to the current selection. Otherwise selection will be cleared before.</param>
|
||||
public void Select(List<ContentItem> items, bool additive = false)
|
||||
{
|
||||
if (items == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
// Check if nothing to select
|
||||
if (items.Count == 0)
|
||||
{
|
||||
// Deselect items if need to
|
||||
if (!additive)
|
||||
ClearSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
// Lock layout
|
||||
var wasLayoutLocked = IsLayoutLocked;
|
||||
IsLayoutLocked = true;
|
||||
|
||||
// Select items
|
||||
if (additive)
|
||||
{
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
if (!_selection.Contains(items[i]))
|
||||
_selection.Add(items[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_selection.Clear();
|
||||
_selection.AddRange(items);
|
||||
}
|
||||
|
||||
// Unload and perform UI layout
|
||||
IsLayoutLocked = wasLayoutLocked;
|
||||
PerformLayout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects the specified item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="additive">If set to <c>true</c> item will be added to the current selection. Otherwise selection will be cleared before.</param>
|
||||
public void Select(ContentItem item, bool additive = false)
|
||||
{
|
||||
if (item == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
// Lock layout
|
||||
var wasLayoutLocked = IsLayoutLocked;
|
||||
IsLayoutLocked = true;
|
||||
|
||||
// Select item
|
||||
if (additive)
|
||||
{
|
||||
if (!_selection.Contains(item))
|
||||
_selection.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
_selection.Clear();
|
||||
_selection.Add(item);
|
||||
}
|
||||
|
||||
// Unload and perform UI layout
|
||||
IsLayoutLocked = wasLayoutLocked;
|
||||
PerformLayout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects all the items.
|
||||
/// </summary>
|
||||
public void SelectAll()
|
||||
{
|
||||
// Lock layout
|
||||
var wasLayoutLocked = IsLayoutLocked;
|
||||
IsLayoutLocked = true;
|
||||
|
||||
// Select items
|
||||
_selection.Clear();
|
||||
_selection.AddRange(_items);
|
||||
|
||||
// Unload and perform UI layout
|
||||
IsLayoutLocked = wasLayoutLocked;
|
||||
PerformLayout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deselects the specified item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
public void Deselect(ContentItem item)
|
||||
{
|
||||
if (item == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
// Lock layout
|
||||
var wasLayoutLocked = IsLayoutLocked;
|
||||
IsLayoutLocked = true;
|
||||
|
||||
// Deselect item
|
||||
if (_selection.Contains(item))
|
||||
_selection.Remove(item);
|
||||
|
||||
// Unload and perform UI layout
|
||||
IsLayoutLocked = wasLayoutLocked;
|
||||
PerformLayout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Duplicates the selected items.
|
||||
/// </summary>
|
||||
public void Duplicate()
|
||||
{
|
||||
OnDuplicate?.Invoke(_selection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the selected items (to the system clipboard).
|
||||
/// </summary>
|
||||
public void Copy()
|
||||
{
|
||||
if (_selection.Count == 0)
|
||||
return;
|
||||
|
||||
var files = _selection.ConvertAll(x => x.Path).ToArray();
|
||||
Clipboard.Files = files;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if user can paste data to the view (copied any files before).
|
||||
/// </summary>
|
||||
/// <returns>True if can paste files.</returns>
|
||||
public bool CanPaste()
|
||||
{
|
||||
var files = Clipboard.Files;
|
||||
return files != null && files.Length > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pastes the copied items (from the system clipboard).
|
||||
/// </summary>
|
||||
public void Paste()
|
||||
{
|
||||
var files = Clipboard.Files;
|
||||
if (files == null || files.Length == 0)
|
||||
return;
|
||||
|
||||
OnPaste?.Invoke(files);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gives focus and selects the first item in the view.
|
||||
/// </summary>
|
||||
public void SelectFirstItem()
|
||||
{
|
||||
if (_items.Count > 0)
|
||||
{
|
||||
_items[0].Focus();
|
||||
Select(_items[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Focus();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes thumbnails of all items in the <see cref="ContentView"/>.
|
||||
/// </summary>
|
||||
public void RefreshThumbnails()
|
||||
{
|
||||
for (int i = 0; i < _items.Count; i++)
|
||||
_items[i].RefreshThumbnail();
|
||||
}
|
||||
|
||||
#region Internal events
|
||||
|
||||
/// <summary>
|
||||
/// Called when user clicks on an item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
public void OnItemClick(ContentItem item)
|
||||
{
|
||||
bool isSelected = _selection.Contains(item);
|
||||
|
||||
// Add/remove from selection
|
||||
if (Root.GetKey(KeyboardKeys.Control))
|
||||
{
|
||||
if (isSelected)
|
||||
Deselect(item);
|
||||
else
|
||||
Select(item, true);
|
||||
}
|
||||
// Range select
|
||||
else if (Root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
int min = _selection.Min(x => x.IndexInParent);
|
||||
int max = _selection.Max(x => x.IndexInParent);
|
||||
min = Mathf.Min(min, item.IndexInParent);
|
||||
max = Mathf.Max(max, item.IndexInParent);
|
||||
var selection = new List<ContentItem>(_selection);
|
||||
for (int i = min; i <= max; i++)
|
||||
{
|
||||
if (_children[i] is ContentItem cc && !selection.Contains(cc))
|
||||
{
|
||||
selection.Add(cc);
|
||||
}
|
||||
}
|
||||
Select(selection);
|
||||
}
|
||||
// Select
|
||||
else
|
||||
{
|
||||
Select(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when user wants to rename item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
public void OnItemDoubleClickName(ContentItem item)
|
||||
{
|
||||
OnRename?.Invoke(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when user wants to open item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
public void OnItemDoubleClick(ContentItem item)
|
||||
{
|
||||
OnOpen?.Invoke(item);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IContentItemOwner
|
||||
|
||||
/// <inheritdoc />
|
||||
void IContentItemOwner.OnItemDeleted(ContentItem item)
|
||||
{
|
||||
_selection.Remove(item);
|
||||
_items.Remove(item);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
void IContentItemOwner.OnItemRenamed(ContentItem item)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
void IContentItemOwner.OnItemReimported(ContentItem item)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
void IContentItemOwner.OnItemDispose(ContentItem item)
|
||||
{
|
||||
_selection.Remove(item);
|
||||
_items.Remove(item);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
var style = Style.Current;
|
||||
|
||||
// Check if drag is over
|
||||
if (IsDragOver && _validDragOver)
|
||||
{
|
||||
Render2D.FillRectangle(new Rectangle(Vector2.Zero, Size), style.BackgroundSelected * 0.4f);
|
||||
}
|
||||
|
||||
// Check if it's an empty thing
|
||||
if (_items.Count == 0)
|
||||
{
|
||||
Render2D.DrawText(style.FontSmall, IsSearching ? "No results" : "Empty", new Rectangle(Vector2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseWheel(Vector2 location, float delta)
|
||||
{
|
||||
// Check if pressing control key
|
||||
if (Root.GetKey(KeyboardKeys.Control))
|
||||
{
|
||||
// Zoom
|
||||
ViewScale += delta * 0.05f;
|
||||
|
||||
// Handled
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnMouseWheel(location, delta);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
// Navigate backward
|
||||
if (key == KeyboardKeys.Backspace)
|
||||
{
|
||||
OnNavigateBack?.Invoke();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (InputActions.Process(Editor.Instance, this, key))
|
||||
return true;
|
||||
|
||||
// Check if sth is selected
|
||||
if (HasSelection)
|
||||
{
|
||||
// Open
|
||||
if (key == KeyboardKeys.Return && _selection.Count != 0)
|
||||
{
|
||||
foreach (var e in _selection)
|
||||
OnOpen?.Invoke(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Movement with arrows
|
||||
{
|
||||
var root = _selection[0];
|
||||
Vector2 size = root.Size;
|
||||
Vector2 offset = Vector2.Minimum;
|
||||
ContentItem item = null;
|
||||
if (key == KeyboardKeys.ArrowUp)
|
||||
{
|
||||
offset = new Vector2(0, -size.Y);
|
||||
}
|
||||
else if (key == KeyboardKeys.ArrowDown)
|
||||
{
|
||||
offset = new Vector2(0, size.Y);
|
||||
}
|
||||
else if (key == KeyboardKeys.ArrowRight)
|
||||
{
|
||||
offset = new Vector2(size.X, 0);
|
||||
}
|
||||
else if (key == KeyboardKeys.ArrowLeft)
|
||||
{
|
||||
offset = new Vector2(-size.X, 0);
|
||||
}
|
||||
if (offset != Vector2.Minimum)
|
||||
{
|
||||
item = GetChildAt(root.Location + size / 2 + offset) as ContentItem;
|
||||
}
|
||||
if (item != null)
|
||||
{
|
||||
OnItemClick(item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnKeyDown(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PerformLayoutBeforeChildren()
|
||||
{
|
||||
float width = GetClientArea().Width;
|
||||
float x = 0, y = 0;
|
||||
float viewScale = _viewScale * 0.97f;
|
||||
|
||||
switch (ViewType)
|
||||
{
|
||||
case ContentViewType.Tiles:
|
||||
{
|
||||
float defaultItemsWidth = ContentItem.DefaultWidth * viewScale;
|
||||
int itemsToFit = Mathf.FloorToInt(width / defaultItemsWidth);
|
||||
float itemsWidth = width / Mathf.Max(itemsToFit, 1);
|
||||
float itemsHeight = itemsWidth / defaultItemsWidth * (ContentItem.DefaultHeight * viewScale);
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
var c = _children[i];
|
||||
c.Bounds = new Rectangle(x, y, itemsWidth, itemsHeight);
|
||||
|
||||
x += itemsWidth;
|
||||
if (x + itemsWidth > width)
|
||||
{
|
||||
x = 0;
|
||||
y += itemsHeight + 1;
|
||||
}
|
||||
}
|
||||
if (x > 0)
|
||||
y += itemsHeight;
|
||||
|
||||
break;
|
||||
}
|
||||
case ContentViewType.List:
|
||||
{
|
||||
float itemsHeight = 50.0f * viewScale;
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
var c = _children[i];
|
||||
c.Bounds = new Rectangle(x, y, width, itemsHeight);
|
||||
y += itemsHeight + 1;
|
||||
}
|
||||
y += 40.0f;
|
||||
|
||||
break;
|
||||
}
|
||||
default: throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
// Set maximum size and fit the parent container
|
||||
if (HasParent)
|
||||
y = Mathf.Max(y, Parent.Height);
|
||||
Height = y;
|
||||
|
||||
base.PerformLayoutBeforeChildren();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
// Ensure to unlink all items
|
||||
ClearItems();
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Source/Editor/Content/IFileEntryAction.cs
Normal file
26
Source/Editor/Content/IFileEntryAction.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// File entry action (import or create).
|
||||
/// </summary>
|
||||
public interface IFileEntryAction
|
||||
{
|
||||
/// <summary>
|
||||
/// The source file path (may be empty or null).
|
||||
/// </summary>
|
||||
string SourceUrl { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The result file path.
|
||||
/// </summary>
|
||||
string ResultUrl { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Executes this action.
|
||||
/// </summary>
|
||||
/// <returns>True if, failed, otherwise false.</returns>
|
||||
bool Execute();
|
||||
}
|
||||
}
|
||||
23
Source/Editor/Content/Import/AssetImportEntry.cs
Normal file
23
Source/Editor/Content/Import/AssetImportEntry.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
namespace FlaxEditor.Content.Import
|
||||
{
|
||||
/// <summary>
|
||||
/// Asset import entry.
|
||||
/// </summary>
|
||||
public class AssetImportEntry : ImportFileEntry
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public AssetImportEntry(ref Request request)
|
||||
: base(ref request)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Import()
|
||||
{
|
||||
// Use engine backend
|
||||
return Editor.Import(SourceUrl, ResultUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
190
Source/Editor/Content/Import/AudioImportSettings.cs
Normal file
190
Source/Editor/Content/Import/AudioImportSettings.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Import
|
||||
{
|
||||
/// <summary>
|
||||
/// Proxy object to present audio import settings in <see cref="ImportFilesDialog"/>.
|
||||
/// </summary>
|
||||
public class AudioImportSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom set of bit depth audio import sizes.
|
||||
/// </summary>
|
||||
public enum CustomBitDepth
|
||||
{
|
||||
/// <summary>
|
||||
/// The 8.
|
||||
/// </summary>
|
||||
_8 = 8,
|
||||
|
||||
/// <summary>
|
||||
/// The 16.
|
||||
/// </summary>
|
||||
_16 = 16,
|
||||
|
||||
/// <summary>
|
||||
/// The 24.
|
||||
/// </summary>
|
||||
_24 = 24,
|
||||
|
||||
/// <summary>
|
||||
/// The 32.
|
||||
/// </summary>
|
||||
_32 = 32,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the bit depth to enum.
|
||||
/// </summary>
|
||||
/// <param name="f">The bit depth.</param>
|
||||
/// <returns>The converted enum.</returns>
|
||||
public static CustomBitDepth ConvertBitDepth(int f)
|
||||
{
|
||||
FieldInfo[] fields = typeof(CustomBitDepth).GetFields();
|
||||
for (int i = 0; i < fields.Length; i++)
|
||||
{
|
||||
var field = fields[i];
|
||||
if (field.Name.Equals("value__"))
|
||||
continue;
|
||||
|
||||
if (f == (int)field.GetRawConstantValue())
|
||||
return (CustomBitDepth)f;
|
||||
}
|
||||
|
||||
return CustomBitDepth._16;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The audio data format to import the audio clip as.
|
||||
/// </summary>
|
||||
[EditorOrder(10), DefaultValue(AudioFormat.Vorbis), Tooltip("The audio data format to import the audio clip as.")]
|
||||
public AudioFormat Format { get; set; } = AudioFormat.Vorbis;
|
||||
|
||||
/// <summary>
|
||||
/// The audio data compression quality. Used only if target format is using compression. Value 0 means the smallest size, value 1 means the best quality.
|
||||
/// </summary>
|
||||
[EditorOrder(15), DefaultValue(0.4f), Limit(0, 1, 0.01f), Tooltip("The audio data compression quality. Used only if target format is using compression. Value 0 means the smallest size, value 1 means the best quality.")]
|
||||
public float CompressionQuality { get; set; } = 0.4f;
|
||||
|
||||
/// <summary>
|
||||
/// Disables dynamic audio streaming. The whole clip will be loaded into the memory. Useful for small clips (eg. gunfire sounds).
|
||||
/// </summary>
|
||||
[EditorOrder(20), DefaultValue(false), Tooltip("Disables dynamic audio streaming. The whole clip will be loaded into the memory. Useful for small clips (eg. gunfire sounds).")]
|
||||
public bool DisableStreaming { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Checks should the clip be played as spatial (3D) audio or as normal audio. 3D audio is stored in Mono format.
|
||||
/// </summary>
|
||||
[EditorOrder(30), DefaultValue(false), EditorDisplay(null, "Is 3D"), Tooltip("Checks should the clip be played as spatial (3D) audio or as normal audio. 3D audio is stored in Mono format.")]
|
||||
public bool Is3D { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// The size of a single sample in bits. The clip will be converted to this bit depth on import.
|
||||
/// </summary>
|
||||
[EditorOrder(40), DefaultValue(CustomBitDepth._16), Tooltip("The size of a single sample in bits. The clip will be converted to this bit depth on import.")]
|
||||
public CustomBitDepth BitDepth { get; set; } = CustomBitDepth._16;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct InternalOptions
|
||||
{
|
||||
public AudioFormat Format;
|
||||
public byte DisableStreaming;
|
||||
public byte Is3D;
|
||||
public int BitDepth;
|
||||
public float Quality;
|
||||
}
|
||||
|
||||
internal void ToInternal(out InternalOptions options)
|
||||
{
|
||||
options = new InternalOptions
|
||||
{
|
||||
Format = Format,
|
||||
DisableStreaming = (byte)(DisableStreaming ? 1 : 0),
|
||||
Is3D = (byte)(Is3D ? 1 : 0),
|
||||
Quality = CompressionQuality,
|
||||
BitDepth = (int)BitDepth,
|
||||
};
|
||||
}
|
||||
|
||||
internal void FromInternal(ref InternalOptions options)
|
||||
{
|
||||
Format = options.Format;
|
||||
DisableStreaming = options.DisableStreaming != 0;
|
||||
Is3D = options.Is3D != 0;
|
||||
CompressionQuality = options.Quality;
|
||||
BitDepth = ConvertBitDepth(options.BitDepth);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries the restore the asset import options from the target resource file.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <param name="assetPath">The asset path.</param>
|
||||
/// <returns>True settings has been restored, otherwise false.</returns>
|
||||
public static bool TryRestore(ref AudioImportSettings options, string assetPath)
|
||||
{
|
||||
if (AudioImportEntry.Internal_GetAudioImportOptions(assetPath, out var internalOptions))
|
||||
{
|
||||
// Restore settings
|
||||
options.FromInternal(ref internalOptions);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Audio asset import entry.
|
||||
/// </summary>
|
||||
/// <seealso cref="AssetImportEntry" />
|
||||
public class AudioImportEntry : AssetImportEntry
|
||||
{
|
||||
private AudioImportSettings _settings = new AudioImportSettings();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AudioImportEntry"/> class.
|
||||
/// </summary>
|
||||
/// <param name="request">The import request.</param>
|
||||
public AudioImportEntry(ref Request request)
|
||||
: base(ref request)
|
||||
{
|
||||
// Try to restore target asset Audio import options (useful for fast reimport)
|
||||
AudioImportSettings.TryRestore(ref _settings, ResultUrl);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object Settings => _settings;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool TryOverrideSettings(object settings)
|
||||
{
|
||||
if (settings is AudioImportSettings o)
|
||||
{
|
||||
_settings = o;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Import()
|
||||
{
|
||||
return Editor.Import(SourceUrl, ResultUrl, _settings);
|
||||
}
|
||||
|
||||
#region Internal Calls
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal static extern bool Internal_GetAudioImportOptions(string path, out AudioImportSettings.InternalOptions result);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
52
Source/Editor/Content/Import/FolderImportEntry.cs
Normal file
52
Source/Editor/Content/Import/FolderImportEntry.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace FlaxEditor.Content.Import
|
||||
{
|
||||
/// <summary>
|
||||
/// Folder import entry.
|
||||
/// </summary>
|
||||
public class FolderImportEntry : ImportFileEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Flag used to skip showing import settings dialog to used. Can be used for importing assets from code by plugins.
|
||||
/// </summary>
|
||||
public bool SkipSettingsDialog;
|
||||
|
||||
/// <inheritdoc />
|
||||
public FolderImportEntry(ref Request request)
|
||||
: base(ref request)
|
||||
{
|
||||
SkipSettingsDialog = request.SkipSettingsDialog;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Import()
|
||||
{
|
||||
if (!Directory.Exists(ResultUrl))
|
||||
{
|
||||
Directory.CreateDirectory(ResultUrl);
|
||||
var parentPath = Path.GetDirectoryName(ResultUrl);
|
||||
var parent = Editor.Instance.ContentDatabase.Find(parentPath);
|
||||
if (parent == null)
|
||||
{
|
||||
Editor.LogWarning("Failed to find the parent folder for the imported directory.");
|
||||
return true;
|
||||
}
|
||||
Editor.Instance.ContentDatabase.RefreshFolder(parent, true);
|
||||
}
|
||||
var target = (ContentFolder)Editor.Instance.ContentDatabase.Find(ResultUrl);
|
||||
|
||||
// Import all sub elements
|
||||
var files = Directory.GetFiles(SourceUrl);
|
||||
Editor.Instance.ContentImporting.Import(files, target, SkipSettingsDialog);
|
||||
|
||||
// Import all sub dirs
|
||||
var folders = Directory.GetDirectories(SourceUrl);
|
||||
Editor.Instance.ContentImporting.Import(folders, target, SkipSettingsDialog);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
195
Source/Editor/Content/Import/ImportFileEntry.cs
Normal file
195
Source/Editor/Content/Import/ImportFileEntry.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace FlaxEditor.Content.Import
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates new <see cref="ImportFileEntry"/> for the given source file.
|
||||
/// </summary>
|
||||
/// <param name="request">The import request.</param>
|
||||
/// <returns>The file entry.</returns>
|
||||
public delegate ImportFileEntry ImportFileEntryHandler(ref Request request);
|
||||
|
||||
/// <summary>
|
||||
/// File import entry.
|
||||
/// </summary>
|
||||
public class ImportFileEntry : IFileEntryAction
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string SourceUrl { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ResultUrl { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this entry has settings to modify.
|
||||
/// </summary>
|
||||
public virtual bool HasSettings => Settings != null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the settings object to modify.
|
||||
/// </summary>
|
||||
public virtual object Settings => null;
|
||||
|
||||
/// <summary>
|
||||
/// Tries the override import settings.
|
||||
/// </summary>
|
||||
/// <param name="settings">The settings.</param>
|
||||
/// <returns>True if settings override was successful and there is no need to edit them in dedicated dialog, otherwise false.</returns>
|
||||
public virtual bool TryOverrideSettings(object settings)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImportFileEntry"/> class.
|
||||
/// </summary>
|
||||
/// <param name="request">The import request.</param>
|
||||
public ImportFileEntry(ref Request request)
|
||||
{
|
||||
SourceUrl = request.InputPath;
|
||||
ResultUrl = request.OutputPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies the result URL filename (keeps destination folder and extension).
|
||||
/// </summary>
|
||||
/// <param name="filename">The new filename.</param>
|
||||
public void ModifyResultFilename(string filename)
|
||||
{
|
||||
var directory = Path.GetDirectoryName(ResultUrl) ?? string.Empty;
|
||||
var extension = Path.GetExtension(ResultUrl);
|
||||
ResultUrl = Path.Combine(directory, filename + extension);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs file importing.
|
||||
/// </summary>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
public virtual bool Import()
|
||||
{
|
||||
// Skip if missing
|
||||
if (!Directory.Exists(SourceUrl) && !File.Exists(SourceUrl))
|
||||
return true;
|
||||
|
||||
// Setup output
|
||||
string folder = Path.GetDirectoryName(ResultUrl);
|
||||
if (folder != null && !Directory.Exists(folder))
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
if (Directory.Exists(SourceUrl))
|
||||
{
|
||||
// Copy directory
|
||||
Utilities.Utils.DirectoryCopy(SourceUrl, ResultUrl, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy file
|
||||
File.Copy(SourceUrl, ResultUrl, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The file types registered for importing. Key is a file extension (without a leading dot).
|
||||
/// Allows to plug custom importing options gather for different input file types.
|
||||
/// </summary>
|
||||
public static readonly Dictionary<string, ImportFileEntryHandler> FileTypes = new Dictionary<string, ImportFileEntryHandler>(32);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the entry.
|
||||
/// </summary>
|
||||
/// <param name="request">The import request.</param>
|
||||
/// <returns>Created file entry.</returns>
|
||||
public static ImportFileEntry CreateEntry(ref Request request)
|
||||
{
|
||||
// Get extension (without a dot)
|
||||
var extension = Path.GetExtension(request.InputPath);
|
||||
if (string.IsNullOrEmpty(extension))
|
||||
return new FolderImportEntry(ref request);
|
||||
if (extension[0] == '.')
|
||||
extension = extension.Remove(0, 1);
|
||||
extension = extension.ToLower();
|
||||
|
||||
// Check if use overriden type
|
||||
ImportFileEntryHandler createDelegate;
|
||||
if (FileTypes.TryGetValue(extension, out createDelegate))
|
||||
return createDelegate(ref request);
|
||||
|
||||
// Use default type
|
||||
return request.IsBinaryAsset ? new AssetImportEntry(ref request) : new ImportFileEntry(ref request);
|
||||
}
|
||||
|
||||
internal static void RegisterDefaultTypes()
|
||||
{
|
||||
// Textures
|
||||
FileTypes["tga"] = ImportTexture;
|
||||
FileTypes["png"] = ImportTexture;
|
||||
FileTypes["bmp"] = ImportTexture;
|
||||
FileTypes["gif"] = ImportTexture;
|
||||
FileTypes["tiff"] = ImportTexture;
|
||||
FileTypes["tif"] = ImportTexture;
|
||||
FileTypes["jpeg"] = ImportTexture;
|
||||
FileTypes["jpg"] = ImportTexture;
|
||||
FileTypes["dds"] = ImportTexture;
|
||||
FileTypes["hdr"] = ImportTexture;
|
||||
FileTypes["raw"] = ImportTexture;
|
||||
|
||||
// Models
|
||||
FileTypes["obj"] = ImportModel;
|
||||
FileTypes["fbx"] = ImportModel;
|
||||
FileTypes["x"] = ImportModel;
|
||||
FileTypes["dae"] = ImportModel;
|
||||
FileTypes["gltf"] = ImportModel;
|
||||
FileTypes["glb"] = ImportModel;
|
||||
//
|
||||
FileTypes["blend"] = ImportModel;
|
||||
FileTypes["bvh"] = ImportModel;
|
||||
FileTypes["ase"] = ImportModel;
|
||||
FileTypes["ply"] = ImportModel;
|
||||
FileTypes["dxf"] = ImportModel;
|
||||
FileTypes["ifc"] = ImportModel;
|
||||
FileTypes["nff"] = ImportModel;
|
||||
FileTypes["smd"] = ImportModel;
|
||||
FileTypes["vta"] = ImportModel;
|
||||
FileTypes["mdl"] = ImportModel;
|
||||
FileTypes["md2"] = ImportModel;
|
||||
FileTypes["md3"] = ImportModel;
|
||||
FileTypes["md5mesh"] = ImportModel;
|
||||
FileTypes["q3o"] = ImportModel;
|
||||
FileTypes["q3s"] = ImportModel;
|
||||
FileTypes["ac"] = ImportModel;
|
||||
FileTypes["stl"] = ImportModel;
|
||||
FileTypes["lwo"] = ImportModel;
|
||||
FileTypes["lws"] = ImportModel;
|
||||
FileTypes["lxo"] = ImportModel;
|
||||
|
||||
// Audio
|
||||
FileTypes["wav"] = ImportAudio;
|
||||
FileTypes["mp3"] = ImportAudio;
|
||||
FileTypes["ogg"] = ImportAudio;
|
||||
}
|
||||
|
||||
private static ImportFileEntry ImportModel(ref Request request)
|
||||
{
|
||||
return new ModelImportEntry(ref request);
|
||||
}
|
||||
|
||||
private static ImportFileEntry ImportAudio(ref Request request)
|
||||
{
|
||||
return new AudioImportEntry(ref request);
|
||||
}
|
||||
|
||||
private static ImportFileEntry ImportTexture(ref Request request)
|
||||
{
|
||||
return new TextureImportEntry(ref request);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Execute()
|
||||
{
|
||||
return Import();
|
||||
}
|
||||
}
|
||||
}
|
||||
272
Source/Editor/Content/Import/ImportFilesDialog.cs
Normal file
272
Source/Editor/Content/Import/ImportFilesDialog.cs
Normal file
@@ -0,0 +1,272 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using FlaxEditor.CustomEditors;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Dialogs;
|
||||
using FlaxEditor.GUI.Tree;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content.Import
|
||||
{
|
||||
/// <summary>
|
||||
/// Dialog used to edit import files settings.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.GUI.Dialogs.Dialog" />
|
||||
public class ImportFilesDialog : Dialog
|
||||
{
|
||||
private TreeNode _rootNode;
|
||||
private CustomEditorPresenter _settingsEditor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the entries count.
|
||||
/// </summary>
|
||||
public int EntriesCount
|
||||
{
|
||||
get
|
||||
{
|
||||
var result = 0;
|
||||
for (int i = 0; i < _rootNode.ChildrenCount; i++)
|
||||
{
|
||||
if (_rootNode.Children[i].Tag is ImportFileEntry fileEntry)
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImportFilesDialog"/> class.
|
||||
/// </summary>
|
||||
/// <param name="entries">The entries to edit settings.</param>
|
||||
public ImportFilesDialog(List<ImportFileEntry> entries)
|
||||
: base("Import files settings")
|
||||
{
|
||||
if (entries == null || entries.Count < 1)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
const float TotalWidth = 920;
|
||||
const float EditorHeight = 450;
|
||||
Width = TotalWidth;
|
||||
|
||||
// Header and help description
|
||||
var headerLabel = new Label
|
||||
{
|
||||
Text = "Import settings",
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Offsets = new Margin(0, 0, 0, 40),
|
||||
Parent = this,
|
||||
Font = new FontReference(Style.Current.FontTitle)
|
||||
};
|
||||
var infoLabel = new Label
|
||||
{
|
||||
Text = "Specify options for importing files. Every file can have different settings. Select entries on the left panel to modify them.\nPro Tip: hold CTRL key and select entries to edit multiple at once or use CTRL+A to select them all.",
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
Margin = new Margin(7),
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
Offsets = new Margin(10, -20, 45, 70),
|
||||
Parent = this
|
||||
};
|
||||
|
||||
// Buttons
|
||||
const float ButtonsWidth = 60;
|
||||
const float ButtonsHeight = 24;
|
||||
const float ButtonsMargin = 8;
|
||||
var importButton = new Button
|
||||
{
|
||||
Text = "Import",
|
||||
AnchorPreset = AnchorPresets.BottomRight,
|
||||
Offsets = new Margin(-ButtonsWidth - ButtonsMargin, ButtonsWidth, -ButtonsHeight - ButtonsMargin, ButtonsHeight),
|
||||
Parent = this
|
||||
};
|
||||
importButton.Clicked += OnImport;
|
||||
var cancelButton = new Button
|
||||
{
|
||||
Text = "Cancel",
|
||||
AnchorPreset = AnchorPresets.BottomRight,
|
||||
Offsets = new Margin(-ButtonsWidth - ButtonsMargin - ButtonsWidth - ButtonsMargin, ButtonsWidth, -ButtonsHeight - ButtonsMargin, ButtonsHeight),
|
||||
Parent = this
|
||||
};
|
||||
cancelButton.Clicked += OnCancel;
|
||||
|
||||
// Split panel for entries list and settings editor
|
||||
var splitPanel = new SplitPanel(Orientation.Horizontal, ScrollBars.Both, ScrollBars.Vertical)
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = new Margin(2, 2, infoLabel.Bottom + 2, ButtonsHeight + ButtonsMargin + ButtonsMargin),
|
||||
Parent = this
|
||||
};
|
||||
|
||||
// Settings editor
|
||||
_settingsEditor = new CustomEditorPresenter(null);
|
||||
_settingsEditor.Panel.Parent = splitPanel.Panel2;
|
||||
|
||||
// Setup tree
|
||||
var tree = new Tree(true)
|
||||
{
|
||||
Parent = splitPanel.Panel1
|
||||
};
|
||||
tree.RightClick += OnTreeRightClick;
|
||||
_rootNode = new TreeNode(false);
|
||||
for (int i = 0; i < entries.Count; i++)
|
||||
{
|
||||
var entry = entries[i];
|
||||
|
||||
// TODO: add icons for textures/models/etc from FileEntry to tree node??
|
||||
var node = new ItemNode(entry)
|
||||
{
|
||||
Parent = _rootNode
|
||||
};
|
||||
}
|
||||
_rootNode.Expand();
|
||||
_rootNode.ChildrenIndent = 0;
|
||||
_rootNode.Parent = tree;
|
||||
tree.Margin = new Margin(0.0f, 0.0f, -14.0f, 2.0f); // Hide root node
|
||||
tree.SelectedChanged += OnSelectedChanged;
|
||||
|
||||
// Select the first item
|
||||
tree.Select(_rootNode.Children[0] as TreeNode);
|
||||
|
||||
_dialogSize = new Vector2(TotalWidth, EditorHeight + splitPanel.Offsets.Height);
|
||||
}
|
||||
|
||||
private void OnTreeRightClick(TreeNode node, Vector2 location)
|
||||
{
|
||||
var menu = new ContextMenu();
|
||||
menu.AddButton("Rename", OnRenameClicked);
|
||||
menu.AddButton("Don't import", OnDontImportClicked);
|
||||
menu.AddButton("Show in Explorer", OnShowInExplorerClicked);
|
||||
menu.Tag = node;
|
||||
menu.Show(node, location);
|
||||
}
|
||||
|
||||
private void OnRenameClicked(ContextMenuButton button)
|
||||
{
|
||||
var node = (ItemNode)button.ParentContextMenu.Tag;
|
||||
node.StartRenaming();
|
||||
}
|
||||
|
||||
private void OnDontImportClicked(ContextMenuButton button)
|
||||
{
|
||||
if (EntriesCount == 1)
|
||||
{
|
||||
OnCancel();
|
||||
return;
|
||||
}
|
||||
|
||||
var node = (ItemNode)button.ParentContextMenu.Tag;
|
||||
if (_settingsEditor.Selection.Count == 1 && _settingsEditor.Selection[0] == node.Entry.Settings)
|
||||
_settingsEditor.Deselect();
|
||||
node.Dispose();
|
||||
}
|
||||
|
||||
private void OnShowInExplorerClicked(ContextMenuButton button)
|
||||
{
|
||||
var node = (ItemNode)button.ParentContextMenu.Tag;
|
||||
FileSystem.ShowFileExplorer(Path.GetDirectoryName(node.Entry.SourceUrl));
|
||||
}
|
||||
|
||||
private class ItemNode : TreeNode
|
||||
{
|
||||
public ImportFileEntry Entry => (ImportFileEntry)Tag;
|
||||
|
||||
public ItemNode(ImportFileEntry entry)
|
||||
: base(false)
|
||||
{
|
||||
Text = Path.GetFileName(entry.SourceUrl);
|
||||
Tag = entry;
|
||||
LinkTooltip(entry.SourceUrl);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool OnMouseDoubleClickHeader(ref Vector2 location, MouseButton button)
|
||||
{
|
||||
StartRenaming();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the rename popup for the item.
|
||||
/// </summary>
|
||||
public void StartRenaming()
|
||||
{
|
||||
// Start renaming the folder
|
||||
var entry = (ImportFileEntry)Tag;
|
||||
var shortName = Path.GetFileNameWithoutExtension(entry.ResultUrl);
|
||||
var dialog = RenamePopup.Show(this, HeaderRect, shortName, false);
|
||||
dialog.Tag = Tag;
|
||||
dialog.Renamed += OnRenamed;
|
||||
}
|
||||
|
||||
private void OnRenamed(RenamePopup popup)
|
||||
{
|
||||
var entry = (ImportFileEntry)Tag;
|
||||
entry.ModifyResultFilename(popup.Text);
|
||||
Text = string.Format("{0} ({1})", Path.GetFileName(entry.SourceUrl), popup.Text);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnImport()
|
||||
{
|
||||
var entries = new List<ImportFileEntry>(_rootNode.ChildrenCount);
|
||||
for (int i = 0; i < _rootNode.ChildrenCount; i++)
|
||||
{
|
||||
var fileEntry = _rootNode.Children[i].Tag as ImportFileEntry;
|
||||
if (fileEntry != null)
|
||||
entries.Add(fileEntry);
|
||||
}
|
||||
Editor.Instance.ContentImporting.LetThemBeImportedxD(entries);
|
||||
|
||||
Close(DialogResult.OK);
|
||||
}
|
||||
|
||||
private void OnCancel()
|
||||
{
|
||||
Close(DialogResult.Cancel);
|
||||
}
|
||||
|
||||
private void OnSelectedChanged(List<TreeNode> before, List<TreeNode> after)
|
||||
{
|
||||
var selection = new List<object>(after.Count);
|
||||
for (int i = 0; i < after.Count; i++)
|
||||
{
|
||||
if (after[i].Tag is ImportFileEntry fileEntry && fileEntry.HasSettings)
|
||||
selection.Add(fileEntry.Settings);
|
||||
}
|
||||
|
||||
_settingsEditor.Select(selection);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SetupWindowSettings(ref CreateWindowSettings settings)
|
||||
{
|
||||
base.SetupWindowSettings(ref settings);
|
||||
|
||||
settings.MinimumSize = new Vector2(300, 400);
|
||||
settings.HasSizingFrame = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
if (base.OnKeyDown(key))
|
||||
return true;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case KeyboardKeys.Escape:
|
||||
OnCancel();
|
||||
return true;
|
||||
case KeyboardKeys.Return:
|
||||
OnImport();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
482
Source/Editor/Content/Import/ModelImportEntry.cs
Normal file
482
Source/Editor/Content/Import/ModelImportEntry.cs
Normal file
@@ -0,0 +1,482 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Import
|
||||
{
|
||||
/// <summary>
|
||||
/// Importing model lightmap UVs source
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public enum ModelLightmapUVsSource : int
|
||||
{
|
||||
/// <summary>
|
||||
/// No lightmap UVs.
|
||||
/// </summary>
|
||||
Disable = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Generate lightmap UVs from model geometry.
|
||||
/// </summary>
|
||||
Generate = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The texcoords channel 0.
|
||||
/// </summary>
|
||||
Channel0 = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The texcoords channel 1.
|
||||
/// </summary>
|
||||
Channel1 = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The texcoords channel 2.
|
||||
/// </summary>
|
||||
Channel2 = 4,
|
||||
|
||||
/// <summary>
|
||||
/// The texcoords channel 3.
|
||||
/// </summary>
|
||||
Channel3 = 5
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Declares the imported data type.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public enum ModelType : int
|
||||
{
|
||||
/// <summary>
|
||||
/// The model asset.
|
||||
/// </summary>
|
||||
Model = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The skinned model asset.
|
||||
/// </summary>
|
||||
SkinnedModel = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The animation asset.
|
||||
/// </summary>
|
||||
Animation = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Declares the imported animation clip duration.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public enum AnimationDuration : int
|
||||
{
|
||||
/// <summary>
|
||||
/// The imported duration.
|
||||
/// </summary>
|
||||
Imported = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The custom duration specified via keyframes range.
|
||||
/// </summary>
|
||||
Custom = 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Proxy object to present model import settings in <see cref="ImportFilesDialog"/>.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public class ModelImportSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the imported asset.
|
||||
/// </summary>
|
||||
[EditorOrder(0), Tooltip("Type of the imported asset")]
|
||||
public ModelType Type { get; set; } = ModelType.Model;
|
||||
|
||||
/// <summary>
|
||||
/// True if calculate model normals, otherwise will import them.
|
||||
/// </summary>
|
||||
[EditorOrder(20), DefaultValue(false), EditorDisplay("Geometry"), Tooltip("Enable model normal vectors recalculating")]
|
||||
public bool CalculateNormals { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Calculated normals smoothing angle.
|
||||
/// </summary>
|
||||
[VisibleIf("CalculateNormals")]
|
||||
[EditorOrder(30), DefaultValue(175.0f), Limit(0, 175, 0.1f), EditorDisplay("Geometry"), Tooltip("Specifies the maximum angle (in degrees) that may be between two face normals at the same vertex position that their are smoothed together. The default value is 175.")]
|
||||
public float SmoothingNormalsAngle { get; set; } = 175.0f;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, the imported normal vectors of the mesh will be flipped (scaled by -1).
|
||||
/// </summary>
|
||||
[EditorOrder(35), DefaultValue(false), EditorDisplay("Geometry"), Tooltip("If checked, the imported normal vectors of the mesh will be flipped (scaled by -1).")]
|
||||
public bool FlipNormals { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// True if calculate model tangents, otherwise will import them.
|
||||
/// </summary>
|
||||
[EditorOrder(40), DefaultValue(true), EditorDisplay("Geometry"), Tooltip("Enable model tangent vectors recalculating")]
|
||||
public bool CalculateTangents { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Calculated normals smoothing angle.
|
||||
/// </summary>
|
||||
[VisibleIf("CalculateTangents")]
|
||||
[EditorOrder(45), DefaultValue(45.0f), Limit(0, 45, 0.1f), EditorDisplay("Geometry"), Tooltip("Specifies the maximum angle (in degrees) that may be between two vertex tangents that their tangents and bi-tangents are smoothed. The default value is 45.")]
|
||||
public float SmoothingTangentsAngle { get; set; } = 45.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Enable/disable meshes geometry optimization.
|
||||
/// </summary>
|
||||
[EditorOrder(50), DefaultValue(true), EditorDisplay("Geometry"), Tooltip("Enable/disable meshes geometry optimization")]
|
||||
public bool OptimizeMeshes { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Enable/disable geometry merge for meshes with the same materials.
|
||||
/// </summary>
|
||||
[EditorOrder(60), DefaultValue(true), EditorDisplay("Geometry"), Tooltip("Enable/disable geometry merge for meshes with the same materials")]
|
||||
public bool MergeMeshes { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Enable/disable importing meshes Level of Details.
|
||||
/// </summary>
|
||||
[EditorOrder(70), DefaultValue(true), EditorDisplay("Geometry", "Import LODs"), Tooltip("Enable/disable importing meshes Level of Details")]
|
||||
public bool ImportLODs { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Enable/disable importing vertex colors (channel 0 only).
|
||||
/// </summary>
|
||||
[EditorOrder(80), DefaultValue(true), EditorDisplay("Geometry"), Tooltip("Enable/disable importing vertex colors (channel 0 only)")]
|
||||
public bool ImportVertexColors { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Enable/disable importing blend shapes (morph targets).
|
||||
/// </summary>
|
||||
[EditorOrder(85), DefaultValue(false), EditorDisplay("Geometry"), Tooltip("Enable/disable importing blend shapes (morph targets).")]
|
||||
public bool ImportBlendShapes { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// The lightmap UVs source.
|
||||
/// </summary>
|
||||
[EditorOrder(90), DefaultValue(ModelLightmapUVsSource.Disable), EditorDisplay("Geometry", "Lightmap UVs Source"), Tooltip("Model lightmap UVs source")]
|
||||
public ModelLightmapUVsSource LightmapUVsSource { get; set; } = ModelLightmapUVsSource.Disable;
|
||||
|
||||
/// <summary>
|
||||
/// Custom uniform import scale.
|
||||
/// </summary>
|
||||
[EditorOrder(500), DefaultValue(1.0f), EditorDisplay("Transform"), Tooltip("Custom uniform import scale")]
|
||||
public float Scale { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Custom import geometry rotation.
|
||||
/// </summary>
|
||||
[DefaultValue(typeof(Quaternion), "0,0,0,1")]
|
||||
[EditorOrder(510), EditorDisplay("Transform"), Tooltip("Custom import geometry rotation")]
|
||||
public Quaternion Rotation { get; set; } = Quaternion.Identity;
|
||||
|
||||
/// <summary>
|
||||
/// Custom import geometry offset.
|
||||
/// </summary>
|
||||
[DefaultValue(typeof(Vector3), "0,0,0")]
|
||||
[EditorOrder(520), EditorDisplay("Transform"), Tooltip("Custom import geometry offset")]
|
||||
public Vector3 Translation { get; set; } = Vector3.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, the imported geometry will be shifted to the center of mass.
|
||||
/// </summary>
|
||||
[EditorOrder(530), DefaultValue(false), EditorDisplay("Transform"), Tooltip("If checked, the imported geometry will be shifted to the center of mass.")]
|
||||
public bool CenterGeometry { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// The imported animation duration mode.
|
||||
/// </summary>
|
||||
[EditorOrder(1000), DefaultValue(AnimationDuration.Imported), EditorDisplay("Animation"), Tooltip("Imported animation duration mode. Can use the original value or overriden by settings.")]
|
||||
public AnimationDuration Duration { get; set; } = AnimationDuration.Imported;
|
||||
|
||||
/// <summary>
|
||||
/// The imported animation first frame index. Used only if Duration mode is set to Custom.
|
||||
/// </summary>
|
||||
[EditorOrder(1010), DefaultValue(0.0f), Limit(0), EditorDisplay("Animation"), Tooltip("Imported animation first frame index. Used only if Duration mode is set to Custom.")]
|
||||
public float FramesRangeStart { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The imported animation end frame index. Used only if Duration mode is set to Custom.
|
||||
/// </summary>
|
||||
[EditorOrder(1020), DefaultValue(0.0f), Limit(0), EditorDisplay("Animation"), Tooltip("Imported animation last frame index. Used only if Duration mode is set to Custom.")]
|
||||
public float FramesRangeEnd { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The imported animation default frame rate. Can specify the default frames per second amount for imported animation. If value is 0 then the original animation frame rate will be used.
|
||||
/// </summary>
|
||||
[EditorOrder(1025), DefaultValue(0.0f), Limit(0, 1000, 0.01f), EditorDisplay("Animation"), Tooltip("The imported animation default frame rate. Can specify the default frames per second amount for imported animation. If value is 0 then the original animation frame rate will be used.")]
|
||||
public float DefaultFrameRate { get; set; } = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The imported animation sampling rate. If value is 0 then the original animation speed will be used.
|
||||
/// </summary>
|
||||
[EditorOrder(1030), DefaultValue(0.0f), Limit(0, 1000, 0.01f), EditorDisplay("Animation"), Tooltip("The imported animation sampling rate. If value is 0 then the original animation speed will be used.")]
|
||||
public float SamplingRate { get; set; } = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The imported animation will have removed tracks with no keyframes or unspecified data.
|
||||
/// </summary>
|
||||
[EditorOrder(1040), DefaultValue(true), EditorDisplay("Animation"), Tooltip("The imported animation will have removed tracks with no keyframes or unspecified data.")]
|
||||
public bool SkipEmptyCurves { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// The imported animation channels will be optimized to remove redundant keyframes.
|
||||
/// </summary>
|
||||
[EditorOrder(1050), DefaultValue(true), EditorDisplay("Animation"), Tooltip("The imported animation channels will be optimized to remove redundant keyframes.")]
|
||||
public bool OptimizeKeyframes { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Enables root motion extraction support from this animation.
|
||||
/// </summary>
|
||||
[EditorOrder(1060), DefaultValue(false), EditorDisplay("Animation"), Tooltip("Enables root motion extraction support from this animation.")]
|
||||
public bool EnableRootMotion { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// The custom node name to be used as a root motion source. If not specified the actual root node will be used.
|
||||
/// </summary>
|
||||
[EditorOrder(1070), DefaultValue(typeof(string), ""), EditorDisplay("Animation"), Tooltip("The custom node name to be used as a root motion source. If not specified the actual root node will be used.")]
|
||||
public string RootNodeName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The zero-based index for the animation clip to import. If the source file has more than one animation it can be used to pick a desire clip.
|
||||
/// </summary>
|
||||
[EditorOrder(1080), DefaultValue(-1), EditorDisplay("Animation"), Tooltip("The zero-based index for the animation clip to import. If the source file has more than one animation it can be used to pick a desire clip.")]
|
||||
public int AnimationIndex { get; set; } = -1;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, the importer will generate a sequence of LODs based on the base LOD index.
|
||||
/// </summary>
|
||||
[EditorOrder(1100), DefaultValue(false), EditorDisplay("Level Of Detail", "Generate LODs"), Tooltip("If checked, the importer will generate a sequence of LODs based on the base LOD index.")]
|
||||
public bool GenerateLODs { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// The index of the LOD from the source model data to use as a reference for following LODs generation.
|
||||
/// </summary>
|
||||
[EditorOrder(1110), DefaultValue(0), Limit(0, Model.MaxLODs - 1), EditorDisplay("Level Of Detail", "Base LOD"), Tooltip("The index of the LOD from the source model data to use as a reference for following LODs generation.")]
|
||||
public int BaseLOD { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of LODs to include in the model (all reaming ones starting from Base LOD will be generated).
|
||||
/// </summary>
|
||||
[EditorOrder(1120), DefaultValue(4), Limit(1, Model.MaxLODs), EditorDisplay("Level Of Detail", "LOD Count"), Tooltip("The amount of LODs to include in the model (all reaming ones starting from Base LOD will be generated).")]
|
||||
public int LODCount { get; set; } = 4;
|
||||
|
||||
/// <summary>
|
||||
/// The target amount of triangles for the generated LOD (based on the higher LOD). Normalized to range 0-1. For instance 0.4 cuts the triangle count to 40%.
|
||||
/// </summary>
|
||||
[EditorOrder(1130), DefaultValue(0.5f), Limit(0, 1, 0.001f), EditorDisplay("Level Of Detail"), Tooltip("The target amount of triangles for the generated LOD (based on the higher LOD). Normalized to range 0-1. For instance 0.4 cuts the triangle count to 40%.")]
|
||||
public float TriangleReduction { get; set; } = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, the importer will create materials for model meshes as specified in the file.
|
||||
/// </summary>
|
||||
[EditorOrder(400), DefaultValue(true), EditorDisplay("Materials"), Tooltip("If checked, the importer will create materials for model meshes as specified in the file.")]
|
||||
public bool ImportMaterials { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, the importer will import texture files used by the model and any embedded texture resources.
|
||||
/// </summary>
|
||||
[EditorOrder(410), DefaultValue(true), EditorDisplay("Materials"), Tooltip("If checked, the importer will import texture files used by the model and any embedded texture resources.")]
|
||||
public bool ImportTextures { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, the importer will try to restore the model material slots.
|
||||
/// </summary>
|
||||
[EditorOrder(420), DefaultValue(true), EditorDisplay("Materials", "Restore Materials On Reimport"), Tooltip("If checked, the importer will try to restore the assigned materials to the model slots.")]
|
||||
public bool RestoreMaterialsOnReimport { get; set; } = true;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct InternalOptions
|
||||
{
|
||||
public ModelType Type;
|
||||
|
||||
// Geometry
|
||||
public byte CalculateNormals;
|
||||
public float SmoothingNormalsAngle;
|
||||
public byte FlipNormals;
|
||||
public float SmoothingTangentsAngle;
|
||||
public byte CalculateTangents;
|
||||
public byte OptimizeMeshes;
|
||||
public byte MergeMeshes;
|
||||
public byte ImportLODs;
|
||||
public byte ImportVertexColors;
|
||||
public byte ImportBlendShapes;
|
||||
public ModelLightmapUVsSource LightmapUVsSource;
|
||||
|
||||
// Transform
|
||||
public float Scale;
|
||||
public Quaternion Rotation;
|
||||
public Vector3 Translation;
|
||||
public byte CenterGeometry;
|
||||
|
||||
// Animation
|
||||
public AnimationDuration Duration;
|
||||
public float FramesRangeStart;
|
||||
public float FramesRangeEnd;
|
||||
public float DefaultFrameRate;
|
||||
public float SamplingRate;
|
||||
public byte SkipEmptyCurves;
|
||||
public byte OptimizeKeyframes;
|
||||
public byte EnableRootMotion;
|
||||
public string RootNodeName;
|
||||
public int AnimationIndex;
|
||||
|
||||
// Level Of Detail
|
||||
public byte GenerateLODs;
|
||||
public int BaseLOD;
|
||||
public int LODCount;
|
||||
public float TriangleReduction;
|
||||
|
||||
// Misc
|
||||
public byte ImportMaterials;
|
||||
public byte ImportTextures;
|
||||
public byte RestoreMaterialsOnReimport;
|
||||
}
|
||||
|
||||
internal void ToInternal(out InternalOptions options)
|
||||
{
|
||||
options = new InternalOptions
|
||||
{
|
||||
Type = Type,
|
||||
CalculateNormals = (byte)(CalculateNormals ? 1 : 0),
|
||||
SmoothingNormalsAngle = SmoothingNormalsAngle,
|
||||
FlipNormals = (byte)(FlipNormals ? 1 : 0),
|
||||
SmoothingTangentsAngle = SmoothingTangentsAngle,
|
||||
CalculateTangents = (byte)(CalculateTangents ? 1 : 0),
|
||||
OptimizeMeshes = (byte)(OptimizeMeshes ? 1 : 0),
|
||||
MergeMeshes = (byte)(MergeMeshes ? 1 : 0),
|
||||
ImportLODs = (byte)(ImportLODs ? 1 : 0),
|
||||
ImportVertexColors = (byte)(ImportVertexColors ? 1 : 0),
|
||||
ImportBlendShapes = (byte)(ImportBlendShapes ? 1 : 0),
|
||||
LightmapUVsSource = LightmapUVsSource,
|
||||
Scale = Scale,
|
||||
Rotation = Rotation,
|
||||
Translation = Translation,
|
||||
CenterGeometry = (byte)(CenterGeometry ? 1 : 0),
|
||||
Duration = Duration,
|
||||
FramesRangeStart = FramesRangeStart,
|
||||
FramesRangeEnd = FramesRangeEnd,
|
||||
DefaultFrameRate = DefaultFrameRate,
|
||||
SamplingRate = SamplingRate,
|
||||
SkipEmptyCurves = (byte)(SkipEmptyCurves ? 1 : 0),
|
||||
OptimizeKeyframes = (byte)(OptimizeKeyframes ? 1 : 0),
|
||||
EnableRootMotion = (byte)(EnableRootMotion ? 1 : 0),
|
||||
RootNodeName = RootNodeName,
|
||||
AnimationIndex = AnimationIndex,
|
||||
GenerateLODs = (byte)(GenerateLODs ? 1 : 0),
|
||||
BaseLOD = BaseLOD,
|
||||
LODCount = LODCount,
|
||||
TriangleReduction = TriangleReduction,
|
||||
ImportMaterials = (byte)(ImportMaterials ? 1 : 0),
|
||||
ImportTextures = (byte)(ImportTextures ? 1 : 0),
|
||||
RestoreMaterialsOnReimport = (byte)(RestoreMaterialsOnReimport ? 1 : 0),
|
||||
};
|
||||
}
|
||||
|
||||
internal void FromInternal(ref InternalOptions options)
|
||||
{
|
||||
Type = options.Type;
|
||||
CalculateNormals = options.CalculateNormals != 0;
|
||||
SmoothingNormalsAngle = options.SmoothingNormalsAngle;
|
||||
FlipNormals = options.FlipNormals != 0;
|
||||
SmoothingTangentsAngle = options.SmoothingTangentsAngle;
|
||||
CalculateTangents = options.CalculateTangents != 0;
|
||||
OptimizeMeshes = options.OptimizeMeshes != 0;
|
||||
MergeMeshes = options.MergeMeshes != 0;
|
||||
ImportLODs = options.ImportLODs != 0;
|
||||
ImportVertexColors = options.ImportVertexColors != 0;
|
||||
ImportBlendShapes = options.ImportBlendShapes != 0;
|
||||
LightmapUVsSource = options.LightmapUVsSource;
|
||||
Scale = options.Scale;
|
||||
Rotation = options.Rotation;
|
||||
Translation = options.Translation;
|
||||
CenterGeometry = options.CenterGeometry != 0;
|
||||
FramesRangeStart = options.FramesRangeStart;
|
||||
FramesRangeEnd = options.FramesRangeEnd;
|
||||
DefaultFrameRate = options.DefaultFrameRate;
|
||||
SamplingRate = options.SamplingRate;
|
||||
SkipEmptyCurves = options.SkipEmptyCurves != 0;
|
||||
OptimizeKeyframes = options.OptimizeKeyframes != 0;
|
||||
EnableRootMotion = options.EnableRootMotion != 0;
|
||||
RootNodeName = options.RootNodeName;
|
||||
AnimationIndex = options.AnimationIndex;
|
||||
GenerateLODs = options.GenerateLODs != 0;
|
||||
BaseLOD = options.BaseLOD;
|
||||
LODCount = options.LODCount;
|
||||
TriangleReduction = options.TriangleReduction;
|
||||
ImportMaterials = options.ImportMaterials != 0;
|
||||
ImportTextures = options.ImportTextures != 0;
|
||||
RestoreMaterialsOnReimport = options.RestoreMaterialsOnReimport != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries the restore the asset import options from the target resource file.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <param name="assetPath">The asset path.</param>
|
||||
/// <returns>True settings has been restored, otherwise false.</returns>
|
||||
public static bool TryRestore(ref ModelImportSettings options, string assetPath)
|
||||
{
|
||||
if (ModelImportEntry.Internal_GetModelImportOptions(assetPath, out var internalOptions))
|
||||
{
|
||||
// Restore settings
|
||||
options.FromInternal(ref internalOptions);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Model asset import entry.
|
||||
/// </summary>
|
||||
/// <seealso cref="AssetImportEntry" />
|
||||
public class ModelImportEntry : AssetImportEntry
|
||||
{
|
||||
private ModelImportSettings _settings = new ModelImportSettings();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ModelImportEntry"/> class.
|
||||
/// </summary>
|
||||
/// <param name="request">The import request.</param>
|
||||
public ModelImportEntry(ref Request request)
|
||||
: base(ref request)
|
||||
{
|
||||
// Try to restore target asset model import options (useful for fast reimport)
|
||||
ModelImportSettings.TryRestore(ref _settings, ResultUrl);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object Settings => _settings;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool TryOverrideSettings(object settings)
|
||||
{
|
||||
if (settings is ModelImportSettings o)
|
||||
{
|
||||
_settings = o;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Import()
|
||||
{
|
||||
return Editor.Import(SourceUrl, ResultUrl, _settings);
|
||||
}
|
||||
|
||||
#region Internal Calls
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal static extern bool Internal_GetModelImportOptions(string path, out ModelImportSettings.InternalOptions result);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
38
Source/Editor/Content/Import/Request.cs
Normal file
38
Source/Editor/Content/Import/Request.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Import
|
||||
{
|
||||
/// <summary>
|
||||
/// The content item import request data container.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public struct Request
|
||||
{
|
||||
/// <summary>
|
||||
/// The input item path (folder or file).
|
||||
/// </summary>
|
||||
public string InputPath;
|
||||
|
||||
/// <summary>
|
||||
/// The output path (folder or file).
|
||||
/// </summary>
|
||||
public string OutputPath;
|
||||
|
||||
/// <summary>
|
||||
/// Flag set to true for binary assets handled by the engine internally.
|
||||
/// </summary>
|
||||
public bool IsBinaryAsset;
|
||||
|
||||
/// <summary>
|
||||
/// Flag used to skip showing import settings dialog to used. Can be used for importing assets from code by plugins.
|
||||
/// </summary>
|
||||
public bool SkipSettingsDialog;
|
||||
|
||||
/// <summary>
|
||||
/// The custom settings object.
|
||||
/// </summary>
|
||||
public object Settings;
|
||||
}
|
||||
}
|
||||
505
Source/Editor/Content/Import/TextureImportEntry.cs
Normal file
505
Source/Editor/Content/Import/TextureImportEntry.cs
Normal file
@@ -0,0 +1,505 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using FlaxEngine;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace FlaxEditor.Content.Import
|
||||
{
|
||||
/// <summary>
|
||||
/// Texture format types.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public enum TextureFormatType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The unknown.
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The color with RGB channels.
|
||||
/// </summary>
|
||||
ColorRGB = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The color with RGBA channels.
|
||||
/// </summary>
|
||||
ColorRGBA = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The normal map (packed and compressed).
|
||||
/// </summary>
|
||||
NormalMap = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The gray scale (R channel).
|
||||
/// </summary>
|
||||
GrayScale = 4,
|
||||
|
||||
/// <summary>
|
||||
/// The HDR color (RGBA channels).
|
||||
/// </summary>
|
||||
HdrRGBA = 5,
|
||||
|
||||
/// <summary>
|
||||
/// The HDR color (RGB channels).
|
||||
/// </summary>
|
||||
HdrRGB = 6
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Proxy object to present texture import settings in <see cref="ImportFilesDialog"/>.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public class TextureImportSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom version of <see cref="TextureFormatType"/> for GUI.
|
||||
/// </summary>
|
||||
public enum CustomTextureFormatType
|
||||
{
|
||||
/// <summary>
|
||||
/// The color with RGB channels.
|
||||
/// </summary>
|
||||
ColorRGB = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The color with RGBA channels.
|
||||
/// </summary>
|
||||
ColorRGBA = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The normal map (packed and compressed).
|
||||
/// </summary>
|
||||
NormalMap = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The gray scale (R channel).
|
||||
/// </summary>
|
||||
GrayScale = 4,
|
||||
|
||||
/// <summary>
|
||||
/// The HDR color (RGBA channels).
|
||||
/// </summary>
|
||||
HdrRGBA = 5,
|
||||
|
||||
/// <summary>
|
||||
/// The HDR color (RGB channels).
|
||||
/// </summary>
|
||||
HdrRGB = 6
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A custom set of max texture import sizes.
|
||||
/// </summary>
|
||||
public enum CustomMaxSizeType
|
||||
{
|
||||
/// <summary>
|
||||
/// The 32.
|
||||
/// </summary>
|
||||
_32 = 32,
|
||||
|
||||
/// <summary>
|
||||
/// The 64.
|
||||
/// </summary>
|
||||
_64 = 64,
|
||||
|
||||
/// <summary>
|
||||
/// The 128.
|
||||
/// </summary>
|
||||
_128 = 128,
|
||||
|
||||
/// <summary>
|
||||
/// The 256.
|
||||
/// </summary>
|
||||
_256 = 256,
|
||||
|
||||
/// <summary>
|
||||
/// The 512.
|
||||
/// </summary>
|
||||
_512 = 512,
|
||||
|
||||
/// <summary>
|
||||
/// The 1024.
|
||||
/// </summary>
|
||||
_1024 = 1024,
|
||||
|
||||
/// <summary>
|
||||
/// The 2048.
|
||||
/// </summary>
|
||||
_2048 = 2048,
|
||||
|
||||
/// <summary>
|
||||
/// The 4096.
|
||||
/// </summary>
|
||||
_4096 = 4096,
|
||||
|
||||
/// <summary>
|
||||
/// The 8192.
|
||||
/// </summary>
|
||||
_8192 = 8192,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the maximum size to enum.
|
||||
/// </summary>
|
||||
/// <param name="f">The max size.</param>
|
||||
/// <returns>The converted enum.</returns>
|
||||
public static CustomMaxSizeType ConvertMaxSize(int f)
|
||||
{
|
||||
if (!Mathf.IsPowerOfTwo(f))
|
||||
f = Mathf.NextPowerOfTwo(f);
|
||||
|
||||
FieldInfo[] fields = typeof(CustomMaxSizeType).GetFields();
|
||||
for (int i = 0; i < fields.Length; i++)
|
||||
{
|
||||
var field = fields[i];
|
||||
if (field.Name.Equals("value__"))
|
||||
continue;
|
||||
|
||||
if (f == (int)field.GetRawConstantValue())
|
||||
return (CustomMaxSizeType)f;
|
||||
}
|
||||
|
||||
return CustomMaxSizeType._8192;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The sprite info.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SpriteInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The sprite area.
|
||||
/// </summary>
|
||||
public Rectangle Area;
|
||||
|
||||
/// <summary>
|
||||
/// The sprite name.
|
||||
/// </summary>
|
||||
public string Name;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SpriteInfo"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="area">The area.</param>
|
||||
/// <param name="name">The name.</param>
|
||||
public SpriteInfo(Rectangle area, string name)
|
||||
{
|
||||
Area = area;
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Texture format type
|
||||
/// </summary>
|
||||
[EditorOrder(0), DefaultValue(CustomTextureFormatType.ColorRGB), Tooltip("Texture import format type")]
|
||||
public CustomTextureFormatType Type { get; set; } = CustomTextureFormatType.ColorRGB;
|
||||
|
||||
/// <summary>
|
||||
/// True if texture should be imported as a texture atlas resource
|
||||
/// </summary>
|
||||
[EditorOrder(10), DefaultValue(false), Tooltip("True if texture should be imported as a texture atlas (with sprites)")]
|
||||
public bool IsAtlas { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if disable dynamic texture streaming
|
||||
/// </summary>
|
||||
[EditorOrder(20), DefaultValue(false), Tooltip("True if disable dynamic texture streaming")]
|
||||
public bool NeverStream { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables/disables texture data compression.
|
||||
/// </summary>
|
||||
[EditorOrder(30), DefaultValue(true), Tooltip("True if compress texture data")]
|
||||
public bool Compress { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// True if texture channels have independent data
|
||||
/// </summary>
|
||||
[EditorOrder(40), DefaultValue(false), Tooltip("True if texture channels have independent data (for compression methods)")]
|
||||
public bool IndependentChannels { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if use sRGB format for texture data. Recommended for color maps and diffuse color textures.
|
||||
/// </summary>
|
||||
[EditorOrder(50), DefaultValue(false), EditorDisplay(null, "sRGB"), Tooltip("True if use sRGB format for texture data. Recommended for color maps and diffuse color textures.")]
|
||||
public bool sRGB { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if generate mip maps chain for the texture.
|
||||
/// </summary>
|
||||
[EditorOrder(60), DefaultValue(true), Tooltip("True if generate mip maps chain for the texture")]
|
||||
public bool GenerateMipMaps { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// True if flip Y coordinate of the texture.
|
||||
/// </summary>
|
||||
[EditorOrder(65), DefaultValue(false), EditorDisplay(null, "Flip Y"), Tooltip("True if flip Y coordinate of the texture.")]
|
||||
public bool FlipY { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// The import texture scale.
|
||||
/// </summary>
|
||||
[EditorOrder(70), DefaultValue(1.0f), Tooltip("Texture scale. Default is 1.")]
|
||||
public float Scale { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum size of the texture (for both width and height).
|
||||
/// Higher resolution textures will be resized during importing process.
|
||||
/// </summary>
|
||||
[EditorOrder(80), DefaultValue(CustomMaxSizeType._8192), Tooltip("Maximum texture size (will be resized if need to)")]
|
||||
public CustomMaxSizeType MaxSize { get; set; } = CustomMaxSizeType._8192;
|
||||
|
||||
/// <summary>
|
||||
/// True if resize texture on import. Use Size property to define texture width and height. Texture scale property will be ignored.
|
||||
/// </summary>
|
||||
[EditorOrder(90), DefaultValue(false), Tooltip("True if resize texture on import. Use Size property to define texture width and height. Texture scale property will be ignored.")]
|
||||
public bool Resize { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size of the imported texture. If Resize property is set to true then texture will be resized during the import to this value. Otherwise it will be ignored.
|
||||
/// </summary>
|
||||
[EditorOrder(100), VisibleIf("Resize"), DefaultValue(typeof(Int2), "1024,1024"), Tooltip("The size of the imported texture. If Resize property is set to true then texture will be resized during the import to this value. Otherwise it will be ignored.")]
|
||||
public Int2 Size { get; set; } = new Int2(1024, 1024);
|
||||
|
||||
/// <summary>
|
||||
/// True if preserve alpha coverage in generated mips for alpha test reference. Scales mipmap alpha values to preserve alpha coverage based on an alpha test reference value.
|
||||
/// </summary>
|
||||
[EditorOrder(240), DefaultValue(false), Tooltip("Check to preserve alpha coverage in generated mips for alpha test reference. Scales mipmap alpha values to preserve alpha coverage based on an alpha test reference value.")]
|
||||
public bool PreserveAlphaCoverage { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// The reference value for the alpha coverage preserving.
|
||||
/// </summary>
|
||||
[EditorOrder(250), VisibleIf("PreserveAlphaCoverage"), DefaultValue(0.5f), Tooltip("The reference value for the alpha coverage preserving.")]
|
||||
public float PreserveAlphaCoverageReference { get; set; } = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// The sprites. Used to keep created sprites on sprite atlas reimport.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public List<SpriteInfo> Sprites = new List<SpriteInfo>();
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct InternalOptions
|
||||
{
|
||||
public TextureFormatType Type;
|
||||
public byte IsAtlas;
|
||||
public byte NeverStream;
|
||||
public byte Compress;
|
||||
public byte IndependentChannels;
|
||||
public byte sRGB;
|
||||
public byte GenerateMipMaps;
|
||||
public byte FlipY;
|
||||
public byte Resize;
|
||||
public byte PreserveAlphaCoverage;
|
||||
public float PreserveAlphaCoverageReference;
|
||||
public float Scale;
|
||||
public int MaxSize;
|
||||
public Int2 Size;
|
||||
public Rectangle[] SpriteAreas;
|
||||
public string[] SpriteNames;
|
||||
}
|
||||
|
||||
internal void ToInternal(out InternalOptions options)
|
||||
{
|
||||
options = new InternalOptions
|
||||
{
|
||||
Type = (TextureFormatType)(int)Type,
|
||||
IsAtlas = (byte)(IsAtlas ? 1 : 0),
|
||||
NeverStream = (byte)(NeverStream ? 1 : 0),
|
||||
Compress = (byte)(Compress ? 1 : 0),
|
||||
IndependentChannels = (byte)(IndependentChannels ? 1 : 0),
|
||||
sRGB = (byte)(sRGB ? 1 : 0),
|
||||
GenerateMipMaps = (byte)(GenerateMipMaps ? 1 : 0),
|
||||
FlipY = (byte)(FlipY ? 1 : 0),
|
||||
Resize = (byte)(Resize ? 1 : 0),
|
||||
PreserveAlphaCoverage = (byte)(PreserveAlphaCoverage ? 1 : 0),
|
||||
PreserveAlphaCoverageReference = PreserveAlphaCoverageReference,
|
||||
Scale = Scale,
|
||||
Size = Size,
|
||||
MaxSize = (int)MaxSize
|
||||
};
|
||||
if (Sprites != null && Sprites.Count > 0)
|
||||
{
|
||||
int count = Sprites.Count;
|
||||
options.SpriteAreas = new Rectangle[count];
|
||||
options.SpriteNames = new string[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
options.SpriteAreas[i] = Sprites[i].Area;
|
||||
options.SpriteNames[i] = Sprites[i].Name;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
options.SpriteAreas = null;
|
||||
options.SpriteNames = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal void FromInternal(ref InternalOptions options)
|
||||
{
|
||||
Type = (CustomTextureFormatType)(int)options.Type;
|
||||
IsAtlas = options.IsAtlas != 0;
|
||||
NeverStream = options.NeverStream != 0;
|
||||
Compress = options.Compress != 0;
|
||||
IndependentChannels = options.IndependentChannels != 0;
|
||||
sRGB = options.sRGB != 0;
|
||||
GenerateMipMaps = options.GenerateMipMaps != 0;
|
||||
FlipY = options.FlipY != 0;
|
||||
Resize = options.Resize != 0;
|
||||
PreserveAlphaCoverage = options.PreserveAlphaCoverage != 0;
|
||||
PreserveAlphaCoverageReference = options.PreserveAlphaCoverageReference;
|
||||
Scale = options.Scale;
|
||||
MaxSize = ConvertMaxSize(options.MaxSize);
|
||||
Size = options.Size;
|
||||
if (options.SpriteAreas != null)
|
||||
{
|
||||
int spritesCount = options.SpriteAreas.Length;
|
||||
Sprites.Capacity = spritesCount;
|
||||
for (int i = 0; i < spritesCount; i++)
|
||||
{
|
||||
Sprites.Add(new SpriteInfo(options.SpriteAreas[i], options.SpriteNames[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries the restore the asset import options from the target resource file.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <param name="assetPath">The asset path.</param>
|
||||
/// <returns>True settings has been restored, otherwise false.</returns>
|
||||
public static bool TryRestore(ref TextureImportSettings options, string assetPath)
|
||||
{
|
||||
if (TextureImportEntry.Internal_GetTextureImportOptions(assetPath, out var internalOptions))
|
||||
{
|
||||
// Restore settings
|
||||
options.FromInternal(ref internalOptions);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Texture asset import entry.
|
||||
/// </summary>
|
||||
/// <seealso cref="AssetImportEntry" />
|
||||
public class TextureImportEntry : AssetImportEntry
|
||||
{
|
||||
private TextureImportSettings _settings = new TextureImportSettings();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TextureImportEntry"/> class.
|
||||
/// </summary>
|
||||
/// <param name="request">The import request.</param>
|
||||
public TextureImportEntry(ref Request request)
|
||||
: base(ref request)
|
||||
{
|
||||
// Try to guess format type based on file name
|
||||
var snl = System.IO.Path.GetFileNameWithoutExtension(SourceUrl).ToLower();
|
||||
var extension = System.IO.Path.GetExtension(SourceUrl).ToLower();
|
||||
if (extension == ".raw")
|
||||
{
|
||||
// Raw image data in 16bit gray-scale, preserve the quality
|
||||
_settings.Type = TextureImportSettings.CustomTextureFormatType.HdrRGBA;
|
||||
_settings.Compress = false;
|
||||
}
|
||||
else if (extension == ".hdr")
|
||||
{
|
||||
// HDR sky texture
|
||||
_settings.Type = TextureImportSettings.CustomTextureFormatType.HdrRGB;
|
||||
}
|
||||
else if (_settings.Type != TextureImportSettings.CustomTextureFormatType.ColorRGB)
|
||||
{
|
||||
// Skip checking
|
||||
}
|
||||
else if (snl.EndsWith("_n")
|
||||
|| snl.EndsWith("nrm")
|
||||
|| snl.EndsWith("nm")
|
||||
|| snl.EndsWith("norm")
|
||||
|| snl.Contains("normal")
|
||||
|| snl.EndsWith("normals"))
|
||||
{
|
||||
// Normal map
|
||||
_settings.Type = TextureImportSettings.CustomTextureFormatType.NormalMap;
|
||||
}
|
||||
else if (snl.EndsWith("_d")
|
||||
|| snl.Contains("diffuse")
|
||||
|| snl.Contains("diff")
|
||||
|| snl.Contains("color")
|
||||
|| snl.Contains("_col")
|
||||
|| snl.Contains("basecolor")
|
||||
|| snl.Contains("albedo"))
|
||||
{
|
||||
// Albedo or diffuse map
|
||||
_settings.Type = TextureImportSettings.CustomTextureFormatType.ColorRGB;
|
||||
_settings.sRGB = true;
|
||||
}
|
||||
else if (snl.EndsWith("ao")
|
||||
|| snl.EndsWith("ambientocclusion")
|
||||
|| snl.EndsWith("gloss")
|
||||
|| snl.EndsWith("_r")
|
||||
|| snl.EndsWith("_displ")
|
||||
|| snl.EndsWith("_disp")
|
||||
|| snl.EndsWith("roughness")
|
||||
|| snl.EndsWith("_rgh")
|
||||
|| snl.EndsWith("_met")
|
||||
|| snl.EndsWith("metalness")
|
||||
|| snl.EndsWith("displacement")
|
||||
|| snl.EndsWith("spec")
|
||||
|| snl.EndsWith("specular")
|
||||
|| snl.EndsWith("occlusion")
|
||||
|| snl.EndsWith("height")
|
||||
|| snl.EndsWith("heights")
|
||||
|| snl.EndsWith("cavity")
|
||||
|| snl.EndsWith("metalic")
|
||||
|| snl.EndsWith("metallic"))
|
||||
{
|
||||
// Glossiness, metalness, ambient occlusion, displacement, height, cavity or specular
|
||||
_settings.Type = TextureImportSettings.CustomTextureFormatType.GrayScale;
|
||||
}
|
||||
|
||||
// Try to restore target asset texture import options (useful for fast reimport)
|
||||
TextureImportSettings.TryRestore(ref _settings, ResultUrl);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object Settings => _settings;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool TryOverrideSettings(object settings)
|
||||
{
|
||||
if (settings is TextureImportSettings o)
|
||||
{
|
||||
_settings = o;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Import()
|
||||
{
|
||||
return Editor.Import(SourceUrl, ResultUrl, _settings);
|
||||
}
|
||||
|
||||
#region Internal Calls
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal static extern bool Internal_GetTextureImportOptions(string path, out TextureImportSettings.InternalOptions result);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
126
Source/Editor/Content/Items/AssetItem.cs
Normal file
126
Source/Editor/Content/Items/AssetItem.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Asset item object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.ContentItem" />
|
||||
[HideInEditor]
|
||||
public abstract class AssetItem : ContentItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the asset unique identifier.
|
||||
/// </summary>
|
||||
public Guid ID { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the asset type identifier.
|
||||
/// </summary>
|
||||
public string TypeName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssetItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The asset path.</param>
|
||||
/// <param name="typeName">The asset type name.</param>
|
||||
/// <param name="id">The asset identifier.</param>
|
||||
protected AssetItem(string path, string typeName, ref Guid id)
|
||||
: base(path)
|
||||
{
|
||||
TypeName = typeName;
|
||||
ID = id;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void UpdateTooltipText()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
OnBuildTooltipText(sb);
|
||||
TooltipText = sb.ToString();
|
||||
}
|
||||
|
||||
private sealed class TooltipDoubleClickHook : Control
|
||||
{
|
||||
public AssetItem Item;
|
||||
|
||||
public TooltipDoubleClickHook()
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
Offsets = Margin.Zero;
|
||||
}
|
||||
|
||||
public override bool OnMouseDoubleClick(Vector2 location, MouseButton button)
|
||||
{
|
||||
return Item.OnMouseDoubleClick(Item.ScreenToClient(ClientToScreen(location)), button);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnTooltipShown(Tooltip tooltip)
|
||||
{
|
||||
base.OnTooltipShown(tooltip);
|
||||
|
||||
// Inject the hook control for the double-click event (if user double-clicks on tooltip over the asset item it will open that item - helps on small screens)
|
||||
var hook = tooltip.GetChild<TooltipDoubleClickHook>();
|
||||
if (hook == null)
|
||||
hook = tooltip.AddChild<TooltipDoubleClickHook>();
|
||||
hook.Item = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when building tooltip text.
|
||||
/// </summary>
|
||||
/// <param name="sb">The String Builder.</param>
|
||||
protected virtual void OnBuildTooltipText(StringBuilder sb)
|
||||
{
|
||||
sb.Append("Type: ").Append(TypeName).AppendLine();
|
||||
sb.Append("Size: ").Append(Utilities.Utils.FormatBytesCount((int)new FileInfo(Path).Length)).AppendLine();
|
||||
sb.Append("Path: ").Append(Path).AppendLine();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItemType ItemType => ContentItemType.Asset;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether asset is of the specified type (included inheritance checks).
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to check.</typeparam>
|
||||
/// <returns><c>true</c> if asset is of the specified type (including inherited types); otherwise, <c>false</c>.</returns>
|
||||
public bool IsOfType<T>()
|
||||
{
|
||||
return IsOfType(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether asset is of the specified type (included inheritance checks).
|
||||
/// </summary>
|
||||
/// <param name="type">The type to check.</param>
|
||||
/// <returns><c>true</c> if asset is of the specified type (including inherited types); otherwise, <c>false</c>.</returns>
|
||||
public virtual bool IsOfType(Type type)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool DrawShadow => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItem Find(Guid id)
|
||||
{
|
||||
return id == ID ? this : null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return Path + ":" + ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
181
Source/Editor/Content/Items/BinaryAssetItem.cs
Normal file
181
Source/Editor/Content/Items/BinaryAssetItem.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents binary asset item.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.AssetItem" />
|
||||
public class BinaryAssetItem : AssetItem
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of the asset (the same as <see cref="AssetItem.TypeName"/> but cached as type reference).
|
||||
/// </summary>
|
||||
public readonly Type Type;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BinaryAssetItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The asset path.</param>
|
||||
/// <param name="id">The asset identifier.</param>
|
||||
/// <param name="typeName">The asset type name identifier.</param>
|
||||
/// <param name="type">The asset type.</param>
|
||||
/// <param name="searchFilter">The asset type search filter type.</param>
|
||||
public BinaryAssetItem(string path, ref Guid id, string typeName, Type type, ContentItemSearchFilter searchFilter)
|
||||
: base(path, typeName, ref id)
|
||||
{
|
||||
Type = type;
|
||||
SearchFilter = searchFilter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the asset import path.
|
||||
/// </summary>
|
||||
/// <param name="importPath">The import path.</param>
|
||||
/// <returns>True if fails, otherwise false.</returns>
|
||||
public bool GetImportPath(out string importPath)
|
||||
{
|
||||
// TODO: add internal call to content backend with fast import asset metadata gather (without asset loading)
|
||||
|
||||
var asset = FlaxEngine.Content.Load<BinaryAsset>(ID, 100);
|
||||
if (asset)
|
||||
{
|
||||
// Get meta from loaded asset
|
||||
importPath = asset.ImportPath;
|
||||
return string.IsNullOrEmpty(importPath);
|
||||
}
|
||||
|
||||
importPath = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void OnReimport(ref Guid id)
|
||||
{
|
||||
ID = id;
|
||||
OnReimport();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItemSearchFilter SearchFilter { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsOfType(Type type)
|
||||
{
|
||||
return type.IsAssignableFrom(Type);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of <see cref="BinaryAssetItem"/> for <see cref="TextureBase"/> assets.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetItem" />
|
||||
public class TextureAssetItem : BinaryAssetItem
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public TextureAssetItem(string path, ref Guid id, string typeName, Type type)
|
||||
: base(path, ref id, typeName, type, ContentItemSearchFilter.Texture)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnBuildTooltipText(StringBuilder sb)
|
||||
{
|
||||
base.OnBuildTooltipText(sb);
|
||||
|
||||
var asset = FlaxEngine.Content.Load<TextureBase>(ID, 100);
|
||||
if (asset)
|
||||
{
|
||||
sb.Append("Format: ").Append(asset.Format).AppendLine();
|
||||
sb.Append("Size: ").Append(asset.Width).Append('x').Append(asset.Height);
|
||||
if (asset.ArraySize != 1)
|
||||
sb.Append('[').Append(asset.ArraySize).Append(']');
|
||||
sb.AppendLine();
|
||||
sb.Append("Mip Levels: ").Append(asset.MipLevels).AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of <see cref="BinaryAssetItem"/> for <see cref="Model"/> assets.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetItem" />
|
||||
public class ModelAssetItem : BinaryAssetItem
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public ModelAssetItem(string path, ref Guid id, string typeName, Type type)
|
||||
: base(path, ref id, typeName, type, ContentItemSearchFilter.Model)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnBuildTooltipText(StringBuilder sb)
|
||||
{
|
||||
base.OnBuildTooltipText(sb);
|
||||
|
||||
var asset = FlaxEngine.Content.Load<Model>(ID, 100);
|
||||
if (asset)
|
||||
{
|
||||
var lods = asset.LODs;
|
||||
int triangleCount = 0, vertexCount = 0;
|
||||
for (int lodIndex = 0; lodIndex < lods.Length; lodIndex++)
|
||||
{
|
||||
var lod = lods[lodIndex];
|
||||
for (int meshIndex = 0; meshIndex < lod.Meshes.Length; meshIndex++)
|
||||
{
|
||||
var mesh = lod.Meshes[meshIndex];
|
||||
triangleCount += mesh.TriangleCount;
|
||||
vertexCount += mesh.VertexCount;
|
||||
}
|
||||
}
|
||||
sb.Append("LODs: ").Append(lods.Length).AppendLine();
|
||||
sb.Append("Triangles: ").Append(triangleCount.ToString("N0")).AppendLine();
|
||||
sb.Append("Vertices: ").Append(vertexCount.ToString("N0")).AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of <see cref="BinaryAssetItem"/> for <see cref="SkinnedModel"/> assets.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetItem" />
|
||||
public class SkinnedModelAssetItem : BinaryAssetItem
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public SkinnedModelAssetItem(string path, ref Guid id, string typeName, Type type)
|
||||
: base(path, ref id, typeName, type, ContentItemSearchFilter.Model)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnBuildTooltipText(StringBuilder sb)
|
||||
{
|
||||
base.OnBuildTooltipText(sb);
|
||||
|
||||
var asset = FlaxEngine.Content.Load<SkinnedModel>(ID, 100);
|
||||
if (asset)
|
||||
{
|
||||
var lods = asset.LODs;
|
||||
int triangleCount = 0, vertexCount = 0;
|
||||
for (int lodIndex = 0; lodIndex < lods.Length; lodIndex++)
|
||||
{
|
||||
var lod = lods[lodIndex];
|
||||
for (int meshIndex = 0; meshIndex < lod.Meshes.Length; meshIndex++)
|
||||
{
|
||||
var mesh = lod.Meshes[meshIndex];
|
||||
triangleCount += mesh.TriangleCount;
|
||||
vertexCount += mesh.VertexCount;
|
||||
}
|
||||
}
|
||||
sb.Append("LODs: ").Append(lods.Length).AppendLine();
|
||||
sb.Append("Triangles: ").Append(triangleCount.ToString("N0")).AppendLine();
|
||||
sb.Append("Vertices: ").Append(vertexCount.ToString("N0")).AppendLine();
|
||||
sb.Append("Skeleton Nodes: ").Append(asset.Nodes.Length).AppendLine();
|
||||
sb.Append("Blend Shapes: ").Append(asset.BlendShapes.Length).AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Source/Editor/Content/Items/CSharpScriptItem.cs
Normal file
25
Source/Editor/Content/Items/CSharpScriptItem.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Content item that contains C# script file with source code.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.ScriptItem" />
|
||||
public class CSharpScriptItem : ScriptItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CSharpScriptItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the item.</param>
|
||||
public CSharpScriptItem(string path)
|
||||
: base(path)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.CSharpScript64;
|
||||
}
|
||||
}
|
||||
305
Source/Editor/Content/Items/ContentFolder.cs
Normal file
305
Source/Editor/Content/Items/ContentFolder.cs
Normal file
@@ -0,0 +1,305 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using FlaxEditor.GUI.Drag;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Types of content directories.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public enum ContentFolderType
|
||||
{
|
||||
/// <summary>
|
||||
/// The directory with assets.
|
||||
/// </summary>
|
||||
Content,
|
||||
|
||||
/// <summary>
|
||||
/// The directory with source files.
|
||||
/// </summary>
|
||||
Source,
|
||||
|
||||
/// <summary>
|
||||
/// The other type of directory.
|
||||
/// </summary>
|
||||
Other,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents workspace directory item.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public class ContentFolder : ContentItem
|
||||
{
|
||||
private DragItems _dragOverItems;
|
||||
private bool _validDragOver;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the folder.
|
||||
/// </summary>
|
||||
public ContentFolderType FolderType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if that folder can import/manage scripts.
|
||||
/// </summary>
|
||||
public bool CanHaveScripts => FolderType == ContentFolderType.Source;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if that folder can import/manage assets.
|
||||
/// </summary>
|
||||
public bool CanHaveAssets => FolderType == ContentFolderType.Content;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content node.
|
||||
/// </summary>
|
||||
public ContentTreeNode Node { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The subitems of this folder.
|
||||
/// </summary>
|
||||
public readonly List<ContentItem> Children = new List<ContentItem>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentFolder"/> class.
|
||||
/// </summary>
|
||||
/// <param name="type">The folder type.</param>
|
||||
/// <param name="path">The path to the item.</param>
|
||||
/// <param name="node">The folder parent node.</param>
|
||||
internal ContentFolder(ContentFolderType type, string path, ContentTreeNode node)
|
||||
: base(path)
|
||||
{
|
||||
FolderType = type;
|
||||
Node = node;
|
||||
ShortName = System.IO.Path.GetFileName(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find child element with given path
|
||||
/// </summary>
|
||||
/// <param name="path">Element path to find</param>
|
||||
/// <returns>Found element of null</returns>
|
||||
public ContentItem FindChild(string path)
|
||||
{
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
if (Children[i].Path == path)
|
||||
return Children[i];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if folder contains child element with given path
|
||||
/// </summary>
|
||||
/// <param name="path">Element path to find</param>
|
||||
/// <returns>True if contains that element, otherwise false</returns>
|
||||
public bool ContainsChild(string path)
|
||||
{
|
||||
return FindChild(path) != null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItemType ItemType => ContentItemType.Folder;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItemSearchFilter SearchFilter => ContentItemSearchFilter.Other;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanRename => ParentFolder != null; // Deny rename action for root folders
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrag => ParentFolder != null; // Deny rename action for root folders
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Exists => Directory.Exists(Path);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.Folder64;
|
||||
|
||||
/// <inheritdoc />
|
||||
internal override void UpdatePath(string value)
|
||||
{
|
||||
base.UpdatePath(value);
|
||||
|
||||
ShortName = System.IO.Path.GetFileName(value);
|
||||
|
||||
// Update node text
|
||||
Node.Text = ShortName;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void UpdateTooltipText()
|
||||
{
|
||||
TooltipText = Path;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnParentFolderChanged()
|
||||
{
|
||||
// Update tree nodes structure
|
||||
Node.Parent = ParentFolder?.Node;
|
||||
|
||||
base.OnParentFolderChanged();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItem Find(string path)
|
||||
{
|
||||
// TODO: split name into parts and check each going tree structure level down - make it faster
|
||||
|
||||
if (Path == path)
|
||||
return this;
|
||||
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
var result = Children[i].Find(path);
|
||||
if (result != null)
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Find(ContentItem item)
|
||||
{
|
||||
if (item == this)
|
||||
return true;
|
||||
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
if (Children[i].Find(item))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItem Find(Guid id)
|
||||
{
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
var result = Children[i].Find(id);
|
||||
if (result != null)
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ScriptItem FindScriptWitScriptName(string scriptName)
|
||||
{
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
var result = Children[i].FindScriptWitScriptName(scriptName);
|
||||
if (result != null)
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Compare(Control other)
|
||||
{
|
||||
if (other is ContentItem otherItem)
|
||||
{
|
||||
if (!otherItem.IsFolder)
|
||||
return -1;
|
||||
return string.Compare(ShortName, otherItem.ShortName, StringComparison.InvariantCulture);
|
||||
}
|
||||
|
||||
return base.Compare(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
// Check if drag is over
|
||||
if (IsDragOver && _validDragOver)
|
||||
Render2D.FillRectangle(new Rectangle(Vector2.Zero, Size), Style.Current.BackgroundSelected * 0.6f);
|
||||
}
|
||||
|
||||
private bool ValidateDragItem(ContentItem item)
|
||||
{
|
||||
// Reject itself and any parent
|
||||
return item != this && !item.Find(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragEnter(ref Vector2 location, DragData data)
|
||||
{
|
||||
base.OnDragEnter(ref location, data);
|
||||
|
||||
// Check if drop file(s)
|
||||
if (data is DragDataFiles)
|
||||
{
|
||||
_validDragOver = true;
|
||||
return DragDropEffect.Copy;
|
||||
}
|
||||
|
||||
// Check if drop asset(s)
|
||||
if (_dragOverItems == null)
|
||||
_dragOverItems = new DragItems(ValidateDragItem);
|
||||
_dragOverItems.OnDragEnter(data);
|
||||
_validDragOver = _dragOverItems.HasValidDrag;
|
||||
return _dragOverItems.Effect;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragMove(ref Vector2 location, DragData data)
|
||||
{
|
||||
base.OnDragMove(ref location, data);
|
||||
|
||||
if (data is DragDataFiles)
|
||||
return DragDropEffect.Copy;
|
||||
return _dragOverItems.Effect;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragDrop(ref Vector2 location, DragData data)
|
||||
{
|
||||
var result = base.OnDragDrop(ref location, data);
|
||||
|
||||
// Check if drop file(s)
|
||||
if (data is DragDataFiles files)
|
||||
{
|
||||
// Import files
|
||||
Editor.Instance.ContentImporting.Import(files.Files, this);
|
||||
result = DragDropEffect.Copy;
|
||||
}
|
||||
else if (_dragOverItems.HasValidDrag)
|
||||
{
|
||||
// Move items
|
||||
Editor.Instance.ContentDatabase.Move(_dragOverItems.Objects, this);
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
|
||||
// Clear cache
|
||||
_dragOverItems?.OnDragDrop();
|
||||
_validDragOver = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
_dragOverItems?.OnDragLeave();
|
||||
_validDragOver = false;
|
||||
|
||||
base.OnDragLeave();
|
||||
}
|
||||
}
|
||||
}
|
||||
772
Source/Editor/Content/Items/ContentItem.cs
Normal file
772
Source/Editor/Content/Items/ContentItem.cs
Normal file
@@ -0,0 +1,772 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.Content.GUI;
|
||||
using FlaxEditor.GUI.Drag;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Assertions;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Content item types.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public enum ContentItemType
|
||||
{
|
||||
/// <summary>
|
||||
/// The binary or text asset.
|
||||
/// </summary>
|
||||
Asset,
|
||||
|
||||
/// <summary>
|
||||
/// The directory.
|
||||
/// </summary>
|
||||
Folder,
|
||||
|
||||
/// <summary>
|
||||
/// The script file.
|
||||
/// </summary>
|
||||
Script,
|
||||
|
||||
/// <summary>
|
||||
/// The scene file.
|
||||
/// </summary>
|
||||
Scene,
|
||||
|
||||
/// <summary>
|
||||
/// The other type.
|
||||
/// </summary>
|
||||
Other,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Content item filter types used for searching.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public enum ContentItemSearchFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// The model.
|
||||
/// </summary>
|
||||
Model,
|
||||
|
||||
/// <summary>
|
||||
/// The skinned model.
|
||||
/// </summary>
|
||||
SkinnedModel,
|
||||
|
||||
/// <summary>
|
||||
/// The material.
|
||||
/// </summary>
|
||||
Material,
|
||||
|
||||
/// <summary>
|
||||
/// The texture.
|
||||
/// </summary>
|
||||
Texture,
|
||||
|
||||
/// <summary>
|
||||
/// The scene.
|
||||
/// </summary>
|
||||
Scene,
|
||||
|
||||
/// <summary>
|
||||
/// The prefab.
|
||||
/// </summary>
|
||||
Prefab,
|
||||
|
||||
/// <summary>
|
||||
/// The script.
|
||||
/// </summary>
|
||||
Script,
|
||||
|
||||
/// <summary>
|
||||
/// The audio.
|
||||
/// </summary>
|
||||
Audio,
|
||||
|
||||
/// <summary>
|
||||
/// The animation.
|
||||
/// </summary>
|
||||
Animation,
|
||||
|
||||
/// <summary>
|
||||
/// The json.
|
||||
/// </summary>
|
||||
Json,
|
||||
|
||||
/// <summary>
|
||||
/// The particles.
|
||||
/// </summary>
|
||||
Particles,
|
||||
|
||||
/// <summary>
|
||||
/// The shader source files.
|
||||
/// </summary>
|
||||
Shader,
|
||||
|
||||
/// <summary>
|
||||
/// The other.
|
||||
/// </summary>
|
||||
Other,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for objects that can reference the content items in order to receive events from them.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public interface IContentItemOwner
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when referenced item gets deleted (asset unloaded, file deleted, etc.).
|
||||
/// Item should not be used after that.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
void OnItemDeleted(ContentItem item);
|
||||
|
||||
/// <summary>
|
||||
/// Called when referenced item gets renamed (filename change, path change, etc.)
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
void OnItemRenamed(ContentItem item);
|
||||
|
||||
/// <summary>
|
||||
/// Called when item gets reimported or reloaded.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
void OnItemReimported(ContentItem item);
|
||||
|
||||
/// <summary>
|
||||
/// Called when referenced item gets disposed (editor closing, database internal changes, etc.).
|
||||
/// Item should not be used after that.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
void OnItemDispose(ContentItem item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all content items.
|
||||
/// Item parent GUI control is always <see cref="ContentView"/> or null if not in a view.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.Control" />
|
||||
[HideInEditor]
|
||||
public abstract class ContentItem : Control
|
||||
{
|
||||
/// <summary>
|
||||
/// The default margin size.
|
||||
/// </summary>
|
||||
public const int DefaultMarginSize = 4;
|
||||
|
||||
/// <summary>
|
||||
/// The default text height.
|
||||
/// </summary>
|
||||
public const int DefaultTextHeight = 42;
|
||||
|
||||
/// <summary>
|
||||
/// The default thumbnail size.
|
||||
/// </summary>
|
||||
public const int DefaultThumbnailSize = PreviewsCache.AssetIconSize;
|
||||
|
||||
/// <summary>
|
||||
/// The default width.
|
||||
/// </summary>
|
||||
public const int DefaultWidth = (DefaultThumbnailSize + 2 * DefaultMarginSize);
|
||||
|
||||
/// <summary>
|
||||
/// The default height.
|
||||
/// </summary>
|
||||
public const int DefaultHeight = (DefaultThumbnailSize + 2 * DefaultMarginSize + DefaultTextHeight);
|
||||
|
||||
private ContentFolder _parentFolder;
|
||||
|
||||
private bool _isMouseDown;
|
||||
private Vector2 _mouseDownStartPos;
|
||||
private readonly List<IContentItemOwner> _references = new List<IContentItemOwner>(4);
|
||||
|
||||
private SpriteHandle _thumbnail;
|
||||
private SpriteHandle _shadowIcon;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the item.
|
||||
/// </summary>
|
||||
public abstract ContentItemType ItemType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the item searching filter to use.
|
||||
/// </summary>
|
||||
public abstract ContentItemSearchFilter SearchFilter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is asset.
|
||||
/// </summary>
|
||||
public bool IsAsset => ItemType == ContentItemType.Asset;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is folder.
|
||||
/// </summary>
|
||||
public bool IsFolder => ItemType == ContentItemType.Folder;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance can have children.
|
||||
/// </summary>
|
||||
public bool CanHaveChildren => ItemType == ContentItemType.Folder;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this item can be renamed.
|
||||
/// </summary>
|
||||
public virtual bool CanRename => true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this item can be dragged and dropped.
|
||||
/// </summary>
|
||||
public virtual bool CanDrag => true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this <see cref="ContentItem"/> exists on drive.
|
||||
/// </summary>
|
||||
public virtual bool Exists => System.IO.File.Exists(Path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent folder.
|
||||
/// </summary>
|
||||
public ContentFolder ParentFolder
|
||||
{
|
||||
get => _parentFolder;
|
||||
set
|
||||
{
|
||||
if (_parentFolder == value)
|
||||
return;
|
||||
|
||||
// Remove from old
|
||||
_parentFolder?.Children.Remove(this);
|
||||
|
||||
// Link
|
||||
_parentFolder = value;
|
||||
|
||||
// Add to new
|
||||
_parentFolder?.Children.Add(this);
|
||||
|
||||
OnParentFolderChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the item.
|
||||
/// </summary>
|
||||
public string Path { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item file name (filename with extension).
|
||||
/// </summary>
|
||||
public string FileName { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item short name (filename without extension).
|
||||
/// </summary>
|
||||
public string ShortName { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the asset name relative to the project root folder (without asset file extension)
|
||||
/// </summary>
|
||||
public string NamePath
|
||||
{
|
||||
get
|
||||
{
|
||||
string result = Path;
|
||||
if (result.StartsWith(Globals.ProjectFolder))
|
||||
{
|
||||
result = result.Substring(Globals.ProjectFolder.Length + 1);
|
||||
}
|
||||
return StringUtils.GetPathWithoutExtension(result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default name of the content item thumbnail. Returns null if not used.
|
||||
/// </summary>
|
||||
public virtual SpriteHandle DefaultThumbnail => SpriteHandle.Invalid;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this item has default thumbnail.
|
||||
/// </summary>
|
||||
public bool HasDefaultThumbnail => DefaultThumbnail.IsValid;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the item thumbnail. Warning, thumbnail may not be available if item has no references (<see cref="ReferencesCount"/>).
|
||||
/// </summary>
|
||||
public SpriteHandle Thumbnail
|
||||
{
|
||||
get => _thumbnail;
|
||||
set => _thumbnail = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if force show file extension.
|
||||
/// </summary>
|
||||
public bool ShowFileExtension;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the item.</param>
|
||||
protected ContentItem(string path)
|
||||
: base(0, 0, DefaultWidth, DefaultHeight)
|
||||
{
|
||||
// Set path
|
||||
Path = path;
|
||||
FileName = System.IO.Path.GetFileName(path);
|
||||
ShortName = System.IO.Path.GetFileNameWithoutExtension(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the item path. Use with caution or even don't use it. It's dangerous.
|
||||
/// </summary>
|
||||
/// <param name="value">The new path.</param>
|
||||
internal virtual void UpdatePath(string value)
|
||||
{
|
||||
Assert.AreNotEqual(Path, value);
|
||||
|
||||
// Set path
|
||||
Path = StringUtils.NormalizePath(value);
|
||||
FileName = System.IO.Path.GetFileName(value);
|
||||
ShortName = System.IO.Path.GetFileNameWithoutExtension(value);
|
||||
|
||||
// Fire event
|
||||
OnPathChanged();
|
||||
for (int i = 0; i < _references.Count; i++)
|
||||
{
|
||||
_references[i].OnItemRenamed(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the item thumbnail.
|
||||
/// </summary>
|
||||
public virtual void RefreshThumbnail()
|
||||
{
|
||||
// Skip if item has default thumbnail
|
||||
if (HasDefaultThumbnail)
|
||||
return;
|
||||
|
||||
var thumbnails = Editor.Instance.Thumbnails;
|
||||
|
||||
// Delete old thumbnail and remove it from the cache
|
||||
thumbnails.DeletePreview(this);
|
||||
|
||||
// Request new one (if need to)
|
||||
if (_references.Count > 0)
|
||||
{
|
||||
thumbnails.RequestPreview(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the tooltip text text.
|
||||
/// </summary>
|
||||
protected virtual void UpdateTooltipText()
|
||||
{
|
||||
TooltipText = "Path: " + Path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the item at the specified path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>Found item or null if missing.</returns>
|
||||
public virtual ContentItem Find(string path)
|
||||
{
|
||||
return Path == path ? this : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find a specified item in the assets tree.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>True if has been found, otherwise false.</returns>
|
||||
public virtual bool Find(ContentItem item)
|
||||
{
|
||||
return this == item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the item with the specified id.
|
||||
/// </summary>
|
||||
/// <param name="id">The id.</param>
|
||||
/// <returns>Found item or null if missing.</returns>
|
||||
public virtual ContentItem Find(Guid id)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find script with the given name.
|
||||
/// </summary>
|
||||
/// <param name="scriptName">Name of the script.</param>
|
||||
/// <returns>Found script or null if missing.</returns>
|
||||
public virtual ScriptItem FindScriptWitScriptName(string scriptName)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether draw item shadow.
|
||||
/// </summary>
|
||||
protected virtual bool DrawShadow => false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the local space rectangle for element name text area.
|
||||
/// </summary>
|
||||
public Rectangle TextRectangle
|
||||
{
|
||||
get
|
||||
{
|
||||
var view = Parent as ContentView;
|
||||
var size = Size;
|
||||
switch (view?.ViewType ?? ContentViewType.Tiles)
|
||||
{
|
||||
case ContentViewType.Tiles:
|
||||
{
|
||||
var textHeight = DefaultTextHeight * size.X / DefaultWidth;
|
||||
return new Rectangle(0, size.Y - textHeight, size.X, textHeight);
|
||||
}
|
||||
case ContentViewType.List:
|
||||
{
|
||||
var thumbnailSize = size.Y - 2 * DefaultMarginSize;
|
||||
var textHeight = Mathf.Min(size.Y, 24.0f);
|
||||
return new Rectangle(thumbnailSize + DefaultMarginSize * 2, (size.Y - textHeight) * 0.5f, size.X - textHeight - DefaultMarginSize * 3.0f, textHeight);
|
||||
}
|
||||
default: throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws the item thumbnail.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">The thumbnail rectangle.</param>
|
||||
public void DrawThumbnail(ref Rectangle rectangle)
|
||||
{
|
||||
// Draw shadow
|
||||
if (DrawShadow)
|
||||
{
|
||||
const float thumbnailInShadowSize = 50.0f;
|
||||
var shadowRect = rectangle.MakeExpanded((DefaultThumbnailSize - thumbnailInShadowSize) * rectangle.Width / DefaultThumbnailSize * 1.3f);
|
||||
if (!_shadowIcon.IsValid)
|
||||
_shadowIcon = Editor.Instance.Icons.AssetShadow;
|
||||
Render2D.DrawSprite(_shadowIcon, shadowRect);
|
||||
}
|
||||
|
||||
// Draw thumbnail
|
||||
if (_thumbnail.IsValid)
|
||||
Render2D.DrawSprite(_thumbnail, rectangle);
|
||||
else
|
||||
Render2D.FillRectangle(rectangle, Color.Black);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of references to that item.
|
||||
/// </summary>
|
||||
public int ReferencesCount => _references.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Adds the reference to the item.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
public void AddReference(IContentItemOwner obj)
|
||||
{
|
||||
Assert.IsNotNull(obj);
|
||||
Assert.IsFalse(_references.Contains(obj));
|
||||
|
||||
_references.Add(obj);
|
||||
|
||||
// Check if need to generate preview
|
||||
if (_references.Count == 1 && !_thumbnail.IsValid)
|
||||
{
|
||||
RequestThumbnail();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the reference from the item.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
public void RemoveReference(IContentItemOwner obj)
|
||||
{
|
||||
if (_references.Remove(obj))
|
||||
{
|
||||
// Check if need to release the preview
|
||||
if (_references.Count == 0 && _thumbnail.IsValid)
|
||||
{
|
||||
ReleaseThumbnail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when item gets renamed or location gets changed (path modification).
|
||||
/// </summary>
|
||||
public virtual void OnPathChanged()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when content item gets removed (by the user or externally).
|
||||
/// </summary>
|
||||
public virtual void OnDelete()
|
||||
{
|
||||
// Fire event
|
||||
while (_references.Count > 0)
|
||||
{
|
||||
var reference = _references[0];
|
||||
reference.OnItemDeleted(this);
|
||||
RemoveReference(reference);
|
||||
}
|
||||
|
||||
// Release thumbnail
|
||||
if (_thumbnail.IsValid)
|
||||
{
|
||||
ReleaseThumbnail();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when item parent folder gets changed.
|
||||
/// </summary>
|
||||
protected virtual void OnParentFolderChanged()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requests the thumbnail.
|
||||
/// </summary>
|
||||
protected void RequestThumbnail()
|
||||
{
|
||||
Editor.Instance.Thumbnails.RequestPreview(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the thumbnail.
|
||||
/// </summary>
|
||||
protected void ReleaseThumbnail()
|
||||
{
|
||||
// Simply unlink sprite
|
||||
_thumbnail = SpriteHandle.Invalid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when item gets reimported or reloaded.
|
||||
/// </summary>
|
||||
protected virtual void OnReimport()
|
||||
{
|
||||
for (int i = 0; i < _references.Count; i++)
|
||||
_references[i].OnItemReimported(this);
|
||||
RefreshThumbnail();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the drag and drop operation with this asset.
|
||||
/// </summary>
|
||||
protected virtual void DoDrag()
|
||||
{
|
||||
if (!CanDrag)
|
||||
return;
|
||||
|
||||
DragData data;
|
||||
|
||||
// Check if is selected
|
||||
if (Parent is ContentView view && view.IsSelected(this))
|
||||
{
|
||||
// Drag selected item
|
||||
data = DragItems.GetDragData(view.Selection);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Drag single item
|
||||
data = DragItems.GetDragData(this);
|
||||
}
|
||||
|
||||
// Start drag operation
|
||||
DoDragDrop(data);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool ShowTooltip => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnShowTooltip(out string text, out Vector2 location, out Rectangle area)
|
||||
{
|
||||
UpdateTooltipText();
|
||||
var result = base.OnShowTooltip(out text, out location, out area);
|
||||
location = Size * new Vector2(0.9f, 0.5f);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
// Cache data
|
||||
var size = Size;
|
||||
var style = Style.Current;
|
||||
var view = Parent as ContentView;
|
||||
var isSelected = view.IsSelected(this);
|
||||
var clientRect = new Rectangle(Vector2.Zero, size);
|
||||
var textRect = TextRectangle;
|
||||
Rectangle thumbnailRect;
|
||||
TextAlignment nameAlignment;
|
||||
switch (view.ViewType)
|
||||
{
|
||||
case ContentViewType.Tiles:
|
||||
{
|
||||
var thumbnailSize = size.X - 2 * DefaultMarginSize;
|
||||
thumbnailRect = new Rectangle(DefaultMarginSize, DefaultMarginSize, thumbnailSize, thumbnailSize);
|
||||
nameAlignment = TextAlignment.Center;
|
||||
break;
|
||||
}
|
||||
case ContentViewType.List:
|
||||
{
|
||||
var thumbnailSize = size.Y - 2 * DefaultMarginSize;
|
||||
thumbnailRect = new Rectangle(DefaultMarginSize, DefaultMarginSize, thumbnailSize, thumbnailSize);
|
||||
nameAlignment = TextAlignment.Near;
|
||||
break;
|
||||
}
|
||||
default: throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
// Draw background
|
||||
if (isSelected)
|
||||
Render2D.FillRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground);
|
||||
else if (IsMouseOver)
|
||||
Render2D.FillRectangle(clientRect, style.BackgroundHighlighted);
|
||||
|
||||
// Draw preview
|
||||
DrawThumbnail(ref thumbnailRect);
|
||||
|
||||
// Draw short name
|
||||
Render2D.PushClip(ref textRect);
|
||||
Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 0.75f, 0.95f);
|
||||
Render2D.PopClip();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Vector2 location, MouseButton button)
|
||||
{
|
||||
Focus();
|
||||
|
||||
if (button == MouseButton.Left)
|
||||
{
|
||||
// Cache data
|
||||
_isMouseDown = true;
|
||||
_mouseDownStartPos = location;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Vector2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left && _isMouseDown)
|
||||
{
|
||||
// Clear flag
|
||||
_isMouseDown = false;
|
||||
|
||||
// Fire event
|
||||
(Parent as ContentView).OnItemClick(this);
|
||||
}
|
||||
|
||||
return base.OnMouseUp(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Vector2 location, MouseButton button)
|
||||
{
|
||||
Focus();
|
||||
|
||||
// Check if clicked on name area (and can be renamed)
|
||||
if (CanRename && TextRectangle.Contains(ref location))
|
||||
{
|
||||
// Rename
|
||||
(Parent as ContentView).OnItemDoubleClickName(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Open
|
||||
(Parent as ContentView).OnItemDoubleClick(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseMove(Vector2 location)
|
||||
{
|
||||
// Check if start drag and drop
|
||||
if (_isMouseDown && Vector2.Distance(_mouseDownStartPos, location) > 10.0f)
|
||||
{
|
||||
// Clear flag
|
||||
_isMouseDown = false;
|
||||
|
||||
// Start drag drop
|
||||
DoDrag();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
// Check if start drag and drop
|
||||
if (_isMouseDown)
|
||||
{
|
||||
// Clear flag
|
||||
_isMouseDown = false;
|
||||
|
||||
// Start drag drop
|
||||
DoDrag();
|
||||
}
|
||||
|
||||
base.OnMouseLeave();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Compare(Control other)
|
||||
{
|
||||
if (other is ContentItem otherItem)
|
||||
{
|
||||
if (otherItem.IsFolder)
|
||||
return 1;
|
||||
return string.Compare(ShortName, otherItem.ShortName, StringComparison.InvariantCulture);
|
||||
}
|
||||
|
||||
return base.Compare(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
// Fire event
|
||||
while (_references.Count > 0)
|
||||
{
|
||||
var reference = _references[0];
|
||||
reference.OnItemDispose(this);
|
||||
RemoveReference(reference);
|
||||
}
|
||||
|
||||
// Release thumbnail
|
||||
if (_thumbnail.IsValid)
|
||||
{
|
||||
ReleaseThumbnail();
|
||||
}
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return Path;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Source/Editor/Content/Items/CppScriptItem.cs
Normal file
25
Source/Editor/Content/Items/CppScriptItem.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Content item that contains C++ script file with source code.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.ScriptItem" />
|
||||
public class CppScriptItem : ScriptItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CppScriptItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the item.</param>
|
||||
public CppScriptItem(string path)
|
||||
: base(path)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.CppScript64;
|
||||
}
|
||||
}
|
||||
31
Source/Editor/Content/Items/FileItem.cs
Normal file
31
Source/Editor/Content/Items/FileItem.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Content item for the auxiliary files.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.ContentItem" />
|
||||
public class FileItem : ContentItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FileItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the file.</param>
|
||||
public FileItem(string path)
|
||||
: base(path)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItemType ItemType => ContentItemType.Other;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItemSearchFilter SearchFilter => ContentItemSearchFilter.Other;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.Document64;
|
||||
}
|
||||
}
|
||||
34
Source/Editor/Content/Items/JsonAssetItem.cs
Normal file
34
Source/Editor/Content/Items/JsonAssetItem.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Asset item stored in a Json format file.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.AssetItem" />
|
||||
public class JsonAssetItem : AssetItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonAssetItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <param name="typeName">Name of the resource type.</param>
|
||||
public JsonAssetItem(string path, Guid id, string typeName)
|
||||
: base(path, typeName, ref id)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItemSearchFilter SearchFilter => ContentItemSearchFilter.Json;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.Document64;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool DrawShadow => false;
|
||||
}
|
||||
}
|
||||
48
Source/Editor/Content/Items/NewItem.cs
Normal file
48
Source/Editor/Content/Items/NewItem.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper content item used to mock UI during creating new assets by <see cref="FlaxEditor.Windows.ContentWindow"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.ContentItem" />
|
||||
public sealed class NewItem : ContentItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the proxy object related to the created asset.
|
||||
/// </summary>
|
||||
public ContentProxy Proxy { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the argument passed to the proxy for the item creation. In most cases it is null.
|
||||
/// </summary>
|
||||
public object Argument { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NewItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path for the new item.</param>
|
||||
/// <param name="proxy">The content proxy object.</param>
|
||||
/// <param name="arg">The argument passed to the proxy for the item creation. In most cases it is null.</param>
|
||||
public NewItem(string path, ContentProxy proxy, object arg)
|
||||
: base(path)
|
||||
{
|
||||
Proxy = proxy;
|
||||
Argument = arg;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItemType ItemType => ContentItemType.Other;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItemSearchFilter SearchFilter => ContentItemSearchFilter.Other;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.Document64;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool DrawShadow => true;
|
||||
}
|
||||
}
|
||||
39
Source/Editor/Content/Items/PrefabItem.cs
Normal file
39
Source/Editor/Content/Items/PrefabItem.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Content item that contains <see cref="FlaxEngine.Prefab"/> data.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.JsonAssetItem" />
|
||||
public sealed class PrefabItem : JsonAssetItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PrefabItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The asset path.</param>
|
||||
/// <param name="id">The asset identifier.</param>
|
||||
public PrefabItem(string path, Guid id)
|
||||
: base(path, id, PrefabProxy.AssetTypename)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItemType ItemType => ContentItemType.Asset;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItemSearchFilter SearchFilter => ContentItemSearchFilter.Prefab;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SpriteHandle DefaultThumbnail => SpriteHandle.Invalid;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsOfType(Type type)
|
||||
{
|
||||
return type.IsAssignableFrom(typeof(Prefab));
|
||||
}
|
||||
}
|
||||
}
|
||||
39
Source/Editor/Content/Items/SceneItem.cs
Normal file
39
Source/Editor/Content/Items/SceneItem.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Content item that contains <see cref="FlaxEngine.Scene"/> data.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.JsonAssetItem" />
|
||||
public sealed class SceneItem : JsonAssetItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SceneItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The asset path.</param>
|
||||
/// <param name="id">The asset identifier.</param>
|
||||
public SceneItem(string path, Guid id)
|
||||
: base(path, id, Scene.EditorPickerTypename)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItemType ItemType => ContentItemType.Scene;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItemSearchFilter SearchFilter => ContentItemSearchFilter.Scene;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.Scene64;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsOfType(Type type)
|
||||
{
|
||||
return type.IsAssignableFrom(typeof(SceneAsset));
|
||||
}
|
||||
}
|
||||
}
|
||||
97
Source/Editor/Content/Items/ScriptItem.cs
Normal file
97
Source/Editor/Content/Items/ScriptItem.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Content item that contains script file with source code.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.ContentItem" />
|
||||
public abstract class ScriptItem : ContentItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the script (deducted from the asset name).
|
||||
/// </summary>
|
||||
public string ScriptName => FilterScriptName(ShortName);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the script item references the valid use script type that can be used in a gameplay.
|
||||
/// </summary>
|
||||
public bool IsValid => ScriptsBuilder.FindScript(ScriptName) != null;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScriptItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the item.</param>
|
||||
protected ScriptItem(string path)
|
||||
: base(path)
|
||||
{
|
||||
ShowFileExtension = true;
|
||||
}
|
||||
|
||||
private static string FilterScriptName(string input)
|
||||
{
|
||||
var length = input.Length;
|
||||
var sb = new StringBuilder(length);
|
||||
|
||||
// Skip leading '0-9' characters
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
var c = input[i];
|
||||
|
||||
if (char.IsLetterOrDigit(c) && !char.IsDigit(c))
|
||||
break;
|
||||
}
|
||||
|
||||
// Remove all characters that are not '_' or 'a-z' or 'A-Z' or '0-9'
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
var c = input[i];
|
||||
|
||||
if (c == '_' || char.IsLetterOrDigit(c))
|
||||
sb.Append(c);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the name of the script for the given file.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>Script name</returns>
|
||||
public static string CreateScriptName(string path)
|
||||
{
|
||||
return FilterScriptName(System.IO.Path.GetFileNameWithoutExtension(path));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItemType ItemType => ContentItemType.Script;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItemSearchFilter SearchFilter => ContentItemSearchFilter.Script;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ScriptItem FindScriptWitScriptName(string scriptName)
|
||||
{
|
||||
return scriptName == ScriptName ? this : null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnPathChanged()
|
||||
{
|
||||
ScriptsBuilder.MarkWorkspaceDirty();
|
||||
|
||||
base.OnPathChanged();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDelete()
|
||||
{
|
||||
ScriptsBuilder.MarkWorkspaceDirty();
|
||||
|
||||
base.OnDelete();
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Source/Editor/Content/Items/ShaderSourceItem.cs
Normal file
32
Source/Editor/Content/Items/ShaderSourceItem.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Content item that contains shader source code.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.ContentItem" />
|
||||
public class ShaderSourceItem : ContentItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShaderSourceItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the item.</param>
|
||||
public ShaderSourceItem(string path)
|
||||
: base(path)
|
||||
{
|
||||
ShowFileExtension = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItemType ItemType => ContentItemType.Asset;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContentItemSearchFilter SearchFilter => ContentItemSearchFilter.Shader;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.Document64;
|
||||
}
|
||||
}
|
||||
552
Source/Editor/Content/Items/VisualScriptItem.cs
Normal file
552
Source/Editor/Content/Items/VisualScriptItem.cs
Normal file
@@ -0,0 +1,552 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Json;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
sealed class VisualScriptParameterInfo : IScriptMemberInfo
|
||||
{
|
||||
private readonly VisualScriptType _type;
|
||||
private readonly VisjectGraphParameter _parameter;
|
||||
|
||||
internal VisualScriptParameterInfo(VisualScriptType type, VisjectGraphParameter parameter)
|
||||
{
|
||||
_type = type;
|
||||
_parameter = parameter;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => _parameter.Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPublic => _parameter.IsPublic;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsStatic => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsVirtual => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsAbstract => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsGeneric => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsField => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsProperty => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsMethod => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasGet => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasSet => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int ParametersCount => 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScriptType DeclaringType => new ScriptType(_type);
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScriptType ValueType
|
||||
{
|
||||
get
|
||||
{
|
||||
var type = TypeUtils.GetType(_parameter.TypeTypeName);
|
||||
return type ? type : new ScriptType(_parameter.Type);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasAttribute(Type attributeType, bool inherit)
|
||||
{
|
||||
return Surface.SurfaceMeta.HasAttribute(_parameter, attributeType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object[] GetAttributes(bool inherit)
|
||||
{
|
||||
return Surface.SurfaceMeta.GetAttributes(_parameter);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScriptMemberInfo.Parameter[] GetParameters()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object GetValue(object obj)
|
||||
{
|
||||
return _type.Asset.GetScriptInstanceParameterValue(_parameter.Name, (Object)obj);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetValue(object obj, object value)
|
||||
{
|
||||
_type.Asset.SetScriptInstanceParameterValue(_parameter.Name, (Object)obj, value);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class VisualScriptMethodInfo : IScriptMemberInfo
|
||||
{
|
||||
[Flags]
|
||||
private enum Flags
|
||||
{
|
||||
None = 0,
|
||||
Static = 1,
|
||||
Virtual = 2,
|
||||
Override = 4,
|
||||
}
|
||||
|
||||
private readonly VisualScriptType _type;
|
||||
private readonly int _index;
|
||||
private readonly string _name;
|
||||
private readonly byte _flags;
|
||||
private readonly ScriptType _returnType;
|
||||
private readonly ScriptMemberInfo.Parameter[] _parameters;
|
||||
private object[] _attributes;
|
||||
|
||||
internal VisualScriptMethodInfo(VisualScriptType type, int index)
|
||||
{
|
||||
_type = type;
|
||||
_index = index;
|
||||
type.Asset.GetMethodSignature(index, out _name, out _flags, out var returnTypeName, out var paramNames, out var paramTypeNames, out var paramOuts);
|
||||
_returnType = TypeUtils.GetType(returnTypeName);
|
||||
if (paramNames.Length != 0)
|
||||
{
|
||||
_parameters = new ScriptMemberInfo.Parameter[paramNames.Length];
|
||||
for (int i = 0; i < _parameters.Length; i++)
|
||||
{
|
||||
_parameters[i] = new ScriptMemberInfo.Parameter
|
||||
{
|
||||
Name = paramNames[i],
|
||||
Type = TypeUtils.GetType(paramTypeNames[i]),
|
||||
IsOut = paramOuts[i],
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_parameters = Utils.GetEmptyArray<ScriptMemberInfo.Parameter>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => _name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPublic => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsStatic => (_flags & (byte)Flags.Static) != 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsVirtual => (_flags & (byte)Flags.Virtual) != 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsAbstract => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsGeneric => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsField => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsProperty => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsMethod => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasGet => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasSet => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int ParametersCount => _parameters.Length;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScriptType DeclaringType => (_flags & (byte)Flags.Override) != 0 ? _type.BaseType : new ScriptType(_type);
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScriptType ValueType => _returnType;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasAttribute(Type attributeType, bool inherit)
|
||||
{
|
||||
return GetAttributes(inherit).Any(x => x.GetType() == attributeType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object[] GetAttributes(bool inherit)
|
||||
{
|
||||
if (_attributes == null)
|
||||
{
|
||||
var data = _type.Asset.GetMethodMetaData(_index, Surface.SurfaceMeta.AttributeMetaTypeID);
|
||||
_attributes = Surface.SurfaceMeta.GetAttributes(data);
|
||||
}
|
||||
return _attributes;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScriptMemberInfo.Parameter[] GetParameters()
|
||||
{
|
||||
return _parameters;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object GetValue(object obj)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetValue(object obj, object value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The implementation of the <see cref="IScriptType"/>
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Scripting.IScriptType" />
|
||||
[HideInEditor]
|
||||
public sealed class VisualScriptType : IScriptType
|
||||
{
|
||||
private VisualScript _asset;
|
||||
private ScriptMemberInfo[] _parameters;
|
||||
private ScriptMemberInfo[] _methods;
|
||||
private object[] _attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Visual Script asset that contains this type.
|
||||
/// </summary>
|
||||
public VisualScript Asset => _asset;
|
||||
|
||||
internal VisualScriptType(VisualScript asset)
|
||||
{
|
||||
_asset = asset;
|
||||
}
|
||||
|
||||
private void CacheData()
|
||||
{
|
||||
if (_parameters != null)
|
||||
return;
|
||||
if (_asset.WaitForLoaded())
|
||||
return;
|
||||
FlaxEngine.Content.AssetReloading += OnAssetReloading;
|
||||
|
||||
// Cache Visual Script parameters info
|
||||
var parameters = _asset.Parameters;
|
||||
if (parameters.Length != 0)
|
||||
{
|
||||
_parameters = new ScriptMemberInfo[parameters.Length];
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
_parameters[i] = new ScriptMemberInfo(new VisualScriptParameterInfo(this, parameters[i]));
|
||||
}
|
||||
else
|
||||
_parameters = Utils.GetEmptyArray<ScriptMemberInfo>();
|
||||
|
||||
// Cache Visual Script methods info
|
||||
var methodsCount = _asset.GetMethodsCount();
|
||||
if (methodsCount != 0)
|
||||
{
|
||||
_methods = new ScriptMemberInfo[methodsCount];
|
||||
for (int i = 0; i < methodsCount; i++)
|
||||
_methods[i] = new ScriptMemberInfo(new VisualScriptMethodInfo(this, i));
|
||||
}
|
||||
else
|
||||
_methods = Utils.GetEmptyArray<ScriptMemberInfo>();
|
||||
|
||||
// Cache Visual Script attributes
|
||||
var attributesData = _asset.GetMetaData(Surface.SurfaceMeta.AttributeMetaTypeID);
|
||||
if (attributesData != null && attributesData.Length != 0)
|
||||
{
|
||||
_attributes = Surface.SurfaceMeta.GetAttributes(attributesData);
|
||||
}
|
||||
else
|
||||
_attributes = Utils.GetEmptyArray<object>();
|
||||
}
|
||||
|
||||
private void OnAssetReloading(Asset asset)
|
||||
{
|
||||
if (asset == _asset)
|
||||
{
|
||||
_parameters = null;
|
||||
_methods = null;
|
||||
FlaxEngine.Content.AssetReloading -= OnAssetReloading;
|
||||
}
|
||||
}
|
||||
|
||||
internal void Dispose()
|
||||
{
|
||||
OnAssetReloading(_asset);
|
||||
_asset = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => Path.GetFileNameWithoutExtension(_asset.Path);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Namespace => string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string TypeName => JsonSerializer.GetStringID(_asset.ID);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPublic => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsAbstract => _asset && !_asset.WaitForLoaded() && (_asset.Meta.Flags & VisualScript.Flags.Abstract) != 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsSealed => _asset && !_asset.WaitForLoaded() && (_asset.Meta.Flags & VisualScript.Flags.Sealed) != 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsEnum => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsClass => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsArray => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsValueType => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsGenericType => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsReference => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPointer => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsStatic => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanCreateInstance => !IsAbstract;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScriptType BaseType => _asset && !_asset.WaitForLoaded() ? TypeUtils.GetType(_asset.Meta.BaseTypename) : ScriptType.Null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ContentItem ContentItem => _asset ? Editor.Instance.ContentDatabase.FindAsset(_asset.ID) : null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public object CreateInstance()
|
||||
{
|
||||
return Object.New(TypeName);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasAttribute(Type attributeType, bool inherit)
|
||||
{
|
||||
if (inherit && BaseType.HasAttribute(attributeType, true))
|
||||
return true;
|
||||
CacheData();
|
||||
return _attributes.Any(x => x.GetType() == attributeType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object[] GetAttributes(bool inherit)
|
||||
{
|
||||
CacheData();
|
||||
if (inherit)
|
||||
{
|
||||
var thisAttributes = _attributes;
|
||||
var baseAttributes = BaseType.GetAttributes(true);
|
||||
if (thisAttributes.Length != 0)
|
||||
{
|
||||
if (baseAttributes.Length != 0)
|
||||
{
|
||||
var resultAttributes = new object[thisAttributes.Length + baseAttributes.Length];
|
||||
Array.Copy(thisAttributes, 0, resultAttributes, 0, thisAttributes.Length);
|
||||
Array.Copy(baseAttributes, 0, resultAttributes, thisAttributes.Length, baseAttributes.Length);
|
||||
return resultAttributes;
|
||||
}
|
||||
return thisAttributes;
|
||||
}
|
||||
return baseAttributes;
|
||||
}
|
||||
return _attributes;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScriptMemberInfo[] GetMembers(string name, MemberTypes type, BindingFlags bindingAttr)
|
||||
{
|
||||
var members = new List<ScriptMemberInfo>();
|
||||
if ((bindingAttr & BindingFlags.DeclaredOnly) == 0)
|
||||
{
|
||||
var baseType = BaseType;
|
||||
if (baseType)
|
||||
members.AddRange(baseType.GetMembers(name, type, bindingAttr));
|
||||
}
|
||||
CacheData();
|
||||
if ((type & MemberTypes.Field) != 0)
|
||||
{
|
||||
foreach (var parameter in _parameters)
|
||||
{
|
||||
if (parameter.Filter(name, bindingAttr))
|
||||
members.Add(parameter);
|
||||
}
|
||||
}
|
||||
if ((type & MemberTypes.Method) != 0)
|
||||
{
|
||||
foreach (var method in _methods)
|
||||
{
|
||||
if (method.Filter(name, bindingAttr))
|
||||
members.Add(method);
|
||||
}
|
||||
}
|
||||
return members.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScriptMemberInfo[] GetMembers(BindingFlags bindingAttr)
|
||||
{
|
||||
var members = new List<ScriptMemberInfo>();
|
||||
if ((bindingAttr & BindingFlags.DeclaredOnly) == 0)
|
||||
{
|
||||
var baseType = BaseType;
|
||||
if (baseType)
|
||||
members.AddRange(baseType.GetMembers(bindingAttr));
|
||||
}
|
||||
CacheData();
|
||||
foreach (var parameter in _parameters)
|
||||
{
|
||||
if (parameter.Filter(bindingAttr))
|
||||
members.Add(parameter);
|
||||
}
|
||||
foreach (var method in _methods)
|
||||
{
|
||||
if (method.Filter(bindingAttr))
|
||||
members.Add(method);
|
||||
}
|
||||
return members.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScriptMemberInfo[] GetFields(BindingFlags bindingAttr)
|
||||
{
|
||||
CacheData();
|
||||
var baseType = BaseType;
|
||||
if (baseType)
|
||||
{
|
||||
var baseFields = baseType.GetFields(bindingAttr);
|
||||
var newArray = new ScriptMemberInfo[_parameters.Length + baseFields.Length];
|
||||
Array.Copy(_parameters, newArray, _parameters.Length);
|
||||
Array.Copy(baseFields, 0, newArray, _parameters.Length, baseFields.Length);
|
||||
return newArray;
|
||||
}
|
||||
return _parameters;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScriptMemberInfo[] GetProperties(BindingFlags bindingAttr)
|
||||
{
|
||||
var baseType = BaseType;
|
||||
if (baseType)
|
||||
return baseType.GetProperties(bindingAttr);
|
||||
return Utils.GetEmptyArray<ScriptMemberInfo>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScriptMemberInfo[] GetMethods(BindingFlags bindingAttr)
|
||||
{
|
||||
CacheData();
|
||||
var baseType = BaseType;
|
||||
if (baseType)
|
||||
{
|
||||
var baseMethods = baseType.GetMethods(bindingAttr);
|
||||
var newArray = new ScriptMemberInfo[_methods.Length + baseMethods.Length];
|
||||
Array.Copy(_methods, newArray, _methods.Length);
|
||||
Array.Copy(baseMethods, 0, newArray, _methods.Length, baseMethods.Length);
|
||||
return newArray;
|
||||
}
|
||||
return _methods;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of <see cref="BinaryAssetItem"/> for <see cref="VisualScript"/> assets.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetItem" />
|
||||
public class VisualScriptItem : BinaryAssetItem
|
||||
{
|
||||
/// <summary>
|
||||
/// The cached list of all Visual Script assets found in the workspace (engine, game and plugin projects). Updated on workspace modifications.
|
||||
/// </summary>
|
||||
public static readonly List<VisualScriptItem> VisualScripts = new List<VisualScriptItem>();
|
||||
|
||||
private VisualScriptType _scriptType;
|
||||
|
||||
/// <summary>
|
||||
/// The Visual Script type. Can be null if failed to load asset.
|
||||
/// </summary>
|
||||
public ScriptType ScriptType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_scriptType == null)
|
||||
{
|
||||
var asset = FlaxEngine.Content.LoadAsync<VisualScript>(ID);
|
||||
if (asset)
|
||||
_scriptType = new VisualScriptType(asset);
|
||||
}
|
||||
return new ScriptType(_scriptType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public VisualScriptItem(string path, ref Guid id, string typeName, Type type)
|
||||
: base(path, ref id, typeName, type, ContentItemSearchFilter.Script)
|
||||
{
|
||||
VisualScripts.Add(this);
|
||||
Editor.Instance.CodeEditing.ClearTypes();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.CodeScript64;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool DrawShadow => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
if (IsDisposing)
|
||||
return;
|
||||
if (_scriptType != null)
|
||||
{
|
||||
Editor.Instance.CodeEditing.ClearTypes();
|
||||
_scriptType.Dispose();
|
||||
_scriptType = null;
|
||||
}
|
||||
VisualScripts.Remove(this);
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
275
Source/Editor/Content/PreviewsCache.cpp
Normal file
275
Source/Editor/Content/PreviewsCache.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "PreviewsCache.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Graphics/GPUContext.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Content/Factories/BinaryAssetFactory.h"
|
||||
#include "Engine/ContentImporters/AssetsImportingManager.h"
|
||||
#include "Engine/Content/Upgraders/TextureAssetUpgrader.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
|
||||
// Default asset preview icon size (both width and height since it's a square)
|
||||
#define ASSET_ICON_SIZE 64
|
||||
|
||||
// Default assets previews atlas size
|
||||
#define ASSETS_ICONS_ATLAS_SIZE 1024
|
||||
|
||||
// Default assets previews atlas margin between icons
|
||||
#define ASSETS_ICONS_ATLAS_MARGIN 4
|
||||
|
||||
// Default format for assets previews atlas texture
|
||||
#define ASSETS_ICONS_ATLAS_FORMAT PixelFormat::R8G8B8A8_UNorm
|
||||
|
||||
// Util macros
|
||||
#define ASSETS_ICONS_PER_ROW (int)((float)ASSETS_ICONS_ATLAS_SIZE / (ASSET_ICON_SIZE + ASSETS_ICONS_ATLAS_MARGIN))
|
||||
#define ASSETS_ICONS_PER_ATLAS (ASSETS_ICONS_PER_ROW*ASSETS_ICONS_PER_ROW)
|
||||
|
||||
bool PreviewsCache::FlushTask::Run()
|
||||
{
|
||||
// Check if has valid data downloaded
|
||||
if (_data.GetMipLevels() != 1)
|
||||
{
|
||||
LOG(Warning, "Failed to flush asset previews atlas \'{0}\'.", _cache->ToString());
|
||||
return true;
|
||||
}
|
||||
auto mipData = _data.GetData(0, 0);
|
||||
ASSERT(mipData->DepthPitch == CalculateTextureMemoryUsage(_cache->GetTexture()->Format(), _cache->Width(), _cache->Height(), 1));
|
||||
|
||||
ScopeLock lock(_cache->Locker);
|
||||
|
||||
// Link chunks (don't allocate additional memory)
|
||||
auto mipChunk = _cache->GetOrCreateChunk(0);
|
||||
auto dataChunk = _cache->GetOrCreateChunk(15);
|
||||
mipChunk->Data.Link(mipData->Data);
|
||||
dataChunk->Data.Link((byte*)_cache->_assets.Get(), sizeof(Guid) * _cache->_assets.Count());
|
||||
|
||||
// Prepare asset data
|
||||
AssetInitData data;
|
||||
data.SerializedVersion = 4;
|
||||
data.CustomData.Copy(_cache->_texture.GetHeader());
|
||||
|
||||
// Save (use silent mode to prevent asset reloading)
|
||||
bool saveResult = _cache->SaveAsset(data, true);
|
||||
mipChunk->Data.Release();
|
||||
dataChunk->Data.Release();
|
||||
if (saveResult)
|
||||
{
|
||||
LOG(Warning, "Failed to save asset previews atlas \'{0}\'.", _cache->ToString());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Clear flag
|
||||
_cache->_isDirty = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PreviewsCache::FlushTask::OnEnd()
|
||||
{
|
||||
ASSERT(_cache->_flushTask == this);
|
||||
_cache->_flushTask = nullptr;
|
||||
|
||||
// Base
|
||||
ThreadPoolTask::OnEnd();
|
||||
}
|
||||
|
||||
REGISTER_BINARY_ASSET(PreviewsCache, "FlaxEditor.PreviewsCache", ::New<TextureAssetUpgrader>(), false);
|
||||
|
||||
PreviewsCache::PreviewsCache(const SpawnParams& params, const AssetInfo* info)
|
||||
: SpriteAtlas(params, info)
|
||||
{
|
||||
}
|
||||
|
||||
bool PreviewsCache::IsReady() const
|
||||
{
|
||||
return IsLoaded() && GetTexture()->MipLevels() > 0;
|
||||
}
|
||||
|
||||
SpriteHandle PreviewsCache::FindSlot(const Guid& id)
|
||||
{
|
||||
if (WaitForLoaded())
|
||||
return SpriteHandle::Invalid;
|
||||
|
||||
// Find entry
|
||||
int32 index;
|
||||
if (_assets.Find(id, index))
|
||||
{
|
||||
const String spriteName = StringUtils::ToString(index);
|
||||
return FindSprite(spriteName);
|
||||
}
|
||||
|
||||
return SpriteHandle::Invalid;
|
||||
}
|
||||
|
||||
Asset::LoadResult PreviewsCache::load()
|
||||
{
|
||||
// Load previews data
|
||||
auto previewsMetaChunk = GetChunk(15);
|
||||
if (previewsMetaChunk == nullptr || previewsMetaChunk->IsMissing())
|
||||
return LoadResult::MissingDataChunk;
|
||||
if (previewsMetaChunk->Size() != ASSETS_ICONS_PER_ATLAS * sizeof(Guid))
|
||||
return LoadResult::Failed;
|
||||
_assets.Set(previewsMetaChunk->Get<Guid>(), ASSETS_ICONS_PER_ATLAS);
|
||||
|
||||
// Setup atlas sprites array
|
||||
Sprite sprite;
|
||||
sprite.Area.Size = static_cast<float>(ASSET_ICON_SIZE) / ASSETS_ICONS_ATLAS_SIZE;
|
||||
const float positionScale = static_cast<float>(ASSET_ICON_SIZE + ASSETS_ICONS_ATLAS_MARGIN) / ASSETS_ICONS_ATLAS_SIZE;
|
||||
const float positionOffset = static_cast<float>(ASSETS_ICONS_ATLAS_MARGIN) / ASSETS_ICONS_ATLAS_SIZE;
|
||||
for (int32 i = 0; i < ASSETS_ICONS_PER_ATLAS; i++)
|
||||
{
|
||||
sprite.Area.Location = Vector2(static_cast<float>(i % ASSETS_ICONS_PER_ROW), static_cast<float>(i / ASSETS_ICONS_PER_ROW)) * positionScale + positionOffset;
|
||||
sprite.Name = StringUtils::ToString(i);
|
||||
Sprites.Add(sprite);
|
||||
}
|
||||
|
||||
_isDirty = false;
|
||||
return TextureBase::load();
|
||||
}
|
||||
|
||||
void PreviewsCache::unload(bool isReloading)
|
||||
{
|
||||
// Wait for flush end
|
||||
if (IsFlushing())
|
||||
{
|
||||
_flushTask->Cancel();
|
||||
}
|
||||
|
||||
// Release data
|
||||
_assets.Clear();
|
||||
|
||||
SpriteAtlas::unload(isReloading);
|
||||
}
|
||||
|
||||
AssetChunksFlag PreviewsCache::getChunksToPreload() const
|
||||
{
|
||||
// Preload previews ids data chunk
|
||||
return GET_CHUNK_FLAG(15);
|
||||
}
|
||||
|
||||
bool PreviewsCache::HasFreeSlot() const
|
||||
{
|
||||
// Unused slot is which ID is Empty
|
||||
// (Search from back to front since slots are allocated from front to back - it will be faster)
|
||||
return _assets.FindLast(Guid::Empty) != INVALID_INDEX;
|
||||
}
|
||||
|
||||
SpriteHandle PreviewsCache::OccupySlot(GPUTexture* source, const Guid& id)
|
||||
{
|
||||
if (WaitForLoaded())
|
||||
return SpriteHandle::Invalid;
|
||||
|
||||
// Find free slot and for that asset
|
||||
int32 index = _assets.Find(id);
|
||||
if (index == INVALID_INDEX)
|
||||
index = _assets.Find(Guid::Empty);
|
||||
if (index == INVALID_INDEX)
|
||||
{
|
||||
LOG(Warning, "Cannot find free slot in the asset previews atlas.");
|
||||
return SpriteHandle::Invalid;
|
||||
}
|
||||
|
||||
ASSERT(IsReady());
|
||||
|
||||
// Copy texture region
|
||||
uint32 x = ASSETS_ICONS_ATLAS_MARGIN + index % ASSETS_ICONS_PER_ROW * (ASSET_ICON_SIZE + ASSETS_ICONS_ATLAS_MARGIN);
|
||||
uint32 y = ASSETS_ICONS_ATLAS_MARGIN + index / ASSETS_ICONS_PER_ROW * (ASSET_ICON_SIZE + ASSETS_ICONS_ATLAS_MARGIN);
|
||||
GPUDevice::Instance->GetMainContext()->CopyTexture(GetTexture(), 0, x, y, 0, source, 0);
|
||||
|
||||
// Occupy slot
|
||||
_assets[index] = id;
|
||||
|
||||
// Get sprite handle
|
||||
const String spriteName = StringUtils::ToString(index);
|
||||
const auto slot = FindSprite(spriteName);
|
||||
if (!slot.IsValid())
|
||||
{
|
||||
LOG(Warning, "Cannot create sprite handle for asset preview.");
|
||||
return SpriteHandle::Invalid;
|
||||
}
|
||||
|
||||
// Set dirty flag
|
||||
_isDirty = true;
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
bool PreviewsCache::ReleaseSlot(const Guid& id)
|
||||
{
|
||||
bool result = false;
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
int32 index = _assets.Find(id);
|
||||
if (index != INVALID_INDEX)
|
||||
{
|
||||
_assets[index] = Guid::Empty;
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void PreviewsCache::Flush()
|
||||
{
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
// Check if is fully loaded and is dirty and is not during downloading
|
||||
if (_isDirty && !IsFlushing() && IsReady())
|
||||
{
|
||||
// Spawn flushing tasks sequence
|
||||
_flushTask = New<FlushTask>(this);
|
||||
auto downloadDataTask = GetTexture()->DownloadDataAsync(_flushTask->GetData());
|
||||
downloadDataTask->ContinueWith(_flushTask);
|
||||
downloadDataTask->Start();
|
||||
}
|
||||
}
|
||||
|
||||
#if COMPILE_WITH_ASSETS_IMPORTER
|
||||
|
||||
bool PreviewsCache::Create(const StringView& outputPath)
|
||||
{
|
||||
LOG(Info, "Creating new atlas '{0}' for assets previews cache. Size: {1}, capacity: {2}", outputPath, ASSETS_ICONS_ATLAS_SIZE, ASSETS_ICONS_PER_ATLAS);
|
||||
return AssetsImportingManager::Create(&create, outputPath);
|
||||
}
|
||||
|
||||
CreateAssetResult PreviewsCache::create(CreateAssetContext& context)
|
||||
{
|
||||
// Base
|
||||
IMPORT_SETUP(PreviewsCache, 4);
|
||||
|
||||
// Create texture header (custom data)
|
||||
TextureHeader header;
|
||||
header.Width = ASSETS_ICONS_ATLAS_SIZE;
|
||||
header.Height = ASSETS_ICONS_ATLAS_SIZE;
|
||||
header.Format = ASSETS_ICONS_ATLAS_FORMAT;
|
||||
header.IsSRGB = false;
|
||||
header.IsCubeMap = false;
|
||||
header.MipLevels = 1;
|
||||
header.NeverStream = true;
|
||||
header.Type = TextureFormatType::Unknown;
|
||||
context.Data.CustomData.Copy(&header);
|
||||
|
||||
// Create blank image (chunk 0)
|
||||
uint64 imageSize = CalculateTextureMemoryUsage(ASSETS_ICONS_ATLAS_FORMAT, ASSETS_ICONS_ATLAS_SIZE, ASSETS_ICONS_ATLAS_SIZE, 1);
|
||||
ASSERT(imageSize <= MAX_int32);
|
||||
if (context.AllocateChunk(0))
|
||||
return CreateAssetResult::CannotAllocateChunk;
|
||||
auto mipChunk = context.Data.Header.Chunks[0];
|
||||
mipChunk->Data.Allocate((int32)imageSize);
|
||||
Platform::MemoryClear(mipChunk->Get(), mipChunk->Size());
|
||||
|
||||
// Create IDs cache array (chunk 15)
|
||||
int32 idsSize = sizeof(Guid) * ASSETS_ICONS_PER_ATLAS;
|
||||
if (context.AllocateChunk(15))
|
||||
return CreateAssetResult::CannotAllocateChunk;
|
||||
auto dataChunk = context.Data.Header.Chunks[15];
|
||||
dataChunk->Data.Allocate(idsSize);
|
||||
Platform::MemoryClear(dataChunk->Get(), dataChunk->Size());
|
||||
|
||||
return CreateAssetResult::Ok;
|
||||
}
|
||||
|
||||
#endif
|
||||
40
Source/Editor/Content/PreviewsCache.cs
Normal file
40
Source/Editor/Content/PreviewsCache.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor
|
||||
{
|
||||
[HideInEditor]
|
||||
partial class PreviewsCache
|
||||
{
|
||||
/// <summary>
|
||||
/// The default asset previews icon size (both width and height since it's a square).
|
||||
/// </summary>
|
||||
public const int AssetIconSize = 64;
|
||||
|
||||
/// <summary>
|
||||
/// The default assets previews atlas size
|
||||
/// </summary>
|
||||
public const int AssetIconsAtlasSize = 1024;
|
||||
|
||||
/// <summary>
|
||||
/// The default assets previews atlas margin (between icons)
|
||||
/// </summary>
|
||||
public const int AssetIconsAtlasMargin = 4;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of asset icons per atlas row.
|
||||
/// </summary>
|
||||
public const int AssetIconsPerRow = (int)((float)AssetIconsAtlasSize / (AssetIconSize + AssetIconsAtlasMargin));
|
||||
|
||||
/// <summary>
|
||||
/// The amount of asset icons per atlas.
|
||||
/// </summary>
|
||||
public const int AssetIconsPerAtlas = AssetIconsPerRow * AssetIconsPerRow;
|
||||
|
||||
/// <summary>
|
||||
/// The default format of previews atlas.
|
||||
/// </summary>
|
||||
public const PixelFormat AssetIconsAtlasFormat = PixelFormat.R8G8B8A8_UNorm;
|
||||
}
|
||||
}
|
||||
128
Source/Editor/Content/PreviewsCache.h
Normal file
128
Source/Editor/Content/PreviewsCache.h
Normal file
@@ -0,0 +1,128 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Render2D/SpriteAtlas.h"
|
||||
#include "Engine/Content/Asset.h"
|
||||
#include "Engine/ContentImporters/Types.h"
|
||||
#include "Engine/Graphics/Textures/GPUTexture.h"
|
||||
#include "Engine/Threading/ThreadPoolTask.h"
|
||||
#include "Engine/Graphics/Textures/TextureData.h"
|
||||
|
||||
/// <summary>
|
||||
/// Asset which contains set of asset items thumbnails (cached previews).
|
||||
/// </summary>
|
||||
API_CLASS(Sealed, NoSpawn, Namespace="FlaxEditor") class PreviewsCache : public SpriteAtlas
|
||||
{
|
||||
DECLARE_BINARY_ASSET_HEADER(PreviewsCache, TexturesSerializedVersion);
|
||||
private:
|
||||
|
||||
class FlushTask : public ThreadPoolTask
|
||||
{
|
||||
private:
|
||||
|
||||
PreviewsCache* _cache;
|
||||
TextureData _data;
|
||||
|
||||
public:
|
||||
|
||||
FlushTask(PreviewsCache* cache)
|
||||
: _cache(cache)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture data container.
|
||||
/// </summary>
|
||||
TextureData& GetData()
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// [ThreadPoolTask]
|
||||
bool Run() override;
|
||||
void OnEnd() override;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Array<Guid> _assets;
|
||||
bool _isDirty = false;
|
||||
FlushTask* _flushTask = nullptr;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this atlas is ready (is loaded and has texture streamed).
|
||||
/// </summary>
|
||||
API_PROPERTY() bool IsReady() const;
|
||||
|
||||
/// <summary>
|
||||
/// Finds the preview icon for given asset ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The asset id to find preview for it.</param>
|
||||
/// <returns>The output sprite slot handle or invalid if invalid in nothing found.</returns>
|
||||
API_FUNCTION() SpriteHandle FindSlot(const Guid& id);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this atlas has one or more free slots for the asset preview.
|
||||
/// </summary>
|
||||
API_PROPERTY() bool HasFreeSlot() const;
|
||||
|
||||
/// <summary>
|
||||
/// Occupies the atlas slot.
|
||||
/// </summary>
|
||||
/// <param name="source">The source texture to insert.</param>
|
||||
/// <param name="id">The asset identifier.</param>
|
||||
/// <returns>The added sprite slot handle or invalid if invalid in failed to occupy slot.</returns>
|
||||
API_FUNCTION() SpriteHandle OccupySlot(GPUTexture* source, const Guid& id);
|
||||
|
||||
/// <summary>
|
||||
/// Releases the used slot.
|
||||
/// </summary>
|
||||
/// <param name="id">The asset identifier.</param>
|
||||
/// <returns>True if slot has been release, otherwise it was not found.</returns>
|
||||
API_FUNCTION() bool ReleaseSlot(const Guid& id);
|
||||
|
||||
/// <summary>
|
||||
/// Flushes atlas data from the GPU to the asset storage (saves data).
|
||||
/// </summary>
|
||||
API_FUNCTION() void Flush();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this instance is flushing.
|
||||
/// </summary>
|
||||
/// <returns>True if this previews cache is flushing, otherwise false.</returns>
|
||||
FORCE_INLINE bool IsFlushing() const
|
||||
{
|
||||
return _flushTask != nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
#if COMPILE_WITH_ASSETS_IMPORTER
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new atlas.
|
||||
/// </summary>
|
||||
/// <param name="outputPath">The output asset file path.</param>
|
||||
/// <returns>True if this previews cache is flushing, otherwise false.</returns>
|
||||
API_FUNCTION() static bool Create(const StringView& outputPath);
|
||||
|
||||
private:
|
||||
|
||||
static CreateAssetResult create(CreateAssetContext& context);
|
||||
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
// [BinaryAsset]
|
||||
LoadResult load() override;
|
||||
void unload(bool isReloading) override;
|
||||
AssetChunksFlag getChunksToPreload() const override;
|
||||
};
|
||||
44
Source/Editor/Content/Proxy/AnimationGraphFunctionProxy.cs
Normal file
44
Source/Editor/Content/Proxy/AnimationGraphFunctionProxy.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="AnimationGraphFunction"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class AnimationGraphFunctionProxy : BinaryAssetProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Animation Graph Function";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new AnimationGraphFunctionWindow(editor, item as AssetItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x991597);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(AnimationGraphFunction);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
if (Editor.CreateAsset(Editor.NewAssetType.AnimationGraphFunction, outputPath))
|
||||
throw new Exception("Failed to create new asset.");
|
||||
}
|
||||
}
|
||||
}
|
||||
44
Source/Editor/Content/Proxy/AnimationGraphProxy.cs
Normal file
44
Source/Editor/Content/Proxy/AnimationGraphProxy.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="AnimationGraph"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class AnimationGraphProxy : BinaryAssetProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Animation Graph";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new AnimationGraphWindow(editor, item as AssetItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x00B371);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(AnimationGraph);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
if (Editor.CreateAsset(Editor.NewAssetType.AnimationGraph, outputPath))
|
||||
throw new Exception("Failed to create new asset.");
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Source/Editor/Content/Proxy/AnimationProxy.cs
Normal file
52
Source/Editor/Content/Proxy/AnimationProxy.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="Animation"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class AnimationProxy : BinaryAssetProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Animation";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanReimport(ContentItem item)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new AnimationWindow(editor, item as AssetItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0xB37200);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(Animation);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
guiRoot.AddChild(new Label
|
||||
{
|
||||
Text = Path.GetFileNameWithoutExtension(request.Asset.Path),
|
||||
Offsets = Margin.Zero,
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Wrapping = TextWrapping.WrapWords
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
97
Source/Editor/Content/Proxy/AssetProxy.cs
Normal file
97
Source/Editor/Content/Proxy/AssetProxy.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all asset proxy objects used to manage <see cref="AssetItem"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.ContentProxy" />
|
||||
[HideInEditor]
|
||||
public abstract class AssetProxy : ContentProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool IsAsset => true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full name of the asset type (stored data format).
|
||||
/// </summary>
|
||||
public abstract string TypeName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this proxy supports the given asset type id at the given path.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The asset type identifier.</param>
|
||||
/// <param name="path">The asset path.</param>
|
||||
/// <returns>True if proxy supports assets of the given type id and path.</returns>
|
||||
public virtual bool AcceptsAsset(string typeName, string path)
|
||||
{
|
||||
return typeName == TypeName && path.EndsWith(FileExtension, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs the item for the asset.
|
||||
/// </summary>
|
||||
/// <param name="path">The asset path.</param>
|
||||
/// <param name="typeName">The asset type name identifier.</param>
|
||||
/// <param name="id">The asset identifier.</param>
|
||||
/// <returns>Created item.</returns>
|
||||
public abstract AssetItem ConstructItem(string path, string typeName, ref Guid id);
|
||||
|
||||
/// <summary>
|
||||
/// Called when thumbnail request gets prepared for drawing.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public virtual void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether thumbnail can be drawn for the specified item.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns><c>true</c> if this thumbnail can be drawn for the specified item; otherwise, <c>false</c>.</returns>
|
||||
public virtual bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when thumbnail drawing begins. Proxy should setup scene GUI for guiRoot.
|
||||
/// </summary>
|
||||
/// <param name="request">The request to render thumbnail.</param>
|
||||
/// <param name="guiRoot">The GUI root container control.</param>
|
||||
/// <param name="context">GPU context.</param>
|
||||
public virtual void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
guiRoot.AddChild(new Label
|
||||
{
|
||||
Text = Name,
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
Wrapping = TextWrapping.WrapWords
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when thumbnail drawing ends. Proxy should clear custom GUI from guiRoot from that should be not destroyed.
|
||||
/// </summary>
|
||||
/// <param name="request">The request to render thumbnail.</param>
|
||||
/// <param name="guiRoot">The GUI root container control.</param>
|
||||
public virtual void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when thumbnail requests cleans data after drawing.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public virtual void OnThumbnailDrawCleanup(ThumbnailRequest request)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
123
Source/Editor/Content/Proxy/AudioClipProxy.cs
Normal file
123
Source/Editor/Content/Proxy/AudioClipProxy.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="AudioClip"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class AudioClipProxy : BinaryAssetProxy
|
||||
{
|
||||
private List<AudioClipPreview> _previews;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Audio Clip";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanReimport(ContentItem item)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new AudioClipWindow(editor, (AssetItem)item);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0xB3452B);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(AudioClip);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||
{
|
||||
AudioClipPreview preview;
|
||||
if (_previews != null && _previews.Count > 0)
|
||||
{
|
||||
// Reuse preview control
|
||||
preview = _previews[_previews.Count - 1];
|
||||
_previews.RemoveAt(_previews.Count - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create new preview control
|
||||
preview = new AudioClipPreview
|
||||
{
|
||||
DrawMode = AudioClipPreview.DrawModes.Fill,
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
};
|
||||
}
|
||||
|
||||
// Cached used preview in request data
|
||||
request.Tag = preview;
|
||||
|
||||
// Start loading the audio buffers
|
||||
preview.Asset = (AudioClip)request.Asset;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
// Check if asset is loaded and has audio buffer ready to draw
|
||||
var asset = (AudioClip)request.Asset;
|
||||
var preview = (AudioClipPreview)request.Tag;
|
||||
return asset.IsLoaded && preview.HasData;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
var preview = (AudioClipPreview)request.Tag;
|
||||
preview.Parent = guiRoot;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
|
||||
{
|
||||
var preview = (AudioClipPreview)request.Tag;
|
||||
preview.Parent = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawCleanup(ThumbnailRequest request)
|
||||
{
|
||||
// Unlink asset
|
||||
var preview = (AudioClipPreview)request.Tag;
|
||||
preview.Asset = null;
|
||||
|
||||
// Return the preview control back to the pool
|
||||
if (_previews == null)
|
||||
_previews = new List<AudioClipPreview>(8);
|
||||
_previews.Add(preview);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_previews != null)
|
||||
{
|
||||
for (int i = 0; i < _previews.Count; i++)
|
||||
{
|
||||
_previews[i].Dispose();
|
||||
}
|
||||
_previews.Clear();
|
||||
}
|
||||
_previews = null;
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
72
Source/Editor/Content/Proxy/BinaryAssetProxy.cs
Normal file
72
Source/Editor/Content/Proxy/BinaryAssetProxy.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all binary asset proxy objects used to manage <see cref="BinaryAssetItem"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.AssetProxy" />
|
||||
public abstract class BinaryAssetProxy : AssetProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// The binary asset files extension.
|
||||
/// </summary>
|
||||
public static readonly string Extension = "flax";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsProxyFor(ContentItem item)
|
||||
{
|
||||
return item is BinaryAssetItem binaryAssetItem && TypeName == binaryAssetItem.TypeName;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string FileExtension => Extension;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string TypeName => AssetType.FullName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsProxyFor<T>()
|
||||
{
|
||||
return typeof(T) == AssetType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the asset.
|
||||
/// </summary>
|
||||
public abstract Type AssetType { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override AssetItem ConstructItem(string path, string typeName, ref Guid id)
|
||||
{
|
||||
var type = Scripting.TypeUtils.GetType(typeName).Type;
|
||||
|
||||
if (typeof(TextureBase).IsAssignableFrom(type))
|
||||
return new TextureAssetItem(path, ref id, typeName, type);
|
||||
if (typeof(Model).IsAssignableFrom(type))
|
||||
return new ModelAssetItem(path, ref id, typeName, type);
|
||||
if (typeof(SkinnedModel).IsAssignableFrom(type))
|
||||
return new SkinnedModelAssetItem(path, ref id, typeName, type);
|
||||
|
||||
ContentItemSearchFilter searchFilter;
|
||||
if (typeof(MaterialBase).IsAssignableFrom(type))
|
||||
searchFilter = ContentItemSearchFilter.Material;
|
||||
else if (typeof(Prefab).IsAssignableFrom(type))
|
||||
searchFilter = ContentItemSearchFilter.Prefab;
|
||||
else if (typeof(SceneAsset).IsAssignableFrom(type))
|
||||
searchFilter = ContentItemSearchFilter.Scene;
|
||||
else if (typeof(AudioClip).IsAssignableFrom(type))
|
||||
searchFilter = ContentItemSearchFilter.Audio;
|
||||
else if (typeof(Animation).IsAssignableFrom(type))
|
||||
searchFilter = ContentItemSearchFilter.Animation;
|
||||
else if (typeof(ParticleEmitter).IsAssignableFrom(type) || typeof(ParticleSystem).IsAssignableFrom(type))
|
||||
searchFilter = ContentItemSearchFilter.Particles;
|
||||
else
|
||||
searchFilter = ContentItemSearchFilter.Other;
|
||||
return new BinaryAssetItem(path, ref id, typeName, type, searchFilter);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
Source/Editor/Content/Proxy/CSharpScriptProxy.cs
Normal file
65
Source/Editor/Content/Proxy/CSharpScriptProxy.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Context proxy object for C# script files.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.CSharpScriptProxy" />
|
||||
public class CSharpScriptProxy : ScriptProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// The script files extension filter.
|
||||
/// </summary>
|
||||
public static readonly string ExtensionFiler = "*.cs";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "C# Script";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsProxyFor(ContentItem item)
|
||||
{
|
||||
return item is CSharpScriptItem;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
// Load template
|
||||
var templatePath = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ScriptTemplate.cs");
|
||||
var scriptTemplate = File.ReadAllText(templatePath);
|
||||
|
||||
// Find the module that this script is being added (based on the path)
|
||||
var scriptNamespace = Editor.Instance.GameProject.Name;
|
||||
var project = TryGetProjectAtFolder(outputPath, out var moduleName);
|
||||
if (project != null)
|
||||
{
|
||||
scriptNamespace = moduleName.Length != 0 ? moduleName : project.Name;
|
||||
}
|
||||
scriptNamespace = scriptNamespace.Replace(" ", "");
|
||||
|
||||
// Format
|
||||
var gameSettings = GameSettings.Load();
|
||||
var scriptName = ScriptItem.CreateScriptName(outputPath);
|
||||
var copyrightComment = string.IsNullOrEmpty(gameSettings.CopyrightNotice) ? string.Empty : string.Format("// {0}{1}{1}", gameSettings.CopyrightNotice, Environment.NewLine);
|
||||
scriptTemplate = scriptTemplate.Replace("%copyright%", copyrightComment);
|
||||
scriptTemplate = scriptTemplate.Replace("%class%", scriptName);
|
||||
scriptTemplate = scriptTemplate.Replace("%namespace%", scriptNamespace);
|
||||
|
||||
// Save
|
||||
File.WriteAllText(outputPath, scriptTemplate, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string FileExtension => "cs";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x1c9c2b);
|
||||
}
|
||||
}
|
||||
44
Source/Editor/Content/Proxy/CollisionDataProxy.cs
Normal file
44
Source/Editor/Content/Proxy/CollisionDataProxy.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="CollisionData"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class CollisionDataProxy : BinaryAssetProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Collision Data";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new CollisionDataWindow(editor, item as AssetItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x2c3e50);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(CollisionData);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
if (Editor.CreateAsset(Editor.NewAssetType.CollisionData, outputPath))
|
||||
throw new Exception("Failed to create new asset.");
|
||||
}
|
||||
}
|
||||
}
|
||||
127
Source/Editor/Content/Proxy/ContentProxy.cs
Normal file
127
Source/Editor/Content/Proxy/ContentProxy.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for assets proxy objects used to manage <see cref="ContentItem"/>.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public abstract class ContentProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the asset type name (used by GUI, should be localizable).
|
||||
/// </summary>
|
||||
public abstract string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default name for the new items created by this proxy.
|
||||
/// </summary>
|
||||
public virtual string NewItemName => Name;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this proxy is for the specified item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns><c>true</c> if is proxy for asset item; otherwise, <c>false</c>.</returns>
|
||||
public abstract bool IsProxyFor(ContentItem item);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this proxy is for the specified asset.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if is proxy for asset item; otherwise, <c>false</c>.</returns>
|
||||
public virtual bool IsProxyFor<T>() where T : Asset
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this proxy if for assets.
|
||||
/// </summary>
|
||||
public virtual bool IsAsset => false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file extension used by the items managed by this proxy.
|
||||
/// ALL LOWERCASE! WITHOUT A DOT! example: for 'myFile.TxT' proper extension is 'txt'
|
||||
/// </summary>
|
||||
public abstract string FileExtension { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Opens the specified item.
|
||||
/// </summary>
|
||||
/// <param name="editor"></param>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>Opened window or null if cannot do it.</returns>
|
||||
public abstract EditorWindow Open(Editor editor, ContentItem item);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether content items used by this proxy can be exported.
|
||||
/// </summary>
|
||||
public virtual bool CanExport => false;
|
||||
|
||||
/// <summary>
|
||||
/// Exports the specified item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="outputPath">The output path.</param>
|
||||
public virtual void Export(ContentItem item, string outputPath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this proxy can create items in the specified target location.
|
||||
/// </summary>
|
||||
/// <param name="targetLocation">The target location.</param>
|
||||
/// <returns><c>true</c> if this proxy can create items in the specified target location; otherwise, <c>false</c>.</returns>
|
||||
public virtual bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this proxy can reimport specified item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns><c>true</c> if this proxy can reimport given item; otherwise, <c>false</c>.</returns>
|
||||
public virtual bool CanReimport(ContentItem item)
|
||||
{
|
||||
return CanCreate(item.ParentFolder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new item at the specified output path.
|
||||
/// </summary>
|
||||
/// <param name="outputPath">The output path.</param>
|
||||
/// <param name="arg">The custom argument provided for the item creation. Can be used as a source of data or null.</param>
|
||||
public virtual void Create(string outputPath, object arg)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when content window wants to show the context menu. Allows to add custom functions for the given asset type.
|
||||
/// </summary>
|
||||
/// <param name="menu">The menu.</param>
|
||||
/// <param name="item">The item.</param>
|
||||
public virtual void OnContentWindowContextMenu(ContextMenu menu, ContentItem item)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique accent color for that asset type.
|
||||
/// </summary>
|
||||
public abstract Color AccentColor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Releases resources and unregisters the proxy utilities. Called during editor closing. For custom proxies from game/plugin modules it should be called before scripting reload.
|
||||
/// </summary>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
65
Source/Editor/Content/Proxy/CppScriptProxy.cs
Normal file
65
Source/Editor/Content/Proxy/CppScriptProxy.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Context proxy object for C++ script files.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.CSharpScriptProxy" />
|
||||
public class CppScriptProxy : ScriptProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "C++ Script";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsProxyFor(ContentItem item)
|
||||
{
|
||||
return item is CppScriptItem;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
// Load templates
|
||||
var headerTemplate = File.ReadAllText(StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ScriptTemplate.h"));
|
||||
var sourceTemplate = File.ReadAllText(StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ScriptTemplate.cpp"));
|
||||
|
||||
// Find the module that this script is being added (based on the path)
|
||||
var module = string.Empty;
|
||||
var project = TryGetProjectAtFolder(outputPath, out var moduleName);
|
||||
if (project != null)
|
||||
{
|
||||
module = moduleName.ToUpperInvariant() + "_API ";
|
||||
}
|
||||
|
||||
// Format
|
||||
var gameSettings = GameSettings.Load();
|
||||
var scriptName = ScriptItem.CreateScriptName(outputPath);
|
||||
var filename = Path.GetFileNameWithoutExtension(outputPath);
|
||||
var copyrightComment = string.IsNullOrEmpty(gameSettings.CopyrightNotice) ? string.Empty : string.Format("// {0}{1}{1}", gameSettings.CopyrightNotice, Environment.NewLine);
|
||||
headerTemplate = headerTemplate.Replace("%copyright%", copyrightComment);
|
||||
headerTemplate = headerTemplate.Replace("%class%", scriptName);
|
||||
headerTemplate = headerTemplate.Replace("%module%", module);
|
||||
sourceTemplate = sourceTemplate.Replace("%filename%", filename);
|
||||
sourceTemplate = sourceTemplate.Replace("%copyright%", copyrightComment);
|
||||
sourceTemplate = sourceTemplate.Replace("%class%", scriptName);
|
||||
sourceTemplate = sourceTemplate.Replace("%filename%", filename);
|
||||
|
||||
// Save
|
||||
File.WriteAllText(Path.ChangeExtension(outputPath, ".h"), headerTemplate, Encoding.UTF8);
|
||||
File.WriteAllText(outputPath, sourceTemplate, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string FileExtension => "cpp";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x9c1c9c);
|
||||
}
|
||||
}
|
||||
104
Source/Editor/Content/Proxy/CubeTextureProxy.cs
Normal file
104
Source/Editor/Content/Proxy/CubeTextureProxy.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="CubeTexture"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class CubeTextureProxy : BinaryAssetProxy
|
||||
{
|
||||
private CubeTexturePreview _preview;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Cube Texture";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanReimport(ContentItem item)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new CubeTextureWindow(editor, item as AssetItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x3498db);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(CubeTexture);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||
{
|
||||
if (_preview == null)
|
||||
{
|
||||
_preview = new CubeTexturePreview(false)
|
||||
{
|
||||
RenderOnlyWithWindow = false,
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
};
|
||||
_preview.Task.Enabled = false;
|
||||
|
||||
var eyeAdaptation = _preview.PostFxVolume.EyeAdaptation;
|
||||
eyeAdaptation.Mode = EyeAdaptationMode.None;
|
||||
eyeAdaptation.OverrideFlags |= EyeAdaptationSettingsOverride.Mode;
|
||||
_preview.PostFxVolume.EyeAdaptation = eyeAdaptation;
|
||||
}
|
||||
|
||||
// TODO: disable streaming for asset during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
if (!_preview.HasLoadedAssets)
|
||||
return false;
|
||||
|
||||
// Check if all mip maps are streamed
|
||||
var asset = (CubeTexture)request.Asset;
|
||||
return asset.ResidentMipLevels >= Mathf.Max(1, (int)(asset.MipLevels * ThumbnailsModule.MinimumRequiredResourcesQuality));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
_preview.CubeTexture = (CubeTexture)request.Asset;
|
||||
_preview.Parent = guiRoot;
|
||||
_preview.SyncBackbufferSize();
|
||||
|
||||
_preview.Task.OnDraw();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
|
||||
{
|
||||
_preview.CubeTexture = null;
|
||||
_preview.Parent = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_preview != null)
|
||||
{
|
||||
_preview.Dispose();
|
||||
_preview = null;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
35
Source/Editor/Content/Proxy/FileProxy.cs
Normal file
35
Source/Editor/Content/Proxy/FileProxy.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// File proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.ContentProxy" />
|
||||
public class FileProxy : ContentProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "File";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsProxyFor(ContentItem item)
|
||||
{
|
||||
return item is FileItem;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string FileExtension => string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x441c9c);
|
||||
}
|
||||
}
|
||||
52
Source/Editor/Content/Proxy/FontProxy.cs
Normal file
52
Source/Editor/Content/Proxy/FontProxy.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="FontAsset"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class FontProxy : BinaryAssetProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Font";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanReimport(ContentItem item)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new FontAssetWindow(editor, (AssetItem)item);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x2D74B2);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(FontAsset);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
var asset = FlaxEngine.Content.LoadAsync<FontAsset>(request.Item.ID);
|
||||
guiRoot.AddChild(new Label
|
||||
{
|
||||
Text = asset.FamilyName,
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
Wrapping = TextWrapping.WrapWords
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Source/Editor/Content/Proxy/GameplayGlobalsProxy.cs
Normal file
46
Source/Editor/Content/Proxy/GameplayGlobalsProxy.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="GameplayGlobals"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class GameplayGlobalsProxy : BinaryAssetProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Gameplay Globals";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new GameplayGlobalsWindow(editor, (AssetItem)item);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0xccff33);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(GameplayGlobals);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
var asset = FlaxEngine.Content.CreateVirtualAsset<GameplayGlobals>();
|
||||
if (asset.Save(outputPath))
|
||||
throw new Exception("Failed to create new asset.");
|
||||
FlaxEngine.Object.Destroy(asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
91
Source/Editor/Content/Proxy/IESProfileProxy.cs
Normal file
91
Source/Editor/Content/Proxy/IESProfileProxy.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="IESProfile"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class IESProfileProxy : BinaryAssetProxy
|
||||
{
|
||||
private IESProfilePreview _preview;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "IES Profile";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanReimport(ContentItem item)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new IESProfileWindow(editor, (AssetItem)item);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x695C7F);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(IESProfile);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||
{
|
||||
if (_preview == null)
|
||||
{
|
||||
_preview = new IESProfilePreview
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: disable streaming for asset during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
// Check if asset is streamed enough
|
||||
var asset = (IESProfile)request.Asset;
|
||||
return asset.ResidentMipLevels >= Mathf.Max(1, (int)(asset.MipLevels * ThumbnailsModule.MinimumRequiredResourcesQuality));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
_preview.Asset = (IESProfile)request.Asset;
|
||||
_preview.Parent = guiRoot;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
|
||||
{
|
||||
_preview.Asset = null;
|
||||
_preview.Parent = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_preview != null)
|
||||
{
|
||||
_preview.Dispose();
|
||||
_preview = null;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
183
Source/Editor/Content/Proxy/JsonAssetProxy.cs
Normal file
183
Source/Editor/Content/Proxy/JsonAssetProxy.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Create;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.CustomEditors;
|
||||
using FlaxEditor.CustomEditors.Editors;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all Json asset proxy objects used to manage <see cref="JsonAssetItem"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.AssetProxy" />
|
||||
public abstract class JsonAssetBaseProxy : AssetProxy
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Json assets proxy.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.JsonAssetBaseProxy" />
|
||||
public abstract class JsonAssetProxy : JsonAssetBaseProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// The json files extension.
|
||||
/// </summary>
|
||||
public static readonly string Extension = "json";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Json Asset";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string FileExtension => Extension;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new JsonAssetWindow(editor, (JsonAssetItem)item);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsProxyFor(ContentItem item)
|
||||
{
|
||||
return item is JsonAssetItem;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0xd14f67);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool AcceptsAsset(string typeName, string path)
|
||||
{
|
||||
return typeName == TypeName && base.AcceptsAsset(typeName, path);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override AssetItem ConstructItem(string path, string typeName, ref Guid id)
|
||||
{
|
||||
return new JsonAssetItem(path, id, typeName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic Json asset creating handler. Allows to specify type of the archetype class to use for the asset data object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.Create.CreateFileEntry" />
|
||||
public class GenericJsonCreateEntry : CreateFileEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// The create options.
|
||||
/// </summary>
|
||||
public class Options
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of the archetype class to use for the asset data object to create.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Editor))]
|
||||
[Tooltip("The type of the archetype class to use for the asset data object to create.")]
|
||||
public System.Type Type;
|
||||
|
||||
private sealed class Editor : TypeEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
internal override void Initialize(CustomEditorPresenter presenter, LayoutElementsContainer layout, ValueContainer values)
|
||||
{
|
||||
base.Initialize(presenter, layout, values);
|
||||
|
||||
if (_element != null)
|
||||
{
|
||||
// Define the rule for the types that can be used to create a json data asset
|
||||
_element.CustomControl.CheckValid += type =>
|
||||
type.Type != null &&
|
||||
type.IsClass &&
|
||||
type.Type.IsVisible &&
|
||||
!type.IsAbstract &&
|
||||
!type.IsGenericType &&
|
||||
type.Type.GetConstructor(Type.EmptyTypes) != null &&
|
||||
!typeof(FlaxEngine.Object).IsAssignableFrom(type.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Options _options = new Options();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object Settings => _options;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SettingsCreateEntry"/> class.
|
||||
/// </summary>
|
||||
/// <param name="resultUrl">The result file url.</param>
|
||||
public GenericJsonCreateEntry(string resultUrl)
|
||||
: base("Json Asset", resultUrl)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Create()
|
||||
{
|
||||
// Create settings asset object and serialize it to pure json asset
|
||||
var data = Activator.CreateInstance(_options.Type);
|
||||
return Editor.SaveJsonAsset(ResultUrl, data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic Json assets proxy (supports all json assets that don't have dedicated proxy).
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.JsonAssetBaseProxy" />
|
||||
public sealed class GenericJsonAssetProxy : JsonAssetProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string TypeName => typeof(JsonAsset).FullName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool AcceptsAsset(string typeName, string path)
|
||||
{
|
||||
return path.EndsWith(FileExtension, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
Editor.Instance.ContentImporting.Create(new GenericJsonCreateEntry(outputPath));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Content proxy for a json assets of the given type that can be spawned in the editor.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.JsonAssetProxy" />
|
||||
public sealed class SpawnableJsonAssetProxy<T> : JsonAssetProxy where T : new()
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name { get; } = CustomEditors.CustomEditorsUtil.GetPropertyNameUI(typeof(T).Name);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
Editor.SaveJsonAsset(outputPath, new T());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string TypeName { get; } = typeof(T).FullName;
|
||||
}
|
||||
}
|
||||
44
Source/Editor/Content/Proxy/MaterialFunctionProxy.cs
Normal file
44
Source/Editor/Content/Proxy/MaterialFunctionProxy.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="MaterialFunction"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class MaterialFunctionProxy : BinaryAssetProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Material Function";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new MaterialFunctionWindow(editor, item as AssetItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x6da016);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(MaterialFunction);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
if (Editor.CreateAsset(Editor.NewAssetType.MaterialFunction, outputPath))
|
||||
throw new Exception("Failed to create new asset.");
|
||||
}
|
||||
}
|
||||
}
|
||||
106
Source/Editor/Content/Proxy/MaterialInstanceProxy.cs
Normal file
106
Source/Editor/Content/Proxy/MaterialInstanceProxy.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="MaterialInstance"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class MaterialInstanceProxy : BinaryAssetProxy
|
||||
{
|
||||
private MaterialPreview _preview;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Material Instance";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new MaterialInstanceWindow(editor, item as AssetItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x2c3e50);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(MaterialInstance);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
if (Editor.CreateAsset(Editor.NewAssetType.MaterialInstance, outputPath))
|
||||
throw new Exception("Failed to create new asset.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||
{
|
||||
if (_preview == null)
|
||||
{
|
||||
_preview = new MaterialPreview(false)
|
||||
{
|
||||
RenderOnlyWithWindow = false,
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
};
|
||||
_preview.Task.Enabled = false;
|
||||
|
||||
var eyeAdaptation = _preview.PostFxVolume.EyeAdaptation;
|
||||
eyeAdaptation.Mode = EyeAdaptationMode.None;
|
||||
eyeAdaptation.OverrideFlags |= EyeAdaptationSettingsOverride.Mode;
|
||||
_preview.PostFxVolume.EyeAdaptation = eyeAdaptation;
|
||||
}
|
||||
|
||||
// TODO: disable streaming for dependant assets during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
return _preview.HasLoadedAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
_preview.Material = (MaterialInstance)request.Asset;
|
||||
_preview.Parent = guiRoot;
|
||||
_preview.SyncBackbufferSize();
|
||||
|
||||
_preview.Task.OnDraw();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
|
||||
{
|
||||
_preview.Material = null;
|
||||
_preview.Parent = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_preview != null)
|
||||
{
|
||||
_preview.Dispose();
|
||||
_preview = null;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
152
Source/Editor/Content/Proxy/MaterialProxy.cs
Normal file
152
Source/Editor/Content/Proxy/MaterialProxy.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="Material"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class MaterialProxy : BinaryAssetProxy
|
||||
{
|
||||
private MaterialPreview _preview;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Material";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new MaterialWindow(editor, item as AssetItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x16a085);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(Material);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
if (Editor.CreateAsset(Editor.NewAssetType.Material, outputPath))
|
||||
throw new Exception("Failed to create new asset.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnContentWindowContextMenu(ContextMenu menu, ContentItem item)
|
||||
{
|
||||
base.OnContentWindowContextMenu(menu, item);
|
||||
|
||||
if (item is BinaryAssetItem binaryAssetItem)
|
||||
{
|
||||
var button = menu.AddButton("Create Material Instance", CreateMaterialInstanceClicked);
|
||||
button.Tag = binaryAssetItem;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateMaterialInstanceClicked(ContextMenuButton obj)
|
||||
{
|
||||
var binaryAssetItem = (BinaryAssetItem)obj.Tag;
|
||||
CreateMaterialInstance(binaryAssetItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the material instance from the given material.
|
||||
/// </summary>
|
||||
/// <param name="materialItem">The material item to use as a base material.</param>
|
||||
public static void CreateMaterialInstance(BinaryAssetItem materialItem)
|
||||
{
|
||||
if (materialItem == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
var materialInstanceProxy = Editor.Instance.ContentDatabase.GetProxy<MaterialInstance>();
|
||||
Editor.Instance.Windows.ContentWin.NewItem(materialInstanceProxy, null, item => OnMaterialInstanceCreated(item, materialItem));
|
||||
}
|
||||
|
||||
private static void OnMaterialInstanceCreated(ContentItem item, BinaryAssetItem materialItem)
|
||||
{
|
||||
var assetItem = (AssetItem)item;
|
||||
var materialInstance = FlaxEngine.Content.LoadAsync<MaterialInstance>(assetItem.ID);
|
||||
if (materialInstance == null || materialInstance.WaitForLoaded())
|
||||
{
|
||||
Editor.LogError("Failed to load created material instance.");
|
||||
return;
|
||||
}
|
||||
|
||||
materialInstance.BaseMaterial = FlaxEngine.Content.LoadAsync<Material>(materialItem.ID);
|
||||
materialInstance.Save();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||
{
|
||||
if (_preview == null)
|
||||
{
|
||||
_preview = new MaterialPreview(false)
|
||||
{
|
||||
RenderOnlyWithWindow = false,
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
};
|
||||
_preview.Task.Enabled = false;
|
||||
|
||||
var eyeAdaptation = _preview.PostFxVolume.EyeAdaptation;
|
||||
eyeAdaptation.Mode = EyeAdaptationMode.None;
|
||||
eyeAdaptation.OverrideFlags |= EyeAdaptationSettingsOverride.Mode;
|
||||
_preview.PostFxVolume.EyeAdaptation = eyeAdaptation;
|
||||
}
|
||||
|
||||
// TODO: disable streaming for dependant assets during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
return _preview.HasLoadedAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
_preview.Material = (Material)request.Asset;
|
||||
_preview.Parent = guiRoot;
|
||||
_preview.SyncBackbufferSize();
|
||||
|
||||
_preview.Task.OnDraw();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
|
||||
{
|
||||
_preview.Material = null;
|
||||
_preview.Parent = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_preview != null)
|
||||
{
|
||||
_preview.Dispose();
|
||||
_preview = null;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
104
Source/Editor/Content/Proxy/ModelProxy.cs
Normal file
104
Source/Editor/Content/Proxy/ModelProxy.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="Model"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class ModelProxy : BinaryAssetProxy
|
||||
{
|
||||
private ModelPreview _preview;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Model";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanReimport(ContentItem item)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new ModelWindow(editor, item as AssetItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0xe67e22);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(Model);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||
{
|
||||
if (_preview == null)
|
||||
{
|
||||
_preview = new ModelPreview(false)
|
||||
{
|
||||
RenderOnlyWithWindow = false,
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
};
|
||||
_preview.Task.Enabled = false;
|
||||
|
||||
var eyeAdaptation = _preview.PostFxVolume.EyeAdaptation;
|
||||
eyeAdaptation.Mode = EyeAdaptationMode.None;
|
||||
eyeAdaptation.OverrideFlags |= EyeAdaptationSettingsOverride.Mode;
|
||||
_preview.PostFxVolume.EyeAdaptation = eyeAdaptation;
|
||||
}
|
||||
|
||||
// TODO: disable streaming for asset during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
if (!_preview.HasLoadedAssets)
|
||||
return false;
|
||||
|
||||
// Check if asset is streamed enough
|
||||
var asset = (Model)request.Asset;
|
||||
return asset.LoadedLODs >= Mathf.Max(1, (int)(asset.LODs.Length * ThumbnailsModule.MinimumRequiredResourcesQuality));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
_preview.Model = (Model)request.Asset;
|
||||
_preview.Parent = guiRoot;
|
||||
_preview.SyncBackbufferSize();
|
||||
|
||||
_preview.Task.OnDraw();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
|
||||
{
|
||||
_preview.Model = null;
|
||||
_preview.Parent = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_preview != null)
|
||||
{
|
||||
_preview.Dispose();
|
||||
_preview = null;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
44
Source/Editor/Content/Proxy/ParticleEmitterFunctionProxy.cs
Normal file
44
Source/Editor/Content/Proxy/ParticleEmitterFunctionProxy.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="ParticleEmitterFunction"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class ParticleEmitterFunctionProxy : BinaryAssetProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Particle Emitter Function";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new ParticleEmitterFunctionWindow(editor, item as AssetItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x1795a3);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(ParticleEmitterFunction);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
if (Editor.CreateAsset(Editor.NewAssetType.ParticleEmitterFunction, outputPath))
|
||||
throw new Exception("Failed to create new asset.");
|
||||
}
|
||||
}
|
||||
}
|
||||
153
Source/Editor/Content/Proxy/ParticleEmitterProxy.cs
Normal file
153
Source/Editor/Content/Proxy/ParticleEmitterProxy.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Create;
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="ParticleEmitter"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class ParticleEmitterProxy : BinaryAssetProxy
|
||||
{
|
||||
private ParticleEmitterPreview _preview;
|
||||
private ThumbnailRequest _warmupRequest;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Particle Emitter";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new ParticleEmitterWindow(editor, item as AssetItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0xFF79D2B0);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(ParticleEmitter);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
Editor.Instance.ContentImporting.Create(new ParticleEmitterCreateEntry(outputPath));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||
{
|
||||
if (_preview == null)
|
||||
{
|
||||
_preview = new ParticleEmitterPreview(false)
|
||||
{
|
||||
RenderOnlyWithWindow = false,
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
};
|
||||
_preview.Task.Enabled = false;
|
||||
|
||||
var eyeAdaptation = _preview.PostFxVolume.EyeAdaptation;
|
||||
eyeAdaptation.Mode = EyeAdaptationMode.None;
|
||||
eyeAdaptation.OverrideFlags |= EyeAdaptationSettingsOverride.Mode;
|
||||
_preview.PostFxVolume.EyeAdaptation = eyeAdaptation;
|
||||
}
|
||||
|
||||
// Mark for initial warmup
|
||||
request.Tag = 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
var state = (int)request.Tag;
|
||||
if (state == 2)
|
||||
return true;
|
||||
|
||||
// Allow only one request at once during warmup time
|
||||
if (_warmupRequest != null && _warmupRequest != request)
|
||||
return false;
|
||||
|
||||
// Ensure assets are ready to be used
|
||||
if (!_preview.HasLoadedAssets)
|
||||
return false;
|
||||
var asset = (ParticleEmitter)request.Asset;
|
||||
if (!asset.IsLoaded)
|
||||
return false;
|
||||
|
||||
if (state == 0)
|
||||
{
|
||||
// Start the warmup
|
||||
_warmupRequest = request;
|
||||
request.Tag = 1;
|
||||
_preview.Emitter = asset;
|
||||
}
|
||||
else if (_preview.PreviewActor.Time >= 0.6f)
|
||||
{
|
||||
// End the warmup
|
||||
request.Tag = 2;
|
||||
_preview.FitIntoView();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle warmup time for the preview
|
||||
_preview.PreviewActor.UpdateSimulation();
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
_preview.Parent = guiRoot;
|
||||
_preview.SyncBackbufferSize();
|
||||
|
||||
_preview.Task.OnDraw();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
|
||||
{
|
||||
if (_warmupRequest == request)
|
||||
{
|
||||
_warmupRequest = null;
|
||||
}
|
||||
|
||||
_preview.Emitter = null;
|
||||
_preview.Parent = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawCleanup(ThumbnailRequest request)
|
||||
{
|
||||
if (_warmupRequest == request)
|
||||
{
|
||||
_warmupRequest = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_preview != null)
|
||||
{
|
||||
_preview.Dispose();
|
||||
_preview = null;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
153
Source/Editor/Content/Proxy/ParticleSystemProxy.cs
Normal file
153
Source/Editor/Content/Proxy/ParticleSystemProxy.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="ParticleSystem"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class ParticleSystemProxy : BinaryAssetProxy
|
||||
{
|
||||
private ParticleSystemPreview _preview;
|
||||
private ThumbnailRequest _warmupRequest;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Particle System";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new ParticleSystemWindow(editor, item as AssetItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0xFF790200);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(ParticleSystem);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
if (Editor.CreateAsset(Editor.NewAssetType.ParticleSystem, outputPath))
|
||||
throw new Exception("Failed to create new asset.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||
{
|
||||
if (_preview == null)
|
||||
{
|
||||
_preview = new ParticleEmitterPreview(false)
|
||||
{
|
||||
RenderOnlyWithWindow = false,
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
};
|
||||
_preview.Task.Enabled = false;
|
||||
|
||||
var eyeAdaptation = _preview.PostFxVolume.EyeAdaptation;
|
||||
eyeAdaptation.Mode = EyeAdaptationMode.None;
|
||||
eyeAdaptation.OverrideFlags |= EyeAdaptationSettingsOverride.Mode;
|
||||
_preview.PostFxVolume.EyeAdaptation = eyeAdaptation;
|
||||
}
|
||||
|
||||
// Mark for initial warmup
|
||||
request.Tag = 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
var state = (int)request.Tag;
|
||||
if (state == 2)
|
||||
return true;
|
||||
|
||||
// Allow only one request at once during warmup time
|
||||
if (_warmupRequest != null && _warmupRequest != request)
|
||||
return false;
|
||||
|
||||
// Ensure assets are ready to be used
|
||||
if (!_preview.HasLoadedAssets)
|
||||
return false;
|
||||
var asset = (ParticleSystem)request.Asset;
|
||||
if (!asset.IsLoaded)
|
||||
return false;
|
||||
|
||||
if (state == 0)
|
||||
{
|
||||
// Start the warmup
|
||||
_warmupRequest = request;
|
||||
request.Tag = 1;
|
||||
_preview.System = asset;
|
||||
}
|
||||
else if (_preview.PreviewActor.Time >= Mathf.Min(0.2f * asset.Duration, 0.6f))
|
||||
{
|
||||
// End the warmup
|
||||
request.Tag = 2;
|
||||
_preview.FitIntoView();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle warmup time for the preview
|
||||
_preview.PreviewActor.UpdateSimulation();
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
_preview.Parent = guiRoot;
|
||||
_preview.SyncBackbufferSize();
|
||||
|
||||
_preview.Task.OnDraw();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
|
||||
{
|
||||
if (_warmupRequest == request)
|
||||
{
|
||||
_warmupRequest = null;
|
||||
}
|
||||
|
||||
_preview.System = null;
|
||||
_preview.Parent = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawCleanup(ThumbnailRequest request)
|
||||
{
|
||||
if (_warmupRequest == request)
|
||||
{
|
||||
_warmupRequest = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_preview != null)
|
||||
{
|
||||
_preview.Dispose();
|
||||
_preview = null;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
194
Source/Editor/Content/Proxy/PrefabProxy.cs
Normal file
194
Source/Editor/Content/Proxy/PrefabProxy.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Content proxy for <see cref="PrefabItem"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.JsonAssetBaseProxy" />
|
||||
public sealed class PrefabProxy : JsonAssetBaseProxy
|
||||
{
|
||||
private PrefabPreview _preview;
|
||||
|
||||
/// <summary>
|
||||
/// The prefab files extension.
|
||||
/// </summary>
|
||||
public static readonly string Extension = "prefab";
|
||||
|
||||
/// <summary>
|
||||
/// The prefab asset data typename.
|
||||
/// </summary>
|
||||
public static readonly string AssetTypename = typeof(Prefab).FullName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Prefab";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string FileExtension => Extension;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new PrefabWindow(editor, (AssetItem)item);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsProxyFor(ContentItem item)
|
||||
{
|
||||
return item is PrefabItem;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsProxyFor<T>()
|
||||
{
|
||||
return typeof(T) == typeof(Prefab);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x7eef21);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string TypeName => AssetTypename;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override AssetItem ConstructItem(string path, string typeName, ref Guid id)
|
||||
{
|
||||
return new PrefabItem(path, id);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
var actor = arg as Actor;
|
||||
if (actor == null)
|
||||
{
|
||||
// Create default prefab root object
|
||||
actor = new EmptyActor();
|
||||
actor.Name = "Root";
|
||||
|
||||
// Cleanup it after usage
|
||||
Object.Destroy(actor, 20.0f);
|
||||
}
|
||||
|
||||
PrefabManager.CreatePrefab(actor, outputPath, true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||
{
|
||||
if (_preview == null)
|
||||
{
|
||||
_preview = new PrefabPreview(false)
|
||||
{
|
||||
RenderOnlyWithWindow = false,
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
};
|
||||
_preview.Task.Enabled = false;
|
||||
|
||||
var eyeAdaptation = _preview.PostFxVolume.EyeAdaptation;
|
||||
eyeAdaptation.Mode = EyeAdaptationMode.None;
|
||||
eyeAdaptation.OverrideFlags |= EyeAdaptationSettingsOverride.Mode;
|
||||
_preview.PostFxVolume.EyeAdaptation = eyeAdaptation;
|
||||
}
|
||||
|
||||
// TODO: disable streaming for asset during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
if (!_preview.HasLoadedAssets)
|
||||
return false;
|
||||
|
||||
// Check if asset is streamed enough
|
||||
var asset = (Prefab)request.Asset;
|
||||
return asset.IsLoaded;
|
||||
}
|
||||
|
||||
private void Prepare(Actor actor)
|
||||
{
|
||||
if (actor is TextRender textRender)
|
||||
{
|
||||
textRender.UpdateLayout();
|
||||
}
|
||||
|
||||
for (int i = 0; i < actor.ChildrenCount; i++)
|
||||
{
|
||||
Prepare(actor.GetChild(i));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
_preview.Prefab = (Prefab)request.Asset;
|
||||
_preview.Parent = guiRoot;
|
||||
_preview.Scale = Vector2.One;
|
||||
_preview.ShowDefaultSceneActors = true;
|
||||
_preview.SyncBackbufferSize();
|
||||
|
||||
// Special case for UI prefabs
|
||||
if (_preview.Instance is UIControl uiControl && uiControl.HasControl)
|
||||
{
|
||||
// Ensure to place UI in a proper way
|
||||
uiControl.Control.Location = Vector2.Zero;
|
||||
uiControl.Control.Scale *= PreviewsCache.AssetIconSize / uiControl.Control.Size.MaxValue;
|
||||
uiControl.Control.AnchorPreset = AnchorPresets.TopLeft;
|
||||
uiControl.Control.AnchorPreset = AnchorPresets.MiddleCenter;
|
||||
|
||||
// Tweak preview
|
||||
_preview.ShowDefaultSceneActors = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update some actors data (some actor types update bounds/data later but its required to be done before rendering)
|
||||
Prepare(_preview.Instance);
|
||||
|
||||
// Auto fit actor to camera
|
||||
float targetSize = 30.0f;
|
||||
BoundingBox bounds;
|
||||
Editor.GetActorEditorBox(_preview.Instance, out bounds);
|
||||
float maxSize = Mathf.Max(0.001f, bounds.Size.MaxValue);
|
||||
_preview.Instance.Scale = new Vector3(targetSize / maxSize);
|
||||
_preview.Instance.Position = Vector3.Zero;
|
||||
}
|
||||
|
||||
_preview.Task.OnDraw();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
|
||||
{
|
||||
_preview.Prefab = null;
|
||||
_preview.Parent = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_preview != null)
|
||||
{
|
||||
_preview.Dispose();
|
||||
_preview = null;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Source/Editor/Content/Proxy/PreviewsCacheProxy.cs
Normal file
31
Source/Editor/Content/Proxy/PreviewsCacheProxy.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="PreviewsCache"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class PreviewsCacheProxy : BinaryAssetProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Previews Cache";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new PreviewsCacheWindow(editor, (AssetItem)item);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x80FFAE);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(PreviewsCache);
|
||||
}
|
||||
}
|
||||
44
Source/Editor/Content/Proxy/SceneAnimationProxy.cs
Normal file
44
Source/Editor/Content/Proxy/SceneAnimationProxy.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="SceneAnimation"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class SceneAnimationProxy : BinaryAssetProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Scene Animation";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new SceneAnimationWindow(editor, item as AssetItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0xff5c4a87);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(SceneAnimation);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
if (Editor.CreateAsset(Editor.NewAssetType.SceneAnimation, outputPath))
|
||||
throw new Exception("Failed to create new asset.");
|
||||
}
|
||||
}
|
||||
}
|
||||
65
Source/Editor/Content/Proxy/SceneProxy.cs
Normal file
65
Source/Editor/Content/Proxy/SceneProxy.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Content proxy for <see cref="SceneItem"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.JsonAssetBaseProxy" />
|
||||
public sealed class SceneProxy : JsonAssetBaseProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// The scene files extension.
|
||||
/// </summary>
|
||||
public static readonly string Extension = "scene";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Scene";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string FileExtension => Extension;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsProxyFor(ContentItem item)
|
||||
{
|
||||
return item is SceneItem;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
Editor.Instance.Scene.CreateSceneFile(outputPath);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
// Load scene
|
||||
Editor.Instance.Scene.OpenScene(((SceneItem)item).ID);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0xbb37ef);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string TypeName => Scene.AssetTypename;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override AssetItem ConstructItem(string path, string typeName, ref Guid id)
|
||||
{
|
||||
return new SceneItem(path, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
144
Source/Editor/Content/Proxy/ScriptProxy.cs
Normal file
144
Source/Editor/Content/Proxy/ScriptProxy.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Context proxy object for script files (represented by <see cref="ScriptItem"/>).
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.ContentProxy" />
|
||||
public abstract class ScriptProxy : ContentProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// Tries the get project that is related to the given source file path. Works only for source files located under Source folder of the project.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="moduleName">The name of the module where the source file lays. Computed from path. Might be invalid..</param>
|
||||
/// <returns>The found project or null.</returns>
|
||||
protected ProjectInfo TryGetProjectAtFolder(string path, out string moduleName)
|
||||
{
|
||||
moduleName = string.Empty;
|
||||
var projects = Editor.Instance.GameProject.GetAllProjects();
|
||||
foreach (var project in projects)
|
||||
{
|
||||
var projectSourceFolderPath = StringUtils.CombinePaths(project.ProjectFolderPath, "Source");
|
||||
if (path == projectSourceFolderPath)
|
||||
return project;
|
||||
if (path.StartsWith(projectSourceFolderPath))
|
||||
{
|
||||
var localProjectPath = path.Substring(projectSourceFolderPath.Length + 1);
|
||||
var split = localProjectPath.IndexOf('/');
|
||||
if (split != -1)
|
||||
{
|
||||
moduleName = localProjectPath.Substring(0, split);
|
||||
}
|
||||
return project;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string NewItemName => "Script";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
if (targetLocation.CanHaveScripts)
|
||||
{
|
||||
var path = targetLocation.Path;
|
||||
var projects = Editor.Instance.GameProject.GetAllProjects();
|
||||
foreach (var project in projects)
|
||||
{
|
||||
var projectSourceFolderPath = StringUtils.CombinePaths(project.ProjectFolderPath, "Source");
|
||||
if (path == projectSourceFolderPath)
|
||||
return false;
|
||||
if (path.StartsWith(projectSourceFolderPath))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
// Load template
|
||||
var templatePath = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ScriptTemplate.cs");
|
||||
var scriptTemplate = File.ReadAllText(templatePath);
|
||||
var scriptNamespace = Editor.Instance.GameProject.Name.Replace(" ", "") + ".Source";
|
||||
|
||||
// Get directories
|
||||
var sourceDirectory = Globals.ProjectFolder.Replace('\\', '/') + "/Source/";
|
||||
var outputDirectory = new FileInfo(outputPath).DirectoryName.Replace('\\', '/');
|
||||
|
||||
// Generate "sub" namespace from relative path between source root and output path
|
||||
// NOTE: Could probably use Replace instead substring, but this is faster :)
|
||||
var subNamespaceStr = outputDirectory.Substring(sourceDirectory.Length - 1).Replace(" ", "").Replace(".", "").Replace('/', '.');
|
||||
|
||||
// Replace all namespace invalid characters
|
||||
// NOTE: Need to handle number sequence at the beginning since namespace which begin with numeric sequence are invalid
|
||||
string subNamespace = string.Empty;
|
||||
bool isStart = true;
|
||||
for (int pos = 0; pos < subNamespaceStr.Length; pos++)
|
||||
{
|
||||
var c = subNamespaceStr[pos];
|
||||
|
||||
if (isStart)
|
||||
{
|
||||
// Skip characters that cannot start the sub namespace
|
||||
if (char.IsLetter(c))
|
||||
{
|
||||
isStart = false;
|
||||
subNamespace += '.';
|
||||
subNamespace += c;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add only valid characters
|
||||
if (char.IsLetterOrDigit(c) || c == '_')
|
||||
{
|
||||
subNamespace += c;
|
||||
}
|
||||
// Check for sub namespace start
|
||||
else if (c == '.')
|
||||
{
|
||||
isStart = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Append if valid
|
||||
if (subNamespace.Length > 1)
|
||||
scriptNamespace += subNamespace;
|
||||
|
||||
// Format
|
||||
var gameSettings = GameSettings.Load();
|
||||
var scriptName = ScriptItem.CreateScriptName(outputPath);
|
||||
var copyrightComment = string.IsNullOrEmpty(gameSettings.CopyrightNotice) ? string.Empty : string.Format("// {0}{1}{1}", gameSettings.CopyrightNotice, Environment.NewLine);
|
||||
scriptTemplate = scriptTemplate.Replace("%copyright%", copyrightComment);
|
||||
scriptTemplate = scriptTemplate.Replace("%class%", scriptName);
|
||||
scriptTemplate = scriptTemplate.Replace("%namespace%", scriptNamespace);
|
||||
|
||||
// Save
|
||||
File.WriteAllText(outputPath, scriptTemplate, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
Editor.Instance.CodeEditing.OpenFile(item.Path);
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x1c9c2b);
|
||||
}
|
||||
}
|
||||
56
Source/Editor/Content/Proxy/SettingsProxy.cs
Normal file
56
Source/Editor/Content/Proxy/SettingsProxy.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Create;
|
||||
using FlaxEditor.Content.Settings;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Content proxy for json settings assets (e.g <see cref="GameSettings"/> or <see cref="TimeSettings"/>).
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.JsonAssetProxy" />
|
||||
public sealed class SettingsProxy : JsonAssetProxy
|
||||
{
|
||||
private readonly Type _type;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SettingsProxy"/> class.
|
||||
/// </summary>
|
||||
/// <param name="type">The settings asset type (must be subclass of SettingsBase type).</param>
|
||||
public SettingsProxy(Type type)
|
||||
{
|
||||
_type = type;
|
||||
TypeName = type.FullName;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Settings";
|
||||
//public override string Name { get; } = CustomEditors.CustomEditorsUtil.GetPropertyNameUI(_type.Name);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
// Use proxy only for GameSettings for creating
|
||||
if (_type != typeof(GameSettings))
|
||||
return false;
|
||||
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
Editor.Instance.ContentImporting.Create(new SettingsCreateEntry(outputPath));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsProxyFor<T>()
|
||||
{
|
||||
return typeof(T) == _type;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string TypeName { get; }
|
||||
}
|
||||
}
|
||||
43
Source/Editor/Content/Proxy/ShaderProxy.cs
Normal file
43
Source/Editor/Content/Proxy/ShaderProxy.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="Shader"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public sealed class ShaderProxy : BinaryAssetProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Shader";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanReimport(ContentItem item)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
var assetItem = (BinaryAssetItem)item;
|
||||
var asset = FlaxEngine.Content.Load<Shader>(assetItem.ID);
|
||||
if (asset)
|
||||
{
|
||||
var source = Editor.GetShaderSourceCode(asset);
|
||||
Utilities.Utils.ShowSourceCodeWindow(source, "Shader Source");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x7542f5);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(Shader);
|
||||
}
|
||||
}
|
||||
68
Source/Editor/Content/Proxy/ShaderSourceProxy.cs
Normal file
68
Source/Editor/Content/Proxy/ShaderSourceProxy.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Context proxy object for shader source files (represented by <see cref="ShaderSourceItem"/>).
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.ContentProxy" />
|
||||
public class ShaderSourceProxy : ContentProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
// Allow to create shaders only under '<root>/Source/Shaders' directory and it's sub-dirs
|
||||
var prevTargetLocation = targetLocation;
|
||||
while (targetLocation.ParentFolder?.ParentFolder != null)
|
||||
{
|
||||
prevTargetLocation = targetLocation;
|
||||
targetLocation = targetLocation.ParentFolder;
|
||||
}
|
||||
return targetLocation.ShortName == "Source" && prevTargetLocation.ShortName == "Shaders";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
// Load template
|
||||
var shaderTemplate = File.ReadAllText(StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ShaderTemplate.shader"));
|
||||
|
||||
// Format
|
||||
var gameSettings = GameSettings.Load();
|
||||
var copyrightComment = string.IsNullOrEmpty(gameSettings.CopyrightNotice) ? string.Empty : string.Format("// {0}{1}{1}", gameSettings.CopyrightNotice, Environment.NewLine);
|
||||
shaderTemplate = shaderTemplate.Replace("%copyright%", copyrightComment);
|
||||
|
||||
// Save
|
||||
File.WriteAllText(outputPath, shaderTemplate, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
Editor.Instance.CodeEditing.OpenFile(item.Path);
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x7542f5);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string FileExtension => "shader";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Shader Source";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsProxyFor(ContentItem item)
|
||||
{
|
||||
return item is ShaderSourceItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
Source/Editor/Content/Proxy/SkeletonMaskProxy.cs
Normal file
44
Source/Editor/Content/Proxy/SkeletonMaskProxy.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="SkeletonMask"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class SkeletonMaskProxy : BinaryAssetProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Skeleton Mask";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new SkeletonMaskWindow(editor, item as AssetItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x00B31C);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(SkeletonMask);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
if (Editor.CreateAsset(Editor.NewAssetType.SkeletonMask, outputPath))
|
||||
throw new Exception("Failed to create new asset.");
|
||||
}
|
||||
}
|
||||
}
|
||||
104
Source/Editor/Content/Proxy/SkinnedModelProxy.cs
Normal file
104
Source/Editor/Content/Proxy/SkinnedModelProxy.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="SkinnedModel"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class SkinnedModelProxy : BinaryAssetProxy
|
||||
{
|
||||
private AnimatedModelPreview _preview;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Skinned Model";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanReimport(ContentItem item)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new SkinnedModelWindow(editor, item as AssetItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0xB30031);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(SkinnedModel);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||
{
|
||||
if (_preview == null)
|
||||
{
|
||||
_preview = new AnimatedModelPreview(false)
|
||||
{
|
||||
RenderOnlyWithWindow = false,
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
};
|
||||
_preview.Task.Enabled = false;
|
||||
|
||||
var eyeAdaptation = _preview.PostFxVolume.EyeAdaptation;
|
||||
eyeAdaptation.Mode = EyeAdaptationMode.None;
|
||||
eyeAdaptation.OverrideFlags |= EyeAdaptationSettingsOverride.Mode;
|
||||
_preview.PostFxVolume.EyeAdaptation = eyeAdaptation;
|
||||
}
|
||||
|
||||
// TODO: disable streaming for asset during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
if (!_preview.HasLoadedAssets)
|
||||
return false;
|
||||
|
||||
// Check if asset is streamed enough
|
||||
var asset = (SkinnedModel)request.Asset;
|
||||
return asset.LoadedLODs >= Mathf.Max(1, (int)(asset.LODs.Length * ThumbnailsModule.MinimumRequiredResourcesQuality));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
_preview.SkinnedModel = (SkinnedModel)request.Asset;
|
||||
_preview.Parent = guiRoot;
|
||||
_preview.SyncBackbufferSize();
|
||||
|
||||
_preview.Task.OnDraw();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
|
||||
{
|
||||
_preview.SkinnedModel = null;
|
||||
_preview.Parent = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_preview != null)
|
||||
{
|
||||
_preview.Dispose();
|
||||
_preview = null;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
91
Source/Editor/Content/Proxy/SpriteAtlasProxy.cs
Normal file
91
Source/Editor/Content/Proxy/SpriteAtlasProxy.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="SpriteAtlas"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public sealed class SpriteAtlasProxy : BinaryAssetProxy
|
||||
{
|
||||
private SimpleSpriteAtlasPreview _preview;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Sprite Atlas";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanReimport(ContentItem item)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new SpriteAtlasWindow(editor, (AssetItem)item);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x5C7F69);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(SpriteAtlas);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||
{
|
||||
if (_preview == null)
|
||||
{
|
||||
_preview = new SimpleSpriteAtlasPreview
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: disable streaming for asset during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
// Check if asset is streamed enough
|
||||
var asset = (SpriteAtlas)request.Asset;
|
||||
return asset.ResidentMipLevels >= Mathf.Max(1, (int)(asset.MipLevels * ThumbnailsModule.MinimumRequiredResourcesQuality));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
_preview.Asset = (SpriteAtlas)request.Asset;
|
||||
_preview.Parent = guiRoot;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
|
||||
{
|
||||
_preview.Asset = null;
|
||||
_preview.Parent = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_preview != null)
|
||||
{
|
||||
_preview.Dispose();
|
||||
_preview = null;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
93
Source/Editor/Content/Proxy/TextureProxy.cs
Normal file
93
Source/Editor/Content/Proxy/TextureProxy.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="Texture"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public sealed class TextureProxy : BinaryAssetProxy
|
||||
{
|
||||
private SimpleTexturePreview _preview;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Texture";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanReimport(ContentItem item)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new TextureWindow(editor, (AssetItem)item);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0x25B84C);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(Texture);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||
{
|
||||
if (_preview == null)
|
||||
{
|
||||
_preview = new SimpleTexturePreview
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: disable streaming for asset during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
// Check if asset is streamed enough
|
||||
var asset = (Texture)request.Asset;
|
||||
var mipLevels = asset.MipLevels;
|
||||
var minMipLevels = Mathf.Min(mipLevels, 7);
|
||||
return asset.ResidentMipLevels >= Mathf.Max(minMipLevels, (int)(mipLevels * ThumbnailsModule.MinimumRequiredResourcesQuality));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
_preview.Asset = (Texture)request.Asset;
|
||||
_preview.Parent = guiRoot;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
|
||||
{
|
||||
_preview.Asset = null;
|
||||
_preview.Parent = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_preview != null)
|
||||
{
|
||||
_preview.Dispose();
|
||||
_preview = null;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
94
Source/Editor/Content/Proxy/VisualScriptProxy.cs
Normal file
94
Source/Editor/Content/Proxy/VisualScriptProxy.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.Content.Create;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Json;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="VisualScript"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public class VisualScriptProxy : BinaryAssetProxy, IScriptTypesContainer
|
||||
{
|
||||
internal VisualScriptProxy()
|
||||
{
|
||||
TypeUtils.CustomTypes.Add(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Visual Script";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new VisualScriptWindow(editor, item as VisualScriptItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0xf5cb42);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(VisualScript);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
Editor.Instance.ContentImporting.Create(new VisualScriptCreateEntry(outputPath));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override AssetItem ConstructItem(string path, string typeName, ref Guid id)
|
||||
{
|
||||
return new VisualScriptItem(path, ref id, typeName, AssetType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
TypeUtils.CustomTypes.Remove(this);
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScriptType GetType(string typeName)
|
||||
{
|
||||
// For Visual Script types the typeName is an asset id as Guid string
|
||||
if (typeName.Length == 32)
|
||||
{
|
||||
JsonSerializer.ParseID(typeName, out var id);
|
||||
if (Editor.Instance.ContentDatabase.FindAsset(id) is VisualScriptItem visualScriptItem)
|
||||
{
|
||||
return visualScriptItem.ScriptType;
|
||||
}
|
||||
}
|
||||
|
||||
return ScriptType.Null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void GetDerivedTypes(ScriptType baseType, List<ScriptType> result, Func<ScriptType, bool> checkFunc)
|
||||
{
|
||||
var visualScripts = VisualScriptItem.VisualScripts;
|
||||
for (var i = 0; i < visualScripts.Count; i++)
|
||||
{
|
||||
var t = visualScripts[i].ScriptType;
|
||||
if (baseType.IsAssignableFrom(t) && t != baseType && checkFunc(t))
|
||||
result.Add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
Source/Editor/Content/Settings/AndroidPlatformSettings.cs
Normal file
30
Source/Editor/Content/Settings/AndroidPlatformSettings.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// The Android platform settings asset archetype. Allows to edit asset via editor.
|
||||
/// </summary>
|
||||
public class AndroidPlatformSettings : SettingsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The application package name (eg. com.company.product). Custom tokens: ${PROJECT_NAME}, ${COMPANY_NAME}.
|
||||
/// </summary>
|
||||
[EditorOrder(0), EditorDisplay("General"), Tooltip("The application package name (eg. com.company.product). Custom tokens: ${PROJECT_NAME}, ${COMPANY_NAME}.")]
|
||||
public string PackageName = "com.${COMPANY_NAME}.${PROJECT_NAME}";
|
||||
|
||||
/// <summary>
|
||||
/// The application permissions list (eg. android.media.action.IMAGE_CAPTURE). Added to the generated manifest file.
|
||||
/// </summary>
|
||||
[EditorOrder(100), EditorDisplay("General"), Tooltip("The application permissions list (eg. android.media.action.IMAGE_CAPTURE). Added to the generated manifest file.")]
|
||||
public string[] Permissions;
|
||||
|
||||
/// <summary>
|
||||
/// Custom icon texture to use for the application (overrides the default one).
|
||||
/// </summary>
|
||||
[EditorOrder(1030), EditorDisplay("Other"), Tooltip("Custom icon texture to use for the application (overrides the default one).")]
|
||||
public Texture OverrideIcon;
|
||||
}
|
||||
}
|
||||
34
Source/Editor/Content/Settings/AudioSettings.cs
Normal file
34
Source/Editor/Content/Settings/AudioSettings.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// The audio payback engine settings container. Allows to edit asset via editor.
|
||||
/// </summary>
|
||||
public sealed class AudioSettings : SettingsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// If checked, audio playback will be disabled in build game. Can be used if game uses custom audio playback engine.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorOrder(0), EditorDisplay("General"), Tooltip("If checked, audio playback will be disabled in build game. Can be used if game uses custom audio playback engine.")]
|
||||
public bool DisableAudio;
|
||||
|
||||
/// <summary>
|
||||
/// The doppler doppler effect factor. Scale for source and listener velocities. Default is 1.
|
||||
/// </summary>
|
||||
[DefaultValue(1.0f)]
|
||||
[EditorOrder(100), EditorDisplay("General"), Limit(0, 10.0f, 0.01f), Tooltip("The doppler doppler effect factor. Scale for source and listener velocities. Default is 1.")]
|
||||
public float DopplerFactor = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// True if mute all audio playback when game has no use focus.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorOrder(200), EditorDisplay("General", "Mute On Focus Loss"), Tooltip("If checked, engine will mute all audio playback when game has no use focus.")]
|
||||
public bool MuteOnFocusLoss = true;
|
||||
}
|
||||
}
|
||||
44
Source/Editor/Content/Settings/BuildPreset.cs
Normal file
44
Source/Editor/Content/Settings/BuildPreset.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="GameCooker"/> game building preset with set of build targets.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class BuildPreset
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the preset.
|
||||
/// </summary>
|
||||
[EditorOrder(10), Tooltip("Name of the preset")]
|
||||
public string Name;
|
||||
|
||||
/// <summary>
|
||||
/// The target configurations.
|
||||
/// </summary>
|
||||
[EditorOrder(20), Tooltip("Target configurations")]
|
||||
public BuildTarget[] Targets;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target of the given name (ignore case search) or returns null if cannot find it.
|
||||
/// </summary>
|
||||
/// <param name="name">The target name.</param>
|
||||
/// <returns>Found target or null if is missing.</returns>
|
||||
public BuildTarget GetTarget(string name)
|
||||
{
|
||||
if (Targets != null)
|
||||
{
|
||||
for (int i = 0; i < Targets.Length; i++)
|
||||
{
|
||||
if (string.Equals(Targets[i].Name, name, StringComparison.OrdinalIgnoreCase))
|
||||
return Targets[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
143
Source/Editor/Content/Settings/BuildSettings.cs
Normal file
143
Source/Editor/Content/Settings/BuildSettings.cs
Normal file
@@ -0,0 +1,143 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// The game building settings container. Allows to edit asset via editor.
|
||||
/// </summary>
|
||||
public sealed class BuildSettings : SettingsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum amount of assets to include into a single assets package. Assets will be split into several packages if need to.
|
||||
/// </summary>
|
||||
[DefaultValue(4096)]
|
||||
[EditorOrder(10), Limit(32, short.MaxValue), EditorDisplay("General", "Max assets per package"), Tooltip("The maximum amount of assets to include into a single assets package. Assets will be split into several packages if need to.")]
|
||||
public int MaxAssetsPerPackage = 4096;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum size of the single assets package (in megabytes). Assets will be split into several packages if need to.
|
||||
/// </summary>
|
||||
[DefaultValue(1024)]
|
||||
[EditorOrder(20), Limit(16, short.MaxValue), EditorDisplay("General", "Max package size (in MB)"), Tooltip("The maximum size of the single assets package (in megabytes). Assets will be split into several packages if need to.")]
|
||||
public int MaxPackageSizeMB = 1024;
|
||||
|
||||
/// <summary>
|
||||
/// The game content cooking Keys. Use the same value for a game and DLC packages to support loading them by the build game. Use 0 to randomize it during building.
|
||||
/// </summary>
|
||||
[DefaultValue(0)]
|
||||
[EditorOrder(30), EditorDisplay("General"), Tooltip("The game content cooking Keys. Use the same value for a game and DLC packages to support loading them by the build game. Use 0 to randomize it during building.")]
|
||||
public int ContentKey = 0;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, the builds produced by the Game Cooker will be treated as for final game distribution (eg. for game store upload). Builds done this way cannot be tested on console devkits (eg. Xbox One, Xbox Scarlett).
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorOrder(40), EditorDisplay("General"), Tooltip("If checked, the builds produced by the Game Cooker will be treated as for final game distribution (eg. for game store upload). Builds done this way cannot be tested on console devkits (eg. Xbox One, Xbox Scarlett).")]
|
||||
public bool ForDistribution;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, the output build files won't be packaged for the destination platform. Useful when debugging build from local PC.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorOrder(50), EditorDisplay("General"), Tooltip("If checked, the output build files won't be packaged for the destination platform. Useful when debugging build from local PC.")]
|
||||
public bool SkipPackaging;
|
||||
|
||||
/// <summary>
|
||||
/// The additional assets to include into build (into root assets set).
|
||||
/// </summary>
|
||||
[EditorOrder(1000), EditorDisplay("Additional Data"), Tooltip("The additional assets to include into build (into root assets set).")]
|
||||
public Asset[] AdditionalAssets;
|
||||
|
||||
/// <summary>
|
||||
/// The additional folders with assets to include into build (into root assets set). List of paths relative to the project directory (or absolute).
|
||||
/// </summary>
|
||||
[EditorOrder(1010), EditorDisplay("Additional Data"), Tooltip("The additional folders with assets to include into build (to root assets set). List of paths relative to the project directory (or absolute).")]
|
||||
public string[] AdditionalAssetFolders;
|
||||
|
||||
/// <summary>
|
||||
/// Disables shaders compiler optimizations in cooked game. Can be used to debug shaders on a target platform or to speed up the shaders compilation time.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorOrder(2000), EditorDisplay("Content", "Shaders No Optimize"), Tooltip("Disables shaders compiler optimizations in cooked game. Can be used to debug shaders on a target platform or to speed up the shaders compilation time.")]
|
||||
public bool ShadersNoOptimize;
|
||||
|
||||
/// <summary>
|
||||
/// Enables shader debug data generation for shaders in cooked game (depends on the target platform rendering backend).
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorOrder(2010), EditorDisplay("Content"), Tooltip("Enables shader debug data generation for shaders in cooked game (depends on the target platform rendering backend).")]
|
||||
public bool ShadersGenerateDebugData;
|
||||
|
||||
/// <summary>
|
||||
/// The build presets.
|
||||
/// </summary>
|
||||
[EditorOrder(5000), EditorDisplay("Presets", EditorDisplayAttribute.InlineStyle), Tooltip("Build presets configuration")]
|
||||
public BuildPreset[] Presets =
|
||||
{
|
||||
new BuildPreset
|
||||
{
|
||||
Name = "Development",
|
||||
Targets = new[]
|
||||
{
|
||||
new BuildTarget
|
||||
{
|
||||
Name = "Windows 64bit",
|
||||
Output = "Output\\Win64",
|
||||
Platform = BuildPlatform.Windows64,
|
||||
Mode = BuildConfiguration.Development,
|
||||
},
|
||||
new BuildTarget
|
||||
{
|
||||
Name = "Windows 32bit",
|
||||
Output = "Output\\Win32",
|
||||
Platform = BuildPlatform.Windows32,
|
||||
Mode = BuildConfiguration.Development,
|
||||
},
|
||||
}
|
||||
},
|
||||
new BuildPreset
|
||||
{
|
||||
Name = "Release",
|
||||
Targets = new[]
|
||||
{
|
||||
new BuildTarget
|
||||
{
|
||||
Name = "Windows 64bit",
|
||||
Output = "Output\\Win64",
|
||||
Platform = BuildPlatform.Windows64,
|
||||
Mode = BuildConfiguration.Development,
|
||||
},
|
||||
new BuildTarget
|
||||
{
|
||||
Name = "Windows 32bit",
|
||||
Output = "Output\\Win32",
|
||||
Platform = BuildPlatform.Windows32,
|
||||
Mode = BuildConfiguration.Development,
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the preset of the given name (ignore case search) or returns null if cannot find it.
|
||||
/// </summary>
|
||||
/// <param name="name">The preset name.</param>
|
||||
/// <returns>Found preset or null if is missing.</returns>
|
||||
public BuildPreset GetPreset(string name)
|
||||
{
|
||||
if (Presets != null)
|
||||
{
|
||||
for (int i = 0; i < Presets.Length; i++)
|
||||
{
|
||||
if (string.Equals(Presets[i].Name, name, StringComparison.OrdinalIgnoreCase))
|
||||
return Presets[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
56
Source/Editor/Content/Settings/BuildTarget.cs
Normal file
56
Source/Editor/Content/Settings/BuildTarget.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="GameCooker"/> game building target with configuration properties.
|
||||
/// </summary>
|
||||
[Serializable, HideInEditor]
|
||||
public class BuildTarget
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the target.
|
||||
/// </summary>
|
||||
[EditorOrder(10), Tooltip("Name of the target")]
|
||||
public string Name;
|
||||
|
||||
/// <summary>
|
||||
/// The output folder path.
|
||||
/// </summary>
|
||||
[EditorOrder(20), Tooltip("Output folder path")]
|
||||
public string Output;
|
||||
|
||||
/// <summary>
|
||||
/// The target platform.
|
||||
/// </summary>
|
||||
[EditorOrder(30), Tooltip("Target platform")]
|
||||
public BuildPlatform Platform;
|
||||
|
||||
/// <summary>
|
||||
/// The configuration mode.
|
||||
/// </summary>
|
||||
[EditorOrder(30), Tooltip("Configuration build mode")]
|
||||
public BuildConfiguration Mode;
|
||||
|
||||
/// <summary>
|
||||
/// The pre-build action command line.
|
||||
/// </summary>
|
||||
[EditorOrder(100)]
|
||||
public string PreBuildAction;
|
||||
|
||||
/// <summary>
|
||||
/// The post-build action command line.
|
||||
/// </summary>
|
||||
[EditorOrder(110)]
|
||||
public string PostBuildAction;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the build options computed from the target configuration.
|
||||
/// </summary>
|
||||
[HideInEditor, NoSerialize]
|
||||
public virtual BuildOptions Options => BuildOptions.None;
|
||||
}
|
||||
}
|
||||
362
Source/Editor/Content/Settings/GameSettings.cs
Normal file
362
Source/Editor/Content/Settings/GameSettings.cs
Normal file
@@ -0,0 +1,362 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// The game settings asset archetype. Allows to edit asset via editor.
|
||||
/// </summary>
|
||||
public sealed class GameSettings : SettingsBase
|
||||
{
|
||||
internal const string PS4PlatformSettingsTypename = "FlaxEditor.Content.Settings.PS4PlatformSettings";
|
||||
internal const string XboxScarlettPlatformSettingsTypename = "FlaxEditor.Content.Settings.XboxScarlettPlatformSettings";
|
||||
|
||||
/// <summary>
|
||||
/// The product full name.
|
||||
/// </summary>
|
||||
[EditorOrder(0), EditorDisplay("General"), Tooltip("The name of your product.")]
|
||||
public string ProductName;
|
||||
|
||||
/// <summary>
|
||||
/// The company full name.
|
||||
/// </summary>
|
||||
[EditorOrder(10), EditorDisplay("General"), Tooltip("The name of your company or organization.")]
|
||||
public string CompanyName;
|
||||
|
||||
/// <summary>
|
||||
/// The copyright note used for content signing (eg. source code header).
|
||||
/// </summary>
|
||||
[EditorOrder(15), EditorDisplay("General"), Tooltip("The copyright note used for content signing (eg. source code header).")]
|
||||
public string CopyrightNotice;
|
||||
|
||||
/// <summary>
|
||||
/// The default application icon.
|
||||
/// </summary>
|
||||
[EditorOrder(30), EditorDisplay("General"), Tooltip("The default icon of the application.")]
|
||||
public Texture Icon;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the first scene to load on a game startup.
|
||||
/// </summary>
|
||||
[EditorOrder(900), EditorDisplay("Startup"), Tooltip("Reference to the first scene to load on a game startup.")]
|
||||
public SceneReference FirstScene;
|
||||
|
||||
/// <summary>
|
||||
/// True if skip showing splash screen image on the game startup.
|
||||
/// </summary>
|
||||
[EditorOrder(910), EditorDisplay("Startup", "No Splash Screen"), Tooltip("True if skip showing splash screen image on the game startup.")]
|
||||
public bool NoSplashScreen;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the splash screen image to show on a game startup.
|
||||
/// </summary>
|
||||
[EditorOrder(920), EditorDisplay("Startup"), Tooltip("Reference to the splash screen image to show on a game startup.")]
|
||||
public Texture SplashScreen;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="TimeSettings"/> asset.
|
||||
/// </summary>
|
||||
[EditorOrder(1010), EditorDisplay("Other Settings"), AssetReference(typeof(TimeSettings), true), Tooltip("Reference to Time Settings asset")]
|
||||
public JsonAsset Time;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="AudioSettings"/> asset.
|
||||
/// </summary>
|
||||
[EditorOrder(1015), EditorDisplay("Other Settings"), AssetReference(typeof(AudioSettings), true), Tooltip("Reference to Audio Settings asset")]
|
||||
public JsonAsset Audio;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="LayersAndTagsSettings"/> asset.
|
||||
/// </summary>
|
||||
[EditorOrder(1020), EditorDisplay("Other Settings"), AssetReference(typeof(LayersAndTagsSettings), true), Tooltip("Reference to Layers & Tags Settings asset")]
|
||||
public JsonAsset LayersAndTags;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="PhysicsSettings"/> asset.
|
||||
/// </summary>
|
||||
[EditorOrder(1030), EditorDisplay("Other Settings"), AssetReference(typeof(PhysicsSettings), true), Tooltip("Reference to Physics Settings asset")]
|
||||
public JsonAsset Physics;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="InputSettings"/> asset.
|
||||
/// </summary>
|
||||
[EditorOrder(1035), EditorDisplay("Other Settings"), AssetReference(typeof(InputSettings), true), Tooltip("Reference to Input Settings asset")]
|
||||
public JsonAsset Input;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="GraphicsSettings"/> asset.
|
||||
/// </summary>
|
||||
[EditorOrder(1040), EditorDisplay("Other Settings"), AssetReference(typeof(GraphicsSettings), true), Tooltip("Reference to Graphics Settings asset")]
|
||||
public JsonAsset Graphics;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="NavigationSettings"/> asset.
|
||||
/// </summary>
|
||||
[EditorOrder(1045), EditorDisplay("Other Settings"), AssetReference(typeof(NavigationSettings), true), Tooltip("Reference to Navigation Settings asset")]
|
||||
public JsonAsset Navigation;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="BuildSettings"/> asset.
|
||||
/// </summary>
|
||||
[EditorOrder(1050), EditorDisplay("Other Settings"), AssetReference(typeof(BuildSettings), true), Tooltip("Reference to Build Settings asset")]
|
||||
public JsonAsset GameCooking;
|
||||
|
||||
/// <summary>
|
||||
/// The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair).
|
||||
/// </summary>
|
||||
[EditorOrder(1100), EditorDisplay("Other Settings"), Tooltip("The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair).")]
|
||||
public Dictionary<string, JsonAsset> CustomSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="WindowsPlatformSettings"/> asset. Used to apply configuration on Windows platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2010), EditorDisplay("Platform Settings", "Windows"), AssetReference(typeof(WindowsPlatformSettings), true), Tooltip("Reference to Windows Platform Settings asset")]
|
||||
public JsonAsset WindowsPlatform;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="UWPPlatformSettings"/> asset. Used to apply configuration on Universal Windows Platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2020), EditorDisplay("Platform Settings", "Universal Windows Platform"), AssetReference(typeof(UWPPlatformSettings), true), Tooltip("Reference to Universal Windows Platform Settings asset")]
|
||||
public JsonAsset UWPPlatform;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="LinuxPlatformSettings"/> asset. Used to apply configuration on Linux platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2030), EditorDisplay("Platform Settings", "Linux"), AssetReference(typeof(LinuxPlatformSettings), true), Tooltip("Reference to Linux Platform Settings asset")]
|
||||
public JsonAsset LinuxPlatform;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to PS4 Platform Settings asset. Used to apply configuration on PS4 platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2040), EditorDisplay("Platform Settings", "PlayStation 4"), AssetReference(PS4PlatformSettingsTypename, true), Tooltip("Reference to PS4 Platform Settings asset")]
|
||||
public JsonAsset PS4Platform;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to Xbox Scarlett Platform Settings asset. Used to apply configuration on Xbox Scarlett platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2050), EditorDisplay("Platform Settings", "Xbox Scarlett"), AssetReference(XboxScarlettPlatformSettingsTypename, true), Tooltip("Reference to Xbox Scarlett Platform Settings asset")]
|
||||
public JsonAsset XboxScarlettPlatform;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="AndroidPlatformSettings"/> asset. Used to apply configuration on Android platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2060), EditorDisplay("Platform Settings", "Android"), AssetReference(typeof(AndroidPlatformSettings), true), Tooltip("Reference to Android Platform Settings asset")]
|
||||
public JsonAsset AndroidPlatform;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the absolute path to the game settings asset file.
|
||||
/// </summary>
|
||||
public static string GameSettingsAssetPath
|
||||
{
|
||||
get { return StringUtils.CombinePaths(Globals.ProjectContentFolder, "GameSettings.json"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the game settings asset.
|
||||
/// </summary>
|
||||
/// <returns>The loaded game settings.</returns>
|
||||
public static GameSettings Load()
|
||||
{
|
||||
var asset = FlaxEngine.Content.LoadAsync<JsonAsset>(GameSettingsAssetPath);
|
||||
if (asset && !asset.WaitForLoaded())
|
||||
{
|
||||
if (asset.CreateInstance() is GameSettings result)
|
||||
return result;
|
||||
}
|
||||
return new GameSettings();
|
||||
}
|
||||
|
||||
private static T LoadAsset<T>(JsonAsset asset) where T : new()
|
||||
{
|
||||
if (asset && !asset.WaitForLoaded())
|
||||
{
|
||||
if (asset.CreateInstance() is T result)
|
||||
return result;
|
||||
}
|
||||
return new T();
|
||||
}
|
||||
|
||||
private static SettingsBase LoadAsset(JsonAsset asset, string typename)
|
||||
{
|
||||
if (asset && !asset.WaitForLoaded() && asset.DataTypeName == typename)
|
||||
{
|
||||
if (asset.CreateInstance() is SettingsBase result)
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the settings of the given type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Supports loading game settings, any sub settings container (e.g. <see cref="PhysicsSettings"/>) and custom settings (see <see cref="CustomSettings"/>).
|
||||
/// </remarks>
|
||||
/// <code>
|
||||
/// var time = GameSettings.Load&ltTimeSettings&gt;();
|
||||
/// </code>
|
||||
/// <typeparam name="T">The game settings type (e.g. <see cref="TimeSettings"/>).</typeparam>
|
||||
/// <returns>Loaded settings object or null if fails.</returns>
|
||||
public static T Load<T>() where T : SettingsBase
|
||||
{
|
||||
var gameSettings = Load();
|
||||
var type = typeof(T);
|
||||
|
||||
if (type == typeof(GameSettings))
|
||||
return gameSettings as T;
|
||||
|
||||
if (type == typeof(TimeSettings))
|
||||
return LoadAsset<TimeSettings>(gameSettings.Time) as T;
|
||||
if (type == typeof(LayersAndTagsSettings))
|
||||
return LoadAsset<LayersAndTagsSettings>(gameSettings.LayersAndTags) as T;
|
||||
if (type == typeof(PhysicsSettings))
|
||||
return LoadAsset<PhysicsSettings>(gameSettings.Physics) as T;
|
||||
if (type == typeof(GraphicsSettings))
|
||||
return LoadAsset<GraphicsSettings>(gameSettings.Graphics) as T;
|
||||
if (type == typeof(NavigationSettings))
|
||||
return LoadAsset<NavigationSettings>(gameSettings.Navigation) as T;
|
||||
if (type == typeof(BuildSettings))
|
||||
return LoadAsset<BuildSettings>(gameSettings.GameCooking) as T;
|
||||
if (type == typeof(InputSettings))
|
||||
return LoadAsset<InputSettings>(gameSettings.Input) as T;
|
||||
if (type == typeof(WindowsPlatformSettings))
|
||||
return LoadAsset<WindowsPlatformSettings>(gameSettings.WindowsPlatform) as T;
|
||||
if (type == typeof(UWPPlatformSettings))
|
||||
return LoadAsset<UWPPlatformSettings>(gameSettings.UWPPlatform) as T;
|
||||
if (type == typeof(LinuxPlatformSettings))
|
||||
return LoadAsset<LinuxPlatformSettings>(gameSettings.LinuxPlatform) as T;
|
||||
if (type.FullName == PS4PlatformSettingsTypename)
|
||||
return LoadAsset(gameSettings.PS4Platform, PS4PlatformSettingsTypename) as T;
|
||||
if (type.FullName == XboxScarlettPlatformSettingsTypename)
|
||||
return LoadAsset(gameSettings.XboxScarlettPlatform, XboxScarlettPlatformSettingsTypename) as T;
|
||||
if (type == typeof(AndroidPlatformSettings))
|
||||
return LoadAsset<AndroidPlatformSettings>(gameSettings.AndroidPlatform) as T;
|
||||
if (type == typeof(AudioSettings))
|
||||
return LoadAsset<AudioSettings>(gameSettings.Audio) as T;
|
||||
|
||||
if (gameSettings.CustomSettings != null)
|
||||
{
|
||||
foreach (var e in gameSettings.CustomSettings)
|
||||
{
|
||||
if (e.Value && !e.Value.WaitForLoaded() && e.Value.DataTypeName == type.FullName)
|
||||
{
|
||||
var custom = e.Value.CreateInstance();
|
||||
if (custom is T result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool SaveAsset<T>(GameSettings gameSettings, ref JsonAsset asset, T obj) where T : SettingsBase
|
||||
{
|
||||
if (asset)
|
||||
{
|
||||
// Override settings
|
||||
return Editor.SaveJsonAsset(asset.Path, obj);
|
||||
}
|
||||
|
||||
// Create new settings asset and link it to the game settings
|
||||
var path = StringUtils.CombinePaths(Globals.ProjectContentFolder, CustomEditors.CustomEditorsUtil.GetPropertyNameUI(typeof(T).Name) + ".json");
|
||||
if (Editor.SaveJsonAsset(path, obj))
|
||||
return true;
|
||||
asset = FlaxEngine.Content.LoadAsync<JsonAsset>(path);
|
||||
return Editor.SaveJsonAsset(GameSettingsAssetPath, gameSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the settings of the given type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Supports saving game settings, any sub settings container (e.g. <see cref="PhysicsSettings"/>).
|
||||
/// </remarks>
|
||||
/// <code>
|
||||
/// var time = GameSettings.Load&ltTimeSettings&gt;();
|
||||
/// time.TimeScale = 0.5f;
|
||||
/// GameSettings.Save&ltTimeSettings&gt;(time);
|
||||
/// </code>
|
||||
/// <typeparam name="T">The game settings type (e.g. <see cref="TimeSettings"/>).</typeparam>
|
||||
/// <returns>True if failed otherwise false.</returns>
|
||||
public static bool Save<T>(T obj) where T : SettingsBase
|
||||
{
|
||||
var type = typeof(T);
|
||||
|
||||
if (type == typeof(GameSettings))
|
||||
{
|
||||
return Editor.SaveJsonAsset(GameSettingsAssetPath, obj);
|
||||
}
|
||||
|
||||
var gameSettings = Load();
|
||||
|
||||
if (type == typeof(TimeSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.Time, obj);
|
||||
if (type == typeof(LayersAndTagsSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.LayersAndTags, obj);
|
||||
if (type == typeof(PhysicsSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.Physics, obj);
|
||||
if (type == typeof(GraphicsSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.Graphics, obj);
|
||||
if (type == typeof(NavigationSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.Navigation, obj);
|
||||
if (type == typeof(BuildSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.GameCooking, obj);
|
||||
if (type == typeof(InputSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.Input, obj);
|
||||
if (type == typeof(WindowsPlatformSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.WindowsPlatform, obj);
|
||||
if (type == typeof(UWPPlatformSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.UWPPlatform, obj);
|
||||
if (type == typeof(LinuxPlatformSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.LinuxPlatform, obj);
|
||||
if (type.FullName == PS4PlatformSettingsTypename)
|
||||
return SaveAsset(gameSettings, ref gameSettings.PS4Platform, obj);
|
||||
if (type.FullName == XboxScarlettPlatformSettingsTypename)
|
||||
return SaveAsset(gameSettings, ref gameSettings.XboxScarlettPlatform, obj);
|
||||
if (type == typeof(AndroidPlatformSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.AndroidPlatform, obj);
|
||||
if (type == typeof(AudioSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.Audio, obj);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the custom settings (or unsets if provided asset is null).
|
||||
/// </summary>
|
||||
/// <param name="key">The custom key (must be unique per context).</param>
|
||||
/// <param name="customSettingsAsset">The custom settings asset.</param>
|
||||
/// <returns>True if failed otherwise false.</returns>
|
||||
public static bool SetCustomSettings(string key, JsonAsset customSettingsAsset)
|
||||
{
|
||||
if (key == null)
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
|
||||
var gameSettings = Load();
|
||||
|
||||
if (customSettingsAsset == null && gameSettings.CustomSettings != null)
|
||||
{
|
||||
gameSettings.CustomSettings.Remove(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gameSettings.CustomSettings == null)
|
||||
gameSettings.CustomSettings = new Dictionary<string, JsonAsset>();
|
||||
gameSettings.CustomSettings[key] = customSettingsAsset;
|
||||
}
|
||||
|
||||
return Editor.SaveJsonAsset(GameSettingsAssetPath, gameSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the current game settings asset and applies it to the engine runtime configuration.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
public static extern void Apply();
|
||||
}
|
||||
}
|
||||
61
Source/Editor/Content/Settings/GraphicsSettings.cs
Normal file
61
Source/Editor/Content/Settings/GraphicsSettings.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// The graphics rendering settings container. Allows to edit asset via editor. To modify those settings at runtime use <see cref="GraphicsSettings"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.Graphics"/>
|
||||
public sealed class GraphicsSettings : SettingsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables rendering synchronization with the refresh rate of the display device to avoid "tearing" artifacts.
|
||||
/// </summary>
|
||||
[EditorOrder(20), EditorDisplay("General", "Use V-Sync"), Tooltip("Enables rendering synchronization with the refresh rate of the display device to avoid \"tearing\" artifacts.")]
|
||||
public bool UseVSync = false;
|
||||
|
||||
/// <summary>
|
||||
/// Anti Aliasing quality setting.
|
||||
/// </summary>
|
||||
[EditorOrder(1000), EditorDisplay("Quality", "AA Quality"), Tooltip("Anti Aliasing quality.")]
|
||||
public Quality AAQuality = Quality.Medium;
|
||||
|
||||
/// <summary>
|
||||
/// Screen Space Reflections quality.
|
||||
/// </summary>
|
||||
[EditorOrder(1100), EditorDisplay("Quality", "SSR Quality"), Tooltip("Screen Space Reflections quality.")]
|
||||
public Quality SSRQuality = Quality.Medium;
|
||||
|
||||
/// <summary>
|
||||
/// Screen Space Ambient Occlusion quality setting.
|
||||
/// </summary>
|
||||
[EditorOrder(1200), EditorDisplay("Quality", "SSAO Quality"), Tooltip("Screen Space Ambient Occlusion quality setting.")]
|
||||
public Quality SSAOQuality = Quality.Medium;
|
||||
|
||||
/// <summary>
|
||||
/// Volumetric Fog quality setting.
|
||||
/// </summary>
|
||||
[EditorOrder(1250), EditorDisplay("Quality", "Volumetric Fog Quality"), Tooltip("Volumetric Fog quality setting.")]
|
||||
public Quality VolumetricFogQuality = Quality.High;
|
||||
|
||||
/// <summary>
|
||||
/// The shadows quality.
|
||||
/// </summary>
|
||||
[EditorOrder(1300), EditorDisplay("Quality", "Shadows Quality"), Tooltip("The shadows quality.")]
|
||||
public Quality ShadowsQuality = Quality.Medium;
|
||||
|
||||
/// <summary>
|
||||
/// The shadow maps quality (textures resolution).
|
||||
/// </summary>
|
||||
[EditorOrder(1310), EditorDisplay("Quality", "Shadow Maps Quality"), Tooltip("The shadow maps quality (textures resolution).")]
|
||||
public Quality ShadowMapsQuality = Quality.Medium;
|
||||
|
||||
/// <summary>
|
||||
/// Enables cascades splits blending for directional light shadows.
|
||||
/// </summary>
|
||||
[EditorOrder(1320), EditorDisplay("Quality", "Allow CSM Blending"), Tooltip("Enables cascades splits blending for directional light shadows.")]
|
||||
public bool AllowCSMBlending = false;
|
||||
}
|
||||
}
|
||||
28
Source/Editor/Content/Settings/InputSettings.cs
Normal file
28
Source/Editor/Content/Settings/InputSettings.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// The input settings container. Allows to edit asset via editor.
|
||||
/// </summary>
|
||||
public sealed class InputSettings : SettingsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps a discrete button or key press events to a "friendly name" that will later be bound to event-driven behavior. The end effect is that pressing (and/or releasing) a key, mouse button, or keypad button.
|
||||
/// </summary>
|
||||
/// <seealso cref="Input.ActionMappings"/>
|
||||
[Collection(Spacing = 10)]
|
||||
[EditorOrder(100), EditorDisplay("Input Map"), Tooltip("Maps a discrete button or key press events to a \"friendly name\" that will later be bound to event-driven behavior. The end effect is that pressing (and/or releasing) a key, mouse button, or keypad button.")]
|
||||
public ActionConfig[] ActionMappings;
|
||||
|
||||
/// <summary>
|
||||
/// Maps keyboard, controller, or mouse inputs to a "friendly name" that will later be bound to continuous game behavior, such as movement. The inputs mapped in AxisMappings are continuously polled, even if they are just reporting that their input value.
|
||||
/// </summary>
|
||||
/// <seealso cref="Input.AxisMappings"/>
|
||||
[Collection(Spacing = 10)]
|
||||
[EditorOrder(200), EditorDisplay("Input Map"), Tooltip("Maps keyboard, controller, or mouse inputs to a \"friendly name\" that will later be bound to continuous game behavior, such as movement. The inputs mapped in AxisMappings are continuously polled, even if they are just reporting that their input value.")]
|
||||
public AxisConfig[] AxisMappings;
|
||||
}
|
||||
}
|
||||
40
Source/Editor/Content/Settings/LayersAndTagsSettings.cs
Normal file
40
Source/Editor/Content/Settings/LayersAndTagsSettings.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// The layers and objects tags settings. Allows to edit asset via editor.
|
||||
/// </summary>
|
||||
public sealed class LayersAndTagsSettings : SettingsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The tag names.
|
||||
/// </summary>
|
||||
[EditorOrder(10), EditorDisplay("Tags", EditorDisplayAttribute.InlineStyle)]
|
||||
public List<string> Tags = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// The layers names.
|
||||
/// </summary>
|
||||
[EditorOrder(10), EditorDisplay("Layers", EditorDisplayAttribute.InlineStyle), Collection(ReadOnly = true)]
|
||||
public string[] Layers = new string[32];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current tags collection.
|
||||
/// </summary>
|
||||
/// <returns>The tags collection.</returns>
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal static extern string[] GetCurrentTags();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current layer names (max 32 items but trims last empty items).
|
||||
/// </summary>
|
||||
/// <returns>The layers.</returns>
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
public static extern string[] GetCurrentLayers();
|
||||
}
|
||||
}
|
||||
60
Source/Editor/Content/Settings/LinuxPlatformSettings.cs
Normal file
60
Source/Editor/Content/Settings/LinuxPlatformSettings.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// The Linux platform settings asset archetype. Allows to edit asset via editor.
|
||||
/// </summary>
|
||||
public class LinuxPlatformSettings : SettingsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The default game window mode.
|
||||
/// </summary>
|
||||
[EditorOrder(10), EditorDisplay("Window"), Tooltip("The default game window mode.")]
|
||||
public GameWindowMode WindowMode = GameWindowMode.Windowed;
|
||||
|
||||
/// <summary>
|
||||
/// The default game window width (in pixels).
|
||||
/// </summary>
|
||||
[EditorOrder(20), EditorDisplay("Window"), Tooltip("The default game window width (in pixels).")]
|
||||
public int ScreenWidth = 1280;
|
||||
|
||||
/// <summary>
|
||||
/// The default game window height (in pixels).
|
||||
/// </summary>
|
||||
[EditorOrder(30), EditorDisplay("Window"), Tooltip("The default game window height (in pixels).")]
|
||||
public int ScreenHeight = 720;
|
||||
|
||||
/// <summary>
|
||||
/// Enables resizing the game window by the user.
|
||||
/// </summary>
|
||||
[EditorOrder(40), EditorDisplay("Window"), Tooltip("Enables resizing the game window by the user.")]
|
||||
public bool ResizableWindow = false;
|
||||
|
||||
/// <summary>
|
||||
/// Enables game running when application window loses focus.
|
||||
/// </summary>
|
||||
[EditorOrder(1010), EditorDisplay("Other", "Run In Background"), Tooltip("Enables game running when application window loses focus.")]
|
||||
public bool RunInBackground = false;
|
||||
|
||||
/// <summary>
|
||||
/// Limits maximum amount of concurrent game instances running to one, otherwise user may launch application more than once.
|
||||
/// </summary>
|
||||
[EditorOrder(1020), EditorDisplay("Other"), Tooltip("Limits maximum amount of concurrent game instances running to one, otherwise user may launch application more than once.")]
|
||||
public bool ForceSingleInstance = false;
|
||||
|
||||
/// <summary>
|
||||
/// Custom icon texture to use for the application (overrides the default one).
|
||||
/// </summary>
|
||||
[EditorOrder(1030), EditorDisplay("Other"), Tooltip("Custom icon texture to use for the application (overrides the default one).")]
|
||||
public Texture OverrideIcon;
|
||||
|
||||
/// <summary>
|
||||
/// Enables support for Vulkan. Disabling it reduces compiled shaders count.
|
||||
/// </summary>
|
||||
[EditorOrder(2020), EditorDisplay("Graphics", "Support Vulkan"), Tooltip("Enables support for Vulkan. Disabling it reduces compiled shaders count.")]
|
||||
public bool SupportVulkan = true;
|
||||
}
|
||||
}
|
||||
106
Source/Editor/Content/Settings/NavigationSettings.cs
Normal file
106
Source/Editor/Content/Settings/NavigationSettings.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// The navigation system settings container.
|
||||
/// </summary>
|
||||
public sealed class NavigationSettings : SettingsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The height of a grid cell in the navigation mesh building steps using heightfields.
|
||||
/// A lower number means higher precision on the vertical axis but longer build times.
|
||||
/// </summary>
|
||||
[DefaultValue(10.0f), Limit(1, 400)]
|
||||
[EditorOrder(10), EditorDisplay("Nav Mesh Options"), Tooltip("The height of a grid cell in the navigation mesh building steps using heightfields. A lower number means higher precision on the vertical axis but longer build times.")]
|
||||
public float CellHeight = 10.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The width/height of a grid cell in the navigation mesh building steps using heightfields.
|
||||
/// A lower number means higher precision on the horizontal axes but longer build times.
|
||||
/// </summary>
|
||||
[DefaultValue(30.0f), Limit(1, 400)]
|
||||
[EditorOrder(20), EditorDisplay("Nav Mesh Options"), Tooltip("The width/height of a grid cell in the navigation mesh building steps using heightfields. A lower number means higher precision on the vertical axis but longer build times.")]
|
||||
public float CellSize = 30.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Tile size used for Navigation mesh tiles, the final size of a tile is CellSize*TileSize.
|
||||
/// </summary>
|
||||
[DefaultValue(64), Limit(8, 4096)]
|
||||
[EditorOrder(30), EditorDisplay("Nav Mesh Options"), Tooltip("Tile size used for Navigation mesh tiles, the final size of a tile is CellSize*TileSize.")]
|
||||
public int TileSize = 64;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum number of cells allowed to form isolated island areas.
|
||||
/// </summary>
|
||||
[DefaultValue(0), Limit(0, 100)]
|
||||
[EditorOrder(40), EditorDisplay("Nav Mesh Options"), Tooltip("The minimum number of cells allowed to form isolated island areas.")]
|
||||
public int MinRegionArea = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Any regions with a span count smaller than this value will, if possible, be merged with larger regions.
|
||||
/// </summary>
|
||||
[DefaultValue(20), Limit(0, 100)]
|
||||
[EditorOrder(50), EditorDisplay("Nav Mesh Options"), Tooltip("Any regions with a span count smaller than this value will, if possible, be merged with larger regions.")]
|
||||
public int MergeRegionArea = 20;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum allowed length for contour edges along the border of the mesh.
|
||||
/// </summary>
|
||||
[DefaultValue(1200.0f), Limit(100)]
|
||||
[EditorOrder(60), EditorDisplay("Nav Mesh Options", "Max Edge Length"), Tooltip("The maximum allowed length for contour edges along the border of the mesh.")]
|
||||
public float MaxEdgeLen = 1200.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum distance a simplified contour's border edges should deviate from the original raw contour.
|
||||
/// </summary>
|
||||
[DefaultValue(1.3f), Limit(0.1f, 4)]
|
||||
[EditorOrder(70), EditorDisplay("Nav Mesh Options"), Tooltip("The maximum distance a simplified contour's border edges should deviate from the original raw contour.")]
|
||||
public float MaxEdgeError = 1.3f;
|
||||
|
||||
/// <summary>
|
||||
/// The sampling distance to use when generating the detail mesh. For height detail only.
|
||||
/// </summary>
|
||||
[DefaultValue(600.0f), Limit(1)]
|
||||
[EditorOrder(80), EditorDisplay("Nav Mesh Options", "Detail Sampling Distance"), Tooltip("The sampling distance to use when generating the detail mesh.")]
|
||||
public float DetailSamplingDist = 600.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum distance the detail mesh surface should deviate from heightfield data. For height detail only.
|
||||
/// </summary>
|
||||
[DefaultValue(1.0f), Limit(0, 3)]
|
||||
[EditorOrder(90), EditorDisplay("Nav Mesh Options"), Tooltip("The maximum distance the detail mesh surface should deviate from heightfield data.")]
|
||||
public float MaxDetailSamplingError = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The radius of the smallest objects to traverse this nav mesh. Objects can't pass through gaps of less than twice the radius.
|
||||
/// </summary>
|
||||
[DefaultValue(34.0f), Limit(0)]
|
||||
[EditorOrder(1000), EditorDisplay("Agent Options"), Tooltip("The radius of the smallest objects to traverse this nav mesh. Objects can't pass through gaps of less than twice the radius.")]
|
||||
public float WalkableRadius = 34.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The height of the smallest objects to traverse this nav mesh. Objects can't enter areas with ceilings lower than this value.
|
||||
/// </summary>
|
||||
[DefaultValue(144.0f), Limit(0)]
|
||||
[EditorOrder(1010), EditorDisplay("Agent Options"), Tooltip("The height of the smallest objects to traverse this nav mesh. Objects can't enter areas with ceilings lower than this value.")]
|
||||
public float WalkableHeight = 144.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum ledge height that is considered to still be traversable.
|
||||
/// </summary>
|
||||
[DefaultValue(35.0f), Limit(0)]
|
||||
[EditorOrder(1020), EditorDisplay("Agent Options"), Tooltip("The maximum ledge height that is considered to still be traversable.")]
|
||||
public float WalkableMaxClimb = 35.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum slope that is considered walkable (in degrees). Objects can't go up or down slopes higher than this value.
|
||||
/// </summary>
|
||||
[DefaultValue(60.0f), Limit(0, 89.0f)]
|
||||
[EditorOrder(1030), EditorDisplay("Agent Options"), Tooltip("The maximum slope that is considered walkable (in degrees). Objects can't go up or down slopes higher than this value.")]
|
||||
public float WalkableMaxSlopeAngle = 60.0f;
|
||||
}
|
||||
}
|
||||
121
Source/Editor/Content/Settings/PhysicsSettings.cs
Normal file
121
Source/Editor/Content/Settings/PhysicsSettings.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// The physics simulation settings container. Allows to edit asset via editor.
|
||||
/// </summary>
|
||||
public sealed class PhysicsSettings : SettingsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The default gravity force value (in cm^2/s).
|
||||
/// </summary>
|
||||
[DefaultValue(typeof(Vector3), "0,-981.0,0")]
|
||||
[EditorOrder(0), EditorDisplay("Simulation"), Tooltip("The default gravity force value (in cm^2/s).")]
|
||||
public Vector3 DefaultGravity = new Vector3(0, -981.0f, 0);
|
||||
|
||||
/// <summary>
|
||||
/// If enabled, any Raycast or other scene query that intersects with a Collider marked as a Trigger will returns with a hit. Individual raycasts can override this behavior.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorOrder(10), EditorDisplay("Simulation"), Tooltip("If enabled, any Raycast or other scene query that intersects with a Collider marked as a Trigger will returns with a hit. Individual raycasts can override this behavior.")]
|
||||
public bool QueriesHitTriggers = true;
|
||||
|
||||
/// <summary>
|
||||
/// Triangles from triangle meshes (CSG) with an area less than or equal to this value will be removed from physics collision data. Set to less than or equal 0 to disable.
|
||||
/// </summary>
|
||||
[DefaultValue(5.0f)]
|
||||
[EditorOrder(20), EditorDisplay("Simulation"), Limit(-1, 10), Tooltip("Triangles from triangle meshes (CSG) with an area less than or equal to this value will be removed from physics collision data. Set to less than or equal 0 to disable.")]
|
||||
public float TriangleMeshTriangleMinAreaThreshold = 5.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum relative velocity required for an object to bounce. A typical value for simulation stability is about 0.2 * gravity
|
||||
/// </summary>
|
||||
[DefaultValue(200.0f)]
|
||||
[EditorOrder(30), EditorDisplay("Simulation"), Limit(0), Tooltip("Minimum relative velocity required for an object to bounce. A typical value for simulation stability is about 0.2 * gravity")]
|
||||
public float BounceThresholdVelocity = 200.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Default friction combine mode, controls how friction is computed for multiple materials.
|
||||
/// </summary>
|
||||
[DefaultValue(PhysicsCombineMode.Average)]
|
||||
[EditorOrder(40), EditorDisplay("Simulation"), Tooltip("Default friction combine mode, controls how friction is computed for multiple materials.")]
|
||||
public PhysicsCombineMode FrictionCombineMode = PhysicsCombineMode.Average;
|
||||
|
||||
/// <summary>
|
||||
/// Default restitution combine mode, controls how restitution is computed for multiple materials.
|
||||
/// </summary>
|
||||
[DefaultValue(PhysicsCombineMode.Average)]
|
||||
[EditorOrder(50), EditorDisplay("Simulation"), Tooltip("Default restitution combine mode, controls how restitution is computed for multiple materials.")]
|
||||
public PhysicsCombineMode RestitutionCombineMode = PhysicsCombineMode.Average;
|
||||
|
||||
/// <summary>
|
||||
/// If true CCD will be ignored. This is an optimization when CCD is never used which removes the need for PhysX to check it internally.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorOrder(70), EditorDisplay("Simulation", "Disable CCD"), Tooltip("If true CCD will be ignored. This is an optimization when CCD is never used which removes the need for PhysX to check it internally.")]
|
||||
public bool DisableCCD;
|
||||
|
||||
/// <summary>
|
||||
/// Enables adaptive forces to accelerate convergence of the solver. Can improve physics simulation performance but lead to artifacts.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorOrder(80), EditorDisplay("Simulation"), Tooltip("Enables adaptive forces to accelerate convergence of the solver. Can improve physics simulation performance but lead to artifacts.")]
|
||||
public bool EnableAdaptiveForce;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum allowed delta time (in seconds) for the physics simulation step.
|
||||
/// </summary>
|
||||
[DefaultValue(1.0f / 10.0f)]
|
||||
[EditorOrder(1000), EditorDisplay("Framerate"), Limit(0.0013f, 2.0f), Tooltip("The maximum allowed delta time (in seconds) for the physics simulation step.")]
|
||||
public float MaxDeltaTime = 1.0f / 10.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to substep the physics simulation.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorOrder(1005), EditorDisplay("Framerate"), Tooltip("Whether to substep the physics simulation.")]
|
||||
public bool EnableSubstepping;
|
||||
|
||||
/// <summary>
|
||||
/// Delta time (in seconds) for an individual simulation substep.
|
||||
/// </summary>
|
||||
[DefaultValue(1.0f / 120.0f)]
|
||||
[EditorOrder(1010), EditorDisplay("Framerate"), Limit(0.0013f, 1.0f), Tooltip("Delta time (in seconds) for an individual simulation substep.")]
|
||||
public float SubstepDeltaTime = 1.0f / 120.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of substeps for physics simulation.
|
||||
/// </summary>
|
||||
[DefaultValue(5)]
|
||||
[EditorOrder(1020), EditorDisplay("Framerate"), Limit(1, 16), Tooltip("The maximum number of substeps for physics simulation.")]
|
||||
public int MaxSubsteps = 5;
|
||||
|
||||
/// <summary>
|
||||
/// The collision layers masks. Used to define layer-based collision detection.
|
||||
/// </summary>
|
||||
[EditorOrder(1040), EditorDisplay("Layers Matrix"), CustomEditor(typeof(FlaxEditor.CustomEditors.Dedicated.LayersMatrixEditor))]
|
||||
public uint[] LayerMasks = new uint[32];
|
||||
|
||||
/// <summary>
|
||||
/// Enables support for cooking physical collision shapes geometry at runtime. Use it to enable generating runtime terrain collision or convex mesh colliders.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorOrder(1100), EditorDisplay("Other", "Support Cooking At Runtime"), Tooltip("Enables support for cooking physical collision shapes geometry at runtime. Use it to enable generating runtime terrain collision or convex mesh colliders.")]
|
||||
public bool SupportCookingAtRuntime;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PhysicsSettings"/> class.
|
||||
/// </summary>
|
||||
public PhysicsSettings()
|
||||
{
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
LayerMasks[i] = uint.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48
Source/Editor/Content/Settings/TimeSettings.cs
Normal file
48
Source/Editor/Content/Settings/TimeSettings.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// The time settings asset archetype. Allows to edit asset via editor.
|
||||
/// </summary>
|
||||
public sealed class TimeSettings : SettingsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The target amount of the game logic updates per second (script updates frequency). Use 0 for infinity.
|
||||
/// </summary>
|
||||
[DefaultValue(30.0f)]
|
||||
[EditorOrder(1), Limit(0, 1000), EditorDisplay(null, "Update FPS"), Tooltip("Target amount of the game logic updates per second (script updates frequency). Use 0 for infinity.")]
|
||||
public float UpdateFPS = 30.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The target amount of the physics simulation updates per second (also fixed updates frequency). Use 0 for infinity.
|
||||
/// </summary>
|
||||
[DefaultValue(60.0f)]
|
||||
[EditorOrder(2), Limit(0, 1000), EditorDisplay(null, "Physics FPS"), Tooltip("Target amount of the physics simulation updates per second (also fixed updates frequency). Use 0 for infinity.")]
|
||||
public float PhysicsFPS = 60.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The target amount of the frames rendered per second (actual game FPS). Use 0 for infinity.
|
||||
/// </summary>
|
||||
[DefaultValue(60.0f)]
|
||||
[EditorOrder(3), Limit(0, 1000), EditorDisplay(null, "Draw FPS"), Tooltip("Target amount of the frames rendered per second (actual game FPS). Use 0 for infinity.")]
|
||||
public float DrawFPS = 60.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The game time scale factor. Default is 1.
|
||||
/// </summary>
|
||||
[DefaultValue(1.0f)]
|
||||
[EditorOrder(10), Limit(0, 1000.0f, 0.1f), Tooltip("Game time scaling factor. Default is 1 for real-time simulation.")]
|
||||
public float TimeScale = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum allowed delta time (in seconds) for the game logic update step.
|
||||
/// </summary>
|
||||
[DefaultValue(1.0f / 10.0f)]
|
||||
[EditorOrder(20), Limit(0.1f, 1000.0f, 0.01f), Tooltip("The maximum allowed delta time (in seconds) for the game logic update step.")]
|
||||
public float MaxUpdateDeltaTime = 1.0f / 10.0f;
|
||||
}
|
||||
}
|
||||
97
Source/Editor/Content/Settings/UWPPlatformSettings.cs
Normal file
97
Source/Editor/Content/Settings/UWPPlatformSettings.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// The Universal Windows Platform (UWP) platform settings asset archetype. Allows to edit asset via editor.
|
||||
/// </summary>
|
||||
public class UWPPlatformSettings : SettingsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The preferred launch windowing mode.
|
||||
/// </summary>
|
||||
public enum WindowMode
|
||||
{
|
||||
/// <summary>
|
||||
/// The full screen mode
|
||||
/// </summary>
|
||||
FullScreen = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The view size.
|
||||
/// </summary>
|
||||
ViewSize = 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The display orientation modes. Can be combined as flags.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum DisplayOrientations
|
||||
{
|
||||
/// <summary>
|
||||
/// The none.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The landscape.
|
||||
/// </summary>
|
||||
Landscape = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The landscape flipped.
|
||||
/// </summary>
|
||||
LandscapeFlipped = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The portrait.
|
||||
/// </summary>
|
||||
Portrait = 4,
|
||||
|
||||
/// <summary>
|
||||
/// The portrait flipped.
|
||||
/// </summary>
|
||||
PortraitFlipped = 8,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The preferred launch windowing mode. Always fullscreen on Xbox.
|
||||
/// </summary>
|
||||
[DefaultValue(WindowMode.FullScreen)]
|
||||
[EditorOrder(10), EditorDisplay("Window"), Tooltip("The preferred launch windowing mode. Always fullscreen on Xbox.")]
|
||||
public WindowMode PreferredLaunchWindowingMode = WindowMode.FullScreen;
|
||||
|
||||
/// <summary>
|
||||
/// The display orientation modes. Can be combined as flags.
|
||||
/// </summary>
|
||||
[DefaultValue(DisplayOrientations.Landscape | DisplayOrientations.LandscapeFlipped | DisplayOrientations.Portrait | DisplayOrientations.PortraitFlipped)]
|
||||
[EditorOrder(20), EditorDisplay("Window"), Tooltip("The display orientation modes. Can be combined as flags.")]
|
||||
public DisplayOrientations AutoRotationPreferences = DisplayOrientations.Landscape | DisplayOrientations.LandscapeFlipped | DisplayOrientations.Portrait | DisplayOrientations.PortraitFlipped;
|
||||
|
||||
/// <summary>
|
||||
/// The location of the package certificate (relative to the project).
|
||||
/// </summary>
|
||||
[DefaultValue("")]
|
||||
[EditorOrder(1010), EditorDisplay("Other"), Tooltip("The location of the package certificate (relative to the project).")]
|
||||
public string CertificateLocation = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Enables support for DirectX 11. Disabling it reduces compiled shaders count.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorOrder(2000), EditorDisplay("Graphics", "Support DirectX 11"), Tooltip("Enables support for DirectX 11. Disabling it reduces compiled shaders count.")]
|
||||
public bool SupportDX11 = true;
|
||||
|
||||
/// <summary>
|
||||
/// Enables support for DirectX 10 and DirectX 10.1. Disabling it reduces compiled shaders count.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorOrder(2010), EditorDisplay("Graphics", "Support DirectX 10"), Tooltip("Enables support for DirectX 10 and DirectX 10.1. Disabling it reduces compiled shaders count.")]
|
||||
public bool SupportDX10 = false;
|
||||
}
|
||||
}
|
||||
90
Source/Editor/Content/Settings/WindowsPlatformSettings.cs
Normal file
90
Source/Editor/Content/Settings/WindowsPlatformSettings.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// The Windows platform settings asset archetype. Allows to edit asset via editor.
|
||||
/// </summary>
|
||||
public class WindowsPlatformSettings : SettingsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The default game window mode.
|
||||
/// </summary>
|
||||
[DefaultValue(GameWindowMode.Windowed)]
|
||||
[EditorOrder(10), EditorDisplay("Window"), Tooltip("The default game window mode.")]
|
||||
public GameWindowMode WindowMode = GameWindowMode.Windowed;
|
||||
|
||||
/// <summary>
|
||||
/// The default game window width (in pixels).
|
||||
/// </summary>
|
||||
[DefaultValue(1280)]
|
||||
[EditorOrder(20), EditorDisplay("Window"), Tooltip("The default game window width (in pixels).")]
|
||||
public int ScreenWidth = 1280;
|
||||
|
||||
/// <summary>
|
||||
/// The default game window height (in pixels).
|
||||
/// </summary>
|
||||
[DefaultValue(720)]
|
||||
[EditorOrder(30), EditorDisplay("Window"), Tooltip("The default game window height (in pixels).")]
|
||||
public int ScreenHeight = 720;
|
||||
|
||||
/// <summary>
|
||||
/// Enables resizing the game window by the user.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorOrder(40), EditorDisplay("Window"), Tooltip("Enables resizing the game window by the user.")]
|
||||
public bool ResizableWindow = false;
|
||||
|
||||
/// <summary>
|
||||
/// Enables game running when application window loses focus.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorOrder(1010), EditorDisplay("Other", "Run In Background"), Tooltip("Enables game running when application window loses focus.")]
|
||||
public bool RunInBackground = false;
|
||||
|
||||
/// <summary>
|
||||
/// Limits maximum amount of concurrent game instances running to one, otherwise user may launch application more than once.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorOrder(1020), EditorDisplay("Other"), Tooltip("Limits maximum amount of concurrent game instances running to one, otherwise user may launch application more than once.")]
|
||||
public bool ForceSingleInstance = false;
|
||||
|
||||
/// <summary>
|
||||
/// Custom icon texture to use for the application (overrides the default one).
|
||||
/// </summary>
|
||||
[DefaultValue(null)]
|
||||
[EditorOrder(1030), EditorDisplay("Other"), Tooltip("Custom icon texture to use for the application (overrides the default one).")]
|
||||
public Texture OverrideIcon;
|
||||
|
||||
/// <summary>
|
||||
/// Enables support for DirectX 12. Disabling it reduces compiled shaders count.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorOrder(2000), EditorDisplay("Graphics", "Support DirectX 12"), Tooltip("Enables support for DirectX 12. Disabling it reduces compiled shaders count.")]
|
||||
public bool SupportDX12 = false;
|
||||
|
||||
/// <summary>
|
||||
/// Enables support for DirectX 11. Disabling it reduces compiled shaders count.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorOrder(2010), EditorDisplay("Graphics", "Support DirectX 11"), Tooltip("Enables support for DirectX 11. Disabling it reduces compiled shaders count.")]
|
||||
public bool SupportDX11 = true;
|
||||
|
||||
/// <summary>
|
||||
/// Enables support for DirectX 10 and DirectX 10.1. Disabling it reduces compiled shaders count.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorOrder(2020), EditorDisplay("Graphics", "Support DirectX 10"), Tooltip("Enables support for DirectX 10 and DirectX 10.1. Disabling it reduces compiled shaders count.")]
|
||||
public bool SupportDX10 = false;
|
||||
|
||||
/// <summary>
|
||||
/// Enables support for Vulkan. Disabling it reduces compiled shaders count.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorOrder(2030), EditorDisplay("Graphics", "Support Vulkan"), Tooltip("Enables support for Vulkan. Disabling it reduces compiled shaders count.")]
|
||||
public bool SupportVulkan = false;
|
||||
}
|
||||
}
|
||||
129
Source/Editor/Content/Thumbnails/ThumbnailRequest.cs
Normal file
129
Source/Editor/Content/Thumbnails/ThumbnailRequest.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Content.Thumbnails
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about asset thumbnail rendering.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public class ThumbnailRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// The request state types.
|
||||
/// </summary>
|
||||
public enum States
|
||||
{
|
||||
/// <summary>
|
||||
/// The initial state.
|
||||
/// </summary>
|
||||
Created,
|
||||
|
||||
/// <summary>
|
||||
/// Request has been prepared for the rendering but still may wait for resources to load fully.
|
||||
/// </summary>
|
||||
Prepared,
|
||||
|
||||
/// <summary>
|
||||
/// The thumbnail has been rendered. Request can be finalized.
|
||||
/// </summary>
|
||||
Rendered,
|
||||
|
||||
/// <summary>
|
||||
/// The finalized state.
|
||||
/// </summary>
|
||||
Disposed,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the state.
|
||||
/// </summary>
|
||||
public States State { get; private set; } = States.Created;
|
||||
|
||||
/// <summary>
|
||||
/// The item.
|
||||
/// </summary>
|
||||
public readonly AssetItem Item;
|
||||
|
||||
/// <summary>
|
||||
/// The proxy object for the asset item.
|
||||
/// </summary>
|
||||
public readonly AssetProxy Proxy;
|
||||
|
||||
/// <summary>
|
||||
/// The asset reference. May be null if not cached yet.
|
||||
/// </summary>
|
||||
public Asset Asset;
|
||||
|
||||
/// <summary>
|
||||
/// The custom tag object used by the thumbnails rendering pipeline. Can be used to store the data related to the thumbnail rendering by the asset proxy.
|
||||
/// </summary>
|
||||
public object Tag;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether thumbnail can be drawn for the item.
|
||||
/// </summary>
|
||||
public bool IsReady => State == States.Prepared && Asset && Asset.IsLoaded && Proxy.CanDrawThumbnail(this);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ThumbnailRequest"/> class.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="proxy">The proxy.</param>
|
||||
public ThumbnailRequest(AssetItem item, AssetProxy proxy)
|
||||
{
|
||||
Item = item;
|
||||
Proxy = proxy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepares this request.
|
||||
/// </summary>
|
||||
public void Prepare()
|
||||
{
|
||||
if (State != States.Created)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
// Prepare
|
||||
Asset = FlaxEngine.Content.LoadAsync(Item.Path);
|
||||
Proxy.OnThumbnailDrawPrepare(this);
|
||||
|
||||
State = States.Prepared;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finishes the rendering and updates the item thumbnail.
|
||||
/// </summary>
|
||||
/// <param name="icon">The icon.</param>
|
||||
public void FinishRender(ref SpriteHandle icon)
|
||||
{
|
||||
if (State != States.Prepared)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
Item.Thumbnail = icon;
|
||||
|
||||
State = States.Rendered;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes this request.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (State == States.Disposed)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
if (State != States.Created)
|
||||
{
|
||||
// Cleanup
|
||||
Proxy.OnThumbnailDrawCleanup(this);
|
||||
Asset = null;
|
||||
}
|
||||
|
||||
Tag = null;
|
||||
State = States.Disposed;
|
||||
}
|
||||
}
|
||||
}
|
||||
523
Source/Editor/Content/Thumbnails/ThumbnailsModule.cs
Normal file
523
Source/Editor/Content/Thumbnails/ThumbnailsModule.cs
Normal file
@@ -0,0 +1,523 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using FlaxEditor.Modules;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Content.Thumbnails
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages asset thumbnails rendering and presentation.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Modules.EditorModule" />
|
||||
public sealed class ThumbnailsModule : EditorModule, IContentItemOwner
|
||||
{
|
||||
/// <summary>
|
||||
/// The minimum required quality (in range [0;1]) for content streaming resources to be loaded in order to generate thumbnail for them.
|
||||
/// </summary>
|
||||
public const float MinimumRequiredResourcesQuality = 0.8f;
|
||||
|
||||
// TODO: free atlas slots for deleted assets
|
||||
|
||||
private readonly List<PreviewsCache> _cache = new List<PreviewsCache>(4);
|
||||
private readonly string _cacheFolder;
|
||||
|
||||
private DateTime _lastFlushTime;
|
||||
|
||||
private readonly List<ThumbnailRequest> _requests = new List<ThumbnailRequest>(128);
|
||||
private readonly PreviewRoot _guiRoot = new PreviewRoot();
|
||||
private RenderTask _task;
|
||||
private GPUTexture _output;
|
||||
|
||||
internal ThumbnailsModule(Editor editor)
|
||||
: base(editor)
|
||||
{
|
||||
_cacheFolder = StringUtils.CombinePaths(Globals.ProjectCacheFolder, "Thumbnails");
|
||||
_lastFlushTime = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requests the item preview.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <exception cref="System.ArgumentNullException"></exception>
|
||||
public void RequestPreview(ContentItem item)
|
||||
{
|
||||
if (item == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
// Check if use default icon
|
||||
var defaultThumbnail = item.DefaultThumbnail;
|
||||
if (defaultThumbnail.IsValid)
|
||||
{
|
||||
item.Thumbnail = defaultThumbnail;
|
||||
return;
|
||||
}
|
||||
|
||||
// We cache previews only for items with 'ID', for now we support only AssetItems
|
||||
var assetItem = item as AssetItem;
|
||||
if (assetItem == null)
|
||||
return;
|
||||
|
||||
// Ensure that there is valid proxy for that item
|
||||
var proxy = Editor.ContentDatabase.GetProxy(item) as AssetProxy;
|
||||
if (proxy == null)
|
||||
{
|
||||
Editor.LogWarning($"Cannot generate preview for item {item.Path}. Cannot find proxy for it.");
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_requests)
|
||||
{
|
||||
// Check if element hasn't been already processed for generating preview
|
||||
if (FindRequest(assetItem) == null)
|
||||
{
|
||||
// Check each cache atlas
|
||||
for (int i = 0; i < _cache.Count; i++)
|
||||
{
|
||||
var sprite = _cache[i].FindSlot(assetItem.ID);
|
||||
if (sprite.IsValid)
|
||||
{
|
||||
// Found!
|
||||
item.Thumbnail = sprite;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add request
|
||||
AddRequest(assetItem, proxy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the item preview from the cache.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <exception cref="System.ArgumentNullException"></exception>
|
||||
public void DeletePreview(ContentItem item)
|
||||
{
|
||||
if (item == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
// We cache previews only for items with 'ID', for now we support only AssetItems
|
||||
var assetItem = item as AssetItem;
|
||||
if (assetItem == null)
|
||||
return;
|
||||
|
||||
lock (_requests)
|
||||
{
|
||||
// Cancel loading
|
||||
RemoveRequest(assetItem);
|
||||
|
||||
// Find atlas with preview and remove it
|
||||
for (int i = 0; i < _cache.Count; i++)
|
||||
{
|
||||
if (_cache[i].ReleaseSlot(assetItem.ID))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region IContentItemOwner
|
||||
|
||||
/// <inheritdoc />
|
||||
void IContentItemOwner.OnItemDeleted(ContentItem item)
|
||||
{
|
||||
if (item is AssetItem assetItem)
|
||||
{
|
||||
lock (_requests)
|
||||
{
|
||||
RemoveRequest(assetItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
void IContentItemOwner.OnItemRenamed(ContentItem item)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
void IContentItemOwner.OnItemReimported(ContentItem item)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
void IContentItemOwner.OnItemDispose(ContentItem item)
|
||||
{
|
||||
if (item is AssetItem assetItem)
|
||||
{
|
||||
lock (_requests)
|
||||
{
|
||||
RemoveRequest(assetItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnInit()
|
||||
{
|
||||
// Create cache folder
|
||||
if (!Directory.Exists(_cacheFolder))
|
||||
{
|
||||
Directory.CreateDirectory(_cacheFolder);
|
||||
}
|
||||
|
||||
// Find atlases in a Editor cache directory
|
||||
var files = Directory.GetFiles(_cacheFolder, "cache_*.flax", SearchOption.TopDirectoryOnly);
|
||||
int atlases = 0;
|
||||
for (int i = 0; i < files.Length; i++)
|
||||
{
|
||||
// Load asset
|
||||
var asset = FlaxEngine.Content.LoadAsync(files[i]);
|
||||
if (asset == null)
|
||||
continue;
|
||||
|
||||
// Validate type
|
||||
if (asset is PreviewsCache atlas)
|
||||
{
|
||||
// Cache atlas
|
||||
atlases++;
|
||||
_cache.Add(atlas);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip asset
|
||||
Editor.LogWarning(string.Format("Asset \'{0}\' is inside Editor\'s private directory for Assets Thumbnails Cache. Please move it.", asset.Path));
|
||||
}
|
||||
}
|
||||
Editor.Log(string.Format("Previews cache count: {0} (capacity for {1} icons)", atlases, atlases * PreviewsCache.AssetIconsPerAtlas));
|
||||
|
||||
// Prepare at least one atlas
|
||||
if (_cache.Count == 0)
|
||||
{
|
||||
GetValidAtlas();
|
||||
}
|
||||
|
||||
// Create render task but disabled for now
|
||||
_output = GPUDevice.Instance.CreateTexture("ThumbnailsOutput");
|
||||
var desc = GPUTextureDescription.New2D(PreviewsCache.AssetIconSize, PreviewsCache.AssetIconSize, PreviewsCache.AssetIconsAtlasFormat);
|
||||
_output.Init(ref desc);
|
||||
_task = Object.New<RenderTask>();
|
||||
_task.Order = 50; // Render this task later
|
||||
_task.Enabled = false;
|
||||
_task.Render += OnRender;
|
||||
}
|
||||
|
||||
private void OnRender(RenderTask task, GPUContext context)
|
||||
{
|
||||
lock (_requests)
|
||||
{
|
||||
// Check if there is ready next asset to render thumbnail for it
|
||||
// But don't check whole queue, only a few items
|
||||
var request = GetReadyRequest(10);
|
||||
if (request == null)
|
||||
{
|
||||
// Disable task
|
||||
_task.Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Find atlas with an free slot
|
||||
var atlas = GetValidAtlas();
|
||||
if (atlas == null)
|
||||
{
|
||||
// Error
|
||||
_task.Enabled = false;
|
||||
_requests.Clear();
|
||||
Editor.LogError("Failed to get atlas.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for atlas being loaded
|
||||
if (!atlas.IsReady)
|
||||
return;
|
||||
|
||||
// Setup
|
||||
_guiRoot.RemoveChildren();
|
||||
_guiRoot.AccentColor = request.Proxy.AccentColor;
|
||||
|
||||
// Call proxy to prepare for thumbnail rendering
|
||||
request.Proxy.OnThumbnailDrawBegin(request, _guiRoot, context);
|
||||
_guiRoot.UnlockChildrenRecursive();
|
||||
|
||||
// Draw preview
|
||||
context.Clear(_output.View(), Color.Black);
|
||||
Render2D.CallDrawing(_guiRoot, context, _output);
|
||||
|
||||
// Call proxy and cleanup UI (delete create controls, shared controls should be unlinked during OnThumbnailDrawEnd event)
|
||||
request.Proxy.OnThumbnailDrawEnd(request, _guiRoot);
|
||||
_guiRoot.DisposeChildren();
|
||||
|
||||
// Copy backbuffer with rendered preview into atlas
|
||||
SpriteHandle icon = atlas.OccupySlot(_output, request.Item.ID);
|
||||
if (!icon.IsValid)
|
||||
{
|
||||
// Error
|
||||
_task.Enabled = false;
|
||||
_requests.Clear();
|
||||
Editor.LogError("Failed to occupy previews cache atlas slot.");
|
||||
return;
|
||||
}
|
||||
|
||||
// End
|
||||
request.FinishRender(ref icon);
|
||||
RemoveRequest(request);
|
||||
}
|
||||
}
|
||||
|
||||
private void StartPreviewsQueue()
|
||||
{
|
||||
// Ensure to have valid atlas
|
||||
GetValidAtlas();
|
||||
|
||||
// Enable task
|
||||
_task.Enabled = true;
|
||||
}
|
||||
|
||||
#region Requests Management
|
||||
|
||||
private ThumbnailRequest FindRequest(AssetItem item)
|
||||
{
|
||||
for (int i = 0; i < _requests.Count; i++)
|
||||
{
|
||||
if (_requests[i].Item == item)
|
||||
return _requests[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void AddRequest(AssetItem item, AssetProxy proxy)
|
||||
{
|
||||
var request = new ThumbnailRequest(item, proxy);
|
||||
_requests.Add(request);
|
||||
item.AddReference(this);
|
||||
}
|
||||
|
||||
private void RemoveRequest(ThumbnailRequest request)
|
||||
{
|
||||
request.Dispose();
|
||||
_requests.Remove(request);
|
||||
request.Item.RemoveReference(this);
|
||||
}
|
||||
|
||||
private void RemoveRequest(AssetItem item)
|
||||
{
|
||||
var request = FindRequest(item);
|
||||
if (request != null)
|
||||
RemoveRequest(request);
|
||||
}
|
||||
|
||||
private ThumbnailRequest GetReadyRequest(int maxChecks)
|
||||
{
|
||||
maxChecks = Mathf.Min(maxChecks, _requests.Count);
|
||||
for (int i = 0; i < maxChecks; i++)
|
||||
{
|
||||
var request = _requests[i];
|
||||
|
||||
try
|
||||
{
|
||||
if (request.IsReady)
|
||||
{
|
||||
return request;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Editor.LogWarning(ex);
|
||||
Editor.LogWarning($"Failed to prepare thumbnail rendering for {request.Item.ShortName}.");
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Atlas Management
|
||||
|
||||
private PreviewsCache CreateAtlas()
|
||||
{
|
||||
// Create atlas path
|
||||
var path = StringUtils.CombinePaths(_cacheFolder, string.Format("cache_{0:N}.flax", Guid.NewGuid()));
|
||||
|
||||
// Create atlas
|
||||
if (PreviewsCache.Create(path))
|
||||
{
|
||||
// Error
|
||||
Editor.LogError("Failed to create thumbnails atlas.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Load atlas
|
||||
var atlas = FlaxEngine.Content.LoadAsync<PreviewsCache>(path);
|
||||
if (atlas == null)
|
||||
{
|
||||
// Error
|
||||
Editor.LogError("Failed to load thumbnails atlas.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Register new atlas
|
||||
_cache.Add(atlas);
|
||||
|
||||
return atlas;
|
||||
}
|
||||
|
||||
private void Flush()
|
||||
{
|
||||
for (int i = 0; i < _cache.Count; i++)
|
||||
{
|
||||
_cache[i].Flush();
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasAllAtlasesLoaded()
|
||||
{
|
||||
for (int i = 0; i < _cache.Count; i++)
|
||||
{
|
||||
if (!_cache[i].IsReady)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private PreviewsCache GetValidAtlas()
|
||||
{
|
||||
// Check if has no free slots
|
||||
for (int i = 0; i < _cache.Count; i++)
|
||||
{
|
||||
if (_cache[i].HasFreeSlot)
|
||||
{
|
||||
return _cache[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Create new atlas
|
||||
return CreateAtlas();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnUpdate()
|
||||
{
|
||||
// Wait some frames before start generating previews (late init feature)
|
||||
if (Time.TimeSinceStartup < 1.0f || HasAllAtlasesLoaded() == false)
|
||||
{
|
||||
// Back
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_requests)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
// Check if has any request pending
|
||||
int count = _requests.Count;
|
||||
if (count > 0)
|
||||
{
|
||||
// Prepare requests
|
||||
bool isAnyReady = false;
|
||||
int checks = Mathf.Min(10, _requests.Count);
|
||||
for (int i = 0; i < checks; i++)
|
||||
{
|
||||
var request = _requests[i];
|
||||
|
||||
try
|
||||
{
|
||||
if (request.IsReady)
|
||||
{
|
||||
isAnyReady = true;
|
||||
}
|
||||
else if (request.State == ThumbnailRequest.States.Created)
|
||||
{
|
||||
request.Prepare();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Editor.LogWarning(ex);
|
||||
Editor.LogWarning($"Failed to prepare thumbnail rendering for {request.Item.ShortName}.");
|
||||
}
|
||||
}
|
||||
|
||||
// Check if has no rendering task enabled but should be
|
||||
if (isAnyReady && _task.Enabled == false)
|
||||
{
|
||||
// Start generating preview
|
||||
StartPreviewsQueue();
|
||||
}
|
||||
}
|
||||
// Don't flush every frame
|
||||
else if (now - _lastFlushTime >= TimeSpan.FromSeconds(1))
|
||||
{
|
||||
// Flush data
|
||||
_lastFlushTime = now;
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnExit()
|
||||
{
|
||||
if (_task)
|
||||
_task.Enabled = false;
|
||||
|
||||
lock (_requests)
|
||||
{
|
||||
// Clear data
|
||||
while (_requests.Count > 0)
|
||||
RemoveRequest(_requests[0]);
|
||||
_cache.Clear();
|
||||
}
|
||||
|
||||
_guiRoot.Dispose();
|
||||
Object.Destroy(ref _task);
|
||||
Object.Destroy(ref _output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thumbnails GUI root control.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
private class PreviewRoot : ContainerControl
|
||||
{
|
||||
/// <summary>
|
||||
/// The item accent color to draw.
|
||||
/// </summary>
|
||||
public Color AccentColor;
|
||||
|
||||
/// <inheritdoc />
|
||||
public PreviewRoot()
|
||||
: base(0, 0, PreviewsCache.AssetIconSize, PreviewsCache.AssetIconSize)
|
||||
{
|
||||
AutoFocus = false;
|
||||
AccentColor = Color.Pink;
|
||||
IsLayoutLocked = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
// Draw accent
|
||||
const float accentHeight = 2;
|
||||
Render2D.FillRectangle(new Rectangle(0, Height - accentHeight, Width, accentHeight), AccentColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
315
Source/Editor/Content/Tree/ContentTreeNode.cs
Normal file
315
Source/Editor/Content/Tree/ContentTreeNode.cs
Normal file
@@ -0,0 +1,315 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.Drag;
|
||||
using FlaxEditor.GUI.Tree;
|
||||
using FlaxEditor.Utilities;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Content folder tree node.
|
||||
/// </summary>
|
||||
/// <seealso cref="TreeNode" />
|
||||
public class ContentTreeNode : TreeNode
|
||||
{
|
||||
private DragItems _dragOverItems;
|
||||
private List<Rectangle> _highlights;
|
||||
|
||||
/// <summary>
|
||||
/// The folder.
|
||||
/// </summary>
|
||||
protected ContentFolder _folder;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content folder item.
|
||||
/// </summary>
|
||||
public ContentFolder Folder => _folder;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the folder.
|
||||
/// </summary>
|
||||
public ContentFolderType FolderType => _folder.FolderType;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if that folder can import/manage scripts.
|
||||
/// </summary>
|
||||
public bool CanHaveScripts => _folder.CanHaveScripts;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if that folder can import/manage assets.
|
||||
/// </summary>
|
||||
/// <returns>True if can contain assets for project, otherwise false</returns>
|
||||
public bool CanHaveAssets => _folder.CanHaveAssets;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent node.
|
||||
/// </summary>
|
||||
public ContentTreeNode ParentNode => Parent as ContentTreeNode;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the folder path.
|
||||
/// </summary>
|
||||
public string Path => _folder.Path;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the navigation button label.
|
||||
/// </summary>
|
||||
public virtual string NavButtonLabel => _folder.ShortName;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentTreeNode"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent node.</param>
|
||||
/// <param name="path">The folder path.</param>
|
||||
public ContentTreeNode(ContentTreeNode parent, string path)
|
||||
: this(parent, parent?.FolderType ?? ContentFolderType.Other, path)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentTreeNode"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent node.</param>
|
||||
/// <param name="type">The folder type.</param>
|
||||
/// <param name="path">The folder path.</param>
|
||||
protected ContentTreeNode(ContentTreeNode parent, ContentFolderType type, string path)
|
||||
: base(false, Editor.Instance.Icons.FolderClosed12, Editor.Instance.Icons.FolderOpened12)
|
||||
{
|
||||
_folder = new ContentFolder(type, path, this);
|
||||
Text = _folder.ShortName;
|
||||
if (parent != null)
|
||||
{
|
||||
Folder.ParentFolder = parent.Folder;
|
||||
Parent = parent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the rename popup for the item.
|
||||
/// </summary>
|
||||
public void StartRenaming()
|
||||
{
|
||||
if (!_folder.CanRename)
|
||||
return;
|
||||
|
||||
// Start renaming the folder
|
||||
var dialog = RenamePopup.Show(this, HeaderRect, _folder.ShortName, false);
|
||||
dialog.Tag = _folder;
|
||||
dialog.Renamed += popup => Editor.Instance.Windows.ContentWin.Rename((ContentFolder)popup.Tag, popup.Text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the query search filter.
|
||||
/// </summary>
|
||||
/// <param name="filterText">The filter text.</param>
|
||||
public void UpdateFilter(string filterText)
|
||||
{
|
||||
bool noFilter = string.IsNullOrWhiteSpace(filterText);
|
||||
|
||||
// Update itself
|
||||
bool isThisVisible;
|
||||
if (noFilter)
|
||||
{
|
||||
// Clear filter
|
||||
_highlights?.Clear();
|
||||
isThisVisible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
QueryFilterHelper.Range[] ranges;
|
||||
var text = Text;
|
||||
if (QueryFilterHelper.Match(filterText, text, out ranges))
|
||||
{
|
||||
// Update highlights
|
||||
if (_highlights == null)
|
||||
_highlights = new List<Rectangle>(ranges.Length);
|
||||
else
|
||||
_highlights.Clear();
|
||||
var style = Style.Current;
|
||||
var font = style.FontSmall;
|
||||
var textRect = TextRect;
|
||||
for (int i = 0; i < ranges.Length; i++)
|
||||
{
|
||||
var start = font.GetCharPosition(text, ranges[i].StartIndex);
|
||||
var end = font.GetCharPosition(text, ranges[i].EndIndex);
|
||||
_highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height));
|
||||
}
|
||||
isThisVisible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hide
|
||||
_highlights?.Clear();
|
||||
isThisVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update children
|
||||
bool isAnyChildVisible = false;
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
if (_children[i] is ContentTreeNode child)
|
||||
{
|
||||
child.UpdateFilter(filterText);
|
||||
isAnyChildVisible |= child.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
bool isExpanded = isAnyChildVisible;
|
||||
|
||||
if (isExpanded)
|
||||
{
|
||||
Expand(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Collapse(true);
|
||||
}
|
||||
|
||||
Visible = isThisVisible | isAnyChildVisible;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
// Draw all highlights
|
||||
if (_highlights != null)
|
||||
{
|
||||
var style = Style.Current;
|
||||
var color = style.ProgressNormal * 0.6f;
|
||||
for (int i = 0; i < _highlights.Count; i++)
|
||||
Render2D.FillRectangle(_highlights[i], color);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
// Delete folder item
|
||||
_folder.Dispose();
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
private DragDropEffect GetDragEffect(DragData data)
|
||||
{
|
||||
if (data is DragDataFiles)
|
||||
{
|
||||
if (_folder.CanHaveAssets)
|
||||
return DragDropEffect.Copy;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_dragOverItems.HasValidDrag)
|
||||
return DragDropEffect.Move;
|
||||
}
|
||||
|
||||
return DragDropEffect.None;
|
||||
}
|
||||
|
||||
private bool ValidateDragItem(ContentItem item)
|
||||
{
|
||||
// Reject itself and any parent
|
||||
return item != _folder && !item.Find(_folder);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override DragDropEffect OnDragEnterHeader(DragData data)
|
||||
{
|
||||
if (_dragOverItems == null)
|
||||
_dragOverItems = new DragItems(ValidateDragItem);
|
||||
|
||||
_dragOverItems.OnDragEnter(data);
|
||||
return GetDragEffect(data);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override DragDropEffect OnDragMoveHeader(DragData data)
|
||||
{
|
||||
return GetDragEffect(data);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDragLeaveHeader()
|
||||
{
|
||||
_dragOverItems.OnDragLeave();
|
||||
base.OnDragLeaveHeader();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override DragDropEffect OnDragDropHeader(DragData data)
|
||||
{
|
||||
var result = DragDropEffect.None;
|
||||
|
||||
// Check if drop element or files
|
||||
if (data is DragDataFiles files)
|
||||
{
|
||||
// Import files
|
||||
Editor.Instance.ContentImporting.Import(files.Files, _folder);
|
||||
result = DragDropEffect.Copy;
|
||||
|
||||
Expand();
|
||||
}
|
||||
else if (_dragOverItems.HasValidDrag)
|
||||
{
|
||||
// Move items
|
||||
Editor.Instance.ContentDatabase.Move(_dragOverItems.Objects, _folder);
|
||||
result = DragDropEffect.Move;
|
||||
|
||||
Expand();
|
||||
}
|
||||
|
||||
_dragOverItems.OnDragDrop();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void DoDragDrop()
|
||||
{
|
||||
DoDragDrop(DragItems.GetDragData(_folder));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnLongPress()
|
||||
{
|
||||
Select();
|
||||
|
||||
StartRenaming();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
if (IsFocused)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case KeyboardKeys.F2:
|
||||
StartRenaming();
|
||||
return true;
|
||||
case KeyboardKeys.Delete:
|
||||
Editor.Instance.Windows.ContentWin.Delete(Folder);
|
||||
return true;
|
||||
}
|
||||
if (RootWindow.GetKey(KeyboardKeys.Control))
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case KeyboardKeys.D:
|
||||
Editor.Instance.Windows.ContentWin.Duplicate(Folder);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnKeyDown(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
Source/Editor/Content/Tree/MainContentTreeNode.cs
Normal file
56
Source/Editor/Content/Tree/MainContentTreeNode.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Content tree node used for main directories.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.ContentTreeNode" />
|
||||
public class MainContentTreeNode : ContentTreeNode
|
||||
{
|
||||
private FileSystemWatcher _watcher;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MainContentTreeNode"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent project.</param>
|
||||
/// <param name="type">The folder type.</param>
|
||||
/// <param name="path">The folder path.</param>
|
||||
public MainContentTreeNode(ProjectTreeNode parent, ContentFolderType type, string path)
|
||||
: base(parent, type, path)
|
||||
{
|
||||
_watcher = new FileSystemWatcher(path)
|
||||
{
|
||||
IncludeSubdirectories = true,
|
||||
EnableRaisingEvents = true
|
||||
};
|
||||
//_watcher.Changed += OnEvent;
|
||||
_watcher.Created += OnEvent;
|
||||
_watcher.Deleted += OnEvent;
|
||||
//_watcher.Renamed += OnEvent;
|
||||
}
|
||||
|
||||
private void OnEvent(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
Editor.Instance.ContentDatabase.OnDirectoryEvent(this, e);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void DoDragDrop()
|
||||
{
|
||||
// No drag for root nodes
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_watcher.EnableRaisingEvents = false;
|
||||
_watcher.Dispose();
|
||||
_watcher = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
57
Source/Editor/Content/Tree/ProjectTreeNode.cs
Normal file
57
Source/Editor/Content/Tree/ProjectTreeNode.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Root tree node for the project workspace.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.ContentTreeNode" />
|
||||
public sealed class ProjectTreeNode : ContentTreeNode
|
||||
{
|
||||
/// <summary>
|
||||
/// The project/
|
||||
/// </summary>
|
||||
public readonly ProjectInfo Project;
|
||||
|
||||
/// <summary>
|
||||
/// The project content directory.
|
||||
/// </summary>
|
||||
public MainContentTreeNode Content;
|
||||
|
||||
/// <summary>
|
||||
/// The project source code directory.
|
||||
/// </summary>
|
||||
public MainContentTreeNode Source;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProjectTreeNode"/> class.
|
||||
/// </summary>
|
||||
/// <param name="project">The project.</param>
|
||||
public ProjectTreeNode(ProjectInfo project)
|
||||
: base(null, project.ProjectFolderPath)
|
||||
{
|
||||
Project = project;
|
||||
Folder.FileName = Folder.ShortName = Text = project.Name;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string NavButtonLabel => Project.Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void DoDragDrop()
|
||||
{
|
||||
// No drag for root nodes
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Compare(Control other)
|
||||
{
|
||||
// Move the main game project to the top
|
||||
if (Project.Name == Editor.Instance.GameProject.Name)
|
||||
return -1;
|
||||
return base.Compare(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
Source/Editor/Content/Tree/RootContentTreeNode.cs
Normal file
22
Source/Editor/Content/Tree/RootContentTreeNode.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Root tree node for the content workspace.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.ContentTreeNode" />
|
||||
public sealed class RootContentTreeNode : ContentTreeNode
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RootContentTreeNode"/> class.
|
||||
/// </summary>
|
||||
public RootContentTreeNode()
|
||||
: base(null, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string NavButtonLabel => string.Empty;
|
||||
}
|
||||
}
|
||||
373
Source/Editor/Cooker/CookingData.h
Normal file
373
Source/Editor/Cooker/CookingData.h
Normal file
@@ -0,0 +1,373 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Enums.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Core/Collections/HashSet.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
|
||||
class GameCooker;
|
||||
class PlatformTools;
|
||||
|
||||
/// <summary>
|
||||
/// Game building options. Used as flags.
|
||||
/// </summary>
|
||||
API_ENUM(Attributes="Flags") enum class BuildOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// No special options declared.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Shows the output directory folder on building end.
|
||||
/// </summary>
|
||||
ShowOutput = 1 << 0,
|
||||
};
|
||||
|
||||
DECLARE_ENUM_OPERATORS(BuildOptions);
|
||||
|
||||
/// <summary>
|
||||
/// Game build target platform.
|
||||
/// </summary>
|
||||
API_ENUM() enum class BuildPlatform
|
||||
{
|
||||
/// <summary>
|
||||
/// Windows (32-bit architecture)
|
||||
/// </summary>
|
||||
API_ENUM(Attributes="EditorDisplay(null, \"Windows 32bit\")")
|
||||
Windows32 = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Windows (64-bit architecture)
|
||||
/// </summary>
|
||||
API_ENUM(Attributes="EditorDisplay(null, \"Windows 64bit\")")
|
||||
Windows64 = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Universal Windows Platform (UWP) (x86 architecture)
|
||||
/// </summary>
|
||||
API_ENUM(Attributes="EditorDisplay(null, \"Windows Store x86\")")
|
||||
UWPx86 = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Universal Windows Platform (UWP) (x64 architecture)
|
||||
/// </summary>
|
||||
API_ENUM(Attributes="EditorDisplay(null, \"Windows Store x64\")")
|
||||
UWPx64 = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Xbox One
|
||||
/// </summary>
|
||||
API_ENUM(Attributes="EditorDisplay(null, \"Xbox One\")")
|
||||
XboxOne = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Linux (64-bit architecture)
|
||||
/// </summary>
|
||||
API_ENUM(Attributes="EditorDisplay(null, \"Linux x64\")")
|
||||
LinuxX64 = 6,
|
||||
|
||||
/// <summary>
|
||||
/// PlayStation 4
|
||||
/// </summary>
|
||||
API_ENUM(Attributes="EditorDisplay(null, \"PlayStation 4\")")
|
||||
PS4 = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Xbox Series X.
|
||||
/// </summary>
|
||||
API_ENUM(Attributes="EditorDisplay(null, \"Xbox Scarlett\")")
|
||||
XboxScarlett = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Android ARM64 (arm64-v8a).
|
||||
/// </summary>
|
||||
API_ENUM(Attributes="EditorDisplay(null, \"Android ARM64 (arm64-v8a)\")")
|
||||
AndroidARM64 = 9,
|
||||
};
|
||||
|
||||
inline const Char* ToString(const BuildPlatform platform)
|
||||
{
|
||||
switch (platform)
|
||||
{
|
||||
case BuildPlatform::Windows32:
|
||||
return TEXT("Windows x86");
|
||||
case BuildPlatform::Windows64:
|
||||
return TEXT("Windows x64");
|
||||
case BuildPlatform::UWPx86:
|
||||
return TEXT("Windows Store x86");
|
||||
case BuildPlatform::UWPx64:
|
||||
return TEXT("Windows Store x64");
|
||||
case BuildPlatform::XboxOne:
|
||||
return TEXT("Xbox One");
|
||||
case BuildPlatform::LinuxX64:
|
||||
return TEXT("Linux x64");
|
||||
case BuildPlatform::PS4:
|
||||
return TEXT("PlayStation 4");
|
||||
case BuildPlatform::XboxScarlett:
|
||||
return TEXT("Xbox Scarlett");
|
||||
case BuildPlatform::AndroidARM64:
|
||||
return TEXT("Android ARM64");
|
||||
default:
|
||||
return TEXT("?");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Game build configuration modes.
|
||||
/// </summary>
|
||||
API_ENUM() enum class BuildConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Debug configuration. Without optimizations but with full debugging information.
|
||||
/// </summary>
|
||||
Debug = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Development configuration. With basic optimizations and partial debugging data.
|
||||
/// </summary>
|
||||
Development = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Shipping configuration. With full optimization and no debugging data.
|
||||
/// </summary>
|
||||
Release = 2,
|
||||
};
|
||||
|
||||
inline const Char* ToString(const BuildConfiguration configuration)
|
||||
{
|
||||
switch (configuration)
|
||||
{
|
||||
case BuildConfiguration::Debug:
|
||||
return TEXT("Debug");
|
||||
case BuildConfiguration::Development:
|
||||
return TEXT("Development");
|
||||
case BuildConfiguration::Release:
|
||||
return TEXT("Release");
|
||||
default:
|
||||
return TEXT("?");
|
||||
}
|
||||
}
|
||||
|
||||
#define BUILD_STEP_CANCEL_CHECK if (GameCooker::IsCancelRequested()) return true
|
||||
|
||||
/// <summary>
|
||||
/// Game cooking temporary data.
|
||||
/// </summary>
|
||||
struct FLAXENGINE_API CookingData
|
||||
{
|
||||
/// <summary>
|
||||
/// The platform.
|
||||
/// </summary>
|
||||
BuildPlatform Platform;
|
||||
|
||||
/// <summary>
|
||||
/// The configuration.
|
||||
/// </summary>
|
||||
BuildConfiguration Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// The options.
|
||||
/// </summary>
|
||||
BuildOptions Options;
|
||||
|
||||
/// <summary>
|
||||
/// The original output path (actual OutputPath could be modified by the Platform Tools or a plugin for additional layout customizations or packaging). This path is preserved.
|
||||
/// </summary>
|
||||
String OriginalOutputPath;
|
||||
|
||||
/// <summary>
|
||||
/// The output path.
|
||||
/// </summary>
|
||||
String OutputPath;
|
||||
|
||||
/// <summary>
|
||||
/// The platform tools.
|
||||
/// </summary>
|
||||
PlatformTools* Tools;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The asset type build stats storage.
|
||||
/// </summary>
|
||||
struct AssetTypeStatistics
|
||||
{
|
||||
/// <summary>
|
||||
/// The asset type name.
|
||||
/// </summary>
|
||||
String TypeName;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of assets of that type in a build.
|
||||
/// </summary>
|
||||
int32 Count = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The final output size of the assets of that type in a build.
|
||||
/// </summary>
|
||||
uint64 ContentSize = 0;
|
||||
|
||||
bool operator<(const AssetTypeStatistics& other) const;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The build stats storage.
|
||||
/// </summary>
|
||||
struct Statistics
|
||||
{
|
||||
/// <summary>
|
||||
/// The total assets amount in the build.
|
||||
/// </summary>
|
||||
int32 TotalAssets;
|
||||
|
||||
/// <summary>
|
||||
/// The cooked assets (TotalAssets - CookedAssets is amount of reused cached assets).
|
||||
/// </summary>
|
||||
int32 CookedAssets;
|
||||
|
||||
/// <summary>
|
||||
/// The final output content size in MB.
|
||||
/// </summary>
|
||||
int32 ContentSizeMB;
|
||||
|
||||
/// <summary>
|
||||
/// The asset type stats. Key is the asset typename, value is the stats container.
|
||||
/// </summary>
|
||||
Dictionary<String, AssetTypeStatistics> AssetStats;
|
||||
|
||||
Statistics();
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The build stats.
|
||||
/// </summary>
|
||||
Statistics Stats;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The temporary directory used for the building cache. Can be used for the incremental building.
|
||||
/// </summary>
|
||||
String CacheDirectory;
|
||||
|
||||
/// <summary>
|
||||
/// The root assets collection to include in build in the first place (can be used only before CollectAssetsStep).
|
||||
/// Game cooker will find dependant assets and deploy them as well.
|
||||
/// </summary>
|
||||
HashSet<Guid> RootAssets;
|
||||
|
||||
/// <summary>
|
||||
/// The final assets collection to include in build (valid only after CollectAssetsStep).
|
||||
/// </summary>
|
||||
HashSet<Guid> Assets;
|
||||
|
||||
struct BinaryModule
|
||||
{
|
||||
String Name;
|
||||
String NativePath;
|
||||
String ManagedPath;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The binary modules used in the build. Valid after scripts compilation step. This list includes game, all plugins modules and engine module.
|
||||
/// </summary>
|
||||
Array<BinaryModule, InlinedAllocation<64>> BinaryModules;
|
||||
|
||||
public:
|
||||
|
||||
void Init()
|
||||
{
|
||||
RootAssets.Clear();
|
||||
Assets.Clear();
|
||||
Stats = Statistics();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the absolute path to the Platform Data folder that contains the binary files used by the current build configuration.
|
||||
/// </summary>
|
||||
/// <returns>The platform data folder path.</returns>
|
||||
String GetGameBinariesPath() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the absolute path to the platform folder that contains the dependency files used by the current build configuration.
|
||||
/// </summary>
|
||||
/// <returns>The platform deps folder path.</returns>
|
||||
String GetPlatformBinariesRoot() const;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The total amount of baking steps to perform.
|
||||
/// </summary>
|
||||
int32 StepsCount;
|
||||
|
||||
/// <summary>
|
||||
/// The current step index.
|
||||
/// </summary>
|
||||
int32 CurrentStepIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the progress.
|
||||
/// </summary>
|
||||
/// <param name="stepsCount">The total steps count.</param>
|
||||
void InitProgress(const int32 stepsCount)
|
||||
{
|
||||
StepsCount = stepsCount;
|
||||
CurrentStepIndex = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the progress reporting to the next step
|
||||
/// </summary>
|
||||
void NextStep()
|
||||
{
|
||||
CurrentStepIndex++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reports the current step progress (normalized 0-1 value)
|
||||
/// </summary>
|
||||
/// <param name="info">The step info message.</param>
|
||||
/// <param name="stepProgress">The step progress.</param>
|
||||
void StepProgress(const String& info, const float stepProgress) const;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Adds the asset to the build.
|
||||
/// </summary>
|
||||
/// <param name="id">The asset id.</param>
|
||||
void AddRootAsset(const Guid& id);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the asset to the build.
|
||||
/// </summary>
|
||||
/// <param name="path">The absolute asset path.</param>
|
||||
void AddRootAsset(const String& path);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the internal engine asset to the build.
|
||||
/// </summary>
|
||||
/// <param name="internalPath">The internal path (relative to the engine content path).</param>
|
||||
void AddRootEngineAsset(const String& internalPath);
|
||||
|
||||
public:
|
||||
|
||||
void Error(const String& msg);
|
||||
|
||||
void Error(const Char* msg)
|
||||
{
|
||||
Error(String(msg));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void Error(const Char* format, const Args& ... args)
|
||||
{
|
||||
const String msg = String::Format(format, args...);
|
||||
Error(msg);
|
||||
}
|
||||
};
|
||||
555
Source/Editor/Cooker/GameCooker.cpp
Normal file
555
Source/Editor/Cooker/GameCooker.cpp
Normal file
@@ -0,0 +1,555 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "GameCooker.h"
|
||||
#include "FlaxEngine.Gen.h"
|
||||
#include "Engine/Scripting/MainThreadManagedInvokeAction.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Scripting/ScriptingType.h"
|
||||
#include "Engine/Scripting/BinaryModule.h"
|
||||
#include "Engine/Serialization/JsonTools.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Threading/ThreadSpawner.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Steps/ValidateStep.h"
|
||||
#include "Steps/CompileScriptsStep.h"
|
||||
#include "Steps/PrecompileAssembliesStep.h"
|
||||
#include "Steps/DeployDataStep.h"
|
||||
#include "Steps/CollectAssetsStep.h"
|
||||
#include "Steps/CookAssetsStep.h"
|
||||
#include "Steps/PostProcessStep.h"
|
||||
#include "Engine/Platform/ConditionVariable.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MDomain.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MCore.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MAssembly.h"
|
||||
#include "Engine/Content/JsonAsset.h"
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
#if PLATFORM_TOOLS_WINDOWS
|
||||
#include "Platform/Windows/WindowsPlatformTools.h"
|
||||
#include "Engine/Platform/Windows/WindowsPlatformSettings.h"
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_UWP || PLATFORM_TOOLS_XBOX_ONE
|
||||
#include "Platform/UWP/UWPPlatformTools.h"
|
||||
#include "Engine/Platform/UWP/UWPPlatformSettings.h"
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_LINUX
|
||||
#include "Platform/Linux/LinuxPlatformTools.h"
|
||||
#include "Engine/Platform/Linux/LinuxPlatformSettings.h"
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_PS4
|
||||
#include "Platforms/PS4/Editor/PlatformTools/PS4PlatformTools.h"
|
||||
#include "Platforms/PS4/Engine/Platform/PS4PlatformSettings.h"
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_XBOX_SCARLETT
|
||||
#include "Platforms/XboxScarlett/Editor/PlatformTools/XboxScarlettPlatformTools.h"
|
||||
#include "Platforms/XboxScarlett/Engine/Platform/XboxScarlettPlatformSettings.h"
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_ANDROID
|
||||
#include "Platform/Android/AndroidPlatformTools.h"
|
||||
#include "Engine/Platform/Android/AndroidPlatformSettings.h"
|
||||
#endif
|
||||
|
||||
void LoadPlatformSettingsEditor(ISerializable::DeserializeStream& data)
|
||||
{
|
||||
#define LOAD_SETTINGS(nodeName, settingsType) \
|
||||
{ \
|
||||
Guid id = JsonTools::GetGuid(data, nodeName); \
|
||||
if (id.IsValid()) \
|
||||
{ \
|
||||
AssetReference<JsonAsset> subAsset = Content::LoadAsync<JsonAsset>(id); \
|
||||
if (subAsset) \
|
||||
{ \
|
||||
if (!subAsset->WaitForLoaded()) \
|
||||
{ \
|
||||
settingsType::Instance()->Deserialize(*subAsset->Data, nullptr); \
|
||||
settingsType::Instance()->Apply(); \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
{ LOG(Warning, "Cannot load " nodeName " settings"); } \
|
||||
} \
|
||||
}
|
||||
#if PLATFORM_TOOLS_WINDOWS
|
||||
LOAD_SETTINGS("WindowsPlatform", WindowsPlatformSettings);
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_UWP || PLATFORM_TOOLS_XBOX_ONE
|
||||
LOAD_SETTINGS("UWPPlatform", UWPPlatformSettings);
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_LINUX
|
||||
LOAD_SETTINGS("LinuxPlatform", LinuxPlatformSettings);
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_PS4
|
||||
LOAD_SETTINGS("PS4Platform", PS4PlatformSettings);
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_XBOX_SCARLETT
|
||||
LOAD_SETTINGS("XboxScarlettPlatform", XboxScarlettPlatformSettings);
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_ANDROID
|
||||
LOAD_SETTINGS("AndroidPlatform", AndroidPlatformSettings);
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace GameCookerImpl
|
||||
{
|
||||
MMethod* Internal_OnEvent = nullptr;
|
||||
MMethod* Internal_OnProgress = nullptr;
|
||||
MMethod* Internal_CanDeployPlugin = nullptr;
|
||||
|
||||
bool IsRunning = false;
|
||||
bool IsThreadRunning = false;
|
||||
int64 CancelFlag = 0;
|
||||
int64 CancelThreadFlag = 0;
|
||||
ConditionVariable ThreadCond;
|
||||
|
||||
CriticalSection ProgressLocker;
|
||||
String ProgressMsg;
|
||||
float ProgressValue;
|
||||
|
||||
CookingData Data;
|
||||
Array<GameCooker::BuildStep*> Steps;
|
||||
Dictionary<BuildPlatform, PlatformTools*> Tools;
|
||||
|
||||
BuildPlatform PluginDeployPlatform;
|
||||
MAssembly* PluginDeployAssembly;
|
||||
bool PluginDeployResult;
|
||||
|
||||
void CallEvent(GameCooker::EventType type);
|
||||
void ReportProgress(const String& info, float totalProgress);
|
||||
bool Build();
|
||||
int32 ThreadFunction();
|
||||
|
||||
void OnEditorAssemblyUnloading(MAssembly* assembly)
|
||||
{
|
||||
Internal_OnEvent = nullptr;
|
||||
Internal_OnProgress = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
using namespace GameCookerImpl;
|
||||
|
||||
Delegate<GameCooker::EventType> GameCooker::OnEvent;
|
||||
Delegate<const String&, float> GameCooker::OnProgress;
|
||||
|
||||
bool CookingData::AssetTypeStatistics::operator<(const AssetTypeStatistics& other) const
|
||||
{
|
||||
if (ContentSize != other.ContentSize)
|
||||
return ContentSize > other.ContentSize;
|
||||
return Count > other.Count;
|
||||
}
|
||||
|
||||
CookingData::Statistics::Statistics()
|
||||
{
|
||||
TotalAssets = 0;
|
||||
CookedAssets = 0;
|
||||
ContentSizeMB = 0;
|
||||
}
|
||||
|
||||
String CookingData::GetGameBinariesPath() const
|
||||
{
|
||||
const Char* archDir;
|
||||
switch (Tools->GetArchitecture())
|
||||
{
|
||||
case ArchitectureType::AnyCPU:
|
||||
archDir = TEXT("AnyCPU");
|
||||
break;
|
||||
case ArchitectureType::x86:
|
||||
archDir = TEXT("x86");
|
||||
break;
|
||||
case ArchitectureType::x64:
|
||||
archDir = TEXT("x64");
|
||||
break;
|
||||
case ArchitectureType::ARM:
|
||||
archDir = TEXT("ARM");
|
||||
break;
|
||||
case ArchitectureType::ARM64:
|
||||
archDir = TEXT("ARM64");
|
||||
break;
|
||||
default:
|
||||
CRASH;
|
||||
return String::Empty;
|
||||
}
|
||||
|
||||
return GetPlatformBinariesRoot() / TEXT("Game") / archDir / ToString(Configuration);
|
||||
}
|
||||
|
||||
String CookingData::GetPlatformBinariesRoot() const
|
||||
{
|
||||
return Globals::StartupFolder / TEXT("Source/Platforms") / Tools->GetName() / TEXT("Binaries");
|
||||
}
|
||||
|
||||
void CookingData::StepProgress(const String& info, const float stepProgress) const
|
||||
{
|
||||
const float singleStepProgress = 1.0f / (StepsCount + 1);
|
||||
const float totalProgress = (CurrentStepIndex + stepProgress) * singleStepProgress;
|
||||
ReportProgress(info, totalProgress);
|
||||
}
|
||||
|
||||
void CookingData::AddRootAsset(const Guid& id)
|
||||
{
|
||||
RootAssets.Add(id);
|
||||
}
|
||||
|
||||
void CookingData::AddRootAsset(const String& path)
|
||||
{
|
||||
AssetInfo info;
|
||||
if (Content::GetAssetInfo(path, info))
|
||||
{
|
||||
RootAssets.Add(info.ID);
|
||||
}
|
||||
}
|
||||
|
||||
void CookingData::AddRootEngineAsset(const String& internalPath)
|
||||
{
|
||||
const String path = Globals::EngineContentFolder / internalPath + ASSET_FILES_EXTENSION_WITH_DOT;
|
||||
AssetInfo info;
|
||||
if (Content::GetAssetInfo(path, info))
|
||||
{
|
||||
RootAssets.Add(info.ID);
|
||||
}
|
||||
}
|
||||
|
||||
void CookingData::Error(const String& msg)
|
||||
{
|
||||
LOG_STR(Error, msg);
|
||||
}
|
||||
|
||||
class GameCookerService : public EngineService
|
||||
{
|
||||
public:
|
||||
|
||||
GameCookerService()
|
||||
: EngineService(TEXT("Game Cooker"))
|
||||
{
|
||||
}
|
||||
|
||||
bool Init() override;
|
||||
void Update() override;
|
||||
void Dispose() override;
|
||||
};
|
||||
|
||||
GameCookerService GameCookerServiceInstance;
|
||||
|
||||
const CookingData& GameCooker::GetCurrentData()
|
||||
{
|
||||
return Data;
|
||||
}
|
||||
|
||||
bool GameCooker::IsRunning()
|
||||
{
|
||||
return GameCookerImpl::IsRunning;
|
||||
}
|
||||
|
||||
bool GameCooker::IsCancelRequested()
|
||||
{
|
||||
return Platform::AtomicRead(&CancelFlag) != 0;
|
||||
}
|
||||
|
||||
PlatformTools* GameCooker::GetTools(BuildPlatform platform)
|
||||
{
|
||||
PlatformTools* result = nullptr;
|
||||
if (!Tools.TryGet(platform, result))
|
||||
{
|
||||
switch (platform)
|
||||
{
|
||||
#if PLATFORM_TOOLS_WINDOWS
|
||||
case BuildPlatform::Windows32:
|
||||
result = New<WindowsPlatformTools>(ArchitectureType::x86);
|
||||
break;
|
||||
case BuildPlatform::Windows64:
|
||||
result = New<WindowsPlatformTools>(ArchitectureType::x64);
|
||||
break;
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_UWP
|
||||
case BuildPlatform::UWPx86:
|
||||
result = New<WSAPlatformTools>(ArchitectureType::x86);
|
||||
break;
|
||||
case BuildPlatform::UWPx64:
|
||||
result = New<WSAPlatformTools>(ArchitectureType::x64);
|
||||
break;
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_XBOX_ONE
|
||||
case BuildPlatform::XboxOne:
|
||||
result = New<XboxOnePlatformTools>();
|
||||
break;
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_LINUX
|
||||
case BuildPlatform::LinuxX64:
|
||||
result = New<LinuxPlatformTools>();
|
||||
break;
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_PS4
|
||||
case BuildPlatform::PS4:
|
||||
result = New<PS4PlatformTools>();
|
||||
break;
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_XBOX_SCARLETT
|
||||
case BuildPlatform::XboxScarlett:
|
||||
result = New<XboxScarlettPlatformTools>();
|
||||
break;
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_ANDROID
|
||||
case BuildPlatform::AndroidARM64:
|
||||
result = New<AndroidPlatformTools>(ArchitectureType::ARM64);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
Tools.Add(platform, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void GameCooker::Build(BuildPlatform platform, BuildConfiguration configuration, const StringView& outputPath, BuildOptions options)
|
||||
{
|
||||
if (IsRunning())
|
||||
{
|
||||
LOG(Warning, "Cannot start a build. Already running.");
|
||||
return;
|
||||
}
|
||||
PlatformTools* tools = GetTools(platform);
|
||||
if (tools == nullptr)
|
||||
{
|
||||
LOG(Error, "Build platform {0} is not suppoerd.", ::ToString(platform));
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup
|
||||
CancelFlag = 0;
|
||||
ProgressMsg.Clear();
|
||||
ProgressValue = 1.0f;
|
||||
CookingData& data = Data;
|
||||
data.Init();
|
||||
data.Tools = tools;
|
||||
data.Platform = platform;
|
||||
data.Configuration = configuration;
|
||||
data.Options = options;
|
||||
data.OutputPath = outputPath;
|
||||
FileSystem::NormalizePath(data.OutputPath);
|
||||
data.OutputPath = data.OriginalOutputPath = FileSystem::ConvertRelativePathToAbsolute(Globals::ProjectFolder, data.OutputPath);
|
||||
data.CacheDirectory = Globals::ProjectCacheFolder / TEXT("Cooker") / tools->GetName();
|
||||
if (!FileSystem::DirectoryExists(data.CacheDirectory))
|
||||
{
|
||||
if (FileSystem::CreateDirectory(data.CacheDirectory))
|
||||
{
|
||||
LOG(Error, "Cannot setup game building cache directory.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Start
|
||||
GameCookerImpl::IsRunning = true;
|
||||
|
||||
// Start thread if need to
|
||||
if (!IsThreadRunning)
|
||||
{
|
||||
Function<int32()> f;
|
||||
f.Bind(ThreadFunction);
|
||||
const auto thread = ThreadSpawner::Start(f, GameCookerServiceInstance.Name, ThreadPriority::Highest);
|
||||
if (thread == nullptr)
|
||||
{
|
||||
GameCookerImpl::IsRunning = false;
|
||||
LOG(Error, "Failed to start a build thread.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ThreadCond.NotifyOne();
|
||||
}
|
||||
}
|
||||
|
||||
void GameCooker::Cancel(bool waitForEnd)
|
||||
{
|
||||
if (!IsRunning())
|
||||
return;
|
||||
|
||||
// Set flag
|
||||
Platform::InterlockedIncrement(&CancelFlag);
|
||||
|
||||
if (waitForEnd)
|
||||
{
|
||||
LOG(Warning, "Waiting for the Game Cooker end...");
|
||||
|
||||
// Wait for the end
|
||||
while (GameCookerImpl::IsRunning)
|
||||
{
|
||||
Platform::Sleep(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameCookerImpl::CallEvent(GameCooker::EventType type)
|
||||
{
|
||||
if (Internal_OnEvent == nullptr)
|
||||
{
|
||||
auto c = GameCooker::GetStaticClass();
|
||||
if (c)
|
||||
Internal_OnEvent = c->GetMethod("Internal_OnEvent", 1);
|
||||
ASSERT(GameCookerImpl::Internal_OnEvent);
|
||||
}
|
||||
|
||||
MainThreadManagedInvokeAction::ParamsBuilder params;
|
||||
params.AddParam((int32)type);
|
||||
MainThreadManagedInvokeAction::Invoke(Internal_OnEvent, params);
|
||||
|
||||
GameCooker::OnEvent(type);
|
||||
}
|
||||
|
||||
void GameCookerImpl::ReportProgress(const String& info, float totalProgress)
|
||||
{
|
||||
ScopeLock lock(ProgressLocker);
|
||||
|
||||
ProgressMsg = info;
|
||||
ProgressValue = totalProgress;
|
||||
}
|
||||
|
||||
bool GameCookerImpl::Build()
|
||||
{
|
||||
CookingData& data = Data;
|
||||
LOG(Info, "Starting Game Cooker...");
|
||||
LOG(Info, "Platform: {0}, Configuration: {2}, Options: {1}", ::ToString(data.Platform), (int32)data.Options, ::ToString(data.Configuration));
|
||||
LOG(Info, "Output Path: {0}", data.OutputPath);
|
||||
|
||||
// Late init feature
|
||||
if (Steps.IsEmpty())
|
||||
{
|
||||
// Create steps
|
||||
Steps.Add(New<ValidateStep>());
|
||||
Steps.Add(New<CompileScriptsStep>());
|
||||
Steps.Add(New<DeployDataStep>());
|
||||
Steps.Add(New<PrecompileAssembliesStep>());
|
||||
Steps.Add(New<CollectAssetsStep>());
|
||||
Steps.Add(New<CookAssetsStep>());
|
||||
Steps.Add(New<PostProcessStep>());
|
||||
}
|
||||
|
||||
MCore::Instance()->AttachThread();
|
||||
|
||||
// Build Started
|
||||
CallEvent(GameCooker::EventType::BuildStarted);
|
||||
for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
|
||||
Steps[stepIndex]->OnBuildStarted(data);
|
||||
Data.Tools->OnBuildStarted(data);
|
||||
data.InitProgress(Steps.Count());
|
||||
|
||||
// Execute all steps in a sequence
|
||||
bool failed = false;
|
||||
for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
|
||||
{
|
||||
if (GameCooker::IsCancelRequested())
|
||||
break;
|
||||
auto step = Steps[stepIndex];
|
||||
data.NextStep();
|
||||
|
||||
// Execute step
|
||||
failed = step->Perform(data);
|
||||
if (failed)
|
||||
break;
|
||||
}
|
||||
|
||||
// Process result
|
||||
if (GameCooker::IsCancelRequested())
|
||||
{
|
||||
LOG(Warning, "Game building cancelled!");
|
||||
failed = true;
|
||||
}
|
||||
else if (failed)
|
||||
{
|
||||
LOG(Error, "Game building failed!");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Info, "Game building done!");
|
||||
|
||||
if (data.Options & BuildOptions::ShowOutput)
|
||||
{
|
||||
FileSystem::ShowFileExplorer(data.OriginalOutputPath);
|
||||
}
|
||||
}
|
||||
IsRunning = false;
|
||||
CancelFlag = 0;
|
||||
Data.Tools->OnBuildEnded(data, failed);
|
||||
for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
|
||||
Steps[stepIndex]->OnBuildEnded(data, failed);
|
||||
CallEvent(failed ? GameCooker::EventType::BuildFailed : GameCooker::EventType::BuildDone);
|
||||
|
||||
return failed;
|
||||
}
|
||||
|
||||
int32 GameCookerImpl::ThreadFunction()
|
||||
{
|
||||
IsThreadRunning = true;
|
||||
|
||||
CriticalSection mutex;
|
||||
while (Platform::AtomicRead(&CancelThreadFlag) == 0)
|
||||
{
|
||||
if (IsRunning)
|
||||
{
|
||||
Build();
|
||||
}
|
||||
|
||||
ThreadCond.Wait(mutex);
|
||||
}
|
||||
|
||||
IsThreadRunning = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool GameCookerService::Init()
|
||||
{
|
||||
auto editorAssembly = GetBinaryModuleFlaxEngine()->Assembly;
|
||||
editorAssembly->Unloading.Bind(OnEditorAssemblyUnloading);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GameCookerService::Update()
|
||||
{
|
||||
if (IsRunning)
|
||||
{
|
||||
ScopeLock lock(ProgressLocker);
|
||||
|
||||
if (ProgressMsg.HasChars())
|
||||
{
|
||||
if (Internal_OnProgress == nullptr)
|
||||
{
|
||||
auto c = GameCooker::GetStaticClass();
|
||||
if (c)
|
||||
Internal_OnProgress = c->GetMethod("Internal_OnProgress", 2);
|
||||
ASSERT(GameCookerImpl::Internal_OnProgress);
|
||||
}
|
||||
|
||||
MainThreadManagedInvokeAction::ParamsBuilder params;
|
||||
params.AddParam(ProgressMsg, Scripting::GetScriptsDomain()->GetNative());
|
||||
params.AddParam(ProgressValue);
|
||||
MainThreadManagedInvokeAction::Invoke(Internal_OnProgress, params);
|
||||
GameCooker::OnProgress(ProgressMsg, ProgressValue);
|
||||
|
||||
ProgressMsg.Clear();
|
||||
ProgressValue = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameCookerService::Dispose()
|
||||
{
|
||||
// Always stop on exit
|
||||
GameCooker::Cancel(true);
|
||||
|
||||
// End thread
|
||||
if (IsThreadRunning)
|
||||
{
|
||||
LOG(Warning, "Waiting for the Game Cooker thread end...");
|
||||
|
||||
Platform::AtomicStore(&CancelThreadFlag, 1);
|
||||
ThreadCond.NotifyOne();
|
||||
while (IsThreadRunning)
|
||||
{
|
||||
Platform::Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
Steps.ClearDelete();
|
||||
Tools.ClearDelete();
|
||||
}
|
||||
113
Source/Editor/Cooker/GameCooker.cs
Normal file
113
Source/Editor/Cooker/GameCooker.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor
|
||||
{
|
||||
partial class GameCooker
|
||||
{
|
||||
/// <summary>
|
||||
/// Build options data.
|
||||
/// </summary>
|
||||
public struct Options
|
||||
{
|
||||
/// <summary>
|
||||
/// The platform.
|
||||
/// </summary>
|
||||
public BuildPlatform Platform;
|
||||
|
||||
/// <summary>
|
||||
/// The build configuration.
|
||||
/// </summary>
|
||||
public BuildConfiguration Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// The options.
|
||||
/// </summary>
|
||||
public BuildOptions Flags;
|
||||
|
||||
/// <summary>
|
||||
/// The output path (normalized, absolute).
|
||||
/// </summary>
|
||||
public string OutputPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Building event type.
|
||||
/// </summary>
|
||||
public enum EventType
|
||||
{
|
||||
/// <summary>
|
||||
/// The build started.
|
||||
/// </summary>
|
||||
BuildStarted = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The build failed.
|
||||
/// </summary>
|
||||
BuildFailed = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The build done.
|
||||
/// </summary>
|
||||
BuildDone = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Game building event delegate.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
public delegate void BuildEventDelegate(EventType type);
|
||||
|
||||
/// <summary>
|
||||
/// Game building progress reporting delegate type.
|
||||
/// </summary>
|
||||
/// <param name="info">The information text.</param>
|
||||
/// <param name="totalProgress">The total progress percentage (normalized to 0-1).</param>
|
||||
public delegate void BuildProgressDelegate(string info, float totalProgress);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when building event rises.
|
||||
/// </summary>
|
||||
public static event BuildEventDelegate Event;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when building game progress fires.
|
||||
/// </summary>
|
||||
public static event BuildProgressDelegate Progress;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the platform from the game build platform type.
|
||||
/// </summary>
|
||||
/// <param name="buildPlatform">The build platform.</param>
|
||||
/// <returns>The run-type platform type.</returns>
|
||||
public static PlatformType GetPlatformType(BuildPlatform buildPlatform)
|
||||
{
|
||||
switch (buildPlatform)
|
||||
{
|
||||
case BuildPlatform.Windows32:
|
||||
case BuildPlatform.Windows64: return PlatformType.Windows;
|
||||
case BuildPlatform.UWPx86:
|
||||
case BuildPlatform.UWPx64: return PlatformType.UWP;
|
||||
case BuildPlatform.XboxOne: return PlatformType.XboxOne;
|
||||
case BuildPlatform.LinuxX64: return PlatformType.Linux;
|
||||
case BuildPlatform.PS4: return PlatformType.PS4;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(buildPlatform), buildPlatform, null);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Internal_OnEvent(EventType type)
|
||||
{
|
||||
Event?.Invoke(type);
|
||||
}
|
||||
|
||||
internal static void Internal_OnProgress(string info, float totalProgress)
|
||||
{
|
||||
Progress?.Invoke(info, totalProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user