Add support for masking Material Slots when cooking Collision Data

This commit is contained in:
Wojtek Figat
2021-03-31 12:15:38 +02:00
parent 382a35aabf
commit 51926f517e
13 changed files with 102 additions and 89 deletions

View File

@@ -752,10 +752,11 @@ namespace FlaxEditor
/// <param name="type">The collision data type.</param>
/// <param name="model">The source model.</param>
/// <param name="modelLodIndex">The source model LOD index.</param>
/// <param name="materialSlotsMask">The source model material slots mask. One bit per-slot. Can be sued to exclude particular material slots from collision cooking.</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>
/// <returns>True if failed, otherwise false.</returns>
public static bool CookMeshCollision(string path, CollisionDataType type, ModelBase model, int modelLodIndex = 0, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags.None, int convexVertexLimit = 255)
public static bool CookMeshCollision(string path, CollisionDataType type, ModelBase model, int modelLodIndex = 0, uint materialSlotsMask = uint.MaxValue, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags.None, int convexVertexLimit = 255)
{
if (string.IsNullOrEmpty(path))
throw new ArgumentNullException(nameof(path));
@@ -764,7 +765,7 @@ namespace FlaxEditor
if (type == CollisionDataType.None)
throw new ArgumentException(nameof(type));
return Internal_CookMeshCollision(path, type, FlaxEngine.Object.GetUnmanagedPtr(model), modelLodIndex, convexFlags, convexVertexLimit);
return Internal_CookMeshCollision(path, type, FlaxEngine.Object.GetUnmanagedPtr(model), modelLodIndex, materialSlotsMask, convexFlags, convexVertexLimit);
}
/// <summary>
@@ -1344,7 +1345,7 @@ namespace FlaxEditor
internal static extern string Internal_GetShaderAssetSourceCode(IntPtr obj);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_CookMeshCollision(string path, CollisionDataType type, IntPtr model, int modelLodIndex, ConvexMeshGenerationFlags convexFlags, int convexVertexLimit);
internal static extern bool Internal_CookMeshCollision(string path, CollisionDataType type, IntPtr model, int modelLodIndex, uint materialSlotsMask, ConvexMeshGenerationFlags convexFlags, int convexVertexLimit);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_GetCollisionWires(IntPtr collisionData, out Vector3[] triangles, out int[] indices);

View File

@@ -715,7 +715,7 @@ public:
return str;
}
static bool CookMeshCollision(MonoString* pathObj, CollisionDataType type, ModelBase* modelObj, int32 modelLodIndex, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
static bool CookMeshCollision(MonoString* pathObj, CollisionDataType type, ModelBase* modelObj, int32 modelLodIndex, uint32 materialSlotsMask, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
{
#if COMPILE_WITH_PHYSICS_COOKING
CollisionCooking::Argument arg;
@@ -725,6 +725,7 @@ public:
arg.Type = type;
arg.Model = modelObj;
arg.ModelLodIndex = modelLodIndex;
arg.MaterialSlotsMask = materialSlotsMask;
arg.ConvexFlags = convexFlags;
arg.ConvexVertexLimit = convexVertexLimit;
return CreateCollisionData::CookMeshCollision(path, arg);
@@ -742,8 +743,8 @@ public:
const auto& debugLines = collisionData->GetDebugLines();
const int32 linesCount = debugLines.Count() / 2;
*triangles = mono_array_new(mono_domain_get(), StdTypesContainer::Instance()->Vector3Class->GetNative(), debugLines.Count());
*indices = mono_array_new(mono_domain_get(), mono_get_int32_class(), linesCount * 3);
mono_gc_wbarrier_generic_store(triangles, (MonoObject*)mono_array_new(mono_domain_get(), StdTypesContainer::Instance()->Vector3Class->GetNative(), debugLines.Count()));
mono_gc_wbarrier_generic_store(indices, (MonoObject*)mono_array_new(mono_domain_get(), mono_get_int32_class(), linesCount * 3));
// Use one triangle per debug line
for (int32 i = 0; i < debugLines.Count(); i++)

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System;
using System.Xml;
using FlaxEditor.Content;
using FlaxEditor.Content.Create;
@@ -10,6 +11,7 @@ using FlaxEditor.Viewport.Cameras;
using FlaxEditor.Viewport.Previews;
using FlaxEngine;
using FlaxEngine.GUI;
using Object = FlaxEngine.Object;
namespace FlaxEditor.Windows.Assets
{
@@ -20,6 +22,15 @@ namespace FlaxEditor.Windows.Assets
/// <seealso cref="FlaxEditor.Windows.Assets.AssetEditorWindow" />
public sealed class CollisionDataWindow : AssetEditorWindowBase<CollisionData>
{
[Flags]
private enum MaterialSlotsMask : uint
{
// @formatter:off
Slot0=1u<<0,Slot1=1u<<1,Slot2=1u<<2,Slot3=1u<<3,Slot4=1u<<4,Slot5=1u<<5,Slot6=1u<<6,Slot7=1u<<7,Slot8=1u<<8,Slot9=1u<<9,Slot10=1u<<10,Slot11=1u<<11,Slot12=1u<<12,Slot13=1u<<13,Slot14=1u<<14,Slot15=1u<<15,Slot16=1u<<16,Slot17=1u<<17,Slot18=1u<<18,Slot19=1u<<19,Slot20=1u<<20,Slot21=1u<<21,Slot22=1u<<22,Slot23=1u<<23,Slot24=1u<<24,Slot25=1u<<25,Slot26=1u<<26,Slot27=1u<<27,Slot28=1u<<28,Slot29=1u<<29,Slot30=1u<<30,Slot31=1u<<31,
// @formatter:on
All = uint.MaxValue,
}
/// <summary>
/// The asset properties proxy object.
/// </summary>
@@ -39,6 +50,9 @@ namespace FlaxEditor.Windows.Assets
[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;
[EditorOrder(30), EditorDisplay("General"), Tooltip("The source model material slots mask. One bit per-slot. Can be sued to exclude particular material slots from collision cooking.")]
public MaterialSlotsMask MaterialSlotsMask = MaterialSlotsMask.All;
[EditorOrder(100), EditorDisplay("Convex Mesh", "Convex Flags"), Tooltip("Convex mesh generation flags")]
public ConvexMeshGenerationFlags ConvexFlags;
@@ -88,28 +102,23 @@ namespace FlaxEditor.Windows.Assets
private class CookData : CreateFileEntry
{
private PropertiesProxy Proxy;
private CollisionDataType Type;
private ModelBase Model;
private int ModelLodIndex;
private ConvexMeshGenerationFlags ConvexFlags;
private int ConvexVertexLimit;
public PropertiesProxy Proxy;
public CollisionDataType Type;
public ModelBase Model;
public int ModelLodIndex;
public uint MaterialSlotsMask;
public ConvexMeshGenerationFlags ConvexFlags;
public int ConvexVertexLimit;
public CookData(PropertiesProxy proxy, string resultUrl, CollisionDataType type, ModelBase model, int modelLodIndex, ConvexMeshGenerationFlags convexFlags, int convexVertexLimit)
public CookData(string resultUrl)
: base("Collision Data", resultUrl)
{
Proxy = proxy;
Type = type;
Model = model;
ModelLodIndex = modelLodIndex;
ConvexFlags = convexFlags;
ConvexVertexLimit = convexVertexLimit;
}
/// <inheritdoc />
public override bool Create()
{
bool failed = FlaxEditor.Editor.CookMeshCollision(ResultUrl, Type, Model, ModelLodIndex, ConvexFlags, ConvexVertexLimit);
bool failed = FlaxEditor.Editor.CookMeshCollision(ResultUrl, Type, Model, ModelLodIndex, MaterialSlotsMask, ConvexFlags, ConvexVertexLimit);
Proxy._isCooking = false;
Proxy.Window.UpdateWiresModel();
@@ -121,7 +130,16 @@ namespace FlaxEditor.Windows.Assets
public void Cook()
{
_isCooking = true;
Window.Editor.ContentImporting.Create(new CookData(this, Asset.Path, Type, Model, ModelLodIndex, ConvexFlags, ConvexVertexLimit));
Window.Editor.ContentImporting.Create(new CookData(Asset.Path)
{
Proxy = this,
Type = Type,
Model = Model,
ModelLodIndex = ModelLodIndex,
MaterialSlotsMask = (uint)MaterialSlotsMask,
ConvexFlags = ConvexFlags,
ConvexVertexLimit = ConvexVertexLimit,
});
}
public void OnLoad(CollisionDataWindow window)

View File

@@ -133,20 +133,22 @@ namespace
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, void* ib, bool use16BitIndices)
{
auto model = (Model*)_model;
Unload();
// Setup GPU resources
_model->LODs[_lodIndex]._verticesCount -= _vertices;
model->LODs[_lodIndex]._verticesCount -= _vertices;
const bool failed = Load(vertexCount, triangleCount, vb0, vb1, vb2, ib, use16BitIndices);
if (!failed)
{
_model->LODs[_lodIndex]._verticesCount += _vertices;
model->LODs[_lodIndex]._verticesCount += _vertices;
// Calculate mesh bounds
SetBounds(BoundingBox::FromPoints((Vector3*)vb0, vertexCount));
// Send event (actors using this model can update bounds, etc.)
_model->onLoaded();
model->onLoaded();
}
return failed;
@@ -214,17 +216,6 @@ Mesh::~Mesh()
SAFE_DELETE_GPU_RESOURCE(_indexBuffer);
}
void Mesh::SetMaterialSlotIndex(int32 value)
{
if (value < 0 || value >= _model->MaterialSlots.Count())
{
LOG(Warning, "Cannot set mesh material slot to {0} while model has {1} slots.", value, _model->MaterialSlots.Count());
return;
}
_materialSlotIndex = value;
}
bool Mesh::Load(uint32 vertices, uint32 triangles, void* vb0, void* vb1, void* vb2, void* ib, bool use16BitIndexBuffer)
{
// Cache data
@@ -443,7 +434,7 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float
// TODO: cache vertexOffset within the model LOD per-mesh
uint32 vertexOffset = 0;
for (int32 meshIndex = 0; meshIndex < _index; meshIndex++)
vertexOffset += _model->LODs[_lodIndex].Meshes[meshIndex].GetVertexCount();
vertexOffset += ((Model*)_model)->LODs[_lodIndex].Meshes[meshIndex].GetVertexCount();
drawCall.Geometry.VertexBuffers[2] = info.VertexColors[_lodIndex];
drawCall.Geometry.VertexBuffersOffsets[2] = vertexOffset * sizeof(VB2ElementType);
}

View File

@@ -22,10 +22,8 @@ API_CLASS(NoSpawn) class FLAXENGINE_API Mesh : public MeshBase
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(Mesh, MeshBase);
protected:
Model* _model;
int32 _index;
int32 _lodIndex;
int32 _materialSlotIndex;
bool _hasLightmapUVs;
GPUBuffer* _vertexBuffers[3];
GPUBuffer* _indexBuffer;
@@ -55,7 +53,7 @@ public:
/// </summary>
FORCE_INLINE Model* GetModel() const
{
return _model;
return (Model*)_model;
}
/// <summary>
@@ -74,19 +72,6 @@ public:
return _index;
}
/// <summary>
/// Gets the index of the material slot to use during this mesh rendering.
/// </summary>
API_PROPERTY() FORCE_INLINE int32 GetMaterialSlotIndex() const
{
return _materialSlotIndex;
}
/// <summary>
/// Sets the index of the material slot to use during this mesh rendering.
/// </summary>
API_PROPERTY() void SetMaterialSlotIndex(int32 value);
/// <summary>
/// Gets the index buffer.
/// </summary>

View File

@@ -9,6 +9,7 @@
#include "Engine/Scripting/ScriptingObject.h"
class Task;
class ModelBase;
/// <summary>
/// Base class for model resources meshes.
@@ -18,11 +19,13 @@ API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API MeshBase : public PersistentSc
DECLARE_SCRIPTING_TYPE_MINIMAL(MeshBase);
protected:
ModelBase* _model;
bool _use16BitIndexBuffer;
BoundingBox _box;
BoundingSphere _sphere;
uint32 _vertices;
uint32 _triangles;
int32 _materialSlotIndex;
explicit MeshBase(const SpawnParams& params)
: PersistentScriptingObject(params)
@@ -31,6 +34,14 @@ protected:
public:
/// <summary>
/// Gets the model owning this mesh.
/// </summary>
FORCE_INLINE ModelBase* GetModelBase() const
{
return _model;
}
/// <summary>
/// Gets the triangle count.
/// </summary>
@@ -76,6 +87,19 @@ public:
return _use16BitIndexBuffer;
}
/// <summary>
/// Gets the index of the material slot to use during this mesh rendering.
/// </summary>
API_PROPERTY() FORCE_INLINE int32 GetMaterialSlotIndex() const
{
return _materialSlotIndex;
}
/// <summary>
/// Sets the index of the material slot to use during this mesh rendering.
/// </summary>
API_PROPERTY() void SetMaterialSlotIndex(int32 value);
/// <summary>
/// Sets the mesh bounds.
/// </summary>

View File

@@ -34,17 +34,6 @@ SkinnedMesh::~SkinnedMesh()
SAFE_DELETE_GPU_RESOURCE(_indexBuffer);
}
void SkinnedMesh::SetMaterialSlotIndex(int32 value)
{
if (value < 0 || value >= _model->MaterialSlots.Count())
{
LOG(Warning, "Cannot set mesh material slot to {0} while model has {1} slots.", value, _model->MaterialSlots.Count());
return;
}
_materialSlotIndex = value;
}
bool SkinnedMesh::Load(uint32 vertices, uint32 triangles, void* vb0, void* ib, bool use16BitIndexBuffer)
{
// Cache data
@@ -101,6 +90,8 @@ void SkinnedMesh::Unload()
bool SkinnedMesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, void* ib, bool use16BitIndices)
{
auto model = (SkinnedModel*)_model;
// Setup GPU resources
const bool failed = Load(vertexCount, triangleCount, vb, ib, use16BitIndices);
if (!failed)
@@ -109,7 +100,7 @@ bool SkinnedMesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0Skinne
SetBounds(BoundingBox::FromPoints((Vector3*)vb, vertexCount));
// Send event (actors using this model can update bounds, etc.)
_model->onLoaded();
model->onLoaded();
}
return failed;

View File

@@ -19,10 +19,8 @@ API_CLASS(NoSpawn) class FLAXENGINE_API SkinnedMesh : public MeshBase
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(SkinnedMesh, MeshBase);
protected:
SkinnedModel* _model;
int32 _index;
int32 _lodIndex;
int32 _materialSlotIndex;
GPUBuffer* _vertexBuffer;
GPUBuffer* _indexBuffer;
mutable Array<byte> _cachedIndexBuffer;
@@ -33,7 +31,7 @@ public:
SkinnedMesh(const SkinnedMesh& other)
: SkinnedMesh()
{
#if !!BUILD_RELEASE
#if !BUILD_RELEASE
CRASH; // Not used
#endif
}
@@ -51,7 +49,7 @@ public:
/// <returns>The skinned model</returns>
FORCE_INLINE SkinnedModel* GetSkinnedModel() const
{
return _model;
return (SkinnedModel*)_model;
}
/// <summary>
@@ -63,21 +61,6 @@ public:
return _index;
}
/// <summary>
/// Gets the material slot index.
/// </summary>
/// <returns>The material slot index</returns>
API_PROPERTY() FORCE_INLINE int32 GetMaterialSlotIndex() const
{
return _materialSlotIndex;
}
/// <summary>
/// Sets the index of the material slot index.
/// </summary>
/// <param name="value">The value.</param>
API_PROPERTY() void SetMaterialSlotIndex(int32 value);
/// <summary>
/// Determines whether this mesh is initialized (has vertex and index buffers initialized).
/// </summary>

View File

@@ -554,6 +554,17 @@ float ViewToCenterLessRadius(const RenderView& view, const Vector3& center, floa
return viewToCenter - radius;
}
void MeshBase::SetMaterialSlotIndex(int32 value)
{
if (value < 0 || value >= _model->MaterialSlots.Count())
{
LOG(Warning, "Cannot set mesh material slot to {0} while model has {1} slots.", value, _model->MaterialSlots.Count());
return;
}
_materialSlotIndex = value;
}
void MeshBase::SetBounds(const BoundingBox& box)
{
_box = box;

View File

@@ -118,15 +118,14 @@ bool CollisionCooking::CookCollision(const Argument& arg, CollisionData::Seriali
for (int32 i = 0; i < meshesCount; i++)
{
const auto mesh = lod->Meshes[i];
if ((arg.MaterialSlotsMask & (1 << mesh->MaterialSlotIndex)) == 0)
continue;
vCount += mesh->Positions.Count();
if (needIndexBuffer)
{
iCount += mesh->Indices.Count() * 3;
}
}
if (meshesCount == 1)
if (meshesCount == 1 && vCount != 0)
{
// Link a single mesh
const auto mesh = lod->Meshes[0];
@@ -145,6 +144,8 @@ bool CollisionCooking::CookCollision(const Argument& arg, CollisionData::Seriali
for (int32 i = 0; i < meshesCount; i++)
{
const auto mesh = lod->Meshes[i];
if ((arg.MaterialSlotsMask & (1 << mesh->MaterialSlotIndex)) == 0)
continue;
const int32 firstVertexIndex = vertexCounter;
const int32 vertexCount = mesh->Positions.Count();
@@ -208,6 +209,8 @@ bool CollisionCooking::CookCollision(const Argument& arg, CollisionData::Seriali
for (int32 i = 0; i < meshesCount; i++)
{
const auto& mesh = *meshes[i];
if ((arg.MaterialSlotsMask & (1 << mesh.GetMaterialSlotIndex())) == 0)
continue;
auto task = mesh.DownloadDataGPUAsync(MeshBufferType::Vertex0, vertexBuffers[i]);
if (task == nullptr)
@@ -239,6 +242,8 @@ bool CollisionCooking::CookCollision(const Argument& arg, CollisionData::Seriali
for (int32 i = 0; i < meshesCount; i++)
{
const auto& mesh = *meshes[i];
if ((arg.MaterialSlotsMask & (1 << mesh.GetMaterialSlotIndex())) == 0)
continue;
const auto& vData = vertexBuffers[i];
const int32 firstVertexIndex = vertexCounter;

View File

@@ -42,6 +42,7 @@ public:
ModelData* OverrideModelData = nullptr;
AssetReference<ModelBase> Model;
int32 ModelLodIndex = 0;
uint32 MaterialSlotsMask = MAX_uint32;
ConvexMeshGenerationFlags ConvexFlags = ConvexMeshGenerationFlags::None;
int32 ConvexVertexLimit = 255;
};

View File

@@ -23,7 +23,7 @@ CollisionData::CollisionData(const SpawnParams& params, const AssetInfo* info)
#if COMPILE_WITH_PHYSICS_COOKING
bool CollisionData::CookCollision(CollisionDataType type, ModelBase* modelObj, int32 modelLodIndex, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
bool CollisionData::CookCollision(CollisionDataType type, ModelBase* modelObj, int32 modelLodIndex, uint32 materialSlotsMask, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
{
// Validate state
if (!IsVirtual())
@@ -37,6 +37,7 @@ bool CollisionData::CookCollision(CollisionDataType type, ModelBase* modelObj, i
arg.Type = type;
arg.Model = modelObj;
arg.ModelLodIndex = modelLodIndex;
arg.MaterialSlotsMask = materialSlotsMask;
arg.ConvexFlags = convexFlags;
arg.ConvexVertexLimit = convexVertexLimit;

View File

@@ -205,9 +205,10 @@ public:
/// <param name="type">The collision data type.</param>
/// <param name="model">The source model.</param>
/// <param name="modelLodIndex">The source model LOD index.</param>
/// <param name="materialSlotsMask">The source model material slots mask. One bit per-slot. Can be sued to exclude particular material slots from collision cooking.</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, ModelBase* model, int32 modelLodIndex = 0, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags::None, int32 convexVertexLimit = 255);
API_FUNCTION() bool CookCollision(CollisionDataType type, ModelBase* model, int32 modelLodIndex = 0, uint32 materialSlotsMask = MAX_uint32, 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.