From 4b21e447ae4008b3c71a5b71a3481efe3f45eb63 Mon Sep 17 00:00:00 2001 From: assiduous Date: Thu, 24 Sep 2026 09:01:05 -0700 Subject: [PATCH 1/3] ParsingTools: add float/double parsing and integer overflow checks --- Common/interface/ParsingTools.hpp | 213 +++++++++++- .../src/Common/ParsingToolsTest.cpp | 302 ++++++++++++++++++ 2 files changed, 502 insertions(+), 13 deletions(-) diff --git a/Common/interface/ParsingTools.hpp b/Common/interface/ParsingTools.hpp index 17bdaa1d8..967b28b78 100644 --- a/Common/interface/ParsingTools.hpp +++ b/Common/interface/ParsingTools.hpp @@ -30,11 +30,13 @@ /// Parsing tools #include +#include #include #include #include #include #include +#include #include "../../Primitives/interface/BasicTypes.h" #include "../../Primitives/interface/FlagEnum.h" @@ -423,39 +425,224 @@ bool SkipString(const IteratorType& Start, const IteratorType& End, const char* } } -/// Parses an integer starting from the given position. +/// Parses a decimal integer starting from the given position. /// -/// \param[in] Start - starting position. -/// \param[in] End - end of the input string -/// \param[out] Value - parsed integer value. -/// \return position immediately following the last character of the number. +/// \param[in] Start - starting position; leading whitespace is not skipped. +/// \param[in] End - end of the input range. +/// \param[out] Value - parsed integer value, unchanged on failure. +/// \return The position immediately following the last digit, or Start if no +/// integer is found or its value cannot be represented by ValueType. +/// +/// Accepts an optional sign. Negative values are rejected for unsigned types. +/// ValueType must be an integral type other than bool. template IteratorType ParseInteger(const IteratorType& Start, const IteratorType& End, ValueType& Value) noexcept { - auto Pos = Start; + static_assert(std::is_integral::value && !std::is_same::value, + "ValueType must be an integral type other than bool"); + + IteratorType Pos = Start; if (Pos == End) - return Pos; + return Start; const bool IsNegative = *Pos == '-'; + if (IsNegative && !std::numeric_limits::is_signed) + return Start; + if (*Pos == '+' || *Pos == '-') ++Pos; - if (Pos == End || !IsNum(*Pos)) + if (Pos == End || !IsDigit(*Pos)) return Start; - Value = 0; - while (Pos != End && IsNum(*Pos)) + ValueType Number = 0; + while (Pos != End && IsDigit(*Pos)) { - Value = Value * 10 + (*Pos - '0'); + const ValueType Digit = static_cast(*Pos - '0'); + if (IsNegative) + { + // Accumulate negative values directly so that the minimum signed + // value never needs to be represented as a positive value. + if (Number < ((std::numeric_limits::min)() + Digit) / 10) + return Start; + Number = static_cast(Number * 10 - Digit); + } + else + { + if (Number > ((std::numeric_limits::max)() - Digit) / 10) + return Start; + Number = static_cast(Number * 10 + Digit); + } ++Pos; } - if (IsNegative) - Value = -Value; + Value = Number; + return Pos; +} +/// Reads a decimal floating-point number starting from the given position. +/// +/// \param[in] Start - starting position; leading whitespace is not skipped. +/// \param[in] End - end of the input range; no null terminator is required. +/// \param[out] Value - parsed value, unchanged on failure. +/// \return The position immediately following the number, or Start if no valid +/// number is found or its magnitude exceeds the finite ValueType range. +/// +/// Accepts an optional sign, decimal digits with an optional decimal point, +/// and an optional e/E exponent. At least one digit is required before the +/// exponent, and an exponent marker must be followed by an optional sign and +/// at least one digit. NaN and infinity are not accepted. Conversion is +/// locale-independent and does not allocate memory. Underflow is accepted, +/// including signed zero. ValueType must be float or double. +template +IteratorType ReadFloat(const IteratorType& Start, const IteratorType& End, ValueType& Value) noexcept +{ + static_assert(std::is_same::value || std::is_same::value, + "ValueType must be float or double"); + + // Retain 19 significant decimal digits for conversion to float or double without + // allocating a terminated string or depending on floating from_chars support. + if (Start == End) + { + return Start; + } + IteratorType Pos = Start; + + // Parse the optional sign. + // -12.34e-5; + // ^ + // Pos + const bool Negative = *Pos == '-'; + if (*Pos == '-' || *Pos == '+') + { + ++Pos; + } + Uint64 Mantissa = 0; + int Digits = 0; + Int64 Power = 0; + bool AnyDigit = false; + + const auto ReadDigits = [&](bool IsFractional) { + while (Pos != End && IsDigit(*Pos)) + { + AnyDigit = true; + if (Digits < 19) + { + Mantissa = Mantissa * 10 + static_cast(*Pos - '0'); + if (Mantissa != 0) + { + ++Digits; + } + if (IsFractional) + { + // Each retained fractional digit shifts the decimal point left. + --Power; + } + } + else if (!IsFractional) + { + // Discarded integer digits still contribute to the magnitude. + // Discarded fractional digits do not change the retained value. + ++Power; + } + ++Pos; + } + }; + + // Parse the integer part, which may be empty (e.g. ".5"). + // -12.34e-5; + // ^ + // Pos + ReadDigits(false); + + // Parse the optional decimal point and fractional part. + // -12.34e-5; + // ^ + // Pos + if (Pos != End && *Pos == '.') + { + ++Pos; + // -12.34e-5; + // ^ + // Pos + ReadDigits(true); + } + // A sign or decimal point alone does not form a number. + if (!AnyDigit) + { + return Start; + } + + // Parse the optional exponent marker and sign. + // -12.34e-5; + // ^ + // Pos + if (Pos != End && (*Pos == 'e' || *Pos == 'E')) + { + ++Pos; + // -12.34e-5; + // ^ + // Pos + bool NegativeExponent = false; + if (Pos != End && (*Pos == '-' || *Pos == '+')) + { + NegativeExponent = *Pos == '-'; + ++Pos; + } + // An exponent marker requires at least one exponent digit. + // -12.34e-5; + // ^ + // Pos + if (Pos == End || !IsDigit(*Pos)) + { + return Start; + } + Int64 Exponent = 0; + while (Pos != End && IsDigit(*Pos)) + { + // Cap the exponent so additional digits cannot overflow Int64. + Exponent = (std::min)(Exponent * 10 + static_cast(*Pos - '0'), Int64{1000000000}); + ++Pos; + } + Power += NegativeExponent ? -Exponent : Exponent; + } + + // Pos now points past the number. Convert Mantissa * 10^Power. + // -12.34e-5; + // ^ + // Pos + double Number = 0; + if (Mantissa != 0) + { + Number = static_cast(Mantissa); + if (Power > std::numeric_limits::max_exponent10 - 19) + { + // Normalize near the upper double limit to avoid overflowing the + // product through rounding of a large power of ten. + Number /= std::pow(10.0, Digits - 1); + Power += Digits - 1; + } + if (Power < std::numeric_limits::min_exponent10) + { + // A subnormal result may still be representable even when 10^Power + // alone underflows. Apply the scale in two representable steps. + Number *= std::pow(10.0, static_cast(Power - std::numeric_limits::min_exponent10)); + Number *= std::pow(10.0, std::numeric_limits::min_exponent10); + } + else + { + Number *= std::pow(10.0, static_cast(Power)); + } + if (!std::isfinite(Number) || Number > (std::numeric_limits::max)()) + { + return Start; + } + } + Value = static_cast(Negative ? -Number : Number); return Pos; } + /// Splits string into chunks separated by comments and delimiters. /// /// \param [in] Start - start of the string to split. diff --git a/Tests/DiligentCoreTest/src/Common/ParsingToolsTest.cpp b/Tests/DiligentCoreTest/src/Common/ParsingToolsTest.cpp index 6751f85e3..7a186a097 100644 --- a/Tests/DiligentCoreTest/src/Common/ParsingToolsTest.cpp +++ b/Tests/DiligentCoreTest/src/Common/ParsingToolsTest.cpp @@ -26,7 +26,9 @@ #include "ParsingTools.hpp" +#include #include +#include #include "gtest/gtest.h" @@ -1489,6 +1491,306 @@ TEST(Common_ParsingTools, ParseInteger) }; +template +void TestParseIntegerLimits(const char* MinText, const char* MaxText, const char* UnderflowText, const char* OverflowText) +{ + struct ValidCase + { + const char* Text; + ValueType Expected; + }; + const ValidCase ValidCases[] = { + {MinText, (std::numeric_limits::min)()}, + {MaxText, (std::numeric_limits::max)()}, + {"+0", ValueType{0}}, + {"0007", ValueType{7}}, + }; + for (const ValidCase& Case : ValidCases) + { + SCOPED_TRACE(Case.Text); + ValueType Value = 7; + const std::string_view Text{Case.Text}; + EXPECT_EQ(ParseInteger(Text.begin(), Text.end(), Value), Text.end()); + EXPECT_EQ(Value, Case.Expected); + + // A valid number may be followed by a delimiter or another token. + const std::string Prefix = std::string{Case.Text} + "x"; + Value = 7; + EXPECT_EQ(ParseInteger(Prefix.begin(), Prefix.end(), Value), Prefix.end() - 1); + EXPECT_EQ(Value, Case.Expected); + } + for (const char* Text : {UnderflowText, OverflowText}) + { + SCOPED_TRACE(Text); + ValueType Value = 7; + const char* End = Text + std::strlen(Text); + EXPECT_EQ(ParseInteger(Text, End, Value), Text); + EXPECT_EQ(Value, ValueType{7}); + } + if constexpr (!std::numeric_limits::is_signed) + { + const char Text[] = "-0"; + ValueType Value = 7; + const char* Start = Text; + const char* End = Text + 2; + EXPECT_EQ(ParseInteger(Start, End, Value), Start); + EXPECT_EQ(Value, ValueType{7}); + } +} + +TEST(Common_ParsingTools, ParseIntegerLimits) +{ + TestParseIntegerLimits("-128", "127", "-129", "128"); + TestParseIntegerLimits("0", "255", "-1", "256"); + TestParseIntegerLimits("-32768", "32767", "-32769", "32768"); + TestParseIntegerLimits("0", "65535", "-1", "65536"); + TestParseIntegerLimits("-2147483648", "2147483647", "-2147483649", "2147483648"); + TestParseIntegerLimits("0", "4294967295", "-1", "4294967296"); + TestParseIntegerLimits("-9223372036854775808", "9223372036854775807", "-9223372036854775809", "9223372036854775808"); + TestParseIntegerLimits("0", "18446744073709551615", "-1", "18446744073709551616"); +} + +TEST(Common_ParsingTools, ParseIntegerBoundedInput) +{ + const char Text[] = {'-', '1', '2', '8', '9'}; + const char* Start = Text; + const char* End = Text + 4; + Int8 Value = 7; + EXPECT_EQ(ParseInteger(Start, End, Value), End); + EXPECT_EQ(Value, -128); + + // The digit beyond End must not turn the valid bounded number into overflow. + Value = 7; + End = Text + 5; + EXPECT_EQ(ParseInteger(Start, End, Value), Start); + EXPECT_EQ(Value, 7); +} + +TEST(Common_ParsingTools, ReadFloat) +{ + struct ValidCase + { + const char* Text; + float Expected; + }; + const ValidCase Cases[] = { + {"0", 0.0f}, + {"+0", 0.0f}, + {"-0.0", -0.0f}, + {"1", 1.0f}, + {"-1", -1.0f}, + {"+2.5", 2.5f}, + {".25", 0.25f}, + {"-.5", -0.5f}, + {"1.", 1.0f}, + {"00012.5", 12.5f}, + {"1.25e2", 125.0f}, + {"-1.25E-2", -0.0125f}, + {"3.e-1", 0.3f}, + {".4e+1", 4.0f}, + {"000.000125e+3", 0.125f}, + {"123456789012345678901234567890e-29", 1.2345679f}, + {"0.00000000000000000000000000000125e30", 1.25f}, + {"3.4028234e38", (std::numeric_limits::max)()}, + {"1.1754943508222875e-38", (std::numeric_limits::min)()}, + {"1e-9999999999999999999999999", 0.0f}, + {"-1e-9999999999999999999999999", -0.0f}, + {"0e9999999999999999999999999", 0.0f}, + {"-0e9999999999999999999999999", -0.0f}, + }; + for (const ValidCase& Case : Cases) + { + SCOPED_TRACE(Case.Text); + const std::string Text{Case.Text}; + float Value = 7.0f; + ASSERT_EQ(ReadFloat(Text.begin(), Text.end(), Value), Text.end()); + EXPECT_FLOAT_EQ(Value, Case.Expected); + EXPECT_EQ(std::signbit(Value), std::signbit(Case.Expected)); + } +} + +TEST(Common_ParsingTools, ReadFloatDouble) +{ + struct ValidCase + { + const char* Text; + double Expected; + }; + const ValidCase Cases[] = { + {"+2.5", 2.5}, + {"-.125e+1", -1.25}, + {"1.2345678901234567", 1.2345678901234567}, + {"1.2345678901234567890123456789", 1.2345678901234567}, + {"123456789012345678901234567890.987654321e-29", 1.2345678901234567}, + {"123456789012345678901234567890e-29", 1.2345678901234567}, + {"1e39", 1e39}, + {"1.7976931348623157e308", (std::numeric_limits::max)()}, + {"1.797693134862315708e308", (std::numeric_limits::max)()}, + {"1.797693134862315700e308", (std::numeric_limits::max)()}, + {"2.2250738585072014e-308", (std::numeric_limits::min)()}, + {"0", 0.0}, + {"-0.0", -0.0}, + {"1e-9999999999999999999999999", 0.0}, + {"-1e-9999999999999999999999999", -0.0}, + {"0e9999999999999999999999999", 0.0}, + {"-0e9999999999999999999999999", -0.0}, + }; + for (const ValidCase& Case : Cases) + { + SCOPED_TRACE(Case.Text); + const std::string Text{Case.Text}; + double Value = 7.0; + ASSERT_EQ(ReadFloat(Text.begin(), Text.end(), Value), Text.end()); + EXPECT_DOUBLE_EQ(Value, Case.Expected); + EXPECT_EQ(std::signbit(Value), std::signbit(Case.Expected)); + } + + for (const char* Case : {"1e309", "-1e309", "1e9999999999999999999999999", "1e+", "nan"}) + { + SCOPED_TRACE(Case); + const std::string Text{Case}; + double Value = 7.0; + EXPECT_EQ(ReadFloat(Text.begin(), Text.end(), Value), Text.begin()); + EXPECT_EQ(Value, 7.0); + } +} + +TEST(Common_ParsingTools, ReadFloatSmallestSubnormal) +{ + const std::string FloatText{"1.401298464324817e-45"}; + float FloatValue = 7.0f; + ASSERT_EQ(ReadFloat(FloatText.begin(), FloatText.end(), FloatValue), FloatText.end()); + // Exact comparisons distinguish the smallest subnormal from zero. + EXPECT_EQ(FloatValue, std::numeric_limits::denorm_min()); + + const std::string DoubleText{"4.9406564584124654e-324"}; + double DoubleValue = 7.0; + ASSERT_EQ(ReadFloat(DoubleText.begin(), DoubleText.end(), DoubleValue), DoubleText.end()); + EXPECT_EQ(DoubleValue, std::numeric_limits::denorm_min()); +} + +TEST(Common_ParsingTools, ReadFloatDoubleBoundedInput) +{ + const char Text[] = {'1', '.', '2', '5', 'e', '3', '0', '0', '9'}; + const char* Start = Text; + const char* End = Text + 8; + double Value = 7.0; + ASSERT_EQ(ReadFloat(Start, End, Value), End); + EXPECT_DOUBLE_EQ(Value, 1.25e300); + + // Including the final digit makes the exponent overflow double's range. + End = Text + 9; + Value = 7.0; + EXPECT_EQ(ReadFloat(Start, End, Value), Start); + EXPECT_EQ(Value, 7.0); +} + +TEST(Common_ParsingTools, ReadFloatPrefixes) +{ + struct PrefixCase + { + const char* Text; + size_t Length; + float Expected; + }; + const PrefixCase Cases[] = { + {"1e1e1", 3, 10.0f}, + {"1.2.3", 3, 1.2f}, + {"1f", 1, 1.0f}, + {"1.0F", 3, 1.0f}, + {"0x1p0", 1, 0.0f}, + {"1 2", 1, 1.0f}, + {"1 ", 1, 1.0f}, + {"1\n", 1, 1.0f}, + {"1.0junk", 3, 1.0f}, + {"-12.5,", 5, -12.5f}, + }; + for (const PrefixCase& Case : Cases) + { + SCOPED_TRACE(Case.Text); + const std::string Text{Case.Text}; + float Value = 7.0f; + EXPECT_EQ(ReadFloat(Text.begin(), Text.end(), Value), Text.begin() + Case.Length); + EXPECT_FLOAT_EQ(Value, Case.Expected); + } +} + +TEST(Common_ParsingTools, ReadFloatRejectsInvalidInput) +{ + const char* Cases[] = { + "", + " ", + "\t", + "+", + "-", + ".", + "+.", + ".e1", + "1e", + "1e+", + "1e-", + "1eX", + "1e+X", + "1e-X", + "NaN", + "nan", + "inf", + "-inf", + "Infinity", + " 1", + "1e39", + "1e9999999999999999999999999", + "-1e9999999999999999999999999", + }; + for (const char* Case : Cases) + { + SCOPED_TRACE(Case); + const std::string Text{Case}; + float Value = 7.0f; + EXPECT_EQ(ReadFloat(Text.begin(), Text.end(), Value), Text.begin()); + EXPECT_FLOAT_EQ(Value, 7.0f); + } +} + +TEST(Common_ParsingTools, ReadFloatBoundedInput) +{ + const char Text[] = {'x', '-', '1', '.', '2', '5', 'e', '1', '!'}; + const char* Start = Text + 1; + const char* End = Text + 8; + float Value = 7.0f; + ASSERT_EQ(ReadFloat(Start, End, Value), End); + EXPECT_FLOAT_EQ(Value, -12.5f); + + End = Text + 9; + Value = 7.0f; + EXPECT_EQ(ReadFloat(Start, End, Value), Text + 8); + EXPECT_FLOAT_EQ(Value, -12.5f); + + const char Unterminated[] = {'1', '.', '5'}; + Start = Unterminated; + End = Unterminated + sizeof(Unterminated); + ASSERT_EQ(ReadFloat(Start, End, Value), End); + EXPECT_FLOAT_EQ(Value, 1.5f); + + const char EmbeddedNull[] = {'1', '\0', '2'}; + Start = EmbeddedNull; + End = EmbeddedNull + sizeof(EmbeddedNull); + EXPECT_EQ(ReadFloat(Start, End, Value), EmbeddedNull + 1); + EXPECT_FLOAT_EQ(Value, 1.0f); + + // Only consider the exponent if it lies inside the supplied range. + const char Exponent[] = {'1', 'e', '+'}; + Start = Exponent; + End = Exponent + 1; + ASSERT_EQ(ReadFloat(Start, End, Value), End); + EXPECT_FLOAT_EQ(Value, 1.0f); + + End = Exponent + sizeof(Exponent); + Value = 7.0f; + EXPECT_EQ(ReadFloat(Start, End, Value), Start); + EXPECT_FLOAT_EQ(Value, 7.0f); +} + TEST(Common_ParsingTools, GetArrayIndex) { auto Test = [](const std::string Var, size_t RefNameEndPos, int RefIndex) { From bb6ae2b95db9cb3d18cec9404d39bd056d0e5409 Mon Sep 17 00:00:00 2001 From: assiduous Date: Thu, 24 Sep 2026 10:28:12 -0700 Subject: [PATCH 2/3] CI: skip two flaky D3D12 WARP ray-tracing cases --- .github/workflows/build-windows.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 4afae9110..23ba569ea 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -124,6 +124,8 @@ jobs: uses: DiligentGraphics/github-action/run-core-gpu-tests@v26 with: mode: d3d12_sw + # Temporarily skip flaky RT2/RT4 until WARP and reference AS synchronization are fixed. + args: --gtest_filter=-RayTracingTest/RT2.*:RayTracingTest/RT4.* - name: DiligentCoreAPITest D3D12 DXC if: ${{ (success() || failure() && steps.build.outcome == 'success') && (matrix.name == 'Win10' || matrix.name == 'Win10-Ninja') }} @@ -131,8 +133,9 @@ jobs: with: mode: d3d12_sw use-dxc: true + # Temporarily skip flaky RT2/RT4 until WARP and reference AS synchronization are fixed. # Disable texture creation tests that are not relevant for DXC to save time - args: --gtest_filter=-TextureCreation* + args: --gtest_filter=-TextureCreation*:RayTracingTest/RT2.*:RayTracingTest/RT4.* - name: DiligentCoreAPITest WebGPU if: ${{ (success() || failure() && steps.build.outcome == 'success') && matrix.name == 'Win10' }} From 18bfa7b7563a0ef5b5fe074d37c2e8304100e965 Mon Sep 17 00:00:00 2001 From: assiduous Date: Thu, 24 Sep 2026 18:45:47 -0700 Subject: [PATCH 3/3] MathLib: use IndexType for vertex IDs and explicit casts in polygon triangulation --- Common/interface/AdvancedMath.hpp | 42 +++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/Common/interface/AdvancedMath.hpp b/Common/interface/AdvancedMath.hpp index 920c5a075..ddf6a9e1a 100644 --- a/Common/interface/AdvancedMath.hpp +++ b/Common/interface/AdvancedMath.hpp @@ -1371,16 +1371,16 @@ class Polygon2DTriangulator m_VertTypes.resize(VertCount); for (int i = 0; i < VertCount; ++i) { - m_RemainingVertIds[i] = i; + m_RemainingVertIds[i] = static_cast(i); m_VertTypes[i] = VertexType::Convexx; } auto CheckConvex = [&](int vert_id) { const int RemainingVertCount = static_cast(m_RemainingVertIds.size()); - const int Idx0 = m_RemainingVertIds[WrapIndex(vert_id - 1, RemainingVertCount)]; - const int Idx1 = m_RemainingVertIds[WrapIndex(vert_id + 0, RemainingVertCount)]; - const int Idx2 = m_RemainingVertIds[WrapIndex(vert_id + 1, RemainingVertCount)]; + const IndexType Idx0 = m_RemainingVertIds[WrapIndex(vert_id - 1, RemainingVertCount)]; + const IndexType Idx1 = m_RemainingVertIds[WrapIndex(vert_id + 0, RemainingVertCount)]; + const IndexType Idx2 = m_RemainingVertIds[WrapIndex(vert_id + 1, RemainingVertCount)]; const auto& V0 = Polygon[Idx0]; const auto& V1 = Polygon[Idx1]; @@ -1394,9 +1394,9 @@ class Polygon2DTriangulator auto CheckEar = [&](int vert_id) { const int RemainingVertCount = static_cast(m_RemainingVertIds.size()); - const int Idx0 = m_RemainingVertIds[WrapIndex(vert_id - 1, RemainingVertCount)]; - const int Idx1 = m_RemainingVertIds[WrapIndex(vert_id + 0, RemainingVertCount)]; - const int Idx2 = m_RemainingVertIds[WrapIndex(vert_id + 1, RemainingVertCount)]; + const IndexType Idx0 = m_RemainingVertIds[WrapIndex(vert_id - 1, RemainingVertCount)]; + const IndexType Idx1 = m_RemainingVertIds[WrapIndex(vert_id + 0, RemainingVertCount)]; + const IndexType Idx2 = m_RemainingVertIds[WrapIndex(vert_id + 1, RemainingVertCount)]; VERIFY_EXPR(m_VertTypes[Idx1] == VertexType::Convexx); @@ -1408,7 +1408,7 @@ class Polygon2DTriangulator // Degenerate ears still need to be removable with collinear vertices. const bool AllowEdges = GetWinding(V0, V1, V2) != 0; - for (const int Idx : m_RemainingVertIds) + for (const IndexType Idx : m_RemainingVertIds) { if (Idx == Idx0 || Idx == Idx1 || Idx == Idx2) continue; @@ -1443,13 +1443,13 @@ class Polygon2DTriangulator // the triangle order produced by clipping the first ear repeatedly. for (int i = 0; i < VertCount - 3; ++i) { - m_Triangles.emplace_back(VertCount - 1); - m_Triangles.emplace_back(i); - m_Triangles.emplace_back(i + 1); + m_Triangles.emplace_back(static_cast(VertCount - 1)); + m_Triangles.emplace_back(static_cast(i)); + m_Triangles.emplace_back(static_cast(i + 1)); } - m_Triangles.emplace_back(VertCount - 3); - m_Triangles.emplace_back(VertCount - 2); - m_Triangles.emplace_back(VertCount - 1); + m_Triangles.emplace_back(static_cast(VertCount - 3)); + m_Triangles.emplace_back(static_cast(VertCount - 2)); + m_Triangles.emplace_back(static_cast(VertCount - 1)); return m_Triangles; } @@ -1470,7 +1470,7 @@ class Polygon2DTriangulator int ear_vert_id = 0; for (; ear_vert_id < RemainingVertCount; ++ear_vert_id) { - const int Idx = m_RemainingVertIds[ear_vert_id]; + const IndexType Idx = m_RemainingVertIds[ear_vert_id]; if (m_VertTypes[Idx] == VertexType::Ear) break; }; @@ -1482,9 +1482,9 @@ class Polygon2DTriangulator ear_vert_id = 0; } - const int Idx0 = m_RemainingVertIds[WrapIndex(ear_vert_id - 1, RemainingVertCount)]; - const int Idx1 = m_RemainingVertIds[ear_vert_id]; - const int Idx2 = m_RemainingVertIds[WrapIndex(ear_vert_id + 1, RemainingVertCount)]; + const IndexType Idx0 = m_RemainingVertIds[WrapIndex(ear_vert_id - 1, RemainingVertCount)]; + const IndexType Idx1 = m_RemainingVertIds[ear_vert_id]; + const IndexType Idx2 = m_RemainingVertIds[WrapIndex(ear_vert_id + 1, RemainingVertCount)]; m_Triangles.emplace_back(Idx0); m_Triangles.emplace_back(Idx1); @@ -1495,8 +1495,8 @@ class Polygon2DTriangulator // Update adjacent vertices if (RemainingVertCount > 3) { - const int IdxL = m_RemainingVertIds[WrapIndex(ear_vert_id - 1, RemainingVertCount)]; - const int IdxR = m_RemainingVertIds[WrapIndex(ear_vert_id, RemainingVertCount)]; + const IndexType IdxL = m_RemainingVertIds[WrapIndex(ear_vert_id - 1, RemainingVertCount)]; + const IndexType IdxR = m_RemainingVertIds[WrapIndex(ear_vert_id, RemainingVertCount)]; // First check for convex vs reflex m_VertTypes[IdxL] = CheckConvex(ear_vert_id - 1); m_VertTypes[IdxR] = CheckConvex(ear_vert_id); @@ -1541,7 +1541,7 @@ class Polygon2DTriangulator std::vector m_VertTypes; // Remaining vertices to process - std::vector m_RemainingVertIds; + std::vector m_RemainingVertIds; };