From ef3694d11757623891ea10b7159f476eee258dec Mon Sep 17 00:00:00 2001 From: Zombie Date: Mon, 16 Sep 2019 17:44:49 +1000 Subject: [PATCH] Numerous API changes --- Native/meson.build | 2 +- VHacdSharp/ComputeParameters.cs | 38 ----- VHacdSharp/ConvexDecomposition.cs | 10 +- VHacdSharp/ConvexDecompositionOptions.cs | 147 +++++++++++++++++++ VHacdSharp/ConvexHull.cs | 18 ++- VHacdSharp/DecompositionMode.cs | 4 +- VHacdSharp/TVector3.cs | 41 ++++++ VHacdSharp/VHacd.Interop.cs | 37 +++-- VHacdSharp/VHacd.cs | 174 ++++++++++------------- VHacdSharp/VHacdSharp.csproj | 22 +-- VHacdSharp/Vector3D.cs | 9 -- 11 files changed, 321 insertions(+), 181 deletions(-) delete mode 100644 VHacdSharp/ComputeParameters.cs create mode 100644 VHacdSharp/ConvexDecompositionOptions.cs create mode 100644 VHacdSharp/TVector3.cs delete mode 100644 VHacdSharp/Vector3D.cs diff --git a/Native/meson.build b/Native/meson.build index a92cd63..c936a22 100644 --- a/Native/meson.build +++ b/Native/meson.build @@ -3,7 +3,7 @@ project('v-hacd-wrapper', 'cpp') vhacd_prj = subproject('v-hacd') vhacd_dep = vhacd_prj.get_variable('vhacd_dep_static') -shared_library('vhacd', +shared_library('VHACD', files( 'source/main.cpp', 'source/context.cpp' diff --git a/VHacdSharp/ComputeParameters.cs b/VHacdSharp/ComputeParameters.cs deleted file mode 100644 index da4367c..0000000 --- a/VHacdSharp/ComputeParameters.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace VHacdSharp -{ - public struct ComputeParameters - { - public double Concavity { get; set; } - public double Alpha { get; set; } - public double Beta { get; set; } - public double MinHullVolume { get; set; } - public uint Resolution { get; set; } - public uint MaxHullVertices { get; set; } - public uint PlaneDownsampling { get; set; } - public uint HullDownsampling { get; set; } - public bool PCA { get; set; } // todo: better name - public DecompositionMode DecompositionMode { get; set; } - public bool HullApproximation { get; set; } - public bool OpenCLAcceleration { get; set; } - public uint MaxHulls { get; set; } - public bool ProjectHullVertices { get; set; } - - public static readonly ComputeParameters Default = new ComputeParameters - { - Resolution = 100_000, - Concavity = .001, - PlaneDownsampling = 4, - HullDownsampling = 4, - Alpha = .05, - Beta = .05, - PCA = false, - DecompositionMode = DecompositionMode.Voxel, - MaxHullVertices = 64, - MinHullVolume = .0001, - HullApproximation = true, - OpenCLAcceleration = true, - MaxHulls = 1024, - ProjectHullVertices = true - }; - } -} diff --git a/VHacdSharp/ConvexDecomposition.cs b/VHacdSharp/ConvexDecomposition.cs index 8d16c2b..cae2209 100644 --- a/VHacdSharp/ConvexDecomposition.cs +++ b/VHacdSharp/ConvexDecomposition.cs @@ -4,7 +4,13 @@ namespace VHacdSharp { public class ConvexDecomposition { - public IReadOnlyList ConvexHulls { get; internal set; } - public Vector3D CenterOfMass { get; internal set; } + public IReadOnlyList ConvexHulls { get; } + public TVector3 CenterOfMass { get; } + + public ConvexDecomposition(IReadOnlyList convexHulls, TVector3 centerOfMass) + { + ConvexHulls = convexHulls; + CenterOfMass = centerOfMass; + } } } diff --git a/VHacdSharp/ConvexDecompositionOptions.cs b/VHacdSharp/ConvexDecompositionOptions.cs new file mode 100644 index 0000000..7918db1 --- /dev/null +++ b/VHacdSharp/ConvexDecompositionOptions.cs @@ -0,0 +1,147 @@ +using System; + +namespace VHacdSharp +{ + public struct ConvexDecompositionOptions + { + /// Maximum concavity. + public double Concavity { get; private set; } + + /// Bias toward clipping along symmetry planes. + public double Alpha { get; private set; } + + /// Bias toward clipping along revolution axes. + public double Beta { get; private set; } + + /// Minimum volume to add vertices to convex-hulls. + public double MinHullVolume { get; private set; } + + /// Maximum number of voxels generated during the voxelization stage. + public uint Resolution { get; private set; } + + /// Maximum number of vertices per convex-hull. + public uint MaxHullVertices { get; private set; } + + /// Granularity of the search for the "best" clipping plane. + public uint PlaneDownsampling { get; private set; } + + /// Precision of the convex-hull generation process during the clipping plane selection stage. + public uint HullDownsampling { get; private set; } + + /// Enable/disable normalizing the mesh before applying the convex decomposition. + public bool PrincipalComponentAnalysis { get; private set; } + + /// Approximate convex decomposition mode. + public DecompositionMode DecompositionMode { get; private set; } + + public bool HullApproximation { get; private set; } + + public bool OpenCLAcceleration { get; private set; } + + /// Maximum number of convex hulls to produce from the merge operation. + public uint MaxHulls { get; private set; } + + /// Project the output convex hull vertices onto the original source mesh to increase the floating point accuracy of the results. + public bool ProjectHullVertices { get; private set; } + + public static readonly ConvexDecompositionOptions Default = new ConvexDecompositionOptions() + .WithResolution(100_000) + .WithConcavity(.001) + .WithPlaneDownsampling(4) + .WithHullDownsampling(4) + .WithAlpha(.05) + .WithBeta(.05) + .WithDecompositionMode(DecompositionMode.Voxel) + .WithMaxHullVertices(64) + .WithMinHullVolume(.0001) + .WithHullApproximation() + .WithOpenCLAcceleration() + .WithMaxHulls(1024) + .WithProjectHullVertices(); + + ConvexDecompositionOptions(ConvexDecompositionOptions source) + => this = source; + + public ConvexDecompositionOptions WithConcavity(double concavity) + { + if (concavity < 0 || concavity > 1) + throw new ArgumentOutOfRangeException(nameof(concavity)); + + return new ConvexDecompositionOptions(this) { Concavity = concavity }; + } + + public ConvexDecompositionOptions WithAlpha(double alpha) + { + if (alpha < 0 || alpha > 1) + throw new ArgumentOutOfRangeException(nameof(alpha)); + + return new ConvexDecompositionOptions(this) { Alpha = alpha }; + } + + public ConvexDecompositionOptions WithBeta(double beta) + { + if (beta < 0 || beta > 1) + throw new ArgumentOutOfRangeException(nameof(beta)); + + return new ConvexDecompositionOptions(this) { Beta = beta }; + } + + public ConvexDecompositionOptions WithMinHullVolume(double minHullVolume) + { + if (minHullVolume < 0 || minHullVolume > .01) + throw new ArgumentOutOfRangeException(nameof(minHullVolume)); + + return new ConvexDecompositionOptions(this) { MinHullVolume = minHullVolume }; + } + + public ConvexDecompositionOptions WithResolution(uint resolution) + { + if (resolution < 10_000 || resolution > 64_000_000) + throw new ArgumentOutOfRangeException(nameof(resolution)); + + return new ConvexDecompositionOptions(this) { Resolution = resolution }; + } + + public ConvexDecompositionOptions WithMaxHullVertices(uint maxHullVertices) + { + if (maxHullVertices < 4 || maxHullVertices > 1024) + throw new ArgumentOutOfRangeException(nameof(maxHullVertices)); + + return new ConvexDecompositionOptions(this) { MaxHullVertices = maxHullVertices }; + } + + public ConvexDecompositionOptions WithPlaneDownsampling(uint planeDownsampling) + { + if (planeDownsampling < 1 || planeDownsampling > 16) + throw new ArgumentOutOfRangeException(nameof(planeDownsampling)); + + return new ConvexDecompositionOptions(this) { PlaneDownsampling = planeDownsampling }; + } + + public ConvexDecompositionOptions WithHullDownsampling(uint hullDownsampling) + { + if (hullDownsampling < 1 || hullDownsampling > 16) + throw new ArgumentOutOfRangeException(nameof(hullDownsampling)); + + return new ConvexDecompositionOptions(this) { HullDownsampling = hullDownsampling }; + } + + public ConvexDecompositionOptions WithPrincipalComponentAnalysis(bool enable = true) + => new ConvexDecompositionOptions(this) { PrincipalComponentAnalysis = enable }; + + public ConvexDecompositionOptions WithDecompositionMode(DecompositionMode mode) + => new ConvexDecompositionOptions(this) { DecompositionMode = mode }; + + public ConvexDecompositionOptions WithHullApproximation(bool enable = true) + => new ConvexDecompositionOptions(this) { HullApproximation = enable }; + + public ConvexDecompositionOptions WithOpenCLAcceleration(bool enable = true) + => new ConvexDecompositionOptions(this) { OpenCLAcceleration = enable }; + + public ConvexDecompositionOptions WithMaxHulls(uint maxHulls) + => new ConvexDecompositionOptions(this) { MaxHulls = maxHulls }; + + public ConvexDecompositionOptions WithProjectHullVertices(bool enable = true) + => new ConvexDecompositionOptions(this) { ProjectHullVertices = enable }; + } +} diff --git a/VHacdSharp/ConvexHull.cs b/VHacdSharp/ConvexHull.cs index b123830..77ea6ec 100644 --- a/VHacdSharp/ConvexHull.cs +++ b/VHacdSharp/ConvexHull.cs @@ -2,11 +2,19 @@ namespace VHacdSharp { - public class ConvexHull + public readonly struct ConvexHull { - public IReadOnlyList Points { get; internal set; } - public IReadOnlyList Triangles { get; internal set; } - public double Volume { get; internal set; } - public Vector3D Center { get; internal set; } + public IReadOnlyList> Vertices { get; } + public IReadOnlyList Indices { get; } + public TVector3 Center { get; } + public double Volume { get; } + + public ConvexHull(IReadOnlyList> vertices, IReadOnlyList indices, double volume, TVector3 center) + { + Vertices = vertices; + Indices = indices; + Center = center; + Volume = volume; + } } } diff --git a/VHacdSharp/DecompositionMode.cs b/VHacdSharp/DecompositionMode.cs index f044688..e148a43 100644 --- a/VHacdSharp/DecompositionMode.cs +++ b/VHacdSharp/DecompositionMode.cs @@ -2,7 +2,7 @@ { public enum DecompositionMode { - Voxel = 0, - Tetrahedron = 1 + Voxel, + Tetrahedron } } diff --git a/VHacdSharp/TVector3.cs b/VHacdSharp/TVector3.cs new file mode 100644 index 0000000..3946315 --- /dev/null +++ b/VHacdSharp/TVector3.cs @@ -0,0 +1,41 @@ +using System; + +namespace VHacdSharp +{ + public struct TVector3 + where T : unmanaged, IConvertible + { + public T X, Y, Z; + + public TVector3(T x, T y, T z) + => (X, Y, Z) = (x, y, z); + + public override int GetHashCode() + => HashCode.Combine(X, Y, Z); + + public override string ToString() + => $"({X}, {Y}, {Z})"; + + public string ToString(IFormatProvider provider) + => $"({X.ToString(provider)}, {Y.ToString(provider)}, {Z.ToString(provider)})"; + } + + public static class TVectorExtensions + { +#if UNITY_STANDALONE + public static Vector3 ToUnity(this TVector3 vector) => new Vector3 + { + x = vector.X, + y = vector.Y, + z = vector.Z + }; + + public static Vector3Int ToUnity(this TVector3 vector) => new Vector3Int + { + x = vector.X, + y = vector.Y, + z = vector.Z + }; +#endif + } +} diff --git a/VHacdSharp/VHacd.Interop.cs b/VHacdSharp/VHacd.Interop.cs index 002793f..ab50609 100644 --- a/VHacdSharp/VHacd.Interop.cs +++ b/VHacdSharp/VHacd.Interop.cs @@ -6,7 +6,7 @@ namespace VHacdSharp { partial class VHacd { - const string DllName = "libvhacd"; + const string DllName = "libVHACD"; [UnmanagedFunctionPointer(Cdecl)] delegate void UserLoggerDelegate(IntPtr gcHandle, IntPtr message); @@ -84,26 +84,23 @@ struct UnmanagedComputeParameters public uint m_maxConvexHulls; public bool m_projectHullVertices; - public static explicit operator UnmanagedComputeParameters(ComputeParameters parameters) + public static UnmanagedComputeParameters FromDecompositionOptions(ConvexDecompositionOptions options) => new UnmanagedComputeParameters { - return new UnmanagedComputeParameters - { - m_resolution = parameters.Resolution, - m_concavity = parameters.Concavity, - m_planeDownsampling = parameters.PlaneDownsampling, - m_convexhullDownsampling = parameters.HullDownsampling, - m_alpha = parameters.Alpha, - m_beta = parameters.Beta, - m_pca = parameters.PCA ? 1u : 0u, - m_mode = (uint)parameters.DecompositionMode, - m_maxNumVerticesPerCH = parameters.MaxHullVertices, - m_minVolumePerCH = parameters.MinHullVolume, - m_convexhullApproximation = parameters.HullApproximation ? 1u : 0u, - m_oclAcceleration = parameters.OpenCLAcceleration ? 1u : 0u, - m_maxConvexHulls = parameters.MaxHulls, - m_projectHullVertices = parameters.ProjectHullVertices - }; - } + m_resolution = options.Resolution, + m_concavity = options.Concavity, + m_planeDownsampling = options.PlaneDownsampling, + m_convexhullDownsampling = options.HullDownsampling, + m_alpha = options.Alpha, + m_beta = options.Beta, + m_pca = options.PrincipalComponentAnalysis ? 1u : 0u, + m_mode = (uint)options.DecompositionMode, + m_maxNumVerticesPerCH = options.MaxHullVertices, + m_minVolumePerCH = options.MinHullVolume, + m_convexhullApproximation = options.HullApproximation ? 1u : 0u, + m_oclAcceleration = options.OpenCLAcceleration ? 1u : 0u, + m_maxConvexHulls = options.MaxHulls, + m_projectHullVertices = options.ProjectHullVertices + }; } } } diff --git a/VHacdSharp/VHacd.cs b/VHacdSharp/VHacd.cs index 096b079..79cad8b 100644 --- a/VHacdSharp/VHacd.cs +++ b/VHacdSharp/VHacd.cs @@ -5,19 +5,17 @@ namespace VHacdSharp { - public delegate void VHacdLogDelegate(string message); - - public delegate void VHacdProgressDelegate(double overallProgress, double stageProgress, double operationProgress, string stage, string operation); - public partial class VHacd : IDisposable { + bool isDisposed; + GCHandle gcHandle; readonly Handle handle; - public event VHacdLogDelegate OnLog; + public event Action OnLog; - public event VHacdProgressDelegate OnProgressReported; + public event Action OnProgressReported; public VHacd() { @@ -27,93 +25,61 @@ public VHacd() public void Dispose() { + if (isDisposed) + return; + if (gcHandle.IsAllocated) gcHandle.Free(); + VHACD_Cancel(handle); handle.Dispose(); + isDisposed = true; } - public Task ComputeAsync(double[] points, uint[] triangles, ComputeParameters parameters, CancellationToken cancellationToken = default) - { - return ComputeAsync(new ArraySegment(points), new ArraySegment(triangles), parameters, cancellationToken); - } - - public Task ComputeAsync(ArraySegment points, ArraySegment triangles, ComputeParameters parameters, CancellationToken cancellationToken = default) - { - return Task.Run(() => - { - var unmanagedParams = (UnmanagedComputeParameters)parameters; - - // A proper API for OpenCL support isn't exposed yet, - // so to be safe it's forced off for now. - unmanagedParams.m_oclAcceleration = 0u; - - VHACD_GetUserPointers(handle, - out unmanagedParams.m_callback, - out unmanagedParams.m_logger - ); - - // The async Compute() method always returns true, - // so we don't need to bother checking its result. - VHACD_Compute(handle, - ref points .Array[points .Offset], (uint)points .Count / 3, - ref triangles.Array[triangles.Offset], (uint)triangles.Count / 3, - ref unmanagedParams - ); - - // If the token is canceled, we need to notify the library - using (cancellationToken.Register(() => VHACD_Cancel(handle))) - { - // Unfortunately, the V-HACD API lacks proper completion events, - // so we need to resort to a busy-loop, checking the IsReady value. - while (!VHACD_IsReady(handle)) - cancellationToken.ThrowIfCancellationRequested(); - } - - return new ConvexDecomposition - { - ConvexHulls = AllocateConvexHulls(), - CenterOfMass = ComputeCenterOfMass() - }; - }); - } + public async Task ComputeAsync(double[] vertices, uint[] triangles, ConvexDecompositionOptions options, CancellationToken cancellationToken = default) + => await ComputeAsync(new ArraySegment(vertices), new ArraySegment(triangles), options, cancellationToken); - public ConvexDecomposition Compute(double[] points, uint[] triangles, ComputeParameters parameters) + public async Task ComputeAsync(ArraySegment vertices, ArraySegment triangles, ConvexDecompositionOptions options, CancellationToken cancellationToken = default) { - return Compute(new ArraySegment(points), new ArraySegment(triangles), parameters); - } + ThrowIfDisposed(); - public ConvexDecomposition Compute(ArraySegment points, ArraySegment triangles, ComputeParameters parameters) - { - var unmanagedParams = (UnmanagedComputeParameters)parameters; + // Convert decomposition options to unmanaged representation + var parameters = UnmanagedComputeParameters.FromDecompositionOptions(options); // A proper API for OpenCL support isn't exposed yet, // so to be safe it's forced off for now. - unmanagedParams.m_oclAcceleration = 0u; + parameters.m_oclAcceleration = 0u; VHACD_GetUserPointers(handle, - out unmanagedParams.m_callback, - out unmanagedParams.m_logger + out parameters.m_callback, + out parameters.m_logger ); // The async Compute() method always returns true, // so we don't need to bother checking its result. - // (we use the async version unconditionally) VHACD_Compute(handle, - ref points .Array[points .Offset], (uint)points .Count / 3, + ref vertices .Array[vertices .Offset], (uint)vertices .Count / 3, ref triangles.Array[triangles.Offset], (uint)triangles.Count / 3, - ref unmanagedParams + ref parameters ); - // Unfortunately, the V-HACD API lacks proper completion events, - // so we need to resort to a busy-loop, checking the IsReady value. - while (!VHACD_IsReady(handle)) ; - - return new ConvexDecomposition + // If the token is cancelled, we need to notify the library + using (cancellationToken.Register(() => VHACD_Cancel(handle))) { - ConvexHulls = AllocateConvexHulls(), - CenterOfMass = ComputeCenterOfMass() - }; + // Unfortunately, the V-HACD API lacks proper completion events, + // so we need to resort to a busy-loop, checking the IsReady value. + while (!VHACD_IsReady(handle)) + { + ThrowIfDisposed(); + cancellationToken.ThrowIfCancellationRequested(); + await Task.Yield(); + } + } + + return new ConvexDecomposition( + convexHulls: AllocateConvexHulls(), + centerOfMass: ComputeCenterOfMass() + ); } ConvexHull[] AllocateConvexHulls() @@ -125,52 +91,52 @@ ConvexHull[] AllocateConvexHulls() { VHACD_GetConvexHull(handle, i, out UnmanagedConvexHull ch); - var points = new double[ch.m_nPoints * 3]; + var vertices = new TVector3[ch.m_nPoints]; var triangles = new uint[ch.m_nTriangles * 3]; unsafe { - for (uint j = 0; j < points.LongLength; j++) - points[j] = ch.m_points[j]; + for (uint j = 0; j < vertices.LongLength; j++) + { + vertices[j] = new TVector3( + ch.m_points[3 * j + 0], + ch.m_points[3 * j + 1], + ch.m_points[3 * j + 2] + ); + } for (uint j = 0; j < triangles.LongLength; j++) triangles[j] = ch.m_triangles[j]; - hulls[i] = new ConvexHull - { - Points = points, - Triangles = triangles, - Volume = ch.m_volume, - Center = new Vector3D - { - X = ch.m_center[0], - Y = ch.m_center[1], - Z = ch.m_center[2] - } - }; + hulls[i] = new ConvexHull( + vertices, + triangles, + ch.m_volume, + new TVector3( + ch.m_center[0], + ch.m_center[1], + ch.m_center[2] + ) + ); } } return hulls; } - unsafe Vector3D ComputeCenterOfMass() + unsafe TVector3 ComputeCenterOfMass() { double* values = stackalloc double[3]; VHACD_ComputeCenterOfMass(handle, out values[0]); - return new Vector3D { X = values[0], Y = values[1], Z = values[2] }; + return new TVector3(values[0], values[1], values[2]); } - static void UserLogger(IntPtr gcHandle, IntPtr message) - { - var vhacd = (VHacd)GCHandle.FromIntPtr(gcHandle).Target; - vhacd.OnLog?.Invoke(Marshal.PtrToStringAnsi(message)); - } + void OnLogCallback(IntPtr message) + => OnLog?.Invoke(Marshal.PtrToStringAnsi(message)); - static void UserCallback(IntPtr gcHandle, double overallProgress, double stageProgress, double operationProgress, IntPtr stage, IntPtr operation) + void OnProgressCallback(double overallProgress, double stageProgress, double operationProgress, IntPtr stage, IntPtr operation) { - var vhacd = (VHacd)GCHandle.FromIntPtr(gcHandle).Target; - vhacd.OnProgressReported?.Invoke( + OnProgressReported?.Invoke( overallProgress, stageProgress, operationProgress, @@ -178,5 +144,23 @@ static void UserCallback(IntPtr gcHandle, double overallProgress, double stagePr Marshal.PtrToStringAnsi(operation) ); } + + void ThrowIfDisposed() + { + if (isDisposed) + throw new ObjectDisposedException(nameof(VHacd)); + } + + static void UserLogger(IntPtr gcHandle, IntPtr message) + { + if (GCHandle.FromIntPtr(gcHandle).Target is VHacd instance) + instance.OnLogCallback(message); + } + + static void UserCallback(IntPtr gcHandle, double overallProgress, double stageProgress, double operationProgress, IntPtr stage, IntPtr operation) + { + if (GCHandle.FromIntPtr(gcHandle).Target is VHacd instance) + instance.OnProgressCallback(overallProgress, stageProgress, operationProgress, stage, operation); + } } } diff --git a/VHacdSharp/VHacdSharp.csproj b/VHacdSharp/VHacdSharp.csproj index 941652b..99035c0 100644 --- a/VHacdSharp/VHacdSharp.csproj +++ b/VHacdSharp/VHacdSharp.csproj @@ -1,9 +1,13 @@ - - - - netstandard2.0 - True - latest - - - + + + + netstandard2.0 + True + latest + + + + + + + diff --git a/VHacdSharp/Vector3D.cs b/VHacdSharp/Vector3D.cs deleted file mode 100644 index 6c074c6..0000000 --- a/VHacdSharp/Vector3D.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace VHacdSharp -{ - public struct Vector3D - { - public double X { get; set; } - public double Y { get; set; } - public double Z { get; set; } - } -}