gr2: binary-search the curve knot (upper_bound) when knots are ascending

Curve::eval scanned every knot to find the first one past t; long animation
tracks made that the top of CPythonCharacterManager::Deform. Knots are checked
for ascending order at load and unsorted curves keep the linear scan, so the
chosen knot is unchanged. Desktop live game: Deform samples 93 -> 58.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-30 11:42:31 +09:00
co-authored by Claude Opus 5.5
parent 6bfcac7d68
commit e4331bca3e
2 changed files with 9 additions and 1 deletions
+1
View File
@@ -103,6 +103,7 @@ struct Curve {
uint32_t dim = 0; // 3=vec, 4=quat, 9=3x3 scaleshear
std::vector<float> knots; // KnotCount 个
std::vector<float> controls; // KnotCount * dim 个
bool knots_sorted = true; // 升序 → eval 二分找 knot;否则按原样线性扫
bool empty() const { return knots.empty() || dim == 0; }
// t 处求值,写 dim 个 float 到 out(identity 由调用方给)。
void eval(float t, float* out) const;
+8 -1
View File
@@ -76,6 +76,7 @@ void File::Impl::read_old_curve(Ref curve_obj, Ref curve_type, Curve& out) {
};
read_farr(*mK, out.knots);
read_farr(*mC, out.controls);
out.knots_sorted = std::is_sorted(out.knots.begin(), out.knots.end());
out.dim = out.knots.empty() ? 0 : uint32_t(out.controls.size() / out.knots.size());
if (out.dim && out.knots.size() * out.dim != out.controls.size()) out.dim = 0; // 不一致 → 弃
@@ -218,8 +219,14 @@ void Curve::eval(float t, float* out) const {
}
if (t <= knots[0]) t = knots[0];
if (t >= knots[n - 1]) t = knots[n - 1];
// 第一个 > t 的 knot(找不到 → n-1);升序时二分
int i = (int)n - 1;
for (uint32_t k = 0; k < n; ++k) if (knots[k] > t) { i = (int)k; break; }
if (knots_sorted) {
const int k = int(std::upper_bound(knots.begin(), knots.end(), t) - knots.begin());
if (k < (int)n) i = k;
} else {
for (uint32_t k = 0; k < n; ++k) if (knots[k] > t) { i = (int)k; break; }
}
if (i < 1) i = 1;
const int span = (degree == 1) ? 2 : 3; // 本 span 的控制点数 = degree+1