483 lines
22 KiB
C#
483 lines
22 KiB
C#
using System.Diagnostics;
|
|
using System.Runtime.InteropServices;
|
|
|
|
const int WarmupIterations = 8;
|
|
const int MeasureIterations = 64;
|
|
|
|
var output = args.Length > 0
|
|
? args[0]
|
|
: Path.GetFullPath(Path.Combine("experiments", "generated", "realtimecsg-cpp-perf-latest.jsonl"));
|
|
|
|
Directory.CreateDirectory(Path.GetDirectoryName(output)!);
|
|
Native.RegisterNoopUnityMethods();
|
|
var cases = new PerfCase[]
|
|
{
|
|
new("single_center_cut", 2, SingleCenterCut),
|
|
new("room_grid_8x8_doors", 192, RoomGrid8x8Doors),
|
|
new("rotated_cut_stack_64", 65, RotatedCutStack64),
|
|
new("common_box_chain_64", 64, CommonBoxChain64),
|
|
new("distant_cutters_512", 513, DistantCutters512),
|
|
};
|
|
|
|
using var writer = new StreamWriter(output, false);
|
|
foreach (var perfCase in cases)
|
|
{
|
|
writer.WriteLine(RunCase(perfCase));
|
|
}
|
|
|
|
static string RunCase(PerfCase perfCase)
|
|
{
|
|
var timings = new List<long>(MeasureIterations);
|
|
var triangles = 0;
|
|
var vertices = 0;
|
|
var meshDescriptions = 0;
|
|
|
|
Native.ClearAllNodes();
|
|
var tree = perfCase.Build();
|
|
if (tree == 0)
|
|
throw new InvalidOperationException($"Failed to build native RealtimeCSG tree for {perfCase.Name}");
|
|
|
|
Native.RebuildAll();
|
|
if (Native.GetBrushCount() != perfCase.Brushes)
|
|
throw new InvalidOperationException($"Native brush count mismatch for {perfCase.Name}: expected {perfCase.Brushes}, got {Native.GetBrushCount()}, nodes={Native.GetNodeCount()}, trees={Native.GetTreeCount()}, brushMeshes={Native.GetBrushMeshCount()}");
|
|
for (var i = 0; i < WarmupIterations; i++)
|
|
MeasureOnce(tree, out triangles, out vertices, out meshDescriptions);
|
|
if (meshDescriptions == 0)
|
|
throw new InvalidOperationException($"Native generated no mesh descriptions for {perfCase.Name}: nodes={Native.GetNodeCount()}, brushes={Native.GetBrushCount()}, trees={Native.GetTreeCount()}, brushMeshes={Native.GetBrushMeshCount()}, treeBrushes={Native.GetNumberOfBrushesInTree(tree)}, treeChildren={Native.GetChildNodeCount(tree)}, treeDirty={Native.IsNodeDirty(tree)}");
|
|
|
|
for (var i = 0; i < MeasureIterations; i++)
|
|
{
|
|
var stopwatch = Stopwatch.StartNew();
|
|
MeasureOnce(tree, out triangles, out vertices, out meshDescriptions);
|
|
stopwatch.Stop();
|
|
timings.Add(stopwatch.ElapsedTicks);
|
|
}
|
|
Native.ClearAllNodes();
|
|
|
|
timings.Sort();
|
|
var mean = timings.Sum() / timings.Count;
|
|
return
|
|
$"{{\"kernel\":\"realtime_csg_cpp\",\"scenario\":\"{perfCase.Name}\",\"brushes\":{perfCase.Brushes},\"iterations\":{MeasureIterations},\"warmup_iterations\":{WarmupIterations},\"mean_ns\":{TicksToNs(mean)},\"min_ns\":{TicksToNs(timings[0])},\"p50_ns\":{TicksToNs(timings[(timings.Count - 1) * 50 / 100])},\"p95_ns\":{TicksToNs(timings[(timings.Count - 1) * 95 / 100])},\"max_ns\":{TicksToNs(timings[^1])},\"triangles\":{triangles},\"vertices\":{vertices},\"mesh_descriptions\":{meshDescriptions}}}";
|
|
}
|
|
|
|
static long TicksToNs(long ticks) => (long)(ticks * (1_000_000_000.0 / Stopwatch.Frequency));
|
|
|
|
static void MeasureOnce(int tree, out int triangles, out int vertices, out int meshDescriptions)
|
|
{
|
|
Native.RebuildAll();
|
|
Native.UpdateAllTreeMeshes();
|
|
|
|
var meshTypes = new[] { MeshQuery.RenderableAll };
|
|
var meshTypesHandle = GCHandle.Alloc(meshTypes, GCHandleType.Pinned);
|
|
var generated = Native.GenerateMeshDescriptions(tree, meshTypes.Length, meshTypesHandle.AddrOfPinnedObject(), VertexChannelFlags.All, out meshDescriptions);
|
|
if (!generated || meshDescriptions == 0)
|
|
{
|
|
meshTypesHandle.Free();
|
|
triangles = 0;
|
|
vertices = 0;
|
|
return;
|
|
}
|
|
meshTypesHandle.Free();
|
|
|
|
var descriptions = new GeneratedMeshDescription[meshDescriptions];
|
|
var descriptionsHandle = GCHandle.Alloc(descriptions, GCHandleType.Pinned);
|
|
var ok = Native.GetMeshDescriptions(tree, meshDescriptions, descriptionsHandle.AddrOfPinnedObject());
|
|
descriptionsHandle.Free();
|
|
if (!ok)
|
|
{
|
|
triangles = 0;
|
|
vertices = 0;
|
|
return;
|
|
}
|
|
|
|
triangles = 0;
|
|
vertices = 0;
|
|
foreach (var description in descriptions)
|
|
{
|
|
if (description.VertexCount <= 0 || description.IndexCount <= 0)
|
|
continue;
|
|
|
|
var indices = new int[description.IndexCount];
|
|
var positions = new Vec3[description.VertexCount];
|
|
var indexHandle = GCHandle.Alloc(indices, GCHandleType.Pinned);
|
|
var positionHandle = GCHandle.Alloc(positions, GCHandleType.Pinned);
|
|
ok = Native.GetGeneratedMesh(
|
|
tree,
|
|
description.MeshQueryIndex,
|
|
description.SubMeshQueryIndex,
|
|
description.IndexCount,
|
|
indexHandle.AddrOfPinnedObject(),
|
|
description.VertexCount,
|
|
positionHandle.AddrOfPinnedObject(),
|
|
IntPtr.Zero,
|
|
IntPtr.Zero,
|
|
IntPtr.Zero,
|
|
out _,
|
|
out _);
|
|
positionHandle.Free();
|
|
indexHandle.Free();
|
|
if (ok)
|
|
{
|
|
triangles += description.IndexCount / 3;
|
|
vertices += description.VertexCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int SingleCenterCut()
|
|
{
|
|
return Tree(
|
|
Brush("source", Vec3.Zero, Vec3.Splat(4), Mat4.Identity, Operation.Additive),
|
|
Brush("void", Vec3.Zero, Vec3.Splat(2), Mat4.Identity, Operation.Subtractive));
|
|
}
|
|
|
|
static int RoomGrid8x8Doors()
|
|
{
|
|
var nodes = new List<int>();
|
|
const float cell = 6.0f;
|
|
for (var y = 0; y < 8; y++)
|
|
for (var x = 0; x < 8; x++)
|
|
{
|
|
var center = new Vec3((x - 3.5f) * cell, (y - 3.5f) * cell, 0);
|
|
nodes.Add(Brush("floor", center + new Vec3(0, 0, -0.1f), new Vec3(5.6f, 5.6f, 0.2f), Mat4.Identity, Operation.Additive));
|
|
nodes.Add(Brush("north_wall", center + new Vec3(0, 2.8f, 1.5f), new Vec3(5.6f, 0.25f, 3.0f), Mat4.Identity, Operation.Additive));
|
|
nodes.Add(Brush("door", center + new Vec3(0, 2.8f, 1.0f), new Vec3(1.2f, 0.5f, 2.0f), Mat4.Identity, Operation.Subtractive));
|
|
}
|
|
return Tree(nodes.ToArray());
|
|
}
|
|
|
|
static int RotatedCutStack64()
|
|
{
|
|
var nodes = new List<int> { Brush("slab", Vec3.Zero, new Vec3(32, 32, 4), Mat4.Identity, Operation.Additive) };
|
|
for (var index = 0; index < 64; index++)
|
|
{
|
|
var angle = index * 0.173f;
|
|
var radius = 11.0f + (index % 7) * 0.35f;
|
|
var center = new Vec3(MathF.Cos(angle) * radius, MathF.Sin(angle) * radius, 0);
|
|
nodes.Add(Brush("rotated_void", center, new Vec3(1.0f + (index % 3) * 0.3f, 8.0f, 5.0f), Mat4.RotationZ(angle), Operation.Subtractive));
|
|
}
|
|
return Tree(nodes.ToArray());
|
|
}
|
|
|
|
static int CommonBoxChain64()
|
|
{
|
|
var nodes = new List<int> { Brush("source", Vec3.Zero, new Vec3(32, 16, 8), Mat4.Identity, Operation.Additive) };
|
|
for (var index = 1; index < 64; index++)
|
|
{
|
|
var t = index / 63.0f;
|
|
var center = new Vec3((t - 0.5f) * 6.0f, MathF.Sin(t * MathF.Tau) * 1.5f, 0);
|
|
nodes.Add(Brush("common", center, new Vec3(30.0f - t * 8.0f, 14.0f, 7.0f), Mat4.RotationZ(t * 0.2f), Operation.Intersecting));
|
|
}
|
|
return Tree(nodes.ToArray());
|
|
}
|
|
|
|
static int DistantCutters512()
|
|
{
|
|
var nodes = new List<int> { Brush("source", Vec3.Zero, Vec3.Splat(8), Mat4.Identity, Operation.Additive) };
|
|
for (var index = 0; index < 512; index++)
|
|
{
|
|
var row = index / 32;
|
|
var col = index % 32;
|
|
nodes.Add(Brush("far_void", new Vec3(1000.0f + col * 4.0f, 1000.0f + row * 4.0f, 0), Vec3.Splat(1), Mat4.Identity, Operation.Subtractive));
|
|
}
|
|
return Tree(nodes.ToArray());
|
|
}
|
|
|
|
static int Tree(params int[] children)
|
|
{
|
|
if (!Native.GenerateTree(0, out var tree))
|
|
return 0;
|
|
Native.SetTreeEnabled(tree, true);
|
|
if (!SetChildNodes(tree, children))
|
|
throw new InvalidOperationException($"SetChildNodes failed for tree {tree} with {children.Length} children");
|
|
foreach (var child in children)
|
|
Native.SetDirty(child);
|
|
Native.SetDirty(tree);
|
|
return tree;
|
|
}
|
|
|
|
static int Brush(string name, Vec3 center, Vec3 size, Mat4 rotation, Operation operation)
|
|
{
|
|
var mesh = Cube(size);
|
|
var meshId = CreateBrushMesh(mesh, name.GetHashCode());
|
|
if (meshId == 0 || !Native.GenerateBrush(name.GetHashCode(), out var brush))
|
|
throw new InvalidOperationException($"Failed to create brush {name}: meshId={meshId}");
|
|
if (!UpdateBrushMesh(meshId, mesh))
|
|
throw new InvalidOperationException($"UpdateBrushMesh failed for brush {name}: meshId={meshId}");
|
|
if (!Native.IsBrushMeshIDValid(meshId))
|
|
throw new InvalidOperationException($"Native rejected brush mesh {name}/{meshId}");
|
|
var transform = rotation.WithTranslation(center);
|
|
if (!Native.SetNodeLocalTransformation(brush, ref transform))
|
|
throw new InvalidOperationException($"SetNodeLocalTransformation failed for brush {name}/{brush}");
|
|
if (!Native.SetBrushMeshID(brush, meshId))
|
|
throw new InvalidOperationException($"SetBrushMeshID failed for brush {name}/{brush}, mesh={meshId}");
|
|
if (!Native.SetBrushOperationType(brush, operation))
|
|
throw new InvalidOperationException($"SetBrushOperationType failed for brush {name}/{brush}, op={operation}");
|
|
if (Native.GetBrushMeshID(brush) != meshId)
|
|
throw new InvalidOperationException($"Brush {name}/{brush} did not retain mesh id {meshId}");
|
|
Native.SetDirty(brush);
|
|
return brush;
|
|
}
|
|
|
|
static bool SetChildNodes(int node, int[] children)
|
|
{
|
|
var childNodes = children.Select(id => new CsgTreeNode(id)).ToArray();
|
|
var handle = GCHandle.Alloc(childNodes, GCHandleType.Pinned);
|
|
var ok = Native.SetChildNodes(node, childNodes.Length, handle.AddrOfPinnedObject());
|
|
handle.Free();
|
|
return ok;
|
|
}
|
|
|
|
static int CreateBrushMesh(BrushMesh mesh, int userId)
|
|
{
|
|
var vh = GCHandle.Alloc(mesh.Vertices, GCHandleType.Pinned);
|
|
var eh = GCHandle.Alloc(mesh.HalfEdges, GCHandleType.Pinned);
|
|
var ph = GCHandle.Alloc(mesh.Polygons, GCHandleType.Pinned);
|
|
var id = Native.CreateBrushMesh(userId, mesh.Vertices.Length, vh.AddrOfPinnedObject(), mesh.HalfEdges.Length, eh.AddrOfPinnedObject(), mesh.Polygons.Length, ph.AddrOfPinnedObject());
|
|
ph.Free();
|
|
eh.Free();
|
|
vh.Free();
|
|
return id;
|
|
}
|
|
|
|
static bool UpdateBrushMesh(int meshId, BrushMesh mesh)
|
|
{
|
|
var vh = GCHandle.Alloc(mesh.Vertices, GCHandleType.Pinned);
|
|
var eh = GCHandle.Alloc(mesh.HalfEdges, GCHandleType.Pinned);
|
|
var ph = GCHandle.Alloc(mesh.Polygons, GCHandleType.Pinned);
|
|
var ok = Native.UpdateBrushMesh(meshId, mesh.Vertices.Length, vh.AddrOfPinnedObject(), mesh.HalfEdges.Length, eh.AddrOfPinnedObject(), mesh.Polygons.Length, ph.AddrOfPinnedObject());
|
|
ph.Free();
|
|
eh.Free();
|
|
vh.Free();
|
|
return ok;
|
|
}
|
|
|
|
static BrushMesh Cube(Vec3 size)
|
|
{
|
|
var min = size * -0.5f;
|
|
var max = size * 0.5f;
|
|
var layers = new SurfaceLayers { LayerUsage = LayerUsageFlags.RenderReceiveCastShadows };
|
|
var surfaces = new[]
|
|
{
|
|
new SurfaceDescription { UV0 = new UvMatrix(new Vec4(-1, 0, 0, -min.X), new Vec4(0, 1, 0, min.Y)) },
|
|
new SurfaceDescription { UV0 = new UvMatrix(new Vec4( 1, 0, 0, min.X), new Vec4(0, 1, 0, min.Y)) },
|
|
new SurfaceDescription { UV0 = new UvMatrix(new Vec4( 0, 0, 1, min.Z), new Vec4(0, 1, 0, min.Y)) },
|
|
new SurfaceDescription { UV0 = new UvMatrix(new Vec4( 0, 0, -1, -min.Z), new Vec4(0, 1, 0, min.Y)) },
|
|
new SurfaceDescription { UV0 = new UvMatrix(new Vec4(-1, 0, 0, -min.X), new Vec4(0, 0, -1, -min.Z)) },
|
|
new SurfaceDescription { UV0 = new UvMatrix(new Vec4( 1, 0, 0, min.X), new Vec4(0, 0, -1, -min.Z)) },
|
|
};
|
|
return new BrushMesh(
|
|
new[]
|
|
{
|
|
new Vec3(min.X, min.Y, min.Z), new Vec3(min.X, max.Y, min.Z),
|
|
new Vec3(max.X, max.Y, min.Z), new Vec3(max.X, min.Y, min.Z),
|
|
new Vec3(min.X, min.Y, max.Z), new Vec3(max.X, min.Y, max.Z),
|
|
new Vec3(max.X, max.Y, max.Z), new Vec3(min.X, max.Y, max.Z),
|
|
},
|
|
new[]
|
|
{
|
|
new HalfEdge(0,17), new HalfEdge(1,8), new HalfEdge(2,20), new HalfEdge(3,13),
|
|
new HalfEdge(4,10), new HalfEdge(5,19), new HalfEdge(6,15), new HalfEdge(7,22),
|
|
new HalfEdge(0,1), new HalfEdge(4,16), new HalfEdge(7,4), new HalfEdge(1,21),
|
|
new HalfEdge(3,18), new HalfEdge(2,3), new HalfEdge(6,23), new HalfEdge(5,6),
|
|
new HalfEdge(0,9), new HalfEdge(3,0), new HalfEdge(5,12), new HalfEdge(4,5),
|
|
new HalfEdge(1,2), new HalfEdge(7,11), new HalfEdge(6,7), new HalfEdge(2,14),
|
|
},
|
|
new[]
|
|
{
|
|
new Polygon(0,4,0,surfaces[0],layers), new Polygon(4,4,1,surfaces[1],layers),
|
|
new Polygon(8,4,2,surfaces[2],layers), new Polygon(12,4,3,surfaces[3],layers),
|
|
new Polygon(16,4,4,surfaces[4],layers), new Polygon(20,4,5,surfaces[5],layers),
|
|
});
|
|
}
|
|
|
|
record PerfCase(string Name, int Brushes, Func<int> Build);
|
|
record BrushMesh(Vec3[] Vertices, HalfEdge[] HalfEdges, Polygon[] Polygons);
|
|
|
|
enum Operation : byte { Additive = 0, Subtractive = 1, Intersecting = 2 }
|
|
enum VertexChannelFlags : byte { Position = 0, Tangent = 2, Normal = 4, UV0 = 8, All = 14 }
|
|
enum LayerUsageFlags : int
|
|
{
|
|
None = 0,
|
|
Renderable = 1,
|
|
CastShadows = 2,
|
|
ReceiveShadows = 4,
|
|
RenderCastShadows = Renderable | CastShadows,
|
|
RenderReceiveShadows = Renderable | ReceiveShadows,
|
|
RenderReceiveCastShadows = Renderable | CastShadows | ReceiveShadows,
|
|
Collidable = 8,
|
|
Culled = 1 << 23,
|
|
}
|
|
enum LayerParameterIndex : byte { None = 0, LayerParameter1 = 1, LayerParameter2 = 2, LayerParameter3 = 3 }
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
struct Vec2 { public float X, Y; }
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
struct Vec3(float x, float y, float z)
|
|
{
|
|
public float X = x;
|
|
public float Y = y;
|
|
public float Z = z;
|
|
public static Vec3 Zero => new(0, 0, 0);
|
|
public static Vec3 Splat(float value) => new(value, value, value);
|
|
public static Vec3 operator +(Vec3 a, Vec3 b) => new(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
|
|
public static Vec3 operator *(Vec3 a, float s) => new(a.X * s, a.Y * s, a.Z * s);
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
struct Vec4(float x, float y, float z, float w) { public float X = x, Y = y, Z = z, W = w; }
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
struct Mat4
|
|
{
|
|
public float M00, M10, M20, M30;
|
|
public float M01, M11, M21, M31;
|
|
public float M02, M12, M22, M32;
|
|
public float M03, M13, M23, M33;
|
|
public static Mat4 Identity => new() { M00 = 1, M11 = 1, M22 = 1, M33 = 1 };
|
|
public static Mat4 RotationZ(float radians)
|
|
{
|
|
var c = MathF.Cos(radians);
|
|
var s = MathF.Sin(radians);
|
|
return new Mat4 { M00 = c, M10 = s, M01 = -s, M11 = c, M22 = 1, M33 = 1 };
|
|
}
|
|
public Mat4 WithTranslation(Vec3 t)
|
|
{
|
|
var m = this;
|
|
m.M03 = t.X; m.M13 = t.Y; m.M23 = t.Z; m.M33 = 1;
|
|
return m;
|
|
}
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
struct UvMatrix(Vec4 u, Vec4 v) { public Vec4 U = u; public Vec4 V = v; }
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
struct SurfaceDescription
|
|
{
|
|
public uint SmoothingGroup;
|
|
public int SurfaceFlags;
|
|
public UvMatrix UV0;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
struct SurfaceLayers
|
|
{
|
|
public LayerUsageFlags LayerUsage;
|
|
public int LayerParameter1;
|
|
public int LayerParameter2;
|
|
public int LayerParameter3;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
struct Polygon(int firstEdge, int edgeCount, int polygonId, SurfaceDescription surface, SurfaceLayers layers)
|
|
{
|
|
public int FirstEdge = firstEdge;
|
|
public int EdgeCount = edgeCount;
|
|
public int PolygonId = polygonId;
|
|
public SurfaceDescription Surface = surface;
|
|
public SurfaceLayers Layers = layers;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
struct HalfEdge(int vertexIndex, int twinIndex)
|
|
{
|
|
public int VertexIndex = vertexIndex;
|
|
public int TwinIndex = twinIndex;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
struct CsgTreeNode(int nodeId) { public int NodeId = nodeId; }
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
struct MeshQuery
|
|
{
|
|
const int BitShift = 24;
|
|
public uint Layers;
|
|
public uint MaskAndChannels;
|
|
public static MeshQuery RenderableAll => new(LayerUsageFlags.Renderable, LayerUsageFlags.RenderReceiveCastShadows, LayerParameterIndex.LayerParameter1, VertexChannelFlags.All);
|
|
MeshQuery(LayerUsageFlags query, LayerUsageFlags mask, LayerParameterIndex parameterIndex, VertexChannelFlags vertexChannels)
|
|
{
|
|
if (mask == LayerUsageFlags.None)
|
|
mask = query;
|
|
Layers = (uint)query;
|
|
Layers |= (uint)parameterIndex << BitShift;
|
|
MaskAndChannels = (uint)mask | ((uint)vertexChannels << BitShift);
|
|
}
|
|
public int UsedVertexChannels => (int)(MaskAndChannels >> BitShift);
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 8)]
|
|
struct GeneratedMeshDescription
|
|
{
|
|
public MeshQuery MeshQuery;
|
|
public int SurfaceParameter;
|
|
public int MeshQueryIndex;
|
|
public int SubMeshQueryIndex;
|
|
public long GeometryHashValue;
|
|
public long SurfaceHashValue;
|
|
public int VertexCount;
|
|
public int IndexCount;
|
|
}
|
|
|
|
static partial class Native
|
|
{
|
|
const string Dll = "RealtimeCSG[1_559]";
|
|
static readonly StringLog Log = (message, userId) => Console.Error.WriteLine($"native[{userId}]: {message}");
|
|
static readonly ReturnString NameForUserId = _ => "<native-bridge>";
|
|
|
|
public static void RegisterNoopUnityMethods()
|
|
{
|
|
var methods = new UnityMethods
|
|
{
|
|
DebugLog = Log,
|
|
DebugLogError = Log,
|
|
DebugLogWarning = Log,
|
|
NameForUserID = NameForUserId,
|
|
};
|
|
RegisterMethods(ref methods);
|
|
}
|
|
|
|
public delegate void StringLog([MarshalAs(UnmanagedType.LPStr)] string text, int uniqueObjectID);
|
|
[return: MarshalAs(UnmanagedType.LPStr)]
|
|
public delegate string ReturnString(int uniqueObjectID);
|
|
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 4)]
|
|
struct UnityMethods
|
|
{
|
|
public StringLog DebugLog;
|
|
public StringLog DebugLogError;
|
|
public StringLog DebugLogWarning;
|
|
public ReturnString NameForUserID;
|
|
}
|
|
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] static extern void RegisterMethods(ref UnityMethods unityMethods);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void ClearAllNodes();
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool UpdateAllTreeMeshes();
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern void RebuildAll();
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int GetNodeCount();
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int GetBrushCount();
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int GetTreeCount();
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int GetBrushMeshCount();
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool GenerateTree(int userID, out int generatedTreeNodeID);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int GetNumberOfBrushesInTree(int nodeID);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetDirty(int nodeID);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool IsNodeDirty(int nodeID);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int GetChildNodeCount(int nodeID);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetTreeEnabled(int modelNodeID, bool isEnabled);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool GenerateBrush(int userID, out int generatedNodeID);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool GenerateBranch(int userID, out int generatedOperationNodeID);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetBranchOperationType(int branchNodeID, Operation operation);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetBrushOperationType(int brushNodeID, Operation operation);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetBrushMeshID(int brushNodeID, int brushMeshID);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int GetBrushMeshID(int brushNodeID);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetNodeLocalTransformation(int brushNodeID, ref Mat4 brushToTreeSpace);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetChildNodes(int nodeID, int childCount, IntPtr childrenNodeIDs);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int CreateBrushMesh(int userID, int vertexCount, IntPtr vertices, int halfEdgeCount, IntPtr halfEdges, int polygonCount, IntPtr polygons);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool UpdateBrushMesh(int brushMeshIndex, int vertexCount, IntPtr vertices, int halfEdgeCount, IntPtr halfEdges, int polygonCount, IntPtr polygons);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool IsBrushMeshIDValid(int brushMeshIndex);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool GenerateMeshDescriptions(int treeNodeID, int meshTypeCount, IntPtr meshTypes, VertexChannelFlags vertexChannelMask, out int meshDescriptionCount);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetMeshDescriptions(int treeNodeID, int meshDescriptionCount, IntPtr meshDescriptions);
|
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetGeneratedMesh(int treeNodeID, int meshIndex, int subMeshIndex, int indexCount, IntPtr indices, int vertexCount, IntPtr positions, IntPtr tangents, IntPtr normals, IntPtr uvs, out Vec3 boundsCenter, out Vec3 boundsSize);
|
|
}
|