Add support for generating Collision Data from Skinned Models
This commit is contained in:
@@ -755,7 +755,7 @@ namespace FlaxEditor
|
||||
/// <param name="convexFlags">The convex mesh generation flags.</param>
|
||||
/// <param name="convexVertexLimit">The convex mesh vertex limit. Use values in range [8;255]</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
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));
|
||||
|
||||
@@ -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.");
|
||||
|
||||
122
Source/Editor/Viewport/Previews/ModelBasePreview.cs
Normal file
122
Source/Editor/Viewport/Previews/ModelBasePreview.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Model Base asset preview editor viewport.
|
||||
/// </summary>
|
||||
/// <seealso cref="AssetPreview" />
|
||||
public class ModelBasePreview : AssetPreview
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the model asset to preview.
|
||||
/// </summary>
|
||||
public ModelBase Asset
|
||||
{
|
||||
get => (ModelBase)StaticModel.Model ?? AnimatedModel.SkinnedModel;
|
||||
set
|
||||
{
|
||||
StaticModel.Model = value as Model;
|
||||
AnimatedModel.SkinnedModel = value as SkinnedModel;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The static model for display.
|
||||
/// </summary>
|
||||
public StaticModel StaticModel;
|
||||
|
||||
/// <summary>
|
||||
/// The animated model for display.
|
||||
/// </summary>
|
||||
public AnimatedModel AnimatedModel;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether scale the model to the normalized bounds.
|
||||
/// </summary>
|
||||
public bool ScaleToFit { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ModelBasePreview"/> class.
|
||||
/// </summary>
|
||||
/// <param name="useWidgets">if set to <c>true</c> use widgets.</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
// Ensure to cleanup created actor objects
|
||||
Object.Destroy(ref StaticModel);
|
||||
Object.Destroy(ref AnimatedModel);
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Model>(options.Model);
|
||||
Model = FlaxEngine.Content.LoadAsync<ModelBase>(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<MaterialBase>(EditorAssets.WiresDebugMaterial));
|
||||
_preview.Model = FlaxEngine.Content.LoadAsync<Model>(_asset.Options.Model);
|
||||
_preview.Asset = FlaxEngine.Content.LoadAsync<ModelBase>(_asset.Options.Model);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void UnlinkItem()
|
||||
{
|
||||
_properties.OnClean();
|
||||
_preview.Model = null;
|
||||
_preview.Asset = null;
|
||||
|
||||
base.UnlinkItem();
|
||||
}
|
||||
@@ -245,7 +245,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// <inheritdoc />
|
||||
protected override void OnAssetLinked()
|
||||
{
|
||||
_preview.Model = null;
|
||||
_preview.Asset = null;
|
||||
|
||||
base.OnAssetLinked();
|
||||
}
|
||||
|
||||
@@ -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 <ThirdParty/PhysX/cooking/PxCooking.h>
|
||||
@@ -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<MeshBase*> 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<BytesContainer> vertexBuffers;
|
||||
Array<BytesContainer> indexBuffers;
|
||||
vertexBuffers.Resize(meshesCount);
|
||||
@@ -205,9 +207,9 @@ bool CollisionCooking::CookCollision(const Argument& arg, CollisionData::Seriali
|
||||
Array<Task*> 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<uint16>();
|
||||
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<uint32>();
|
||||
for (int32 j = 0; j < indexCount; j++)
|
||||
{
|
||||
*dst++ = firstVertexIndex + *src++;
|
||||
|
||||
@@ -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> Model;
|
||||
AssetReference<ModelBase> Model;
|
||||
int32 ModelLodIndex = 0;
|
||||
ConvexMeshGenerationFlags ConvexFlags = ConvexMeshGenerationFlags::None;
|
||||
int32 ConvexVertexLimit = 255;
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="CollisionData"/> storage data type.
|
||||
/// </summary>
|
||||
@@ -156,8 +162,8 @@ public:
|
||||
private:
|
||||
|
||||
CollisionDataOptions _options;
|
||||
PxConvexMesh* _convexMesh;
|
||||
PxTriangleMesh* _triangleMesh;
|
||||
physx::PxConvexMesh* _convexMesh;
|
||||
physx::PxTriangleMesh* _triangleMesh;
|
||||
|
||||
public:
|
||||
|
||||
@@ -173,8 +179,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the convex mesh object (valid only if asset is loaded and has cooked convex data).
|
||||
/// </summary>
|
||||
/// <returns>The convex mesh</returns>
|
||||
FORCE_INLINE PxConvexMesh* GetConvex() const
|
||||
FORCE_INLINE physx::PxConvexMesh* GetConvex() const
|
||||
{
|
||||
return _convexMesh;
|
||||
}
|
||||
@@ -182,8 +187,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the triangle mesh object (valid only if asset is loaded and has cooked triangle data).
|
||||
/// </summary>
|
||||
/// <returns>The triangle mesh</returns>
|
||||
FORCE_INLINE PxTriangleMesh* GetTriangle() const
|
||||
FORCE_INLINE physx::PxTriangleMesh* GetTriangle() const
|
||||
{
|
||||
return _triangleMesh;
|
||||
}
|
||||
@@ -203,7 +207,7 @@ public:
|
||||
/// <param name="modelLodIndex">The source model LOD index.</param>
|
||||
/// <param name="convexFlags">The convex mesh generation flags.</param>
|
||||
/// <param name="convexVertexLimit">The convex mesh vertex limit. Use values in range [8;255]</param>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Cooks the mesh collision data and updates the virtual asset. action cannot be performed on a main thread.
|
||||
|
||||
Reference in New Issue
Block a user