Merge branch 'master' into 1.5
This commit is contained in:
@@ -20,10 +20,11 @@ bool AudioClip::StreamingTask::Run()
|
||||
{
|
||||
AssetReference<AudioClip> ref = _asset.Get();
|
||||
if (ref == nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
ScopeLock lock(ref->Locker);
|
||||
const auto& queue = ref->StreamingQueue;
|
||||
if (queue.Count() == 0)
|
||||
return false;
|
||||
auto clip = ref.Get();
|
||||
|
||||
// Update the buffers
|
||||
@@ -108,7 +109,6 @@ bool AudioClip::StreamingTask::Run()
|
||||
for (int32 sourceIndex = 0; sourceIndex < Audio::Sources.Count(); sourceIndex++)
|
||||
{
|
||||
// TODO: collect refs to audio clip from sources and use faster iteration (but do it thread-safe)
|
||||
|
||||
const auto src = Audio::Sources[sourceIndex];
|
||||
if (src->Clip == clip && src->GetState() == AudioSource::States::Playing)
|
||||
{
|
||||
|
||||
@@ -130,6 +130,10 @@ void AudioSource::Play()
|
||||
{
|
||||
// Request faster streaming update
|
||||
Clip->RequestStreamingUpdate();
|
||||
|
||||
// If we are looping and streaming also update streaming buffers
|
||||
if (_loop)
|
||||
RequestStreamingBuffersUpdate();
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -368,6 +372,7 @@ void AudioSource::Update()
|
||||
if (!UseStreaming() || SourceIDs.IsEmpty())
|
||||
return;
|
||||
auto clip = Clip.Get();
|
||||
clip->Locker.Lock();
|
||||
|
||||
// Handle streaming buffers queue submit (ensure that clip has loaded the first chunk with audio data)
|
||||
if (_needToUpdateStreamingBuffers && clip->Buffers[_streamingFirstChunk] != AUDIO_BUFFER_ID_INVALID)
|
||||
@@ -437,6 +442,8 @@ void AudioSource::Update()
|
||||
clip->RequestStreamingUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
clip->Locker.Unlock();
|
||||
}
|
||||
|
||||
void AudioSource::OnEnable()
|
||||
|
||||
@@ -139,10 +139,12 @@ namespace ALC
|
||||
|
||||
alSourcef(sourceID, AL_GAIN, source->GetVolume());
|
||||
alSourcef(sourceID, AL_PITCH, source->GetPitch());
|
||||
alSourcef(sourceID, AL_SEC_OFFSET, 0.0f);
|
||||
alSourcef(sourceID, AL_REFERENCE_DISTANCE, source->GetMinDistance());
|
||||
alSourcef(sourceID, AL_ROLLOFF_FACTOR, source->GetAttenuation());
|
||||
alSourcei(sourceID, AL_LOOPING, loop);
|
||||
alSourcei(sourceID, AL_SOURCE_RELATIVE, !is3D);
|
||||
alSourcei(sourceID, AL_BUFFER, 0);
|
||||
alSource3f(sourceID, AL_POSITION, FLAX_POS_TO_OAL(position));
|
||||
alSource3f(sourceID, AL_VELOCITY, FLAX_POS_TO_OAL(velocity));
|
||||
}
|
||||
|
||||
@@ -281,7 +281,6 @@ bool AssetsCache::FindAsset(const StringView& path, AssetInfo& info)
|
||||
auto& e = i->Value;
|
||||
if (e.Info.Path == path)
|
||||
{
|
||||
// Validate file exists
|
||||
if (!IsEntryValid(e))
|
||||
{
|
||||
LOG(Warning, "Missing file from registry: \'{0}\':{1}:{2}", e.Info.Path, e.Info.ID, e.Info.TypeName);
|
||||
@@ -304,15 +303,11 @@ bool AssetsCache::FindAsset(const StringView& path, AssetInfo& info)
|
||||
bool AssetsCache::FindAsset(const Guid& id, AssetInfo& info)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
bool result = false;
|
||||
|
||||
ScopeLock lock(_locker);
|
||||
|
||||
auto e = _registry.TryGet(id);
|
||||
if (e != nullptr)
|
||||
{
|
||||
// Validate entry
|
||||
if (!IsEntryValid(*e))
|
||||
{
|
||||
LOG(Warning, "Missing file from registry: \'{0}\':{1}:{2}", e->Info.Path, e->Info.ID, e->Info.TypeName);
|
||||
@@ -325,7 +320,6 @@ bool AssetsCache::FindAsset(const Guid& id, AssetInfo& info)
|
||||
info = e->Info;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -379,8 +373,26 @@ void AssetsCache::RegisterAssets(FlaxStorage* storage)
|
||||
// Check if storage contains ID which has been already registered
|
||||
if (FindAsset(e.ID, info))
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
// On Windows - if you start your project using a shortcut/VS commandline -project, and using a upper/lower drive letter, it could the cache (case doesn't matter on OS)
|
||||
if (StringUtils::CompareIgnoreCase(storagePath.GetText(), info.Path.GetText()) != 0)
|
||||
{
|
||||
LOG(Warning, "Founded duplicated asset \'{0}\'. Locations: \'{1}\' and \'{2}\'", e.ID, storagePath, info.Path);
|
||||
duplicatedEntries.Add(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove from registry so we can add it again later with the original ID, so we don't loose relations
|
||||
for (auto j = _registry.Begin(); j.IsNotEnd(); ++j)
|
||||
{
|
||||
if (StringUtils::CompareIgnoreCase(j->Value.Info.Path.GetText(), storagePath.GetText()) == 0)
|
||||
_registry.Remove(j);
|
||||
}
|
||||
}
|
||||
#else
|
||||
LOG(Warning, "Founded duplicated asset \'{0}\'. Locations: \'{1}\' and \'{2}\'", e.ID, storagePath, info.Path);
|
||||
duplicatedEntries.Add(i);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,7 +563,6 @@ bool AssetsCache::RenameAsset(const StringView& oldPath, const StringView& newPa
|
||||
bool AssetsCache::IsEntryValid(Entry& e)
|
||||
{
|
||||
#if ENABLE_ASSETS_DISCOVERY
|
||||
|
||||
// Check if file exists
|
||||
if (FileSystem::FileExists(e.Info.Path))
|
||||
{
|
||||
@@ -601,10 +612,8 @@ bool AssetsCache::IsEntryValid(Entry& e)
|
||||
return false;
|
||||
|
||||
#else
|
||||
|
||||
// In game we don't care about it because all cached asset entries are valid (precached)
|
||||
// Skip only entries with missing file
|
||||
return e.Info.Path.HasChars();
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ bool String::IsANSI() const
|
||||
bool result = true;
|
||||
for (int32 i = 0; i < _length; i++)
|
||||
{
|
||||
if (_data[i] > 255)
|
||||
if (_data[i] > 127)
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
|
||||
Nullable<bool> Fullscreen;
|
||||
Nullable<Float2> Size;
|
||||
static CursorLockMode CursorLock = CursorLockMode::None;
|
||||
bool CursorVisible = true;
|
||||
CursorLockMode CursorLock = CursorLockMode::None;
|
||||
|
||||
class ScreenService : public EngineService
|
||||
{
|
||||
@@ -25,6 +26,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void Update() override;
|
||||
void Draw() override;
|
||||
};
|
||||
|
||||
@@ -88,12 +90,7 @@ Float2 Screen::GameViewportToScreen(const Float2& viewportPos)
|
||||
|
||||
bool Screen::GetCursorVisible()
|
||||
{
|
||||
#if USE_EDITOR
|
||||
const auto win = Editor::Managed->GetGameWindow(true);
|
||||
#else
|
||||
const auto win = Engine::MainWindow;
|
||||
#endif
|
||||
return win ? win->GetCursor() != CursorType::Hidden : true;
|
||||
return CursorVisible;
|
||||
}
|
||||
|
||||
void Screen::SetCursorVisible(const bool value)
|
||||
@@ -107,6 +104,7 @@ void Screen::SetCursorVisible(const bool value)
|
||||
{
|
||||
win->SetCursor(value ? CursorType::Default : CursorType::Hidden);
|
||||
}
|
||||
CursorVisible = value;
|
||||
}
|
||||
|
||||
CursorLockMode Screen::GetCursorLock()
|
||||
@@ -137,6 +135,14 @@ void Screen::SetCursorLock(CursorLockMode mode)
|
||||
CursorLock = mode;
|
||||
}
|
||||
|
||||
void ScreenService::Update()
|
||||
{
|
||||
#if USE_EDITOR
|
||||
// Sync current cursor state in Editor (eg. when viewport focus can change)
|
||||
Screen::SetCursorVisible(CursorVisible);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ScreenService::Draw()
|
||||
{
|
||||
#if USE_EDITOR
|
||||
|
||||
@@ -697,6 +697,7 @@ ManagedBinaryModule::ManagedBinaryModule(MAssembly* assembly)
|
||||
// Bind for C# assembly events
|
||||
assembly->Loading.Bind<ManagedBinaryModule, &ManagedBinaryModule::OnLoading>(this);
|
||||
assembly->Loaded.Bind<ManagedBinaryModule, &ManagedBinaryModule::OnLoaded>(this);
|
||||
assembly->Unloading.Bind<ManagedBinaryModule, &ManagedBinaryModule::OnUnloading>(this);
|
||||
assembly->Unloaded.Bind<ManagedBinaryModule, &ManagedBinaryModule::OnUnloaded>(this);
|
||||
|
||||
if (Assembly->IsLoaded())
|
||||
@@ -1117,17 +1118,24 @@ void ManagedBinaryModule::InitType(MClass* mclass)
|
||||
#endif
|
||||
}
|
||||
|
||||
void ManagedBinaryModule::OnUnloaded(MAssembly* assembly)
|
||||
void ManagedBinaryModule::OnUnloading(MAssembly* assembly)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
// Clear managed-only types
|
||||
// Clear managed types typenames
|
||||
for (int32 i = _firstManagedTypeIndex; i < Types.Count(); i++)
|
||||
{
|
||||
const ScriptingType& type = Types[i];
|
||||
const MString typeName(type.Fullname.Get(), type.Fullname.Length());
|
||||
TypeNameToTypeIndex.Remove(typeName);
|
||||
}
|
||||
}
|
||||
|
||||
void ManagedBinaryModule::OnUnloaded(MAssembly* assembly)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
// Clear managed-only types
|
||||
Types.Resize(_firstManagedTypeIndex);
|
||||
for (int32 i = 0; i < _managedMemoryBlocks.Count(); i++)
|
||||
Allocator::Free(_managedMemoryBlocks[i]);
|
||||
|
||||
@@ -309,6 +309,7 @@ private:
|
||||
void OnLoading(MAssembly* assembly);
|
||||
void OnLoaded(MAssembly* assembly);
|
||||
void InitType(MClass* mclass);
|
||||
void OnUnloading(MAssembly* assembly);
|
||||
void OnUnloaded(MAssembly* assembly);
|
||||
|
||||
public:
|
||||
|
||||
@@ -868,6 +868,20 @@ ScriptingObject* Scripting::TryFindObject(Guid id, MClass* type)
|
||||
return result;
|
||||
}
|
||||
|
||||
ScriptingObject* Scripting::TryFindObject(MClass* mclass)
|
||||
{
|
||||
if (mclass == nullptr)
|
||||
return nullptr;
|
||||
ScopeLock lock(_objectsLocker);
|
||||
for (auto i = _objectsDictionary.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
const auto obj = i->Value;
|
||||
if (obj->GetClass() == mclass)
|
||||
return obj;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ScriptingObject* Scripting::FindObject(const MObject* managedInstance)
|
||||
{
|
||||
if (managedInstance == nullptr)
|
||||
|
||||
@@ -85,7 +85,7 @@ public:
|
||||
/// <param name="fullname">The full name of the type eg: System.Int64.</param>
|
||||
/// <returns>The MClass object or null if missing.</returns>
|
||||
static MClass* FindClass(const StringAnsiView& fullname);
|
||||
|
||||
|
||||
#if USE_MONO
|
||||
/// <summary>
|
||||
/// Finds the class from the given Mono class object within whole assembly.
|
||||
@@ -144,6 +144,12 @@ public:
|
||||
/// <returns>The found object or null if missing.</returns>
|
||||
static ScriptingObject* FindObject(Guid id, MClass* type = nullptr);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the object by the given class.
|
||||
/// </summary>
|
||||
/// <returns>The found object or null if missing.</returns>
|
||||
static ScriptingObject* TryFindObject(MClass* type);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the object by the given identifier.
|
||||
/// </summary>
|
||||
|
||||
24
Source/Engine/Tests/TestModelTool.cpp
Normal file
24
Source/Engine/Tests/TestModelTool.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "Engine/Tools/ModelTool/ModelTool.h"
|
||||
#include <ThirdParty/catch2/catch.hpp>
|
||||
|
||||
TEST_CASE("ModelTool")
|
||||
{
|
||||
SECTION("Test DetectLodIndex")
|
||||
{
|
||||
CHECK(ModelTool::DetectLodIndex(TEXT("mesh")) == 0);
|
||||
CHECK(ModelTool::DetectLodIndex(TEXT("mesh LOD")) == 0);
|
||||
CHECK(ModelTool::DetectLodIndex(TEXT("mesh LOD0")) == 0);
|
||||
CHECK(ModelTool::DetectLodIndex(TEXT("mesh LOD1")) == 1);
|
||||
CHECK(ModelTool::DetectLodIndex(TEXT("mesh_LOD1")) == 1);
|
||||
CHECK(ModelTool::DetectLodIndex(TEXT("mesh_lod1")) == 1);
|
||||
CHECK(ModelTool::DetectLodIndex(TEXT("mesh_lod2")) == 2);
|
||||
CHECK(ModelTool::DetectLodIndex(TEXT("lod0")) == 0);
|
||||
CHECK(ModelTool::DetectLodIndex(TEXT("lod1")) == 1);
|
||||
CHECK(ModelTool::DetectLodIndex(TEXT("lod_2")) == 2);
|
||||
CHECK(ModelTool::DetectLodIndex(TEXT("mesh_lod_0")) == 0);
|
||||
CHECK(ModelTool::DetectLodIndex(TEXT("mesh_lod_1")) == 1);
|
||||
CHECK(ModelTool::DetectLodIndex(TEXT("mesh lod_2")) == 2);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Flax.Build;
|
||||
using Flax.Build.NativeCpp;
|
||||
|
||||
/// <summary>
|
||||
/// Engine tests module.
|
||||
@@ -14,6 +15,14 @@ public class Tests : EngineModule
|
||||
Deploy = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Setup(BuildOptions options)
|
||||
{
|
||||
base.Setup(options);
|
||||
|
||||
options.PrivateDependencies.Add("ModelTool");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetFilesToDeploy(List<string> files)
|
||||
{
|
||||
|
||||
@@ -467,7 +467,7 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value)
|
||||
}
|
||||
|
||||
const auto texture = eatBox(textureBox->GetParent<Node>(), textureBox->FirstConnection());
|
||||
const auto scale = tryGetValue(scaleBox, node->Values[0]).AsFloat();
|
||||
const auto scale = tryGetValue(scaleBox, node->Values[0]).AsFloat3();
|
||||
const auto blend = tryGetValue(blendBox, node->Values[1]).AsFloat();
|
||||
|
||||
auto result = writeLocal(Value::InitForZero(ValueType::Float4), node);
|
||||
|
||||
@@ -641,6 +641,7 @@ bool ModelTool::ImportDataAssimp(const char* path, ImportedModelData& data, Opti
|
||||
aiProcess_JoinIdenticalVertices |
|
||||
aiProcess_LimitBoneWeights |
|
||||
aiProcess_Triangulate |
|
||||
aiProcess_SortByPType |
|
||||
aiProcess_GenUVCoords |
|
||||
aiProcess_FindDegenerates |
|
||||
aiProcess_FindInvalidData |
|
||||
|
||||
@@ -1552,21 +1552,21 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
|
||||
|
||||
int32 ModelTool::DetectLodIndex(const String& nodeName)
|
||||
{
|
||||
const int32 index = nodeName.FindLast(TEXT("LOD"));
|
||||
int32 index = nodeName.FindLast(TEXT("LOD"), StringSearchCase::IgnoreCase);
|
||||
if (index != -1)
|
||||
{
|
||||
// Some models use LOD_0 to identify LOD levels
|
||||
if (nodeName.Length() > index + 4 && nodeName[index + 3] == '_')
|
||||
index++;
|
||||
|
||||
int32 num;
|
||||
if (!StringUtils::Parse(nodeName.Get() + index + 3, &num))
|
||||
{
|
||||
if (num >= 0 && num < MODEL_MAX_LODS)
|
||||
{
|
||||
return num;
|
||||
}
|
||||
|
||||
LOG(Warning, "Invalid mesh level of detail index at node \'{0}\'. Maximum supported amount of LODs is {1}.", nodeName, MODEL_MAX_LODS);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -129,6 +129,16 @@ namespace FlaxEngine.GUI
|
||||
/// </summary>
|
||||
public event Action<TextBoxBase> TextBoxEditEnd;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when a key is down.
|
||||
/// </summary>
|
||||
public event Action<KeyboardKeys> KeyDown;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when a key is up.
|
||||
/// </summary>
|
||||
public event Action<KeyboardKeys> KeyUp;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this is a multiline text box control.
|
||||
/// </summary>
|
||||
@@ -1168,12 +1178,20 @@ namespace FlaxEngine.GUI
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnKeyUp(KeyboardKeys key)
|
||||
{
|
||||
base.OnKeyUp(key);
|
||||
KeyUp?.Invoke(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
var window = Root;
|
||||
bool shiftDown = window.GetKey(KeyboardKeys.Shift);
|
||||
bool ctrDown = window.GetKey(KeyboardKeys.Control);
|
||||
KeyDown?.Invoke(key);
|
||||
|
||||
switch (key)
|
||||
{
|
||||
|
||||
@@ -948,10 +948,6 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
// Set flag
|
||||
_isDragOver = true;
|
||||
|
||||
// Hide tooltip
|
||||
Tooltip?.Hide();
|
||||
|
||||
return DragDropEffect.None;
|
||||
}
|
||||
|
||||
@@ -998,6 +994,8 @@ namespace FlaxEngine.GUI
|
||||
[NoAnimate]
|
||||
public virtual void DoDragDrop(DragData data)
|
||||
{
|
||||
// Hide tooltip
|
||||
Tooltip?.Hide();
|
||||
Root.DoDragDrop(data);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user