diff --git a/include/ppx/tri_mesh.h b/include/ppx/tri_mesh.h index c187443ff..aa08a0ea3 100644 --- a/include/ppx/tri_mesh.h +++ b/include/ppx/tri_mesh.h @@ -82,8 +82,25 @@ class TriMeshOptions TriMeshOptions() {} ~TriMeshOptions() {} // clang-format off - //! Enable/disable indices - TriMeshOptions& Indices(bool value = true) { mEnableIndices = value; return *this; } + //! Enable/disable indices, using UINT32 by default when enabled + TriMeshOptions& Indices(bool value = true) + { + mEnableIndices = value; + if (value && (mIndexType == grfx::INDEX_TYPE_UNDEFINED)) { + mIndexType = grfx::INDEX_TYPE_UINT32; + } + else if (!value) { + mIndexType = grfx::INDEX_TYPE_UNDEFINED; + } + return *this; + } + //! Set the index type; UNDEFINED disables indices, concrete types enable indices + TriMeshOptions& IndexType(grfx::IndexType value) + { + mIndexType = value; + mEnableIndices = (value != grfx::INDEX_TYPE_UNDEFINED); + return *this; + } //! Enable/disable vertex colors TriMeshOptions& VertexColors(bool value = true) { mEnableVertexColors = value; return *this; } //! Enable/disable normals @@ -108,18 +125,19 @@ class TriMeshOptions TriMeshOptions& InvertWinding() { mInvertWinding = true; return *this; } // clang-format on private: - bool mEnableIndices = false; - bool mEnableVertexColors = false; - bool mEnableNormals = false; - bool mEnableTexCoords = false; - bool mEnableTangents = false; - bool mEnableObjectColor = false; - bool mInvertTexCoordsV = false; - bool mInvertWinding = false; - float3 mObjectColor = float3(0.7f); - float3 mTranslate = float3(0, 0, 0); - float3 mScale = float3(1, 1, 1); - float2 mTexCoordScale = float2(1, 1); + bool mEnableIndices = false; + bool mEnableVertexColors = false; + bool mEnableNormals = false; + bool mEnableTexCoords = false; + bool mEnableTangents = false; + bool mEnableObjectColor = false; + bool mInvertTexCoordsV = false; + bool mInvertWinding = false; + grfx::IndexType mIndexType = grfx::INDEX_TYPE_UINT32; + float3 mObjectColor = float3(0.7f); + float3 mTranslate = float3(0, 0, 0); + float3 mScale = float3(1, 1, 1); + float2 mTexCoordScale = float2(1, 1); friend class TriMesh; }; @@ -161,6 +179,7 @@ class TriMesh uint64_t GetDataSizeTangents() const; uint64_t GetDataSizeBitangents() const; + const uint8_t* GetDataIndicesU8(uint32_t index = 0) const; const uint16_t* GetDataIndicesU16(uint32_t index = 0) const; const uint32_t* GetDataIndicesU32(uint32_t index = 0) const; const float3* GetDataPositions(uint32_t index = 0) const; @@ -199,6 +218,7 @@ class TriMesh static TriMesh CreateFromOBJ(const std::filesystem::path& path, const TriMeshOptions& options = TriMeshOptions()); private: + void AppendIndexU8(uint8_t value); void AppendIndexU16(uint16_t value); void AppendIndexU32(uint32_t value); @@ -212,7 +232,7 @@ class TriMesh private: grfx::IndexType mIndexType = grfx::INDEX_TYPE_UNDEFINED; TriMeshAttributeDim mTexCoordDim = TRI_MESH_ATTRIBUTE_DIM_UNDEFINED; - std::vector mIndices; // Stores both 16 and 32 bit indices + std::vector mIndices; // Stores 8, 16 and 32 bit indices std::vector mPositions; // Vertex positions std::vector mColors; // Vertex colors std::vector mNormals; // Vertex normals diff --git a/include/ppx/wire_mesh.h b/include/ppx/wire_mesh.h index df405be33..4d3957156 100644 --- a/include/ppx/wire_mesh.h +++ b/include/ppx/wire_mesh.h @@ -52,8 +52,25 @@ class WireMeshOptions WireMeshOptions() {} ~WireMeshOptions() {} // clang-format off - //! Enable/disable indices - WireMeshOptions& Indices(bool value = true) { mEnableIndices = value; return *this; } + //! Enable/disable indices, using UINT32 by default when enabled + WireMeshOptions& Indices(bool value = true) + { + mEnableIndices = value; + if (value && (mIndexType == grfx::INDEX_TYPE_UNDEFINED)) { + mIndexType = grfx::INDEX_TYPE_UINT32; + } + else if (!value) { + mIndexType = grfx::INDEX_TYPE_UNDEFINED; + } + return *this; + } + //! Set the index type; UNDEFINED disables indices, concrete types enable indices + WireMeshOptions& IndexType(grfx::IndexType value) + { + mIndexType = value; + mEnableIndices = (value != grfx::INDEX_TYPE_UNDEFINED); + return *this; + } //! Enable/disable vertex colors WireMeshOptions& VertexColors(bool value = true) { mEnableVertexColors = value; return *this; } //! Set and/or enable/disable object color, object color will override vertex colors @@ -62,11 +79,12 @@ class WireMeshOptions WireMeshOptions& Scale(const float3& scale) { mScale = scale; return *this; } // clang-format on private: - bool mEnableIndices = false; - bool mEnableVertexColors = false; - bool mEnableObjectColor = false; - float3 mObjectColor = float3(0.7f); - float3 mScale = float3(1, 1, 1); + bool mEnableIndices = false; + bool mEnableVertexColors = false; + bool mEnableObjectColor = false; + grfx::IndexType mIndexType = grfx::INDEX_TYPE_UINT32; + float3 mObjectColor = float3(0.7f); + float3 mScale = float3(1, 1, 1); friend class WireMesh; }; @@ -93,6 +111,7 @@ class WireMesh uint64_t GetDataSizePositions() const; uint64_t GetDataSizeColors() const; + const uint8_t* GetDataIndicesU8(uint32_t index = 0) const; const uint16_t* GetDataIndicesU16(uint32_t index = 0) const; const uint32_t* GetDataIndicesU32(uint32_t index = 0) const; const float3* GetDataPositions(uint32_t index = 0) const; @@ -113,6 +132,7 @@ class WireMesh static WireMesh CreateSphere(float radius, uint32_t usegs, uint32_t vsegs, const WireMeshOptions& options = WireMeshOptions()); private: + void AppendIndexU8(uint8_t value); void AppendIndexU16(uint16_t value); void AppendIndexU32(uint32_t value); @@ -125,7 +145,7 @@ class WireMesh private: grfx::IndexType mIndexType = grfx::INDEX_TYPE_UNDEFINED; - std::vector mIndices; // Stores both 16 and 32 bit indices + std::vector mIndices; // Stores 8, 16 and 32 bit indices std::vector mPositions; // Vertex positions std::vector mColors; // Vertex colors float3 mBoundingBoxMin; // Bounding box min diff --git a/src/ppx/tri_mesh.cpp b/src/ppx/tri_mesh.cpp index d9875c71d..15f28f689 100644 --- a/src/ppx/tri_mesh.cpp +++ b/src/ppx/tri_mesh.cpp @@ -19,6 +19,8 @@ #include "tiny_obj_loader.h" +#include + namespace ppx { TriMesh::TriMesh() @@ -28,8 +30,6 @@ TriMesh::TriMesh() TriMesh::TriMesh(grfx::IndexType indexType) : mIndexType(indexType) { - // TODO: #514 - Remove assert when UINT8 is supported - PPX_ASSERT_MSG(mIndexType != grfx::INDEX_TYPE_UINT8, "INDEX_TYPE_UINT8 unsupported in TriMesh"); } TriMesh::TriMesh(TriMeshAttributeDim texCoordDim) @@ -40,8 +40,6 @@ TriMesh::TriMesh(TriMeshAttributeDim texCoordDim) TriMesh::TriMesh(grfx::IndexType indexType, TriMeshAttributeDim texCoordDim) : mIndexType(indexType), mTexCoordDim(texCoordDim) { - // TODO: #514 - Remove assert when UINT8 is supported - PPX_ASSERT_MSG(mIndexType != grfx::INDEX_TYPE_UINT8, "INDEX_TYPE_UINT8 unsupported in TriMesh"); } TriMesh::~TriMesh() @@ -162,6 +160,18 @@ uint64_t TriMesh::GetDataSizeBitangents() const return size; } +const uint8_t* TriMesh::GetDataIndicesU8(uint32_t index) const +{ + if (mIndexType != grfx::INDEX_TYPE_UINT8) { + return nullptr; + } + uint32_t count = GetCountIndices(); + if (index >= count) { + return nullptr; + } + return mIndices.data() + index; +} + const uint16_t* TriMesh::GetDataIndicesU16(uint32_t index) const { if (mIndexType != grfx::INDEX_TYPE_UINT16) { @@ -282,6 +292,11 @@ const float3* TriMesh::GetDataBitangents(uint32_t index) const return reinterpret_cast(ptr); } +void TriMesh::AppendIndexU8(uint8_t value) +{ + mIndices.push_back(value); +} + void TriMesh::AppendIndexU16(uint16_t value) { const uint8_t* pBytes = reinterpret_cast(&value); @@ -304,6 +319,9 @@ void TriMesh::PreallocateForTriangleCount(size_t triangleCount, bool enableColor // Reserve for triangles switch (mIndexType) { + case grfx::INDEX_TYPE_UINT8: + mIndices.reserve(vertexCount * sizeof(uint8_t)); + break; case grfx::INDEX_TYPE_UINT16: mIndices.reserve(vertexCount * sizeof(uint16_t)); break; @@ -339,7 +357,19 @@ void TriMesh::PreallocateForTriangleCount(size_t triangleCount, bool enableColor uint32_t TriMesh::AppendTriangle(uint32_t v0, uint32_t v1, uint32_t v2) { - if (mIndexType == grfx::INDEX_TYPE_UINT16) { + if (mIndexType == grfx::INDEX_TYPE_UINT8) { + PPX_ASSERT_MSG(v0 <= std::numeric_limits::max(), "v0 is out of range for index type UINT8"); + PPX_ASSERT_MSG(v1 <= std::numeric_limits::max(), "v1 is out of range for index type UINT8"); + PPX_ASSERT_MSG(v2 <= std::numeric_limits::max(), "v2 is out of range for index type UINT8"); + mIndices.reserve(mIndices.size() + 3 * sizeof(uint8_t)); + AppendIndexU8(static_cast(v0)); + AppendIndexU8(static_cast(v1)); + AppendIndexU8(static_cast(v2)); + } + else if (mIndexType == grfx::INDEX_TYPE_UINT16) { + PPX_ASSERT_MSG(v0 <= std::numeric_limits::max(), "v0 is out of range for index type UINT16"); + PPX_ASSERT_MSG(v1 <= std::numeric_limits::max(), "v1 is out of range for index type UINT16"); + PPX_ASSERT_MSG(v2 <= std::numeric_limits::max(), "v2 is out of range for index type UINT16"); mIndices.reserve(mIndices.size() + 3 * sizeof(uint16_t)); AppendIndexU16(static_cast(v0)); AppendIndexU16(static_cast(v1)); @@ -462,7 +492,14 @@ Result TriMesh::GetTriangle(uint32_t triIndex, uint32_t& v0, uint32_t& v1, uint3 const uint8_t* pData = mIndices.data(); uint32_t elementSize = grfx::IndexTypeSize(mIndexType); - if (mIndexType == grfx::INDEX_TYPE_UINT16) { + if (mIndexType == grfx::INDEX_TYPE_UINT8) { + size_t offset = 3 * triIndex * elementSize; + const uint8_t* pIndexData = pData + offset; + v0 = static_cast(pIndexData[0]); + v1 = static_cast(pIndexData[1]); + v2 = static_cast(pIndexData[2]); + } + else if (mIndexType == grfx::INDEX_TYPE_UINT16) { size_t offset = 3 * triIndex * elementSize; const uint16_t* pIndexData = reinterpret_cast(pData + offset); v0 = static_cast(pIndexData[0]); @@ -525,7 +562,7 @@ void TriMesh::AppendIndexAndVertexData( const TriMeshOptions& options, TriMesh& mesh) { - grfx::IndexType indexType = options.mEnableIndices ? grfx::INDEX_TYPE_UINT32 : grfx::INDEX_TYPE_UNDEFINED; + grfx::IndexType indexType = mesh.GetIndexType(); // Verify expected vertex count size_t vertexCount = (vertexData.size() * sizeof(float)) / sizeof(TriMeshVertexData); @@ -718,7 +755,7 @@ TriMesh TriMesh::CreatePlane(TriMeshPlane plane, const float2& size, uint32_t us } } - grfx::IndexType indexType = options.mEnableIndices ? grfx::INDEX_TYPE_UINT32 : grfx::INDEX_TYPE_UNDEFINED; + grfx::IndexType indexType = options.mEnableIndices ? options.mIndexType : grfx::INDEX_TYPE_UNDEFINED; TriMeshAttributeDim texCoordDim = options.mEnableTexCoords ? TRI_MESH_ATTRIBUTE_DIM_2 : TRI_MESH_ATTRIBUTE_DIM_UNDEFINED; TriMesh mesh = TriMesh(indexType, texCoordDim); @@ -745,7 +782,7 @@ TriMesh TriMesh::CreatePlane(TriMeshPlane plane, const float2& size, uint32_t us }; // clang-format on - grfx::IndexType indexType = options.mEnableIndices ? grfx::INDEX_TYPE_UINT32 : grfx::INDEX_TYPE_UNDEFINED; + grfx::IndexType indexType = options.mEnableIndices ? options.mIndexType : grfx::INDEX_TYPE_UNDEFINED; TriMeshAttributeDim texCoordDim = options.mEnableTexCoords ? TRI_MESH_ATTRIBUTE_DIM_2 : TRI_MESH_ATTRIBUTE_DIM_UNDEFINED; TriMesh mesh = TriMesh(indexType, texCoordDim); @@ -816,7 +853,7 @@ TriMesh TriMesh::CreateCube(const float3& size, const TriMeshOptions& options) }; // clang-format on - grfx::IndexType indexType = options.mEnableIndices ? grfx::INDEX_TYPE_UINT32 : grfx::INDEX_TYPE_UNDEFINED; + grfx::IndexType indexType = options.mEnableIndices ? options.mIndexType : grfx::INDEX_TYPE_UNDEFINED; TriMeshAttributeDim texCoordDim = options.mEnableTexCoords ? TRI_MESH_ATTRIBUTE_DIM_2 : TRI_MESH_ATTRIBUTE_DIM_UNDEFINED; TriMesh mesh = TriMesh(indexType, texCoordDim); @@ -894,7 +931,7 @@ TriMesh TriMesh::CreateSphere(float radius, uint32_t usegs, uint32_t vsegs, cons } } - grfx::IndexType indexType = options.mEnableIndices ? grfx::INDEX_TYPE_UINT32 : grfx::INDEX_TYPE_UNDEFINED; + grfx::IndexType indexType = options.mEnableIndices ? options.mIndexType : grfx::INDEX_TYPE_UNDEFINED; TriMeshAttributeDim texCoordDim = options.mEnableTexCoords ? TRI_MESH_ATTRIBUTE_DIM_2 : TRI_MESH_ATTRIBUTE_DIM_UNDEFINED; TriMesh mesh = TriMesh(indexType, texCoordDim); @@ -915,7 +952,7 @@ Result TriMesh::CreateFromOBJ(const std::filesystem::path& path, const TriMeshOp double fnStartTime = timer.SecondsSinceStart(); // Determine index type and tex coord dim - grfx::IndexType indexType = options.mEnableIndices ? grfx::INDEX_TYPE_UINT32 : grfx::INDEX_TYPE_UNDEFINED; + grfx::IndexType indexType = options.mEnableIndices ? options.mIndexType : grfx::INDEX_TYPE_UNDEFINED; TriMeshAttributeDim texCoordDim = options.mEnableTexCoords ? TRI_MESH_ATTRIBUTE_DIM_2 : TRI_MESH_ATTRIBUTE_DIM_UNDEFINED; // Create new mesh diff --git a/src/ppx/wire_mesh.cpp b/src/ppx/wire_mesh.cpp index cb6f52bdb..5f97389b4 100644 --- a/src/ppx/wire_mesh.cpp +++ b/src/ppx/wire_mesh.cpp @@ -16,6 +16,8 @@ #include "ppx/math_util.h" #include "ppx/timer.h" +#include + namespace ppx { WireMesh::WireMesh() @@ -25,8 +27,6 @@ WireMesh::WireMesh() WireMesh::WireMesh(grfx::IndexType indexType) : mIndexType(indexType) { - // TODO: #514 - Remove assert when UINT8 is supported - PPX_ASSERT_MSG(mIndexType != grfx::INDEX_TYPE_UINT8, "INDEX_TYPE_UINT8 unsupported in WireMesh"); } WireMesh::~WireMesh() @@ -88,6 +88,18 @@ uint64_t WireMesh::GetDataSizeColors() const return size; } +const uint8_t* WireMesh::GetDataIndicesU8(uint32_t index) const +{ + if (mIndexType != grfx::INDEX_TYPE_UINT8) { + return nullptr; + } + uint32_t count = GetCountIndices(); + if (index >= count) { + return nullptr; + } + return mIndices.data() + index; +} + const uint16_t* WireMesh::GetDataIndicesU16(uint32_t index) const { if (mIndexType != grfx::INDEX_TYPE_UINT16) { @@ -136,6 +148,11 @@ const float3* WireMesh::GetDataColors(uint32_t index) const return reinterpret_cast(ptr); } +void WireMesh::AppendIndexU8(uint8_t value) +{ + mIndices.push_back(value); +} + void WireMesh::AppendIndexU16(uint16_t value) { const uint8_t* pBytes = reinterpret_cast(&value); @@ -154,9 +171,16 @@ void WireMesh::AppendIndexU32(uint32_t value) uint32_t WireMesh::AppendEdge(uint32_t v0, uint32_t v1) { - if (mIndexType == grfx::INDEX_TYPE_UINT16) { - PPX_ASSERT_MSG(v0 <= UINT16_MAX, "v0 is out of range for index type UINT16"); - PPX_ASSERT_MSG(v1 <= UINT16_MAX, "v1 is out of range for index type UINT16"); + if (mIndexType == grfx::INDEX_TYPE_UINT8) { + PPX_ASSERT_MSG(v0 <= std::numeric_limits::max(), "v0 is out of range for index type UINT8"); + PPX_ASSERT_MSG(v1 <= std::numeric_limits::max(), "v1 is out of range for index type UINT8"); + mIndices.reserve(mIndices.size() + 2 * sizeof(uint8_t)); + AppendIndexU8(static_cast(v0)); + AppendIndexU8(static_cast(v1)); + } + else if (mIndexType == grfx::INDEX_TYPE_UINT16) { + PPX_ASSERT_MSG(v0 <= std::numeric_limits::max(), "v0 is out of range for index type UINT16"); + PPX_ASSERT_MSG(v1 <= std::numeric_limits::max(), "v1 is out of range for index type UINT16"); mIndices.reserve(mIndices.size() + 2 * sizeof(uint16_t)); AppendIndexU16(static_cast(v0)); AppendIndexU16(static_cast(v1)); @@ -213,7 +237,13 @@ Result WireMesh::GetEdge(uint32_t triIndex, uint32_t& v0, uint32_t& v1) const const uint8_t* pData = mIndices.data(); uint32_t elementSize = grfx::IndexTypeSize(mIndexType); - if (mIndexType == grfx::INDEX_TYPE_UINT16) { + if (mIndexType == grfx::INDEX_TYPE_UINT8) { + size_t offset = 2 * triIndex * elementSize; + const uint8_t* pIndexData = pData + offset; + v0 = static_cast(pIndexData[0]); + v1 = static_cast(pIndexData[1]); + } + else if (mIndexType == grfx::INDEX_TYPE_UINT16) { size_t offset = 2 * triIndex * elementSize; const uint16_t* pIndexData = reinterpret_cast(pData + offset); v0 = static_cast(pIndexData[0]); @@ -255,7 +285,7 @@ void WireMesh::AppendIndexAndVertexData( const WireMeshOptions& options, WireMesh& mesh) { - grfx::IndexType indexType = options.mEnableIndices ? grfx::INDEX_TYPE_UINT32 : grfx::INDEX_TYPE_UNDEFINED; + grfx::IndexType indexType = mesh.GetIndexType(); // Verify expected vertex count size_t vertexCount = (vertexData.size() * sizeof(float)) / sizeof(WireMeshVertexData); @@ -426,7 +456,7 @@ WireMesh WireMesh::CreatePlane(WireMeshPlane plane, const float2& size, uint32_t indexCount += 1; } - grfx::IndexType indexType = options.mEnableIndices ? grfx::INDEX_TYPE_UINT32 : grfx::INDEX_TYPE_UNDEFINED; + grfx::IndexType indexType = options.mEnableIndices ? options.mIndexType : grfx::INDEX_TYPE_UNDEFINED; WireMesh mesh = WireMesh(indexType); uint32_t expectedVertexCount = 2 * (uverts + vverts); @@ -508,7 +538,7 @@ WireMesh WireMesh::CreateCube(const float3& size, const WireMeshOptions& options }; // clang-format on - grfx::IndexType indexType = options.mEnableIndices ? grfx::INDEX_TYPE_UINT32 : grfx::INDEX_TYPE_UNDEFINED; + grfx::IndexType indexType = options.mEnableIndices ? options.mIndexType : grfx::INDEX_TYPE_UNDEFINED; WireMesh mesh = WireMesh(indexType); AppendIndexAndVertexData(indexData, vertexData, 24, options, mesh); @@ -603,7 +633,7 @@ WireMesh WireMesh::CreateSphere(float radius, uint32_t usegs, uint32_t vsegs, co } } - grfx::IndexType indexType = options.mEnableIndices ? grfx::INDEX_TYPE_UINT32 : grfx::INDEX_TYPE_UNDEFINED; + grfx::IndexType indexType = options.mEnableIndices ? options.mIndexType : grfx::INDEX_TYPE_UNDEFINED; WireMesh mesh = WireMesh(indexType); uint32_t expectedVertexCountU = (uverts - 1) * (vverts - 2); diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index b1791a9da..969069acc 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -56,6 +56,7 @@ list( geometry_test.cpp knob_test.cpp log_console_test.cpp + mesh_index_test.cpp metrics_test.cpp ppm_export_test.cpp string_util_test.cpp diff --git a/src/test/mesh_index_test.cpp b/src/test/mesh_index_test.cpp new file mode 100644 index 000000000..f5e5e87fc --- /dev/null +++ b/src/test/mesh_index_test.cpp @@ -0,0 +1,207 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include "ppx/geometry.h" + +#include + +namespace ppx { +namespace { + +// The death tests will always fail with NDEBUG (i.e. Release builds). +// PPX_ASSERT_MSG relies on assert() for death but assert is a no-op with NDEBUG. +#if !defined(NDEBUG) +#define PERFORM_DEATH_TESTS +#endif + +using ::testing::ElementsAreArray; +using ::testing::IsNull; +using ::testing::NotNull; + +std::vector GetTriMeshIndexDataU8(const TriMesh& mesh) +{ + const uint8_t* pData = mesh.GetDataIndicesU8(); + if (pData == nullptr) { + return {}; + } + return std::vector(pData, pData + mesh.GetCountIndices()); +} + +std::vector GetWireMeshIndexDataU8(const WireMesh& mesh) +{ + const uint8_t* pData = mesh.GetDataIndicesU8(); + if (pData == nullptr) { + return {}; + } + return std::vector(pData, pData + mesh.GetCountIndices()); +} + +template +void ExpectIndexBufferEq(const Geometry& geometry, const std::vector& expected) +{ + const Geometry::Buffer* indexBuffer = geometry.GetIndexBuffer(); + ASSERT_THAT(indexBuffer, NotNull()); + + EXPECT_EQ(indexBuffer->GetElementSize(), sizeof(T)); + EXPECT_EQ(indexBuffer->GetElementCount(), static_cast(expected.size())); + EXPECT_EQ(indexBuffer->GetSize(), static_cast(sizeof(T) * expected.size())); + + const char* pData = indexBuffer->GetData(); + ASSERT_THAT(pData, NotNull()); + const T* pIndexData = reinterpret_cast(pData); + EXPECT_THAT(std::vector(pIndexData, pIndexData + expected.size()), ElementsAreArray(expected)); +} + +TEST(TriMeshUint8IndexTest, AppendTrianglePacksDataAsUint8) +{ + TriMesh mesh(grfx::INDEX_TYPE_UINT8); + EXPECT_EQ(mesh.GetIndexType(), grfx::INDEX_TYPE_UINT8); + + EXPECT_EQ(mesh.AppendTriangle(0, 1, 2), 1u); + EXPECT_EQ(mesh.AppendTriangle(2, 3, 4), 2u); + + EXPECT_EQ(mesh.GetCountTriangles(), 2u); + EXPECT_EQ(mesh.GetCountIndices(), 6u); + EXPECT_EQ(mesh.GetDataSizeIndices(), 6u); + EXPECT_THAT(mesh.GetDataIndicesU8(), NotNull()); + EXPECT_THAT(mesh.GetDataIndicesU16(), IsNull()); + EXPECT_THAT(mesh.GetDataIndicesU32(), IsNull()); + EXPECT_THAT(mesh.GetDataIndicesU8(6), IsNull()); + const std::vector expected = {0, 1, 2, 2, 3, 4}; + EXPECT_THAT(GetTriMeshIndexDataU8(mesh), ElementsAreArray(expected)); + + uint32_t v0 = PPX_VALUE_IGNORED; + uint32_t v1 = PPX_VALUE_IGNORED; + uint32_t v2 = PPX_VALUE_IGNORED; + EXPECT_EQ(mesh.GetTriangle(1, v0, v1, v2), ppx::SUCCESS); + EXPECT_EQ(v0, 2u); + EXPECT_EQ(v1, 3u); + EXPECT_EQ(v2, 4u); +} + +TEST(WireMeshUint8IndexTest, AppendEdgePacksDataAsUint8) +{ + WireMesh mesh(grfx::INDEX_TYPE_UINT8); + EXPECT_EQ(mesh.GetIndexType(), grfx::INDEX_TYPE_UINT8); + + EXPECT_EQ(mesh.AppendEdge(0, 1), 1u); + EXPECT_EQ(mesh.AppendEdge(1, 2), 2u); + + EXPECT_EQ(mesh.GetCountEdges(), 2u); + EXPECT_EQ(mesh.GetCountIndices(), 4u); + EXPECT_EQ(mesh.GetDataSizeIndices(), 4u); + EXPECT_THAT(mesh.GetDataIndicesU8(), NotNull()); + EXPECT_THAT(mesh.GetDataIndicesU16(), IsNull()); + EXPECT_THAT(mesh.GetDataIndicesU32(), IsNull()); + EXPECT_THAT(mesh.GetDataIndicesU8(4), IsNull()); + const std::vector expected = {0, 1, 1, 2}; + EXPECT_THAT(GetWireMeshIndexDataU8(mesh), ElementsAreArray(expected)); + + uint32_t v0 = PPX_VALUE_IGNORED; + uint32_t v1 = PPX_VALUE_IGNORED; + EXPECT_EQ(mesh.GetEdge(1, v0, v1), ppx::SUCCESS); + EXPECT_EQ(v0, 1u); + EXPECT_EQ(v1, 2u); +} + +TEST(TriMeshUint8IndexTest, CreateCubeCanUseUint8Indices) +{ + TriMesh mesh = TriMesh::CreateCube(float3(2.0f), TriMeshOptions().IndexType(grfx::INDEX_TYPE_UINT8).VertexColors()); + + EXPECT_EQ(mesh.GetIndexType(), grfx::INDEX_TYPE_UINT8); + EXPECT_EQ(mesh.GetCountPositions(), 24u); + EXPECT_EQ(mesh.GetCountColors(), 24u); + EXPECT_EQ(mesh.GetCountTriangles(), 12u); + EXPECT_EQ(mesh.GetCountIndices(), 36u); + EXPECT_EQ(mesh.GetDataSizeIndices(), 36u); + EXPECT_THAT(mesh.GetDataIndicesU8(), NotNull()); + + uint32_t v0 = PPX_VALUE_IGNORED; + uint32_t v1 = PPX_VALUE_IGNORED; + uint32_t v2 = PPX_VALUE_IGNORED; + EXPECT_EQ(mesh.GetTriangle(0, v0, v1, v2), ppx::SUCCESS); + EXPECT_EQ(v0, 0u); + EXPECT_EQ(v1, 1u); + EXPECT_EQ(v2, 2u); + EXPECT_EQ(mesh.GetTriangle(11, v0, v1, v2), ppx::SUCCESS); + EXPECT_EQ(v0, 20u); + EXPECT_EQ(v1, 22u); + EXPECT_EQ(v2, 23u); +} + +TEST(WireMeshUint8IndexTest, CreateCubeCanUseUint8Indices) +{ + WireMesh mesh = WireMesh::CreateCube(float3(2.0f), WireMeshOptions().IndexType(grfx::INDEX_TYPE_UINT8).VertexColors()); + + EXPECT_EQ(mesh.GetIndexType(), grfx::INDEX_TYPE_UINT8); + EXPECT_EQ(mesh.GetCountPositions(), 24u); + EXPECT_EQ(mesh.GetCountColors(), 24u); + EXPECT_EQ(mesh.GetCountEdges(), 24u); + EXPECT_EQ(mesh.GetCountIndices(), 48u); + EXPECT_EQ(mesh.GetDataSizeIndices(), 48u); + EXPECT_THAT(mesh.GetDataIndicesU8(), NotNull()); + + uint32_t v0 = PPX_VALUE_IGNORED; + uint32_t v1 = PPX_VALUE_IGNORED; + EXPECT_EQ(mesh.GetEdge(0, v0, v1), ppx::SUCCESS); + EXPECT_EQ(v0, 0u); + EXPECT_EQ(v1, 1u); + EXPECT_EQ(mesh.GetEdge(23, v0, v1), ppx::SUCCESS); + EXPECT_EQ(v0, 23u); + EXPECT_EQ(v1, 20u); +} + +TEST(TriMeshUint8IndexTest, GeometryCreatePreservesUint8IndexBuffer) +{ + TriMesh mesh = TriMesh::CreateCube(float3(2.0f), TriMeshOptions().IndexType(grfx::INDEX_TYPE_UINT8).VertexColors()); + + Geometry geometry; + EXPECT_EQ(Geometry::Create(mesh, &geometry), ppx::SUCCESS); + + EXPECT_EQ(geometry.GetIndexType(), grfx::INDEX_TYPE_UINT8); + EXPECT_EQ(geometry.GetIndexCount(), mesh.GetCountIndices()); + ExpectIndexBufferEq(geometry, GetTriMeshIndexDataU8(mesh)); +} + +TEST(WireMeshUint8IndexTest, GeometryCreatePreservesUint8IndexBuffer) +{ + WireMesh mesh = WireMesh::CreateCube(float3(2.0f), WireMeshOptions().IndexType(grfx::INDEX_TYPE_UINT8).VertexColors()); + + Geometry geometry; + EXPECT_EQ(Geometry::Create(mesh, &geometry), ppx::SUCCESS); + + EXPECT_EQ(geometry.GetIndexType(), grfx::INDEX_TYPE_UINT8); + EXPECT_EQ(geometry.GetIndexCount(), mesh.GetCountIndices()); + ExpectIndexBufferEq(geometry, GetWireMeshIndexDataU8(mesh)); +} + +#if defined(PERFORM_DEATH_TESTS) +TEST(TriMeshUint8IndexDeathTest, AppendTriangleDiesIfIndexIsOutOfRange) +{ + TriMesh mesh(grfx::INDEX_TYPE_UINT8); + ASSERT_DEATH(mesh.AppendTriangle(0, 1, 256), ""); +} + +TEST(WireMeshUint8IndexDeathTest, AppendEdgeDiesIfIndexIsOutOfRange) +{ + WireMesh mesh(grfx::INDEX_TYPE_UINT8); + ASSERT_DEATH(mesh.AppendEdge(0, 256), ""); +} +#endif + +} // namespace +} // namespace ppx