diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 5cada5d87..d3e71b474 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -755,7 +755,7 @@ namespace FlaxEditor /// The convex mesh generation flags. /// The convex mesh vertex limit. Use values in range [8;255] /// True if failed, otherwise false. - public static bool CookMeshCollision(string path, CollisionDataType type, Model model, int modelLodIndex = 0, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags.None, int convexVertexLimit = 255) + public static bool CookMeshCollision(string path, CollisionDataType type, ModelBase model, int modelLodIndex = 0, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags.None, int convexVertexLimit = 255) { if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path)); diff --git a/Source/Editor/Managed/ManagedEditor.Internal.cpp b/Source/Editor/Managed/ManagedEditor.Internal.cpp index 7b178b956..f56d8f3e6 100644 --- a/Source/Editor/Managed/ManagedEditor.Internal.cpp +++ b/Source/Editor/Managed/ManagedEditor.Internal.cpp @@ -715,7 +715,7 @@ public: return str; } - static bool CookMeshCollision(MonoString* pathObj, CollisionDataType type, Model* modelObj, int32 modelLodIndex, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit) + static bool CookMeshCollision(MonoString* pathObj, CollisionDataType type, ModelBase* modelObj, int32 modelLodIndex, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit) { #if COMPILE_WITH_PHYSICS_COOKING CollisionCooking::Argument arg; @@ -727,7 +727,6 @@ public: arg.ModelLodIndex = modelLodIndex; arg.ConvexFlags = convexFlags; arg.ConvexVertexLimit = convexVertexLimit; - return CreateCollisionData::CookMeshCollision(path, arg); #else LOG(Warning, "Collision cooking is disabled."); diff --git a/Source/Editor/Viewport/Previews/ModelBasePreview.cs b/Source/Editor/Viewport/Previews/ModelBasePreview.cs new file mode 100644 index 000000000..59ed5f318 --- /dev/null +++ b/Source/Editor/Viewport/Previews/ModelBasePreview.cs @@ -0,0 +1,122 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using FlaxEditor.GUI.Input; +using FlaxEngine; +using Object = FlaxEngine.Object; + +namespace FlaxEditor.Viewport.Previews +{ + /// + /// Model Base asset preview editor viewport. + /// + /// + public class ModelBasePreview : AssetPreview + { + /// + /// Gets or sets the model asset to preview. + /// + public ModelBase Asset + { + get => (ModelBase)StaticModel.Model ?? AnimatedModel.SkinnedModel; + set + { + StaticModel.Model = value as Model; + AnimatedModel.SkinnedModel = value as SkinnedModel; + } + } + + /// + /// The static model for display. + /// + public StaticModel StaticModel; + + /// + /// The animated model for display. + /// + public AnimatedModel AnimatedModel; + + /// + /// Gets or sets a value indicating whether scale the model to the normalized bounds. + /// + public bool ScaleToFit { get; set; } = true; + + /// + /// Initializes a new instance of the class. + /// + /// if set to true use widgets. + public ModelBasePreview(bool useWidgets) + : base(useWidgets) + { + Task.Begin += OnBegin; + + // Setup preview scene + StaticModel = new StaticModel(); + AnimatedModel = new AnimatedModel(); + + // Link actors for rendering + Task.AddCustomActor(StaticModel); + Task.AddCustomActor(AnimatedModel); + + if (useWidgets) + { + // Preview LOD + { + var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD"); + var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f) + { + Parent = previewLOD + }; + previewLODValue.ValueChanged += () => + { + StaticModel.ForcedLOD = previewLODValue.Value; + AnimatedModel.ForcedLOD = previewLODValue.Value; + }; + ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = StaticModel.ForcedLOD; + } + } + } + + private void OnBegin(RenderTask task, GPUContext context) + { + var position = Vector3.Zero; + var scale = Vector3.One; + + // Update preview model scale to fit the preview + var model = Asset; + if (ScaleToFit && model && model.IsLoaded) + { + float targetSize = 50.0f; + BoundingBox box = model is Model ? ((Model)model).GetBox() : ((SkinnedModel)model).GetBox(); + float maxSize = Mathf.Max(0.001f, box.Size.MaxValue); + scale = new Vector3(targetSize / maxSize); + position = box.Center * (-0.5f * scale.X) + new Vector3(0, -10, 0); + } + + StaticModel.Transform = new Transform(position, StaticModel.Orientation, scale); + AnimatedModel.Transform = new Transform(position, AnimatedModel.Orientation, scale); + } + + /// + public override bool OnKeyDown(KeyboardKeys key) + { + switch (key) + { + case KeyboardKeys.F: + // Pay respect.. + ViewportCamera.SetArcBallView(StaticModel.Model != null ? StaticModel.Box : AnimatedModel.Box); + break; + } + return base.OnKeyDown(key); + } + + /// + public override void OnDestroy() + { + // Ensure to cleanup created actor objects + Object.Destroy(ref StaticModel); + Object.Destroy(ref AnimatedModel); + + base.OnDestroy(); + } + } +} diff --git a/Source/Editor/Windows/Assets/CollisionDataWindow.cs b/Source/Editor/Windows/Assets/CollisionDataWindow.cs index 23f9ae5ed..9191283dc 100644 --- a/Source/Editor/Windows/Assets/CollisionDataWindow.cs +++ b/Source/Editor/Windows/Assets/CollisionDataWindow.cs @@ -34,7 +34,7 @@ namespace FlaxEditor.Windows.Assets public CollisionDataType Type; [EditorOrder(10), EditorDisplay("General"), Tooltip("Source model asset to use for collision data generation")] - public Model Model; + public ModelBase Model; [EditorOrder(20), Limit(0, 5), EditorDisplay("General", "Model LOD Index"), Tooltip("Source model LOD index to use for collision data generation (will be clamped to the actual model LODs collection size)")] public int ModelLodIndex; @@ -90,12 +90,12 @@ namespace FlaxEditor.Windows.Assets { private PropertiesProxy Proxy; private CollisionDataType Type; - private Model Model; + private ModelBase Model; private int ModelLodIndex; private ConvexMeshGenerationFlags ConvexFlags; private int ConvexVertexLimit; - public CookData(PropertiesProxy proxy, string resultUrl, CollisionDataType type, Model model, int modelLodIndex, ConvexMeshGenerationFlags convexFlags, int convexVertexLimit) + public CookData(PropertiesProxy proxy, string resultUrl, CollisionDataType type, ModelBase model, int modelLodIndex, ConvexMeshGenerationFlags convexFlags, int convexVertexLimit) : base("Collision Data", resultUrl) { Proxy = proxy; @@ -135,7 +135,7 @@ namespace FlaxEditor.Windows.Assets Type = options.Type; if (Type == CollisionDataType.None) Type = CollisionDataType.ConvexMesh; - Model = FlaxEngine.Content.LoadAsync(options.Model); + Model = FlaxEngine.Content.LoadAsync(options.Model); ModelLodIndex = options.ModelLodIndex; ConvexFlags = options.ConvexFlags; ConvexVertexLimit = options.ConvexVertexLimit; @@ -151,7 +151,7 @@ namespace FlaxEditor.Windows.Assets } private readonly SplitPanel _split; - private readonly ModelPreview _preview; + private readonly ModelBasePreview _preview; private readonly CustomEditorPresenter _propertiesPresenter; private readonly PropertiesProxy _properties; private Model _collisionWiresModel; @@ -176,7 +176,7 @@ namespace FlaxEditor.Windows.Assets }; // Model preview - _preview = new ModelPreview(true) + _preview = new ModelBasePreview(true) { ViewportCamera = new FPSCamera(), Parent = _split.Panel1 @@ -195,7 +195,7 @@ namespace FlaxEditor.Windows.Assets // Sync helper actor size with actual preview model (preview scales model for better usage experience) if (_collisionWiresShowActor && _collisionWiresShowActor.IsActive) { - _collisionWiresShowActor.Transform = _preview.PreviewActor.Transform; + _collisionWiresShowActor.Transform = _preview.StaticModel.Transform; } base.Update(deltaTime); @@ -230,14 +230,14 @@ namespace FlaxEditor.Windows.Assets } _collisionWiresShowActor.Model = _collisionWiresModel; _collisionWiresShowActor.SetMaterial(0, FlaxEngine.Content.LoadAsyncInternal(EditorAssets.WiresDebugMaterial)); - _preview.Model = FlaxEngine.Content.LoadAsync(_asset.Options.Model); + _preview.Asset = FlaxEngine.Content.LoadAsync(_asset.Options.Model); } /// protected override void UnlinkItem() { _properties.OnClean(); - _preview.Model = null; + _preview.Asset = null; base.UnlinkItem(); } @@ -245,7 +245,7 @@ namespace FlaxEditor.Windows.Assets /// protected override void OnAssetLinked() { - _preview.Model = null; + _preview.Asset = null; base.OnAssetLinked(); } diff --git a/Source/Engine/Physics/CollisionCooking.cpp b/Source/Engine/Physics/CollisionCooking.cpp index 9076d979e..0cb8fa7a9 100644 --- a/Source/Engine/Physics/CollisionCooking.cpp +++ b/Source/Engine/Physics/CollisionCooking.cpp @@ -5,6 +5,7 @@ #include "CollisionCooking.h" #include "Engine/Threading/Task.h" #include "Engine/Graphics/Async/GPUTask.h" +#include "Engine/Graphics/Models/MeshBase.h" #include "Engine/Core/Log.h" #include "Physics.h" #include @@ -186,14 +187,15 @@ bool CollisionCooking::CookCollision(const Argument& arg, CollisionData::Seriali } // Pick a proper model LOD - const int32 lodIndex = Math::Clamp(arg.ModelLodIndex, 0, arg.Model->LODs.Count()); - auto lod = &arg.Model->LODs[lodIndex]; + const int32 lodIndex = Math::Clamp(arg.ModelLodIndex, 0, arg.Model->GetLODsCount()); + Array meshes; + arg.Model->GetMeshes(meshes, lodIndex); // Download model LOD data from the GPU. // It's easier than reading internal, versioned mesh storage format. // Also it works with virtual assets that have no dedicated storage. // Note: request all meshes data at once and wait for the tasks to be done. - const int32 meshesCount = lod->Meshes.Count(); + const int32 meshesCount = meshes.Count(); Array vertexBuffers; Array indexBuffers; vertexBuffers.Resize(meshesCount); @@ -205,9 +207,9 @@ bool CollisionCooking::CookCollision(const Argument& arg, CollisionData::Seriali Array tasks(meshesCount + meshesCount); for (int32 i = 0; i < meshesCount; i++) { - const auto& mesh = lod->Meshes[i]; + const auto& mesh = *meshes[i]; - auto task = mesh.ExtractDataAsync(MeshBufferType::Vertex0, vertexBuffers[i]); + auto task = mesh.DownloadDataGPUAsync(MeshBufferType::Vertex0, vertexBuffers[i]); if (task == nullptr) return true; task->Start(); @@ -216,7 +218,7 @@ bool CollisionCooking::CookCollision(const Argument& arg, CollisionData::Seriali if (needIndexBuffer) { - task = mesh.ExtractDataAsync(MeshBufferType::Index, indexBuffers[i]); + task = mesh.DownloadDataGPUAsync(MeshBufferType::Index, indexBuffers[i]); if (task == nullptr) return true; task->Start(); @@ -236,7 +238,7 @@ bool CollisionCooking::CookCollision(const Argument& arg, CollisionData::Seriali int32 vertexCounter = 0, indexCounter = 0; for (int32 i = 0; i < meshesCount; i++) { - const auto& mesh = lod->Meshes[i]; + const auto& mesh = *meshes[i]; const auto& vData = vertexBuffers[i]; const int32 firstVertexIndex = vertexCounter; @@ -251,7 +253,7 @@ bool CollisionCooking::CookCollision(const Argument& arg, CollisionData::Seriali if (mesh.Use16BitIndexBuffer()) { auto dst = finalIndexData.Get() + indexCounter; - auto src = (uint16*)iData.Get(); + auto src = iData.Get(); for (int32 j = 0; j < indexCount; j++) { *dst++ = firstVertexIndex + *src++; @@ -261,7 +263,7 @@ bool CollisionCooking::CookCollision(const Argument& arg, CollisionData::Seriali else { auto dst = finalIndexData.Get() + indexCounter; - auto src = (uint32*)iData.Get(); + auto src = iData.Get(); for (int32 j = 0; j < indexCount; j++) { *dst++ = firstVertexIndex + *src++; diff --git a/Source/Engine/Physics/CollisionCooking.h b/Source/Engine/Physics/CollisionCooking.h index 658f88428..57ef98922 100644 --- a/Source/Engine/Physics/CollisionCooking.h +++ b/Source/Engine/Physics/CollisionCooking.h @@ -7,7 +7,7 @@ #include "Engine/Core/Types/DataContainer.h" #include "Engine/Physics/CollisionData.h" #include "Engine/Graphics/Models/ModelData.h" -#include "Engine/Content/Assets/Model.h" +#include "Engine/Content/Assets/ModelBase.h" namespace physx { @@ -40,7 +40,7 @@ public: { CollisionDataType Type = CollisionDataType::None; ModelData* OverrideModelData = nullptr; - AssetReference Model; + AssetReference Model; int32 ModelLodIndex = 0; ConvexMeshGenerationFlags ConvexFlags = ConvexMeshGenerationFlags::None; int32 ConvexVertexLimit = 255; diff --git a/Source/Engine/Physics/CollisionData.cpp b/Source/Engine/Physics/CollisionData.cpp index 8de5bfcaa..8ab4049d4 100644 --- a/Source/Engine/Physics/CollisionData.cpp +++ b/Source/Engine/Physics/CollisionData.cpp @@ -23,7 +23,7 @@ CollisionData::CollisionData(const SpawnParams& params, const AssetInfo* info) #if COMPILE_WITH_PHYSICS_COOKING -bool CollisionData::CookCollision(CollisionDataType type, Model* modelObj, int32 modelLodIndex, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit) +bool CollisionData::CookCollision(CollisionDataType type, ModelBase* modelObj, int32 modelLodIndex, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit) { // Validate state if (!IsVirtual()) diff --git a/Source/Engine/Physics/CollisionData.h b/Source/Engine/Physics/CollisionData.h index d3621f8d9..9a8d67b0b 100644 --- a/Source/Engine/Physics/CollisionData.h +++ b/Source/Engine/Physics/CollisionData.h @@ -4,11 +4,17 @@ #include "Engine/Content/BinaryAsset.h" #include "Engine/Core/Math/BoundingBox.h" -#include "Engine/Physics/Types.h" class Model; +class ModelBase; class ModelData; +namespace physx +{ + class PxConvexMesh; + class PxTriangleMesh; +} + /// /// A storage data type. /// @@ -156,8 +162,8 @@ public: private: CollisionDataOptions _options; - PxConvexMesh* _convexMesh; - PxTriangleMesh* _triangleMesh; + physx::PxConvexMesh* _convexMesh; + physx::PxTriangleMesh* _triangleMesh; public: @@ -173,8 +179,7 @@ public: /// /// Gets the convex mesh object (valid only if asset is loaded and has cooked convex data). /// - /// The convex mesh - FORCE_INLINE PxConvexMesh* GetConvex() const + FORCE_INLINE physx::PxConvexMesh* GetConvex() const { return _convexMesh; } @@ -182,8 +187,7 @@ public: /// /// Gets the triangle mesh object (valid only if asset is loaded and has cooked triangle data). /// - /// The triangle mesh - FORCE_INLINE PxTriangleMesh* GetTriangle() const + FORCE_INLINE physx::PxTriangleMesh* GetTriangle() const { return _triangleMesh; } @@ -203,7 +207,7 @@ public: /// The source model LOD index. /// The convex mesh generation flags. /// The convex mesh vertex limit. Use values in range [8;255] - API_FUNCTION() bool CookCollision(CollisionDataType type, Model* model, int32 modelLodIndex = 0, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags::None, int32 convexVertexLimit = 255); + API_FUNCTION() bool CookCollision(CollisionDataType type, ModelBase* model, int32 modelLodIndex = 0, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags::None, int32 convexVertexLimit = 255); /// /// Cooks the mesh collision data and updates the virtual asset. action cannot be performed on a main thread.