diff --git a/PxCs.Tests/PxPhoenixTest.cs b/PxCs.Tests/PxPhoenixTest.cs index 4236c0d..edaa4d1 100644 --- a/PxCs.Tests/PxPhoenixTest.cs +++ b/PxCs.Tests/PxPhoenixTest.cs @@ -13,7 +13,7 @@ public abstract class PxPhoenixTest public PxPhoenixTest() { - string? dir = Environment.GetEnvironmentVariable("GOTHIC1_ASSET_DIR"); + string? dir = "/mnt/games/Gothic/Gothic108kDE/"; // Environment.GetEnvironmentVariable("GOTHIC1_ASSET_DIR"); Assert.True(dir != null, "Please start test with dotnet test --environment GOTHIC1_ASSET_DIR=... or configure ./.runsettings to be used by your IDE runner."); Assert.True(Directory.Exists(dir), "Path not exists"); diff --git a/PxCs.Tests/PxWorldTest.cs b/PxCs.Tests/PxWorldTest.cs index 5627abf..5e53f4a 100644 --- a/PxCs.Tests/PxWorldTest.cs +++ b/PxCs.Tests/PxWorldTest.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Numerics; using Microsoft.VisualBasic.CompilerServices; using PxCs.Data.Vob; using PxCs.Interface; @@ -89,5 +90,35 @@ public void Test_load_Vobs() PxWorld.pxWorldDestroy(worldPtr); DestroyVfs(vfsPtr); } + + [Fact] + public void Test_load_BspTree() + { + var vfsPtr = LoadVfs("Data/worlds.VDF"); + var worldPtr = PxWorld.pxWorldLoadFromVfs(vfsPtr, "world.zen"); + var bspPtr = PxWorld.pxWorldGetBspTree(worldPtr); + + var mode = PxBspTree.pxBspGetMode(bspPtr); + Assert.Equal(PxBspTree.PxBspTreeMode.PxBspOutdoor, mode); + + var polygonIndices = PxBspTree.GetPolygonIndices(bspPtr); + Assert.Equal(480135, polygonIndices.Length); + Assert.Equal(102u, polygonIndices[150]); + + var sectors = PxBspTree.GetSectors(bspPtr); + Assert.Equal(299, sectors.Length); + Assert.Equal("WALD11", sectors[0].name); + Assert.Equal(9, sectors[0].nodeIndices.Length); + Assert.Equal(24, sectors[0].portalPolygonIndices.Length); + + var nodes = PxBspTree.GetNodes(bspPtr); + Assert.Equal(6644, nodes.Length); + Assert.Equal(new Vector4(1, 0, 0, 18540.0156f), nodes[0].plane); + Assert.Equal(1, nodes[0].frontNodeIndex); + Assert.Equal(1599, nodes[0].backNodeIndex); + Assert.Equal(-1, nodes[0].parentNodeIndex); + Assert.Equal(0u, nodes[0].polygonIndex); + Assert.Equal(0u, nodes[0].polygonCount); + } } } diff --git a/PxCs/Data/BspTree/PxBspNodeData.cs b/PxCs/Data/BspTree/PxBspNodeData.cs new file mode 100644 index 0000000..849570e --- /dev/null +++ b/PxCs/Data/BspTree/PxBspNodeData.cs @@ -0,0 +1,16 @@ +using System.Numerics; +using PxCs.Data.Struct; + +namespace PxCs.Data.BspTree +{ + public class PxBspNodeData + { + public Vector4 plane; + public PxAABBData bbox; + public uint polygonIndex; + public uint polygonCount; + public int frontNodeIndex; + public int backNodeIndex; + public int parentNodeIndex; + } +} \ No newline at end of file diff --git a/PxCs/Data/BspTree/PxBspSectorData.cs b/PxCs/Data/BspTree/PxBspSectorData.cs new file mode 100644 index 0000000..cfa6779 --- /dev/null +++ b/PxCs/Data/BspTree/PxBspSectorData.cs @@ -0,0 +1,12 @@ +using System; + +namespace PxCs.Data.BspTree +{ + [Serializable] + public class PxBspSectorData + { + public string name; + public uint[] nodeIndices; + public uint[] portalPolygonIndices; + } +} \ No newline at end of file diff --git a/PxCs/Interface/PxBspTree.cs b/PxCs/Interface/PxBspTree.cs new file mode 100644 index 0000000..0508e43 --- /dev/null +++ b/PxCs/Interface/PxBspTree.cs @@ -0,0 +1,110 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using PxCs.Data.BspTree; +using PxCs.Data.Struct; +using PxCs.Extensions; + +namespace PxCs.Interface +{ + public class PxBspTree + { + private const string DLLNAME = PxPhoenix.DLLNAME; + + public enum PxBspTreeMode + { + PxBspIndoor = 0, + PxBspOutdoor = 1, + } + + [DllImport(DLLNAME)] public static extern PxBspTreeMode pxBspGetMode(IntPtr bsp); + [DllImport(DLLNAME)] public static extern ulong pxBspGetPolygonIndicesLength(IntPtr bsp); + [DllImport(DLLNAME)] public static extern IntPtr pxBspGetPolygonIndices(IntPtr bsp, out ulong size); + [DllImport(DLLNAME)] public static extern ulong pxBspGetLeafPolygonIndicesLength(IntPtr bsp); + [DllImport(DLLNAME)] public static extern IntPtr pxBspGetLeafPolygonIndices(IntPtr bsp, out ulong size); + [DllImport(DLLNAME)] public static extern ulong pxBspGetPortalPolygonIndicesLength(IntPtr bsp); + [DllImport(DLLNAME)] public static extern IntPtr pxBspGetPortalPolygonIndices(IntPtr bsp, out ulong size); + [DllImport(DLLNAME)] public static extern ulong pxBspGetLeafNodeIndicesLength(IntPtr bsp); + [DllImport(DLLNAME)] public static extern IntPtr pxBspGetLeafNodeIndices(IntPtr bsp, out ulong size); + [DllImport(DLLNAME)] public static extern ulong pxBspGetLightPointsLength(IntPtr bsp); + [DllImport(DLLNAME)] public static extern Vector3 pxBspGetLightPoint(IntPtr bsp, ulong idx); + [DllImport(DLLNAME)] public static extern ulong pxBspGetSectorsLength(IntPtr bsp); + [DllImport(DLLNAME)] public static extern void pxBspGetSector(IntPtr bsp, ulong idx, out IntPtr name, out IntPtr nodeIndices, + out ulong nodeIndicesLength, out IntPtr portalPolygonIndices, out ulong portalPolygonIndicesLength); + [DllImport(DLLNAME)] public static extern ulong pxBspGetNodesLength(IntPtr bsp); + [DllImport(DLLNAME)] public static extern void pxBspGetNode(IntPtr bsp, ulong idx, out Vector4 plane, out PxAABBData bbox, + out uint polygonIndex, out uint polygonCount, out int frontNodeIndex, out int backNodeIndex, + out int parentNodeIndex); + + + public static uint[] GetPolygonIndices(IntPtr bsp) + { + return pxBspGetPolygonIndices(bsp, out ulong size).MarshalAsArray((uint)size); + } + + public static uint[] GetLeafPolygonIndices(IntPtr bsp) + { + return pxBspGetLeafPolygonIndices(bsp, out ulong size).MarshalAsArray((uint)size); + } + + public static uint[] GetPortalPolygonIndices(IntPtr bsp) + { + return pxBspGetPortalPolygonIndices(bsp, out ulong size).MarshalAsArray((uint)size); + } + + public static ulong[] GetLeadNodeIndices(IntPtr bsp) + { + return pxBspGetLeafNodeIndices(bsp, out ulong size).MarshalAsArray((uint)size); + } + + public static Vector3[] GetLightPoints(IntPtr bsp) + { + var length = pxBspGetLightPointsLength(bsp); + var array = new Vector3[length]; + + for (var i = 0u; i < length; ++i) + { + array[i] = pxBspGetLightPoint(bsp, i); + } + + return array; + } + + public static PxBspSectorData[] GetSectors(IntPtr bsp) + { + var length = pxBspGetSectorsLength(bsp); + var array = new PxBspSectorData[length]; + + for (var i = 0u; i < length; ++i) + { + pxBspGetSector(bsp, i, out IntPtr name, out IntPtr nodeIndices, + out ulong nodeIndicesLength, out IntPtr portalPolygonIndices, out ulong portalPolygonIndicesLength); + + array[i] = new PxBspSectorData + { + name = name.MarshalAsString(), + nodeIndices = nodeIndices.MarshalAsArray((uint)nodeIndicesLength), + portalPolygonIndices = portalPolygonIndices.MarshalAsArray((uint)portalPolygonIndicesLength) + }; + } + + return array; + } + + public static PxBspNodeData[] GetNodes(IntPtr bsp) + { + var length = pxBspGetNodesLength(bsp); + var array = new PxBspNodeData[length]; + + for (var i = 0u; i < length; ++i) + { + array[i] = new PxBspNodeData(); + pxBspGetNode(bsp, i, out array[i].plane, out array[i].bbox, out array[i].polygonIndex, + out array[i].polygonCount, out array[i].frontNodeIndex, out array[i].backNodeIndex, + out array[i].parentNodeIndex); + } + + return array; + } + } +} \ No newline at end of file diff --git a/PxCs/Interface/PxWorld.cs b/PxCs/Interface/PxWorld.cs index 0b5b2a2..87087ed 100644 --- a/PxCs/Interface/PxWorld.cs +++ b/PxCs/Interface/PxWorld.cs @@ -158,6 +158,7 @@ public enum PxVobTriggerBatchMode [DllImport(DLLNAME)] public static extern IntPtr pxWorldLoadFromVfs(IntPtr vfs, string name); [DllImport(DLLNAME)] public static extern void pxWorldDestroy(IntPtr world); + [DllImport(DLLNAME)] public static extern IntPtr pxWorldGetBspTree(IntPtr world); [DllImport(DLLNAME)] public static extern IntPtr pxWorldGetMesh(IntPtr world); [DllImport(DLLNAME)] public static extern uint pxWorldGetWayPointCount(IntPtr world); [DllImport(DLLNAME)]