diff --git a/Content/Shaders/GlobalSignDistanceField.flax b/Content/Shaders/GlobalSignDistanceField.flax index 18f653f86..0e94c5c06 100644 --- a/Content/Shaders/GlobalSignDistanceField.flax +++ b/Content/Shaders/GlobalSignDistanceField.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c8aa181a814d69b15ffec6493a71a6f42ae816ce04f7803cff2d5073b4b3c4f -size 11790 +oid sha256:e075583620e62407503c73f52487c204ddcad421e80fa621aebd55af1cfb08d5 +size 11798 diff --git a/GenerateProjectFiles.bat b/GenerateProjectFiles.bat index 622939c34..28970a203 100644 --- a/GenerateProjectFiles.bat +++ b/GenerateProjectFiles.bat @@ -15,7 +15,7 @@ if errorlevel 1 goto BuildToolFailed :: Build bindings for all editor configurations echo Building C# bindings... -Binaries\Tools\Flax.Build.exe -build -BuildBindingsOnly -arch=x64 -platform=Windows --buildTargets=FlaxEditor,FlaxGame +Binaries\Tools\Flax.Build.exe -build -BuildBindingsOnly -arch=x64 -platform=Windows --buildTargets=FlaxEditor popd echo Done! diff --git a/GenerateProjectFiles.command b/GenerateProjectFiles.command index 5ee5c0783..a42121252 100755 --- a/GenerateProjectFiles.command +++ b/GenerateProjectFiles.command @@ -14,4 +14,4 @@ bash ./Development/Scripts/Mac/CallBuildTool.sh --genproject "$@" # Build bindings for all editor configurations echo Building C# bindings... # TODO: Detect the correct architecture here -Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=ARM64 -platform=Mac --buildTargets=FlaxEditor,FlaxGame +Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=ARM64 -platform=Mac --buildTargets=FlaxEditor diff --git a/GenerateProjectFiles.sh b/GenerateProjectFiles.sh index dceb8abe8..76d96c7ef 100755 --- a/GenerateProjectFiles.sh +++ b/GenerateProjectFiles.sh @@ -14,4 +14,4 @@ bash ./Development/Scripts/Linux/CallBuildTool.sh --genproject "$@" # Build bindings for all editor configurations echo Building C# bindings... # TODO: Detect the correct architecture here -Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=x64 -platform=Linux --buildTargets=FlaxEditor,FlaxGame +Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=x64 -platform=Linux --buildTargets=FlaxEditor diff --git a/Source/Editor/Content/Proxy/SceneProxy.cs b/Source/Editor/Content/Proxy/SceneProxy.cs index 78d88b440..004c2aed7 100644 --- a/Source/Editor/Content/Proxy/SceneProxy.cs +++ b/Source/Editor/Content/Proxy/SceneProxy.cs @@ -30,6 +30,12 @@ namespace FlaxEditor.Content return item is SceneItem; } + /// + public override bool AcceptsAsset(string typeName, string path) + { + return (typeName == Scene.AssetTypename || typeName == Scene.EditorPickerTypename) && path.EndsWith(FileExtension, StringComparison.OrdinalIgnoreCase); + } + /// public override bool CanCreate(ContentFolder targetLocation) { diff --git a/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs b/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs index 160c783ed..1282e4daa 100644 --- a/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs +++ b/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs @@ -406,18 +406,16 @@ namespace FlaxEditor.Content.Thumbnails 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}."); + Editor.LogWarning(ex); + _requests.RemoveAt(i--); } } @@ -515,7 +513,6 @@ namespace FlaxEditor.Content.Thumbnails for (int i = 0; i < checks; i++) { var request = _requests[i]; - try { if (request.IsReady) @@ -529,8 +526,9 @@ namespace FlaxEditor.Content.Thumbnails } catch (Exception ex) { - Editor.LogWarning(ex); Editor.LogWarning($"Failed to prepare thumbnail rendering for {request.Item.ShortName}."); + Editor.LogWarning(ex); + _requests.RemoveAt(i--); } } diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs index b1de78456..80f419a6f 100644 --- a/Source/Editor/Modules/ContentDatabaseModule.cs +++ b/Source/Editor/Modules/ContentDatabaseModule.cs @@ -811,10 +811,9 @@ namespace FlaxEditor.Modules { if (node == null) return; - - // Temporary data var folder = node.Folder; var path = folder.Path; + var canHaveAssets = node.CanHaveAssets; if (_isDuringFastSetup) { @@ -833,20 +832,38 @@ namespace FlaxEditor.Modules var child = folder.Children[i]; if (!child.Exists) { - // Send info + // Item doesn't exist anymore Editor.Log(string.Format($"Content item \'{child.Path}\' has been removed")); - - // Destroy it Delete(child, false); - i--; } + else if (canHaveAssets && child is AssetItem childAsset) + { + // Check if asset type doesn't match the item proxy (eg. item reimported as Material Instance instead of Material) + if (FlaxEngine.Content.GetAssetInfo(child.Path, out var assetInfo)) + { + bool changed = assetInfo.ID != childAsset.ID; + if (!changed && assetInfo.TypeName != childAsset.TypeName) + { + // Use proxy check (eg. scene asset might accept different typename than AssetInfo reports) + var proxy = GetAssetProxy(childAsset.TypeName, child.Path); + if (proxy == null) + proxy = GetAssetProxy(assetInfo.TypeName, child.Path); + changed = !proxy.AcceptsAsset(assetInfo.TypeName, child.Path); + } + if (changed) + { + OnAssetTypeInfoChanged(childAsset, ref assetInfo); + i--; + } + } + } } } // Find files var files = Directory.GetFiles(path, "*.*", SearchOption.TopDirectoryOnly); - if (node.CanHaveAssets) + if (canHaveAssets) { LoadAssets(node, files); } @@ -1157,19 +1174,8 @@ namespace FlaxEditor.Modules // For eg. change texture to sprite atlas on reimport if (binaryAssetItem.TypeName != assetInfo.TypeName) { - // Asset type has been changed! - Editor.LogWarning(string.Format("Asset \'{0}\' changed type from {1} to {2}", item.Path, binaryAssetItem.TypeName, assetInfo.TypeName)); - Editor.Windows.CloseAllEditors(item); - - // Remove this item from the database and some related data var toRefresh = binaryAssetItem.ParentFolder; - binaryAssetItem.Dispose(); - toRefresh.Children.Remove(binaryAssetItem); - if (!binaryAssetItem.HasDefaultThumbnail) - { - // Delete old thumbnail and remove it from the cache - Editor.Instance.Thumbnails.DeletePreview(binaryAssetItem); - } + OnAssetTypeInfoChanged(binaryAssetItem, ref assetInfo); // Refresh the parent folder to find the new asset (it should have different type or some other format) RefreshFolder(toRefresh, false); @@ -1186,6 +1192,23 @@ namespace FlaxEditor.Modules } } + private void OnAssetTypeInfoChanged(AssetItem assetItem, ref AssetInfo assetInfo) + { + // Asset type has been changed! + Editor.LogWarning(string.Format("Asset \'{0}\' changed type from {1} to {2}", assetItem.Path, assetItem.TypeName, assetInfo.TypeName)); + Editor.Windows.CloseAllEditors(assetItem); + + // Remove this item from the database and some related data + assetItem.Dispose(); + assetItem.ParentFolder.Children.Remove(assetItem); + + // Delete old thumbnail and remove it from the cache + if (!assetItem.HasDefaultThumbnail) + { + Editor.Instance.Thumbnails.DeletePreview(assetItem); + } + } + internal void OnDirectoryEvent(MainContentTreeNode node, FileSystemEventArgs e) { // Ensure to be ready for external events diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 492d78918..1369ecd1e 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -2,7 +2,6 @@ using System; using System.IO; -using System.Linq; using System.Collections.Generic; using FlaxEditor.Gizmo; using FlaxEditor.GUI; @@ -11,7 +10,6 @@ using FlaxEditor.GUI.Dialogs; using FlaxEditor.GUI.Input; using FlaxEditor.Progress.Handlers; using FlaxEditor.SceneGraph; -using FlaxEditor.SceneGraph.Actors; using FlaxEditor.Utilities; using FlaxEditor.Viewport.Cameras; using FlaxEditor.Windows; @@ -208,6 +206,7 @@ namespace FlaxEditor.Modules _toolStripScale.Checked = gizmoMode == TransformGizmoBase.Mode.Scale; // _toolStripBuildScenes.Enabled = (canEditScene && !isPlayMode) || Editor.StateMachine.BuildingScenesState.IsActive; + _toolStripBuildScenes.Visible = Editor.Options.Options.General.BuildActions?.Length != 0; _toolStripCook.Enabled = Editor.Windows.GameCookerWin.CanBuild(Platform.PlatformType) && !GameCooker.IsRunning; // var play = _toolStripPlay; @@ -653,7 +652,7 @@ namespace FlaxEditor.Modules cm.AddButton("Information about Flax", () => new AboutDialog().Show()); } - private void OnOptionsChanged(FlaxEditor.Options.EditorOptions options) + private void OnOptionsChanged(EditorOptions options) { var inputOptions = options.Input; @@ -688,6 +687,8 @@ namespace FlaxEditor.Modules _menuToolsTakeScreenshot.ShortKeys = inputOptions.TakeScreenshot.ToString(); MainMenuShortcutKeysUpdated?.Invoke(); + + UpdateToolstrip(); } private void InitToolstrip(RootControl mainWindow) @@ -709,11 +710,11 @@ namespace FlaxEditor.Modules _toolStripScale = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Scale32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale).LinkTooltip($"Change Gizmo tool mode to Scale ({inputOptions.ScaleMode})"); ToolStrip.AddSeparator(); - // Cook scenes + // Build scenes _toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build64, Editor.BuildScenesOrCancel).LinkTooltip($"Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options ({inputOptions.BuildScenesData})"); // Cook and run - _toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.ShipIt64, Editor.Windows.GameCookerWin.BuildAndRun).LinkTooltip($"Cook & Run - build game for the current platform and run it locally ({inputOptions.Play})"); + _toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.ShipIt64, Editor.Windows.GameCookerWin.BuildAndRun).LinkTooltip($"Cook & Run - build game for the current platform and run it locally ({inputOptions.CookAndRun})"); _toolStripCook.ContextMenu = new ContextMenu(); _toolStripCook.ContextMenu.AddButton("Run cooked game", Editor.Windows.GameCookerWin.RunCooked); _toolStripCook.ContextMenu.AddSeparator(); diff --git a/Source/Editor/Options/GeneralOptions.cs b/Source/Editor/Options/GeneralOptions.cs index 33124bab0..9f29f4bdb 100644 --- a/Source/Editor/Options/GeneralOptions.cs +++ b/Source/Editor/Options/GeneralOptions.cs @@ -117,7 +117,7 @@ namespace FlaxEditor.Options /// /// Gets or sets the sequence of actions to perform when using Build Scenes button. Can be used to configure this as button (eg. compile code or just update navmesh). /// - [EditorDisplay("General"), EditorOrder(200), Tooltip("The sequence of actions to perform when using Build Scenes button. Can be used to configure this as button (eg. compile code or just update navmesh).")] + [EditorDisplay("General"), EditorOrder(200), ExpandGroups, Tooltip("The sequence of actions to perform when using Build Scenes button. Can be used to configure this as button (eg. compile code or just update navmesh).")] public BuildAction[] BuildActions { get; set; } = { BuildAction.CSG, diff --git a/Source/Editor/Windows/Assets/AssetEditorWindow.cs b/Source/Editor/Windows/Assets/AssetEditorWindow.cs index 0d244479c..2e048b924 100644 --- a/Source/Editor/Windows/Assets/AssetEditorWindow.cs +++ b/Source/Editor/Windows/Assets/AssetEditorWindow.cs @@ -388,14 +388,16 @@ namespace FlaxEditor.Windows.Assets protected override void OnShow() { // Check if has no asset (but has item linked) - if (_asset == null && _item != null) + var item = _item; + if (_asset == null && item != null) { // Load asset _asset = LoadAsset(); if (_asset == null) { - Editor.LogError(string.Format("Cannot load asset \'{0}\' ({1})", _item.Path, typeof(T))); + Editor.LogError(string.Format("Cannot load asset \'{0}\' ({1})", item.Path, typeof(T))); Close(); + Editor.ContentDatabase.RefreshFolder(item, false); return; } diff --git a/Source/Editor/Windows/Assets/MaterialInstanceWindow.cs b/Source/Editor/Windows/Assets/MaterialInstanceWindow.cs index 5f1273999..3e0525fb7 100644 --- a/Source/Editor/Windows/Assets/MaterialInstanceWindow.cs +++ b/Source/Editor/Windows/Assets/MaterialInstanceWindow.cs @@ -521,8 +521,11 @@ namespace FlaxEditor.Windows.Assets /// protected override void OnClose() { - // Discard unsaved changes - _properties.DiscardChanges(); + if (Asset) + { + // Discard unsaved changes + _properties.DiscardChanges(); + } // Cleanup _undo.Clear(); diff --git a/Source/Engine/Content/Assets/Material.cpp b/Source/Engine/Content/Assets/Material.cpp index 76ed34b80..276b6b1f7 100644 --- a/Source/Engine/Content/Assets/Material.cpp +++ b/Source/Engine/Content/Assets/Material.cpp @@ -20,6 +20,7 @@ #include "Engine/ShadersCompilation/Config.h" #if BUILD_DEBUG #include "Engine/Engine/Globals.h" +#include "Engine/Scripting/BinaryModule.h" #endif #endif @@ -256,7 +257,9 @@ Asset::LoadResult Material::load() #if BUILD_DEBUG && USE_EDITOR // Dump generated material source to the temporary file + BinaryModule::Locker.Lock(); source.SaveToFile(Globals::ProjectCacheFolder / TEXT("material.txt")); + BinaryModule::Locker.Unlock(); #endif // Encrypt source code diff --git a/Source/Engine/ContentImporters/ImportModelFile.cpp b/Source/Engine/ContentImporters/ImportModelFile.cpp index b9a4f5675..b34bbfaaf 100644 --- a/Source/Engine/ContentImporters/ImportModelFile.cpp +++ b/Source/Engine/ContentImporters/ImportModelFile.cpp @@ -124,7 +124,8 @@ CreateAssetResult ImportModelFile::Import(CreateAssetContext& context) // Import model file ModelData modelData; String errorMsg; - String autoImportOutput = String(StringUtils::GetDirectoryName(context.TargetAssetPath)) / StringUtils::GetFileNameWithoutExtension(context.InputPath); + String autoImportOutput(StringUtils::GetDirectoryName(context.TargetAssetPath)); + autoImportOutput /= options.SubAssetFolder.HasChars() ? options.SubAssetFolder.TrimTrailing() : String(StringUtils::GetFileNameWithoutExtension(context.InputPath)); if (ModelTool::ImportModel(context.InputPath, modelData, options, errorMsg, autoImportOutput)) { LOG(Error, "Cannot import model file. {0}", errorMsg); diff --git a/Source/Engine/Core/Collections/ChunkedArray.h b/Source/Engine/Core/Collections/ChunkedArray.h index 38bf92fb8..b3765a9fe 100644 --- a/Source/Engine/Core/Collections/ChunkedArray.h +++ b/Source/Engine/Core/Collections/ChunkedArray.h @@ -100,7 +100,7 @@ public: int32 _chunkIndex; int32 _index; - Iterator(ChunkedArray const* collection, const int32 index) + Iterator(const ChunkedArray* collection, const int32 index) : _collection(const_cast(collection)) , _chunkIndex(index / ChunkSize) , _index(index % ChunkSize) @@ -122,29 +122,29 @@ public: { } - public: - FORCE_INLINE ChunkedArray* GetChunkedArray() const + Iterator(Iterator&& i) + : _collection(i._collection) + , _chunkIndex(i._chunkIndex) + , _index(i._index) { - return _collection; } + public: FORCE_INLINE int32 Index() const { return _chunkIndex * ChunkSize + _index; } - public: - bool IsEnd() const + FORCE_INLINE bool IsEnd() const { - return Index() == _collection->Count(); + return (_chunkIndex * ChunkSize + _index) == _collection->_count; } - bool IsNotEnd() const + FORCE_INLINE bool IsNotEnd() const { - return Index() != _collection->Count(); + return (_chunkIndex * ChunkSize + _index) != _collection->_count; } - public: FORCE_INLINE T& operator*() const { return _collection->_chunks[_chunkIndex]->At(_index); @@ -155,7 +155,6 @@ public: return &_collection->_chunks[_chunkIndex]->At(_index); } - public: FORCE_INLINE bool operator==(const Iterator& v) const { return _collection == v._collection && _chunkIndex == v._chunkIndex && _index == v._index; @@ -166,17 +165,22 @@ public: return _collection != v._collection || _chunkIndex != v._chunkIndex || _index != v._index; } - public: + Iterator& operator=(const Iterator& v) + { + _collection = v._collection; + _chunkIndex = v._chunkIndex; + _index = v._index; + return *this; + } + Iterator& operator++() { // Check if it is not at end - const int32 end = _collection->Count(); - if (Index() != end) + if ((_chunkIndex * ChunkSize + _index) != _collection->_count) { // Move forward within chunk _index++; - // Check if need to change chunk if (_index == ChunkSize && _chunkIndex < _collection->_chunks.Count() - 1) { // Move to next chunk @@ -189,9 +193,9 @@ public: Iterator operator++(int) { - Iterator temp = *this; - ++temp; - return temp; + Iterator i = *this; + ++i; + return i; } Iterator& operator--() @@ -199,7 +203,6 @@ public: // Check if it's not at beginning if (_index != 0 || _chunkIndex != 0) { - // Check if need to change chunk if (_index == 0) { // Move to previous chunk @@ -217,9 +220,9 @@ public: Iterator operator--(int) { - Iterator temp = *this; - --temp; - return temp; + Iterator i = *this; + --i; + return i; } }; @@ -294,7 +297,7 @@ public: { if (IsEmpty()) return; - ASSERT(i.GetChunkedArray() == this); + ASSERT(i._collection == this); ASSERT(i._chunkIndex < _chunks.Count() && i._index < ChunkSize); ASSERT(i.Index() < Count()); @@ -432,11 +435,31 @@ public: Iterator End() const { - return Iterator(this, Count()); + return Iterator(this, _count); } Iterator IteratorAt(int32 index) const { return Iterator(this, index); } + + FORCE_INLINE Iterator begin() + { + return Iterator(this, 0); + } + + FORCE_INLINE Iterator end() + { + return Iterator(this, _count); + } + + FORCE_INLINE const Iterator begin() const + { + return Iterator(this, 0); + } + + FORCE_INLINE const Iterator end() const + { + return Iterator(this, _count); + } }; diff --git a/Source/Engine/Core/Types/Span.h b/Source/Engine/Core/Types/Span.h index e0518ec77..b51c858ec 100644 --- a/Source/Engine/Core/Types/Span.h +++ b/Source/Engine/Core/Types/Span.h @@ -107,6 +107,26 @@ public: ASSERT(index >= 0 && index < _length); return _data[index]; } + + FORCE_INLINE T* begin() + { + return _data; + } + + FORCE_INLINE T* end() + { + return _data + _length; + } + + FORCE_INLINE const T* begin() const + { + return _data; + } + + FORCE_INLINE const T* end() const + { + return _data + _length; + } }; template diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index 82765f151..898139ce7 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -634,15 +634,12 @@ void Foliage::RemoveFoliageType(int32 index) int32 Foliage::GetFoliageTypeInstancesCount(int32 index) const { PROFILE_CPU(); - int32 result = 0; - - for (auto i = Instances.Begin(); i.IsNotEnd(); i++) + for (auto i = Instances.Begin(); i.IsNotEnd(); ++i) { if (i->Type == index) result++; } - return result; } diff --git a/Source/Engine/Particles/ParticleEmitter.cpp b/Source/Engine/Particles/ParticleEmitter.cpp index 20e4c0490..ea1161c6b 100644 --- a/Source/Engine/Particles/ParticleEmitter.cpp +++ b/Source/Engine/Particles/ParticleEmitter.cpp @@ -19,6 +19,7 @@ #include "Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.h" #if BUILD_DEBUG #include "Engine/Engine/Globals.h" +#include "Engine/Scripting/BinaryModule.h" #endif #endif #if COMPILE_WITH_GPU_PARTICLES @@ -185,7 +186,9 @@ Asset::LoadResult ParticleEmitter::load() #if BUILD_DEBUG && USE_EDITOR // Dump generated shader source to the temporary file + BinaryModule::Locker.Lock(); source.SaveToFile(Globals::ProjectCacheFolder / TEXT("particle_emitter.txt")); + BinaryModule::Locker.Unlock(); #endif // Encrypt source code diff --git a/Source/Engine/Render2D/SpriteAtlas.cpp b/Source/Engine/Render2D/SpriteAtlas.cpp index 0e0aa071f..ac711b8b8 100644 --- a/Source/Engine/Render2D/SpriteAtlas.cpp +++ b/Source/Engine/Render2D/SpriteAtlas.cpp @@ -47,10 +47,16 @@ int32 SpriteAtlas::GetSpritesCount() const Sprite SpriteAtlas::GetSprite(int32 index) const { - CHECK_RETURN(index >= 0 && index < Sprites.Count(), Sprite()) + CHECK_RETURN(index >= 0 && index < Sprites.Count(), Sprite()); return Sprites.Get()[index]; } +void SpriteAtlas::GetSpriteArea(int32 index, Rectangle& result) const +{ + CHECK(index >= 0 && index < Sprites.Count()); + result = Sprites.Get()[index].Area; +} + void SpriteAtlas::SetSprite(int32 index, const Sprite& value) { CHECK(index >= 0 && index < Sprites.Count()); diff --git a/Source/Engine/Render2D/SpriteAtlas.cs b/Source/Engine/Render2D/SpriteAtlas.cs index b3796ffd3..93cdf68b2 100644 --- a/Source/Engine/Render2D/SpriteAtlas.cs +++ b/Source/Engine/Render2D/SpriteAtlas.cs @@ -70,7 +70,13 @@ namespace FlaxEngine [NoSerialize] public Float2 Size { - get => Area.Size * Atlas.Size; + get + { + if (Atlas == null) + throw new InvalidOperationException("Cannot use invalid sprite."); + Atlas.GetSpriteArea(Index, out var area); + return area.Size * Atlas.Size; + } set { var area = Area; @@ -89,7 +95,8 @@ namespace FlaxEngine { if (Atlas == null) throw new InvalidOperationException("Cannot use invalid sprite."); - return Atlas.GetSprite(Index).Area; + Atlas.GetSpriteArea(Index, out var area); + return area; } set { diff --git a/Source/Engine/Render2D/SpriteAtlas.h b/Source/Engine/Render2D/SpriteAtlas.h index 6fcda5162..533440c68 100644 --- a/Source/Engine/Render2D/SpriteAtlas.h +++ b/Source/Engine/Render2D/SpriteAtlas.h @@ -120,6 +120,14 @@ public: /// The sprite data. API_FUNCTION() Sprite GetSprite(int32 index) const; + /// + /// Gets the sprite area. + /// + /// The index. + /// The output sprite area. + /// The sprite data. + API_FUNCTION() void GetSpriteArea(int32 index, API_PARAM(Out) Rectangle& result) const; + /// /// Sets the sprite data. /// diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index b703b47d1..116f63407 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -22,6 +22,7 @@ #include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Types/TimeSpan.h" #include "Engine/Core/Types/Pair.h" +#include "Engine/Core/Types/Variant.h" #include "Engine/Graphics/Models/SkeletonUpdater.h" #include "Engine/Graphics/Models/SkeletonMapping.h" #include "Engine/Core/Utilities.h" @@ -396,6 +397,7 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj SERIALIZE(SDFResolution); SERIALIZE(SplitObjects); SERIALIZE(ObjectIndex); + SERIALIZE(SubAssetFolder); } void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) @@ -441,6 +443,7 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi DESERIALIZE(SDFResolution); DESERIALIZE(SplitObjects); DESERIALIZE(ObjectIndex); + DESERIALIZE(SubAssetFolder); // [Deprecated on 23.11.2021, expires on 21.11.2023] int32 AnimationIndex = -1; @@ -744,6 +747,32 @@ void MeshOptDeallocate(void* ptr) Allocator::Free(ptr); } +void TrySetupMaterialParameter(MaterialInstance* instance, Span paramNames, const Variant& value, MaterialParameterType type) +{ + for (const Char* name : paramNames) + { + for (MaterialParameter& param : instance->Params) + { + const MaterialParameterType paramType = param.GetParameterType(); + if (type != paramType) + { + if (type == MaterialParameterType::Color) + { + if (paramType != MaterialParameterType::Vector3 || + paramType != MaterialParameterType::Vector4) + continue; + } + else + continue; + } + if (StringUtils::CompareIgnoreCase(name, param.GetName().Get()) != 0) + continue; + param.SetValue(value); + return; + } + } +} + bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& options, String& errorMsg, const String& autoImportOutput) { LOG(Info, "Importing model from \'{0}\'", path); @@ -1014,9 +1043,18 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op { // Create material instance AssetsImportingManager::Create(AssetsImportingManager::CreateMaterialInstanceTag, assetPath, material.AssetID); - if (MaterialInstance* materialInstance = Content::Load(assetPath)) + if (auto* materialInstance = Content::Load(assetPath)) { materialInstance->SetBaseMaterial(options.InstanceToImportAs); + + // Customize base material based on imported material (blind guess based on the common names used in materials) + const Char* diffuseColorNames[] = { TEXT("color"), TEXT("col"), TEXT("diffuse"), TEXT("basecolor"), TEXT("base color") }; + TrySetupMaterialParameter(materialInstance, ToSpan(diffuseColorNames, ARRAY_COUNT(diffuseColorNames)), material.Diffuse.Color, MaterialParameterType::Color); + const Char* emissiveColorNames[] = { TEXT("emissive"), TEXT("emission"), TEXT("light") }; + TrySetupMaterialParameter(materialInstance, ToSpan(emissiveColorNames, ARRAY_COUNT(emissiveColorNames)), material.Emissive.Color, MaterialParameterType::Color); + const Char* opacityValueNames[] = { TEXT("opacity"), TEXT("alpha") }; + TrySetupMaterialParameter(materialInstance, ToSpan(opacityValueNames, ARRAY_COUNT(opacityValueNames)), material.Opacity.Value, MaterialParameterType::Float); + materialInstance->Save(); } else diff --git a/Source/Engine/Tools/ModelTool/ModelTool.h b/Source/Engine/Tools/ModelTool/ModelTool.h index 36bb9b12c..bad10b86e 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.h +++ b/Source/Engine/Tools/ModelTool/ModelTool.h @@ -370,6 +370,12 @@ public: API_FIELD(Attributes="EditorOrder(2010), EditorDisplay(\"Splitting\")") int32 ObjectIndex = -1; + public: // Other + + // If specified, will be used as sub-directory name for automatically imported sub assets such as textures and materials. Set to whitespace (single space) to import to the same directory. + API_FIELD(Attributes="EditorOrder(3030), EditorDisplay(\"Other\")") + String SubAssetFolder = TEXT(""); + // Runtime data for objects splitting during import (used internally) void* SplitContext = nullptr; Function OnSplitImport; diff --git a/Source/Shaders/GlobalSignDistanceField.shader b/Source/Shaders/GlobalSignDistanceField.shader index 3d4f7dd46..bc4f272fc 100644 --- a/Source/Shaders/GlobalSignDistanceField.shader +++ b/Source/Shaders/GlobalSignDistanceField.shader @@ -211,7 +211,7 @@ float SampleSDF(uint3 voxelCoordMip, int3 offset) float result = GlobalSDFTex[voxelCoordMip].r; // Extend by distance to the sampled texel location - float distanceInWorldUnits = length(offset) * (MaxDistance / (float)GenerateMipTexResolution); + float distanceInWorldUnits = length((float3)offset) * (MaxDistance / (float)GenerateMipTexResolution); float distanceToVoxel = distanceInWorldUnits / MaxDistance; result = CombineDistanceToSDF(result, distanceToVoxel);