From e288e29fa9424fea0f86f82aac3380799be54d35 Mon Sep 17 00:00:00 2001 From: Irlan <-> Date: Tue, 2 Oct 2018 16:19:53 -0300 Subject: [PATCH 001/198] remove unused image --- screenshots/a.png | Bin 122222 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 screenshots/a.png diff --git a/screenshots/a.png b/screenshots/a.png deleted file mode 100644 index fe54a2a492777400f6523737138009bb2ed1a49a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 122222 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

1C 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 Date: Tue, 2 Oct 2018 16:21:49 -0300 Subject: [PATCH 002/198] add shape cast function --- include/bounce/collision/gjk/gjk.h | 14 ++++ src/bounce/collision/gjk/gjk.cpp | 107 +++++++++++++++++++++++++++-- 2 files changed, 115 insertions(+), 6 deletions(-) diff --git a/include/bounce/collision/gjk/gjk.h b/include/bounce/collision/gjk/gjk.h index 2fd5459..4053ecf 100644 --- a/include/bounce/collision/gjk/gjk.h +++ b/include/bounce/collision/gjk/gjk.h @@ -114,4 +114,18 @@ b3GJKOutput b3GJK(const b3Transform& xf1, const b3GJKProxy& proxy1, const b3Transform& xf2, const b3GJKProxy& proxy2, bool applyRadius, b3SimplexCache* cache); +// The output of the GJK-based ray cast algorithm. +struct b3GJKRayCastOutput +{ + float32 t; // time of impact + b3Vec3 point; // contact point at t + b3Vec3 normal; // contact normal at t + u32 iterations; // number of iterations +}; + +// Find the time of impact between two proxies given the relative target translation vector. +bool b3GJKRayCast(b3GJKRayCastOutput* output, + const b3Transform& xf1, const b3GJKProxy& proxy1, + const b3Transform& xf2, const b3GJKProxy& proxy2, const b3Vec3& translation2); + #endif \ No newline at end of file diff --git a/src/bounce/collision/gjk/gjk.cpp b/src/bounce/collision/gjk/gjk.cpp index cfa8b59..1b058df 100644 --- a/src/bounce/collision/gjk/gjk.cpp +++ b/src/bounce/collision/gjk/gjk.cpp @@ -29,7 +29,7 @@ u32 b3_gjkCalls = 0, b3_gjkIters = 0, b3_gjkMaxIters = 0; // Convert a point Q from Cartesian coordinates to Barycentric coordinates (u, v) // with respect to a segment AB. // The last output value is the divisor. -static B3_FORCE_INLINE void b3Barycentric(float32 out[3], +static B3_FORCE_INLINE void b3Barycentric(float32 out[3], const b3Vec3& A, const b3Vec3& B, const b3Vec3& Q) { @@ -38,7 +38,7 @@ static B3_FORCE_INLINE void b3Barycentric(float32 out[3], b3Vec3 QB = B - Q; //float32 divisor = b3Dot(AB, AB); - + out[0] = b3Dot(QB, AB); out[1] = -b3Dot(QA, AB); out[2] = out[0] + out[1]; @@ -63,7 +63,7 @@ static B3_FORCE_INLINE void b3Barycentric(float32 out[4], b3Vec3 QA_x_QB = b3Cross(QA, QB); b3Vec3 AB_x_AC = b3Cross(AB, AC); - + //float32 divisor = b3Dot(AB_x_AC, AB_x_AC); out[0] = b3Dot(QB_x_QC, AB_x_AC); @@ -248,7 +248,7 @@ void b3Simplex::Solve3(const b3Vec3& Q) b3Barycentric(wAB, A.point, B.point, Q); b3Barycentric(wBC, B.point, C.point, Q); b3Barycentric(wCA, C.point, A.point, Q); - + // R A if (wAB[1] <= 0.0f && wCA[0] <= 0.0f) { @@ -279,7 +279,7 @@ void b3Simplex::Solve3(const b3Vec3& Q) // Test edge regions float32 wABC[4]; b3Barycentric(wABC, A.point, B.point, C.point, Q); - + // This is used to help testing if the face degenerates // into an edge. float32 area = wABC[3]; @@ -361,7 +361,7 @@ void b3Simplex::Solve4(const b3Vec3& Q) b3Barycentric(wAD, A.point, D.point, Q); b3Barycentric(wCD, C.point, D.point, Q); b3Barycentric(wDB, D.point, B.point, Q); - + // R A if (wAB[1] <= 0.0f && wAC[1] <= 0.0f && wAD[1] <= 0.0f) { @@ -848,4 +848,99 @@ b3GJKOutput b3GJK(const b3Transform& xf1, const b3GJKProxy& proxy1, b3SimplexCache cache; cache.count = 0; return b3GJK(xf1, proxy1, xf2, proxy2, false, &cache); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// Brian Mirtich +// "Conservative Advancement" +bool b3GJKRayCast(b3GJKRayCastOutput* output, + const b3Transform& xf1, const b3GJKProxy& proxy1, + const b3Transform& xf2, const b3GJKProxy& proxy2, const b3Vec3& translation2) +{ + float32 r1 = proxy1.radius; + float32 r2 = proxy2.radius; + float32 radius = r1 + r2; + + float32 d_max = b3Length(translation2); + B3_ASSERT(d_max > 0.0f); + + float32 t = 0.0f; + + const float32 tolerance = 0.5f * B3_LINEAR_SLOP; + + b3SimplexCache cache; + cache.count = 0; + + b3GJKOutput gjkOut = b3GJK(xf1, proxy1, xf2, proxy2, false, &cache); + float32 d = gjkOut.distance; + + if (d == 0.0f) + { + // Overlap + return false; + } + + if (d < radius + tolerance) + { + b3Vec3 n = gjkOut.point2 - gjkOut.point1; + n /= d; + + // Touch + output->t = t; + output->point = gjkOut.point1 + r1 * n; + output->normal = n; + output->iterations = 0; + return true; + } + + const u32 kMaxIters = 20; + + u32 iter = 0; + for (;;) + { + B3_ASSERT(d < d_max); + B3_ASSERT(d >= radius); + + float32 dt = (d - radius) / d_max; + t += dt; + + if (t >= 1.0f) + { + // No overlap + output->iterations = iter; + return false; + } + + b3Transform txf1 = xf1; + + b3Transform txf2; + txf2.rotation = xf2.rotation; + txf2.position = (1.0f - t) * xf2.position + t * (xf2.position + r); + + gjkOut = b3GJK(txf1, proxy1, txf2, proxy2, false, &cache); + d = gjkOut.distance; + + if (d < radius + tolerance) + { + break; + } + + ++iter; + + if (iter == kMaxIters) + { + output->iterations = iter; + return false; + } + } + + b3Vec3 n = gjkOut.point2 - gjkOut.point1; + n /= d; + + output->t = t; + output->point = gjkOut.point1 + r1 * n; + output->normal = n; + output->iterations = iter; + return true; } \ No newline at end of file From f6903c145c7c396b52e886db3dbca97dc1d9ebf1 Mon Sep 17 00:00:00 2001 From: Irlan <-> Date: Tue, 2 Oct 2018 16:22:03 -0300 Subject: [PATCH 003/198] add shape cast test --- examples/testbed/framework/test_entries.cpp | 2 + examples/testbed/tests/shape_cast.h | 118 ++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 examples/testbed/tests/shape_cast.h diff --git a/examples/testbed/framework/test_entries.cpp b/examples/testbed/framework/test_entries.cpp index d7d5475..6416061 100644 --- a/examples/testbed/framework/test_entries.cpp +++ b/examples/testbed/framework/test_entries.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -75,6 +76,7 @@ TestEntry g_tests[] = { "Convex Hull", &ConvexHull::Create }, { "Cluster", &Cluster::Create }, { "Distance", &Distance::Create }, + { "Shape Cast", &ShapeCast::Create }, { "Capsule Collision", &CapsuleCollision::Create }, { "Hull Collision", &HullCollision::Create }, { "Deep Capsule", &DeepCapsule::Create }, diff --git a/examples/testbed/tests/shape_cast.h b/examples/testbed/tests/shape_cast.h new file mode 100644 index 0000000..cfc4611 --- /dev/null +++ b/examples/testbed/tests/shape_cast.h @@ -0,0 +1,118 @@ +#ifndef SHAPE_CAST_H +#define SHAPE_CAST_H + +class ShapeCast : public Test +{ +public: + ShapeCast() + { + m_xfA.SetIdentity(); + m_xfA.position.Set(-5.0f, 0.0f, 0.0f); + m_xfA.rotation.SetIdentity(); + m_shapeA.m_centers[0].Set(0.0f, -2.0f, 0.0f); + m_shapeA.m_centers[1].Set(0.0f, 2.0f, 0.0f); + m_shapeA.m_radius = 1.0f; + + m_xfB.SetIdentity(); + m_xfB.position.Set(5.0f, 0.0f, 0.0f); + m_xfB.rotation.SetIdentity(); + m_shapeB.m_hull = &b3BoxHull_identity; + + m_proxyA.Set(&m_shapeA, 0); + m_proxyB.Set(&m_shapeB, 0); + } + + void Step() + { + g_draw->DrawString(b3Color_white, "Left/Right/Up/Down Arrow - Translate shape"); + g_draw->DrawString(b3Color_white, "X/Y/Z - Rotate shape"); + + g_draw->DrawTransform(m_xfA); + g_draw->DrawTransform(m_xfB); + + m_world.DrawShape(m_xfA, &m_shapeA); + m_world.DrawShape(m_xfB, &m_shapeB); + + b3Vec3 translationB = -20.0f * b3Vec3_x; + g_draw->DrawSegment(m_xfB.position, m_xfB.position + translationB, b3Color_white); + + b3GJKRayCastOutput out; + bool hit = b3GJKRayCast(&out, m_xfA, m_proxyA, m_xfB, m_proxyB, translationB); + + g_draw->DrawString(b3Color_white, "Iterations = %d", out.iterations); + + if (hit) + { + g_draw->DrawPoint(out.point, 4.0f, b3Color(0.0f, 1.0f, 0.0f)); + g_draw->DrawSegment(out.point, out.point + out.normal, b3Color(0.0f, 1.0f, 0.0f)); + + b3Transform xfB; + xfB.rotation = m_xfB.rotation; + xfB.position = m_xfB.position + out.t * translationB; + + m_world.DrawShape(xfB, &m_shapeB); + } + } + + void KeyDown(int key) + { + if (key == GLFW_KEY_LEFT) + { + m_xfB.position.x -= 0.105f; + } + + if (key == GLFW_KEY_RIGHT) + { + m_xfB.position.x += 0.105f; + } + + if (key == GLFW_KEY_UP) + { + m_xfB.position.y += 0.105f; + } + + if (key == GLFW_KEY_DOWN) + { + m_xfB.position.y -= 0.105f; + } + + if (key == GLFW_KEY_X) + { + b3Quat qx(b3Vec3(1.0f, 0.0f, 0.0f), 0.05f * B3_PI); + b3Mat33 xfx = b3QuatMat33(qx); + + m_xfB.rotation = m_xfB.rotation * xfx; + } + + if (key == GLFW_KEY_Y) + { + b3Quat qy(b3Vec3(0.0f, 1.0f, 0.0f), 0.05f * B3_PI); + b3Mat33 xfy = b3QuatMat33(qy); + + m_xfB.rotation = m_xfB.rotation * xfy; + } + + if (key == GLFW_KEY_Z) + { + b3Quat qy(b3Vec3(0.0f, 0.0f, 1.0f), 0.05f * B3_PI); + b3Mat33 xfz = b3QuatMat33(qy); + + m_xfB.rotation = m_xfB.rotation * xfz; + } + } + + static Test* Create() + { + return new ShapeCast(); + } + + b3CapsuleShape m_shapeA; + b3Transform m_xfA; + b3ShapeGJKProxy m_proxyA; + + b3HullShape m_shapeB; + b3Transform m_xfB; + b3ShapeGJKProxy m_proxyB; +}; + +#endif \ No newline at end of file From 3c2c53bb2bdfea53bf60b7faca56e29a47094eca Mon Sep 17 00:00:00 2001 From: Irlan <-> Date: Tue, 2 Oct 2018 16:25:04 -0300 Subject: [PATCH 004/198] typo --- src/bounce/collision/gjk/gjk.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bounce/collision/gjk/gjk.cpp b/src/bounce/collision/gjk/gjk.cpp index 1b058df..c569598 100644 --- a/src/bounce/collision/gjk/gjk.cpp +++ b/src/bounce/collision/gjk/gjk.cpp @@ -916,7 +916,7 @@ bool b3GJKRayCast(b3GJKRayCastOutput* output, b3Transform txf2; txf2.rotation = xf2.rotation; - txf2.position = (1.0f - t) * xf2.position + t * (xf2.position + r); + txf2.position = (1.0f - t) * xf2.position + t * (xf2.position + translation2); gjkOut = b3GJK(txf1, proxy1, txf2, proxy2, false, &cache); d = gjkOut.distance; From ed068d88576e9670cf2c661cddee163b14f175d3 Mon Sep 17 00:00:00 2001 From: Irlan <-> Date: Tue, 2 Oct 2018 18:56:59 -0300 Subject: [PATCH 005/198] remove assert --- src/bounce/collision/gjk/gjk.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bounce/collision/gjk/gjk.cpp b/src/bounce/collision/gjk/gjk.cpp index c569598..d8c87e8 100644 --- a/src/bounce/collision/gjk/gjk.cpp +++ b/src/bounce/collision/gjk/gjk.cpp @@ -899,7 +899,6 @@ bool b3GJKRayCast(b3GJKRayCastOutput* output, u32 iter = 0; for (;;) { - B3_ASSERT(d < d_max); B3_ASSERT(d >= radius); float32 dt = (d - radius) / d_max; From 8030c3458a50ec2c0493a594361b6f51416f290e Mon Sep 17 00:00:00 2001 From: Irlan <-> Date: Fri, 5 Oct 2018 00:16:56 -0300 Subject: [PATCH 006/198] bugfix, cso gjk cast --- src/bounce/collision/gjk/gjk.cpp | 192 ++++++++++++++++++++++++++++++- 1 file changed, 187 insertions(+), 5 deletions(-) diff --git a/src/bounce/collision/gjk/gjk.cpp b/src/bounce/collision/gjk/gjk.cpp index d8c87e8..40ad582 100644 --- a/src/bounce/collision/gjk/gjk.cpp +++ b/src/bounce/collision/gjk/gjk.cpp @@ -881,11 +881,11 @@ bool b3GJKRayCast(b3GJKRayCastOutput* output, return false; } + b3Vec3 n = gjkOut.point2 - gjkOut.point1; + n /= d; + if (d < radius + tolerance) { - b3Vec3 n = gjkOut.point2 - gjkOut.point1; - n /= d; - // Touch output->t = t; output->point = gjkOut.point1 + r1 * n; @@ -920,6 +920,14 @@ bool b3GJKRayCast(b3GJKRayCastOutput* output, gjkOut = b3GJK(txf1, proxy1, txf2, proxy2, false, &cache); d = gjkOut.distance; + if (d == 0.0f) + { + break; + } + + n = gjkOut.point2 - gjkOut.point1; + n /= d; + if (d < radius + tolerance) { break; @@ -934,12 +942,186 @@ bool b3GJKRayCast(b3GJKRayCastOutput* output, } } - b3Vec3 n = gjkOut.point2 - gjkOut.point1; - n /= d; + if (d > 0.0f) + { + n = gjkOut.point2 - gjkOut.point1; + n /= d; + } output->t = t; output->point = gjkOut.point1 + r1 * n; output->normal = n; output->iterations = iter; return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// Gino van der Bergen +// "Smooth Mesh Contacts with GJK" +// Game Physics Pearls 2010, page 99 +bool b3GJKShapeCast(b3GJKRayCastOutput* output, + const b3Transform& xf1, const b3GJKProxy& proxy1, + const b3Transform& xf2, const b3GJKProxy& proxy2, const b3Vec3& translation2) +{ + float32 r1 = proxy1.radius; + float32 r2 = proxy2.radius; + float32 radius = r1 + r2; + + b3Vec3 r = translation2; + + float32 t = 0.0f; + b3Vec3 n = b3Vec3_zero; + + u32 index1 = proxy1.GetSupportIndex(b3MulT(xf1.rotation, -r)); + u32 index2 = proxy2.GetSupportIndex(b3MulT(xf2.rotation, r)); + b3Vec3 w1 = xf1 * proxy1.GetVertex(index1); + b3Vec3 w2 = xf2 * proxy2.GetVertex(index2); + b3Vec3 v = w1 - w2; + + b3Simplex simplex; + simplex.m_count = 0; + + b3SimplexVertex* vertices = simplex.m_vertices; + + u32 save1[4], save2[4]; + u32 saveCount = 0; + + const u32 kMaxIters = 20; + const float32 kTolerance = 10.0f * B3_EPSILON; + + float32 maxTolerance = 1.0f; + + u32 iter = 0; + while (iter < kMaxIters && b3Abs(b3LengthSquared(v) - radius * radius) > kTolerance * maxTolerance) + { + // Support in direction -v + index1 = proxy1.GetSupportIndex(b3MulT(xf1.rotation, -v)); + index2 = proxy2.GetSupportIndex(b3MulT(xf2.rotation, v)); + w1 = xf1 * proxy1.GetVertex(index1); + w2 = xf2 * proxy2.GetVertex(index2); + b3Vec3 p = w1 - w2; + + // Support plane on boundary of CSO is (-v, p) + // -v is normal at p + float32 vp = b3Dot(v, p); + float32 vr = b3Dot(v, r); + + if (vp - radius > t * vr) + { + if (vr > 0.0f) + { + t = (vp - radius) / vr; + + if (t > 1.0f) + { + output->iterations = iter; + return false; + } + + n = -v; + + // Flush the simplex + simplex.m_count = 0; + saveCount = 0; + } + else + { + output->iterations = iter; + return false; + } + } + + // Copy simplex so we can identify duplicates. + saveCount = simplex.m_count; + for (u32 i = 0; i < saveCount; ++i) + { + save1[i] = vertices[i].index1; + save2[i] = vertices[i].index2; + } + + // Unite p - s to simplex + b3Vec3 s = t * r; + + b3SimplexVertex* vertex = vertices + simplex.m_count; + vertex->index1 = index1; + vertex->point1 = w1; + vertex->index2 = index2; + vertex->point2 = w2; + vertex->point = p - s; + + // If we found a duplicate support point we must exit to avoid cycling. + bool duplicate = false; + for (u32 i = 0; i < saveCount; ++i) + { + if (vertex->index1 == save1[i] && vertex->index2 == save2[i]) + { + duplicate = true; + break; + } + } + + if (duplicate) + { + break; + } + + ++simplex.m_count; + + // Compute tolerance + maxTolerance = -B3_EPSILON; + for (u32 i = 0; i < simplex.m_count; ++i) + { + maxTolerance = b3Max(maxTolerance, b3LengthSquared(vertices[i].point)); + } + + // Sub-solve + const b3Vec3 origin = b3Vec3_zero; + + switch (simplex.m_count) + { + case 1: + break; + case 2: + simplex.Solve2(origin); + break; + case 3: + simplex.Solve3(origin); + break; + case 4: + simplex.Solve4(origin); + break; + default: + B3_ASSERT(false); + break; + } + + if (simplex.m_count == 4) + { + // Overlap + output->iterations = iter; + return false; + } + + v = simplex.GetClosestPoint(); + + ++iter; + } + + // Prepare output. + b3Vec3 point1, point2; + simplex.GetClosestPoints(&point1, &point2); + + if (b3LengthSquared(v) > B3_EPSILON * B3_EPSILON) + { + n = -v; + } + + n.Normalize(); + + output->t = t; + output->point = point1 + r1 * n; + output->normal = n; + output->iterations = iter; + return true; } \ No newline at end of file From 6b95a04a091ced308392fd644c2353624cd2b887 Mon Sep 17 00:00:00 2001 From: Irlan <-> Date: Fri, 5 Oct 2018 11:57:23 -0300 Subject: [PATCH 007/198] rename a function, update a test --- examples/testbed/tests/shape_cast.h | 31 ++++++++++++++++------------- include/bounce/collision/gjk/gjk.h | 6 +++--- src/bounce/collision/gjk/gjk.cpp | 24 ++++++++++------------ 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/examples/testbed/tests/shape_cast.h b/examples/testbed/tests/shape_cast.h index cfc4611..392ce0c 100644 --- a/examples/testbed/tests/shape_cast.h +++ b/examples/testbed/tests/shape_cast.h @@ -6,18 +6,18 @@ class ShapeCast : public Test public: ShapeCast() { - m_xfA.SetIdentity(); + m_shapeA.m_hull = &b3BoxHull_identity; + m_shapeA.m_radius = 0.0f; + + m_shapeB.m_hull = &b3BoxHull_identity; + m_shapeB.m_radius = 0.0f; + m_xfA.position.Set(-5.0f, 0.0f, 0.0f); m_xfA.rotation.SetIdentity(); - m_shapeA.m_centers[0].Set(0.0f, -2.0f, 0.0f); - m_shapeA.m_centers[1].Set(0.0f, 2.0f, 0.0f); - m_shapeA.m_radius = 1.0f; - m_xfB.SetIdentity(); - m_xfB.position.Set(5.0f, 0.0f, 0.0f); + m_xfB.position.Set(10.0f, 0.0f, 0.0f); m_xfB.rotation.SetIdentity(); - m_shapeB.m_hull = &b3BoxHull_identity; - + m_proxyA.Set(&m_shapeA, 0); m_proxyB.Set(&m_shapeB, 0); } @@ -33,18 +33,21 @@ public: m_world.DrawShape(m_xfA, &m_shapeA); m_world.DrawShape(m_xfB, &m_shapeB); - b3Vec3 translationB = -20.0f * b3Vec3_x; + g_draw->DrawSolidShape(&m_shapeA, b3Color_white, m_xfA); + g_draw->DrawSolidShape(&m_shapeB, b3Color_white, m_xfB); + + b3Vec3 translationB = -100.0f * b3Vec3_x; g_draw->DrawSegment(m_xfB.position, m_xfB.position + translationB, b3Color_white); - b3GJKRayCastOutput out; - bool hit = b3GJKRayCast(&out, m_xfA, m_proxyA, m_xfB, m_proxyB, translationB); + b3GJKShapeCastOutput out; + bool hit = b3GJKShapeCast(&out, m_xfA, m_proxyA, m_xfB, m_proxyB, translationB); g_draw->DrawString(b3Color_white, "Iterations = %d", out.iterations); if (hit) { - g_draw->DrawPoint(out.point, 4.0f, b3Color(0.0f, 1.0f, 0.0f)); - g_draw->DrawSegment(out.point, out.point + out.normal, b3Color(0.0f, 1.0f, 0.0f)); + g_draw->DrawPoint(out.point, 4.0f, b3Color_green); + g_draw->DrawSegment(out.point, out.point + out.normal, b3Color_green); b3Transform xfB; xfB.rotation = m_xfB.rotation; @@ -106,7 +109,7 @@ public: return new ShapeCast(); } - b3CapsuleShape m_shapeA; + b3HullShape m_shapeA; b3Transform m_xfA; b3ShapeGJKProxy m_proxyA; diff --git a/include/bounce/collision/gjk/gjk.h b/include/bounce/collision/gjk/gjk.h index 4053ecf..6261de6 100644 --- a/include/bounce/collision/gjk/gjk.h +++ b/include/bounce/collision/gjk/gjk.h @@ -114,8 +114,8 @@ b3GJKOutput b3GJK(const b3Transform& xf1, const b3GJKProxy& proxy1, const b3Transform& xf2, const b3GJKProxy& proxy2, bool applyRadius, b3SimplexCache* cache); -// The output of the GJK-based ray cast algorithm. -struct b3GJKRayCastOutput +// The output of the GJK-based shape cast algorithm. +struct b3GJKShapeCastOutput { float32 t; // time of impact b3Vec3 point; // contact point at t @@ -124,7 +124,7 @@ struct b3GJKRayCastOutput }; // Find the time of impact between two proxies given the relative target translation vector. -bool b3GJKRayCast(b3GJKRayCastOutput* output, +bool b3GJKShapeCast(b3GJKShapeCastOutput* output, const b3Transform& xf1, const b3GJKProxy& proxy1, const b3Transform& xf2, const b3GJKProxy& proxy2, const b3Vec3& translation2); diff --git a/src/bounce/collision/gjk/gjk.cpp b/src/bounce/collision/gjk/gjk.cpp index 40ad582..03ea554 100644 --- a/src/bounce/collision/gjk/gjk.cpp +++ b/src/bounce/collision/gjk/gjk.cpp @@ -854,7 +854,7 @@ b3GJKOutput b3GJK(const b3Transform& xf1, const b3GJKProxy& proxy1, // Brian Mirtich // "Conservative Advancement" -bool b3GJKRayCast(b3GJKRayCastOutput* output, +bool b3GJKShapeCast(b3GJKShapeCastOutput* output, const b3Transform& xf1, const b3GJKProxy& proxy1, const b3Transform& xf2, const b3GJKProxy& proxy2, const b3Vec3& translation2) { @@ -960,7 +960,7 @@ bool b3GJKRayCast(b3GJKRayCastOutput* output, // Gino van der Bergen // "Smooth Mesh Contacts with GJK" // Game Physics Pearls 2010, page 99 -bool b3GJKShapeCast(b3GJKRayCastOutput* output, +bool b3GJKShapeCastCSO(b3GJKShapeCastOutput* output, const b3Transform& xf1, const b3GJKProxy& proxy1, const b3Transform& xf2, const b3GJKProxy& proxy2, const b3Vec3& translation2) { @@ -1032,14 +1032,6 @@ bool b3GJKShapeCast(b3GJKRayCastOutput* output, } } - // Copy simplex so we can identify duplicates. - saveCount = simplex.m_count; - for (u32 i = 0; i < saveCount; ++i) - { - save1[i] = vertices[i].index1; - save2[i] = vertices[i].index2; - } - // Unite p - s to simplex b3Vec3 s = t * r; @@ -1075,6 +1067,14 @@ bool b3GJKShapeCast(b3GJKRayCastOutput* output, maxTolerance = b3Max(maxTolerance, b3LengthSquared(vertices[i].point)); } + // Copy simplex so we can identify duplicates. + saveCount = simplex.m_count; + for (u32 i = 0; i < saveCount; ++i) + { + save1[i] = vertices[i].index1; + save2[i] = vertices[i].index2; + } + // Sub-solve const b3Vec3 origin = b3Vec3_zero; @@ -1098,9 +1098,7 @@ bool b3GJKShapeCast(b3GJKRayCastOutput* output, if (simplex.m_count == 4) { - // Overlap - output->iterations = iter; - return false; + break; } v = simplex.GetClosestPoint(); From 41e036f268be078663bda6b5c01b785d13d325f2 Mon Sep 17 00:00:00 2001 From: Irlan <-> Date: Sat, 6 Oct 2018 00:26:53 -0300 Subject: [PATCH 008/198] restore a function --- include/bounce/collision/shapes/aabb3.h | 86 +++++++++++++------ include/bounce/collision/trees/dynamic_tree.h | 4 +- include/bounce/collision/trees/static_tree.h | 4 +- 3 files changed, 62 insertions(+), 32 deletions(-) diff --git a/include/bounce/collision/shapes/aabb3.h b/include/bounce/collision/shapes/aabb3.h index 46f9c5a..1ffd8b4 100644 --- a/include/bounce/collision/shapes/aabb3.h +++ b/include/bounce/collision/shapes/aabb3.h @@ -146,40 +146,70 @@ struct b3AABB3 } // Test if a ray intersects this AABB. - // Output the minimum and maximum intersection fractions to derive the minimum and maximum intersection points. - bool TestRay(const b3Vec3& p1, const b3Vec3& p2, float32& min_t, float32& max_t) const + bool TestRay(float32& minFraction, const b3Vec3& p1, const b3Vec3& p2, float32 maxFraction) const { b3Vec3 d = p2 - p1; - float32 t = d.Normalize(); - B3_ASSERT(t > B3_EPSILON); - - b3Vec3 inv_d; - inv_d.x = 1.0f / d.x; - inv_d.y = 1.0f / d.y; - inv_d.z = 1.0f / d.z; - - b3Vec3 t1; - t1.x = (m_lower.x - p1.x) * inv_d.x; - t1.y = (m_lower.y - p1.y) * inv_d.y; - t1.z = (m_lower.z - p1.z) * inv_d.z; - - b3Vec3 t2; - t2.x = (m_upper.x - p1.x) * inv_d.x; - t2.y = (m_upper.y - p1.y) * inv_d.y; - t2.z = (m_upper.z - p1.z) * inv_d.z; - + + float32 lower = 0.0f; + float32 upper = maxFraction; + for (u32 i = 0; i < 3; ++i) { - min_t = b3Max(min_t, b3Min(t1[i], t2[i])); - max_t = b3Min(max_t, b3Max(t1[i], t2[i])); + float32 numerators[2], denominators[2]; + + numerators[0] = p1[i] - m_lower[i]; + numerators[1] = m_upper[i] - p1[i]; + + denominators[0] = -d[i]; + denominators[1] = d[i]; + + for (u32 j = 0; j < 2; ++j) + { + float32 numerator = numerators[j]; + float32 denominator = denominators[j]; + + if (denominator == 0.0f) + { + // s is parallel to this half-space. + if (numerator < 0.0f) + { + // s is outside of this half-space. + // dot(n, p1) and dot(n, p2) < 0. + return false; + } + } + else + { + if (denominator < 0.0f) + { + // s enters this half-space. + if (numerator < lower * denominator) + { + // Increase lower. + lower = numerator / denominator; + } + } + else + { + // s exits the half-space. + if (numerator < upper * denominator) + { + // Decrease upper. + upper = numerator / denominator; + } + } + // Exit if intersection becomes empty. + if (upper < lower) + { + return false; + } + } + } } - if (min_t >= 0.0f && min_t >= max_t && max_t <= t) - { - return true; - } - - return false; + B3_ASSERT(lower >= 0.0f && lower <= maxFraction); + minFraction = lower; + return true; } }; diff --git a/include/bounce/collision/trees/dynamic_tree.h b/include/bounce/collision/trees/dynamic_tree.h index 227d68b..761c28c 100644 --- a/include/bounce/collision/trees/dynamic_tree.h +++ b/include/bounce/collision/trees/dynamic_tree.h @@ -211,8 +211,8 @@ inline void b3DynamicTree::RayCast(T* callback, const b3RayCastInput& input) con const b3Node* node = m_nodes + nodeIndex; - float32 minFraction = 0.0f; - if (node->aabb.TestRay(p1, p2, maxFraction, minFraction) == true) + float32 minFraction; + if (node->aabb.TestRay(minFraction, p1, p2, maxFraction) == true) { if (node->IsLeaf() == true) { diff --git a/include/bounce/collision/trees/static_tree.h b/include/bounce/collision/trees/static_tree.h index 1af50ad..069b855 100644 --- a/include/bounce/collision/trees/static_tree.h +++ b/include/bounce/collision/trees/static_tree.h @@ -174,8 +174,8 @@ inline void b3StaticTree::RayCast(T* callback, const b3RayCastInput& input) cons const b3Node* node = m_nodes + nodeIndex; - float32 minFraction = 0.0f; - if (node->aabb.TestRay(p1, p2, maxFraction, minFraction) == true) + float32 minFraction; + if (node->aabb.TestRay(minFraction, p1, p2, maxFraction) == true) { if (node->IsLeaf() == true) { From f4d9df413924c81445013fb6126eea464d16f18d Mon Sep 17 00:00:00 2001 From: Irlan <-> Date: Sat, 6 Oct 2018 00:54:22 -0300 Subject: [PATCH 009/198] externalize window --- examples/testbed/framework/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/testbed/framework/main.cpp b/examples/testbed/framework/main.cpp index 211d510..22ff529 100644 --- a/examples/testbed/framework/main.cpp +++ b/examples/testbed/framework/main.cpp @@ -31,7 +31,7 @@ #include // -static GLFWwindow* g_window; +GLFWwindow* g_window; // static Model* g_model; From 658ac8298ad59cfb2d9a3304ceedbb833fbae1e4 Mon Sep 17 00:00:00 2001 From: Irlan <-> Date: Sat, 6 Oct 2018 13:56:20 -0300 Subject: [PATCH 010/198] replace point click test by character test --- examples/testbed/framework/test_entries.cpp | 4 +- examples/testbed/tests/character_test.h | 569 ++++++++++++++++++++ examples/testbed/tests/point_click.h | 98 ---- 3 files changed, 571 insertions(+), 100 deletions(-) create mode 100644 examples/testbed/tests/character_test.h delete mode 100644 examples/testbed/tests/point_click.h diff --git a/examples/testbed/framework/test_entries.cpp b/examples/testbed/framework/test_entries.cpp index 6416061..69737b1 100644 --- a/examples/testbed/framework/test_entries.cpp +++ b/examples/testbed/framework/test_entries.cpp @@ -54,7 +54,7 @@ #include #include #include -#include +#include #include #include #include @@ -108,7 +108,7 @@ TestEntry g_tests[] = { "Box Pyramid Rows", &Pyramids::Create }, { "Ray Cast", &RayCast::Create }, { "Sensor Test", &SensorTest::Create }, - { "Point & Click", &PointClick::Create }, + { "Character", &CharacterTest::Create }, { "Body Types", &BodyTypes::Create }, { "Varying Friction", &VaryingFriction::Create }, { "Varying Restitution", &VaryingRestitution::Create }, diff --git a/examples/testbed/tests/character_test.h b/examples/testbed/tests/character_test.h new file mode 100644 index 0000000..8cf3c44 --- /dev/null +++ b/examples/testbed/tests/character_test.h @@ -0,0 +1,569 @@ +/* +* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef CHARACTER_TEST_H +#define CHARACTER_TEST_H + +struct Plane +{ + b3Vec3 n; + b3Vec3 p; +}; + +class CharacterController +{ +public: + CharacterController(b3SphereShape* shape) + { + m_characterShape = shape; + m_characterBody = m_characterShape->GetBody(); + m_world = m_characterBody->GetWorld(); + m_translation.SetZero(); + m_isGrounded = false; + + UpdateGrounded(); + } + + ~CharacterController() + { + + } + + bool IsGrounded() const + { + return m_isGrounded; + } + + void Move(const b3Vec3& translation) + { + m_translation += translation; + } + + void Solve() + { + b3StackArray shapes; + CollectStaticShapes(shapes); + + b3StackArray planes; + CollectOverlapPlanes(planes, shapes); + + b3Vec3 startPosition = m_characterBody->GetWorldCenter(); + + b3Vec3 targetPosition = startPosition + m_translation; + + m_translation.SetZero(); + + b3Vec3 solvePosition = SolvePositionConstraints(planes, targetPosition); + + b3Vec3 oldSolvePosition = solvePosition; + + for (;;) + { + b3Vec3 translation = solvePosition - startPosition; + + CollectSweepPlanes(planes, shapes, translation); + + solvePosition = SolvePositionConstraints(planes, targetPosition); + + const float32 tolerance = 0.05f; + + if (b3DistanceSquared(oldSolvePosition, solvePosition) < tolerance * tolerance) + { + break; + } + + oldSolvePosition = solvePosition; + } + + // Update body + b3Quat orientation = m_characterBody->GetOrientation(); + + b3Vec3 axis; + float32 angle; + orientation.GetAxisAngle(&axis, &angle); + + m_characterBody->SetTransform(solvePosition, axis, angle); + } + + void Step() + { + Solve(); + + UpdateGrounded(); + } +private: + b3Sphere GetCharacterSphere() const + { + b3Sphere sphere; + sphere.vertex = m_characterBody->GetWorldPoint(m_characterShape->m_center); + sphere.radius = m_characterShape->m_radius; + return sphere; + } + + void UpdateGrounded() + { + b3StackArray shapes; + CollectStaticShapes(shapes); + + b3StackArray planes; + CollectOverlapPlanes(planes, shapes); + + m_isGrounded = false; + + for (u32 i = 0; i < planes.Count(); ++i) + { + Plane plane = planes[i]; + + if (b3Dot(b3Vec3_y, plane.n) > 0.0f) + { + m_isGrounded = true; + break; + } + } + } + + void CollectStaticShapes(b3Array& shapes) const + { + for (b3Body* b = m_world->GetBodyList().m_head; b; b = b->GetNext()) + { + if (b == m_characterBody) + { + continue; + } + + if (b->GetType() != e_staticBody) + { + continue; + } + + for (b3Shape* s = b->GetShapeList().m_head; s; s = s->GetNext()) + { + if (s->GetType() == e_meshShape) + { + continue; + } + + shapes.PushBack(s); + } + } + } + + void CollectOverlapPlanes(b3Array& planes, + const b3Array& shapes) const + { + b3Sphere sphere2 = GetCharacterSphere(); + + for (u32 i = 0; i < shapes.Count(); ++i) + { + b3Shape* shape1 = shapes[i]; + b3Body* body1 = shape1->GetBody(); + b3Transform xf1 = body1->GetTransform(); + + b3TestSphereOutput output; + bool overlap = shape1->TestSphere(&output, sphere2, xf1); + + if (overlap == false) + { + continue; + } + + Plane plane; + plane.n = output.normal; + plane.p = output.point; + + planes.PushBack(plane); + } + } + + void CollectSweepPlanes(b3Array& planes, + const b3Array& shapes, + const b3Vec3& translation2) const + { + if (b3LengthSquared(translation2) < B3_EPSILON * B3_EPSILON) + { + return; + } + + b3Transform xf2 = m_characterBody->GetTransform(); + + b3ShapeGJKProxy proxy2; + proxy2.Set(m_characterShape, 0); + + for (u32 i = 0; i < shapes.Count(); ++i) + { + b3Shape* shape1 = shapes[i]; + b3Body* body1 = shape1->GetBody(); + + b3Transform xf1 = body1->GetTransform(); + + b3ShapeGJKProxy proxy1; + proxy1.Set(shape1, 0); + + b3GJKShapeCastOutput output; + bool hit = b3GJKShapeCast(&output, xf1, proxy1, xf2, proxy2, translation2); + + if (hit == false) + { + continue; + } + + Plane plane; + plane.n = output.normal; + plane.p = output.point; + + planes.PushBack(plane); + } + } + + b3Vec3 SolvePositionConstraints(const b3Array& planes, const b3Vec3& position) const + { + b3Vec3 localCenter = m_characterBody->GetLocalCenter(); + b3Vec3 c = position; + b3Quat q = m_characterBody->GetOrientation(); + + const u32 kMaxPositionIterations = 10; + + for (u32 i = 0; i < kMaxPositionIterations; ++i) + { + for (u32 j = 0; j < planes.Count(); ++j) + { + Plane nc = planes[j]; + b3Plane plane(nc.n, nc.p); + + b3Transform xf; + xf.rotation = b3QuatMat33(q); + xf.position = c - b3Mul(xf.rotation, localCenter); + + b3Sphere sphere; + sphere.vertex = b3Mul(xf, m_characterShape->m_center); + sphere.radius = m_characterShape->m_radius; + + float32 separation = b3Distance(sphere.vertex, plane); + + if (separation > sphere.radius) + { + continue; + } + + separation -= sphere.radius; + + float32 C = b3Clamp(B3_BAUMGARTE * (separation + B3_LINEAR_SLOP), -B3_MAX_LINEAR_CORRECTION, 0.0f); + + b3Vec3 normal = plane.normal; + b3Vec3 P = C * normal; + + c -= P; + } + } + + return c; + } + + b3World* m_world; + b3Body* m_characterBody; + b3SphereShape* m_characterShape; + b3Vec3 m_translation; + bool m_isGrounded; +}; + +class CharacterTest : public Test +{ +public: + CharacterTest() + { + { + b3BodyDef bdef; + b3Body* body = m_world.CreateBody(bdef); + + b3HullShape hs; + hs.m_hull = &m_groundHull; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(-10.0f, 3.0f, 0.0f); + + b3Body* body = m_world.CreateBody(bdef); + + static b3BoxHull boxHull; + boxHull.Set(2.0f, 2.0f, 5.0f); + + b3HullShape hs; + hs.m_hull = &boxHull; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(-10.0f, 9.0f, 0.0f); + + b3Body* body = m_world.CreateBody(bdef); + + static b3BoxHull boxHull; + boxHull.Set(1.0f, 4.0f, 1.0f); + + b3HullShape hs; + hs.m_hull = &boxHull; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(-10.0f, 2.7f, 7.0f); + bdef.orientation.Set(b3Vec3_x, 0.25f * B3_PI); + + b3Body* body = m_world.CreateBody(bdef); + + static b3BoxHull boxHull; + boxHull.Set(2.0f, 0.25f, 3.0f); + + b3HullShape hs; + hs.m_hull = &boxHull; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(-10.0f, 2.7f, -7.0f); + bdef.orientation.Set(b3Vec3_x, -0.25f * B3_PI); + + b3Body* body = m_world.CreateBody(bdef); + + static b3BoxHull boxHull; + boxHull.Set(2.0f, 0.25f, 3.0f); + + b3HullShape hs; + hs.m_hull = &boxHull; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(10.0f, 3.0f, 0.0f); + + b3Body* body = m_world.CreateBody(bdef); + + static b3BoxHull boxHull; + boxHull.Set(2.0f, 2.0f, 5.0f); + + b3HullShape hs; + hs.m_hull = &boxHull; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(10.0f, 9.0f, 0.0f); + + b3Body* body = m_world.CreateBody(bdef); + + static b3BoxHull boxHull; + boxHull.Set(1.0f, 4.0f, 1.0f); + + b3HullShape hs; + hs.m_hull = &boxHull; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(10.0f, 2.7f, 7.0f); + bdef.orientation.Set(b3Vec3_x, 0.25f * B3_PI); + + b3Body* body = m_world.CreateBody(bdef); + + static b3BoxHull boxHull; + boxHull.Set(2.0f, 0.25f, 3.0f); + + b3HullShape hs; + hs.m_hull = &boxHull; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(10.0f, 2.7f, -7.0f); + bdef.orientation.Set(b3Vec3_x, -0.25f * B3_PI); + + b3Body* body = m_world.CreateBody(bdef); + + static b3BoxHull boxHull; + boxHull.Set(2.0f, 0.25f, 3.0f); + + b3HullShape hs; + hs.m_hull = &boxHull; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.position.Set(0.0f, 4.0f, 0.0f); + + b3Body* body = m_world.CreateBody(bdef); + + static b3BoxHull boxHull; + boxHull.Set(8.0f, 1.0f, 2.0f); + + b3HullShape hs; + hs.m_hull = &boxHull; + + b3ShapeDef sdef; + sdef.shape = &hs; + + b3Shape* shape = body->CreateShape(sdef); + } + + { + b3BodyDef bdef; + bdef.type = b3BodyType::e_kinematicBody; + bdef.position.Set(0.0f, 1.0f, 20.0f); + + b3Body* body = m_world.CreateBody(bdef); + + b3SphereShape sphere; + sphere.m_center.SetZero(); + sphere.m_radius = 1.0f; + + b3ShapeDef sdef; + sdef.shape = &sphere; + + b3SphereShape* shape = (b3SphereShape*)body->CreateShape(sdef); + + m_characterController = new CharacterController(shape); + } + } + + ~CharacterTest() + { + delete m_characterController; + } + + void Step() + { + g_draw->DrawString(b3Color_white, "Arrows - Walk"); + g_draw->DrawString(b3Color_white, "Space - Jump"); + + float32 dt = g_testSettings->inv_hertz; + + const float32 walkSpeed = 10.0f; + const float32 jumpSpeed = 300.0f; + const float32 gravity = 50.0f * 9.8f; + b3Vec3 velocity = b3Vec3_zero; + + bool isGounded = m_characterController->IsGrounded(); + + if (isGounded) + { + extern GLFWwindow* g_window; + + bool leftDown = glfwGetKey(g_window, GLFW_KEY_LEFT); + bool rightDown = glfwGetKey(g_window, GLFW_KEY_RIGHT); + bool downDown = glfwGetKey(g_window, GLFW_KEY_DOWN); + bool upDown = glfwGetKey(g_window, GLFW_KEY_UP); + bool spaceDown = glfwGetKey(g_window, GLFW_KEY_SPACE); + + // Walk + if (leftDown) + { + velocity.x -= walkSpeed; + } + + if (rightDown) + { + velocity.x += walkSpeed; + } + + if (upDown) + { + velocity.z -= walkSpeed; + } + + if (downDown) + { + velocity.z += walkSpeed; + } + + // Jump + if (spaceDown) + { + velocity.y += jumpSpeed; + } + } + + // Integrate gravity + velocity.y -= dt * gravity; + + // Compute translation + b3Vec3 translation = dt * velocity; + + // Move character + m_characterController->Move(translation); + + // Step controllers + m_characterController->Step(); + + // Step world + Test::Step(); + } + + static Test* Create() + { + return new CharacterTest(); + } + + CharacterController* m_characterController; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/point_click.h b/examples/testbed/tests/point_click.h deleted file mode 100644 index 0f1f3a1..0000000 --- a/examples/testbed/tests/point_click.h +++ /dev/null @@ -1,98 +0,0 @@ -/* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef POINT_CLICK_H -#define POINT_CLICK_H - -class PointClick : public Test -{ -public: - PointClick() - { - { - b3BodyDef bdef; - b3Body* ground = m_world.CreateBody(bdef); - - b3MeshShape ms; - ms.m_mesh = &m_groundMesh; - - b3ShapeDef sd; - sd.shape = &ms; - - ground->CreateShape(sd); - } - - { - b3BodyDef bdef; - bdef.type = b3BodyType::e_dynamicBody; - bdef.fixedRotationY = true; - bdef.position.Set(0.0f, 5.0f, 0.0f); - - m_character = m_world.CreateBody(bdef); - - b3CapsuleShape cap; - cap.m_centers[0].Set(0.0f, 1.0f, 0.0f); - cap.m_centers[1].Set(0.0f, -1.0f, 0.0f); - cap.m_radius = 1.0f; - - b3ShapeDef sdef; - sdef.shape = ∩ - sdef.density = 1.0f; - sdef.friction = 0.5f; - - m_character->CreateShape(sdef); - } - } - - void BeginDragging() - { - if (m_bodyDragger.GetBody() == m_character) - { - m_bodyDragger.StopDragging(); - } - } - - void Step() - { - if (m_bodyDragger.IsDragging()) - { - if (m_bodyDragger.GetBody() != m_character) - { - b3Vec3 p1 = m_character->GetPosition(); - b3Vec3 p2 = m_bodyDragger.GetPointA(); - - b3Vec3 n = b3Normalize(p2 - p1); - const float32 k = 1000.0f; - b3Vec3 f = k * n; - - m_character->ApplyForceToCenter(f, true); - } - } - - Test::Step(); - } - - static Test* Create() - { - return new PointClick(); - } - - b3Body* m_character; -}; - -#endif \ No newline at end of file From 17cf8377124f4bbe63f7dcaa4c5ac8525786be53 Mon Sep 17 00:00:00 2001 From: Irlan <-> Date: Sat, 6 Oct 2018 13:58:56 -0300 Subject: [PATCH 011/198] move a function --- examples/testbed/tests/character_test.h | 92 ++++++++++++------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/examples/testbed/tests/character_test.h b/examples/testbed/tests/character_test.h index 8cf3c44..bc4b7b2 100644 --- a/examples/testbed/tests/character_test.h +++ b/examples/testbed/tests/character_test.h @@ -54,52 +54,6 @@ public: m_translation += translation; } - void Solve() - { - b3StackArray shapes; - CollectStaticShapes(shapes); - - b3StackArray planes; - CollectOverlapPlanes(planes, shapes); - - b3Vec3 startPosition = m_characterBody->GetWorldCenter(); - - b3Vec3 targetPosition = startPosition + m_translation; - - m_translation.SetZero(); - - b3Vec3 solvePosition = SolvePositionConstraints(planes, targetPosition); - - b3Vec3 oldSolvePosition = solvePosition; - - for (;;) - { - b3Vec3 translation = solvePosition - startPosition; - - CollectSweepPlanes(planes, shapes, translation); - - solvePosition = SolvePositionConstraints(planes, targetPosition); - - const float32 tolerance = 0.05f; - - if (b3DistanceSquared(oldSolvePosition, solvePosition) < tolerance * tolerance) - { - break; - } - - oldSolvePosition = solvePosition; - } - - // Update body - b3Quat orientation = m_characterBody->GetOrientation(); - - b3Vec3 axis; - float32 angle; - orientation.GetAxisAngle(&axis, &angle); - - m_characterBody->SetTransform(solvePosition, axis, angle); - } - void Step() { Solve(); @@ -274,6 +228,52 @@ private: return c; } + void Solve() + { + b3StackArray shapes; + CollectStaticShapes(shapes); + + b3StackArray planes; + CollectOverlapPlanes(planes, shapes); + + b3Vec3 startPosition = m_characterBody->GetWorldCenter(); + + b3Vec3 targetPosition = startPosition + m_translation; + + m_translation.SetZero(); + + b3Vec3 solvePosition = SolvePositionConstraints(planes, targetPosition); + + b3Vec3 oldSolvePosition = solvePosition; + + for (;;) + { + b3Vec3 translation = solvePosition - startPosition; + + CollectSweepPlanes(planes, shapes, translation); + + solvePosition = SolvePositionConstraints(planes, targetPosition); + + const float32 tolerance = 0.05f; + + if (b3DistanceSquared(oldSolvePosition, solvePosition) < tolerance * tolerance) + { + break; + } + + oldSolvePosition = solvePosition; + } + + // Update body + b3Quat orientation = m_characterBody->GetOrientation(); + + b3Vec3 axis; + float32 angle; + orientation.GetAxisAngle(&axis, &angle); + + m_characterBody->SetTransform(solvePosition, axis, angle); + } + b3World* m_world; b3Body* m_characterBody; b3SphereShape* m_characterShape; From cd4afc58b06eb9b65822ca33cc8ae11018f5f82d Mon Sep 17 00:00:00 2001 From: Irlan <-> Date: Sat, 6 Oct 2018 20:45:41 -0300 Subject: [PATCH 012/198] bugfix --- src/bounce/dynamics/shapes/hull_shape.cpp | 64 +++++++++++++++++------ 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/src/bounce/dynamics/shapes/hull_shape.cpp b/src/bounce/dynamics/shapes/hull_shape.cpp index 7428eba..1c1333e 100644 --- a/src/bounce/dynamics/shapes/hull_shape.cpp +++ b/src/bounce/dynamics/shapes/hull_shape.cpp @@ -17,8 +17,10 @@ */ #include -#include #include +#include +#include +#include b3HullShape::b3HullShape() { @@ -184,10 +186,45 @@ bool b3HullShape::TestSphere(const b3Sphere& sphere, const b3Transform& xf) cons bool b3HullShape::TestSphere(b3TestSphereOutput* output, const b3Sphere& sphere, const b3Transform& xf) const { - // Perform computations in the local space of the first hull. - b3Vec3 support = b3MulT(xf, sphere.vertex); - float32 radius = m_radius + sphere.radius; + b3Transform xf1 = xf; + b3Transform xf2 = b3Transform_identity; + + b3ShapeGJKProxy proxy1(this, 0); + b3GJKProxy proxy2; + proxy2.vertexCount = 1; + proxy2.vertexBuffer[0] = sphere.vertex; + proxy2.vertices = proxy2.vertexBuffer; + proxy2.radius = sphere.radius; + + b3GJKOutput gjk = b3GJK(xf1, proxy1, xf2, proxy2); + + float32 r1 = proxy1.radius; + float32 r2 = proxy2.radius; + + float32 totalRadius = r1 + r2; + + if (gjk.distance > totalRadius) + { + return false; + } + + if (gjk.distance > 0.0f) + { + b3Vec3 c1 = gjk.point1; + b3Vec3 c2 = gjk.point2; + b3Vec3 n = (c2 - c1) / gjk.distance; + + output->point = c1; + output->normal = n; + output->separation = gjk.distance - totalRadius; + + return true; + } + + // Perform computations in the local space of the first hull. + b3Vec3 support = b3MulT(xf1, sphere.vertex); + u32 maxIndex = ~0; float32 maxSeparation = -B3_MAX_FLOAT; @@ -196,7 +233,7 @@ bool b3HullShape::TestSphere(b3TestSphereOutput* output, const b3Sphere& sphere, b3Plane plane = m_hull->GetPlane(i); float32 separation = b3Distance(support, plane); - if (separation > radius) + if (separation > totalRadius) { return false; } @@ -208,17 +245,14 @@ bool b3HullShape::TestSphere(b3TestSphereOutput* output, const b3Sphere& sphere, } } - if (maxIndex != ~0) - { - b3Plane plane = b3Mul(xf, m_hull->GetPlane(maxIndex)); - output->point = b3ClosestPointOnPlane(sphere.vertex, plane); - output->separation = maxSeparation - radius; - output->normal = plane.normal; - return true; - } + B3_ASSERT(maxIndex != ~0); + + b3Plane plane = b3Mul(xf1, m_hull->GetPlane(maxIndex)); - B3_ASSERT(false); - return false; + output->point = b3ClosestPointOnPlane(sphere.vertex, plane); + output->separation = maxSeparation - totalRadius; + output->normal = plane.normal; + return true; } bool b3HullShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, const b3Transform& xf) const From 69e2cd4c563ae42fba3acf937d278456ada72405 Mon Sep 17 00:00:00 2001 From: Irlan <-> Date: Sat, 6 Oct 2018 20:47:06 -0300 Subject: [PATCH 013/198] TestSphere for triangles --- include/bounce/dynamics/shapes/mesh_shape.h | 2 ++ include/bounce/dynamics/shapes/shape.h | 1 + src/bounce/dynamics/shapes/mesh_shape.cpp | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/include/bounce/dynamics/shapes/mesh_shape.h b/include/bounce/dynamics/shapes/mesh_shape.h index c8215e6..7436e3c 100644 --- a/include/bounce/dynamics/shapes/mesh_shape.h +++ b/include/bounce/dynamics/shapes/mesh_shape.h @@ -41,6 +41,8 @@ public: bool TestSphere(b3TestSphereOutput* output, const b3Sphere& sphere, const b3Transform& xf) const; + bool TestSphere(b3TestSphereOutput* output, const b3Sphere& sphere, const b3Transform& xf, u32 childIndex) const; + bool RayCast(b3RayCastOutput* output, const b3RayCastInput& input, const b3Transform& xf) const; bool RayCast(b3RayCastOutput* output, const b3RayCastInput& input, const b3Transform& xf, u32 childIndex) const; diff --git a/include/bounce/dynamics/shapes/shape.h b/include/bounce/dynamics/shapes/shape.h index 53aa35e..c62df71 100644 --- a/include/bounce/dynamics/shapes/shape.h +++ b/include/bounce/dynamics/shapes/shape.h @@ -153,6 +153,7 @@ protected: friend class b3Body; friend class b3Contact; friend class b3ContactManager; + friend class b3MeshShape; friend class b3MeshContact; friend class b3ContactSolver; friend class b3List1; diff --git a/src/bounce/dynamics/shapes/mesh_shape.cpp b/src/bounce/dynamics/shapes/mesh_shape.cpp index 7d2fa9e..012028e 100644 --- a/src/bounce/dynamics/shapes/mesh_shape.cpp +++ b/src/bounce/dynamics/shapes/mesh_shape.cpp @@ -17,7 +17,9 @@ */ #include +#include #include +#include b3MeshShape::b3MeshShape() { @@ -88,6 +90,24 @@ bool b3MeshShape::TestSphere(b3TestSphereOutput* output, const b3Sphere& sphere, return false; } +bool b3MeshShape::TestSphere(b3TestSphereOutput* output, const b3Sphere& sphere, const b3Transform& xf, u32 index) const +{ + B3_ASSERT(index < m_mesh->triangleCount); + b3Triangle* triangle = m_mesh->triangles + index; + b3Vec3 v1 = m_mesh->vertices[triangle->v1]; + b3Vec3 v2 = m_mesh->vertices[triangle->v2]; + b3Vec3 v3 = m_mesh->vertices[triangle->v3]; + + b3TriangleHull hull(v1, v2, v3); + + b3HullShape hullShape; + hullShape.m_body = m_body; + hullShape.m_hull = &hull; + hullShape.m_radius = B3_HULL_RADIUS; + + return hullShape.TestSphere(output, sphere, xf); +} + bool b3MeshShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, const b3Transform& xf, u32 index) const { B3_ASSERT(index < m_mesh->triangleCount); From f598c72faecfb4b9b0550ee03c2001d9eedced9e Mon Sep 17 00:00:00 2001 From: Irlan <-> Date: Sat, 6 Oct 2018 20:48:16 -0300 Subject: [PATCH 014/198] update a test --- examples/testbed/tests/character_test.h | 105 +++++++++++++++++------- 1 file changed, 75 insertions(+), 30 deletions(-) diff --git a/examples/testbed/tests/character_test.h b/examples/testbed/tests/character_test.h index bc4b7b2..762bf0c 100644 --- a/examples/testbed/tests/character_test.h +++ b/examples/testbed/tests/character_test.h @@ -83,9 +83,14 @@ private: { Plane plane = planes[i]; - if (b3Dot(b3Vec3_y, plane.n) > 0.0f) + float32 cosine = b3Dot(b3Vec3_y, plane.n); + + // ~80 degrees + const float32 kTol = 0.17f; + + if (cosine > kTol) { - m_isGrounded = true; + m_isGrounded = true; break; } } @@ -107,11 +112,6 @@ private: for (b3Shape* s = b->GetShapeList().m_head; s; s = s->GetNext()) { - if (s->GetType() == e_meshShape) - { - continue; - } - shapes.PushBack(s); } } @@ -128,19 +128,40 @@ private: b3Body* body1 = shape1->GetBody(); b3Transform xf1 = body1->GetTransform(); - b3TestSphereOutput output; - bool overlap = shape1->TestSphere(&output, sphere2, xf1); - - if (overlap == false) + if (shape1->GetType() == e_meshShape) { - continue; + b3MeshShape* meshShape1 = (b3MeshShape*)shape1; + const b3Mesh* mesh1 = meshShape1->m_mesh; + + for (u32 j = 0; j < mesh1->triangleCount; ++j) + { + b3TestSphereOutput output; + bool overlap = meshShape1->TestSphere(&output, sphere2, xf1, j); + + if (overlap) + { + Plane plane; + plane.n = output.normal; + plane.p = output.point; + + planes.PushBack(plane); + } + } } + else + { + b3TestSphereOutput output; + bool overlap = shape1->TestSphere(&output, sphere2, xf1); - Plane plane; - plane.n = output.normal; - plane.p = output.point; + if (overlap) + { + Plane plane; + plane.n = output.normal; + plane.p = output.point; - planes.PushBack(plane); + planes.PushBack(plane); + } + } } } @@ -165,22 +186,46 @@ private: b3Transform xf1 = body1->GetTransform(); - b3ShapeGJKProxy proxy1; - proxy1.Set(shape1, 0); - - b3GJKShapeCastOutput output; - bool hit = b3GJKShapeCast(&output, xf1, proxy1, xf2, proxy2, translation2); - - if (hit == false) + if (shape1->GetType() == e_meshShape) { - continue; + b3MeshShape* meshShape1 = (b3MeshShape*)shape1; + const b3Mesh* mesh1 = meshShape1->m_mesh; + + for (u32 j = 0; j < mesh1->triangleCount; ++j) + { + b3ShapeGJKProxy proxy1; + proxy1.Set(shape1, j); + + b3GJKShapeCastOutput output; + bool hit = b3GJKShapeCast(&output, xf1, proxy1, xf2, proxy2, translation2); + + if (hit) + { + Plane plane; + plane.n = output.normal; + plane.p = output.point; + + planes.PushBack(plane); + } + } } + else + { + b3ShapeGJKProxy proxy1; + proxy1.Set(shape1, 0); - Plane plane; - plane.n = output.normal; - plane.p = output.point; + b3GJKShapeCastOutput output; + bool hit = b3GJKShapeCast(&output, xf1, proxy1, xf2, proxy2, translation2); - planes.PushBack(plane); + if (hit) + { + Plane plane; + plane.n = output.normal; + plane.p = output.point; + + planes.PushBack(plane); + } + } } } @@ -298,7 +343,7 @@ public: b3Shape* shape = body->CreateShape(sdef); } - + { b3BodyDef bdef; bdef.position.Set(-10.0f, 3.0f, 0.0f); @@ -503,7 +548,7 @@ public: b3Vec3 velocity = b3Vec3_zero; bool isGounded = m_characterController->IsGrounded(); - + if (isGounded) { extern GLFWwindow* g_window; From c0e324c98814a6a75161c7245e5c1d0661c42949 Mon Sep 17 00:00:00 2001 From: Irlan <-> Date: Sun, 7 Oct 2018 11:20:17 -0300 Subject: [PATCH 015/198] remove indirection --- .../contacts/collide/collide_capsules.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/bounce/dynamics/contacts/collide/collide_capsules.cpp b/src/bounce/dynamics/contacts/collide/collide_capsules.cpp index 7f1a558..58463f7 100644 --- a/src/bounce/dynamics/contacts/collide/collide_capsules.cpp +++ b/src/bounce/dynamics/contacts/collide/collide_capsules.cpp @@ -24,7 +24,7 @@ #include // Compute the closest point on a segment to a point. -static b3Vec3 b3ClosestPoint(const b3Vec3& Q, const b3Capsule& hull) +static b3Vec3 b3ClosestPointOnSegment(const b3Vec3& Q, const b3Capsule& hull) { b3Vec3 A = hull.vertices[0]; b3Vec3 B = hull.vertices[1]; @@ -81,13 +81,13 @@ static void b3ClosestPoints(b3Vec3& C1, b3Vec3& C2, if (L1 < B3_LINEAR_SLOP) { C1 = P1; - C2 = b3ClosestPoint(P1, hull2); + C2 = b3ClosestPointOnSegment(P1, hull2); return; } if (L2 < B3_LINEAR_SLOP) { - C1 = b3ClosestPoint(P2, hull1); + C1 = b3ClosestPointOnSegment(P2, hull1); C2 = P2; return; } @@ -144,11 +144,11 @@ static void b3ClosestPoints(b3Vec3& C1, b3Vec3& C2, C2 = P2; } - C1 = b3ClosestPoint(C1, hull1); + C1 = b3ClosestPointOnSegment(C1, hull1); - C2 = b3ClosestPoint(C1, hull2); + C2 = b3ClosestPointOnSegment(C1, hull2); - C1 = b3ClosestPoint(C2, hull1); + C1 = b3ClosestPointOnSegment(C2, hull1); } static bool b3AreParalell(const b3Capsule& hull1, const b3Capsule& hull2) @@ -211,8 +211,8 @@ void b3CollideCapsuleAndCapsule(b3Manifold& manifold, if (clipCount == 2) { - b3Vec3 cp1 = b3ClosestPointOnSegment(clipEdge1[0].position, hull2.vertices[0], hull2.vertices[1]); - b3Vec3 cp2 = b3ClosestPointOnSegment(clipEdge1[1].position, hull2.vertices[0], hull2.vertices[1]); + b3Vec3 cp1 = b3ClosestPointOnSegment(clipEdge1[0].position, hull2); + b3Vec3 cp2 = b3ClosestPointOnSegment(clipEdge1[1].position, hull2); float32 d1 = b3Distance(clipEdge1[0].position, cp1); float32 d2 = b3Distance(clipEdge1[1].position, cp2); From b48b16843a1ccea7dc255d52fc94eb965237789e Mon Sep 17 00:00:00 2001 From: Irlan <-> Date: Sun, 7 Oct 2018 20:48:26 -0300 Subject: [PATCH 016/198] removed a couple of bugs in the profiler --- examples/testbed/framework/main.cpp | 6 +++- examples/testbed/framework/profiler.cpp | 35 +++++++++++-------- examples/testbed/framework/profiler.h | 4 +-- .../testbed/framework/recorder_profiler.cpp | 25 +++++++++++-- .../testbed/framework/recorder_profiler.h | 2 ++ examples/testbed/framework/test.cpp | 10 ++---- src/bounce/dynamics/cloth/cloth.cpp | 10 +++--- src/bounce/dynamics/cloth/cloth_solver.cpp | 2 +- 8 files changed, 60 insertions(+), 34 deletions(-) diff --git a/examples/testbed/framework/main.cpp b/examples/testbed/framework/main.cpp index 22ff529..9cd3863 100644 --- a/examples/testbed/framework/main.cpp +++ b/examples/testbed/framework/main.cpp @@ -124,7 +124,11 @@ static void Run() for (u32 i = 0; i < records.Count(); ++i) { const ProfilerRecord& r = records[i]; - g_draw->DrawString(b3Color_white, "%s %.4f (%.4f) [ms]", r.name, r.elapsed, r.maxElapsed); + + if (r.elapsed > 0.0) + { + g_draw->DrawString(b3Color_white, "%s %.4f (%.4f) [ms]", r.name, r.elapsed, r.maxElapsed); + } } } diff --git a/examples/testbed/framework/profiler.cpp b/examples/testbed/framework/profiler.cpp index bc72572..8a47078 100644 --- a/examples/testbed/framework/profiler.cpp +++ b/examples/testbed/framework/profiler.cpp @@ -30,7 +30,7 @@ Profiler::~Profiler() { } -bool Profiler::PushEvent(const char* name) +void Profiler::PushEvent(const char* name) { m_time.Update(); @@ -43,12 +43,8 @@ bool Profiler::PushEvent(const char* name) e.parent = m_top; ProfilerEvent* back = m_events.Push(e); - if (back) - { - m_top = back; - } - - return back != NULL; + B3_ASSERT(back); + m_top = back; } void Profiler::PopEvent() @@ -66,26 +62,37 @@ void Profiler::Begin() { // If this assert is hit then it means Profiler::End hasn't been called. B3_ASSERT(m_events.IsEmpty()); + B3_ASSERT(m_top == NULL); } void Profiler::End(ProfilerListener* listener) { - listener->BeginEvents(); + if (listener) + { + listener->BeginEvents(); + } while (m_events.IsEmpty() == false) { - const ProfilerEvent& e = m_events.Front(); + ProfilerEvent e = m_events.Front(); m_events.Pop(); - listener->BeginEvent(e.tid, e.pid, e.name, e.t0); + if (listener) + { + listener->BeginEvent(e.tid, e.pid, e.name, e.t0); - listener->EndEvent(e.tid, e.pid, e.name, e.t1); - - listener->Duration(e.name, e.t1 - e.t0); + listener->EndEvent(e.tid, e.pid, e.name, e.t1); + + listener->Duration(e.name, e.t1 - e.t0); + } } B3_ASSERT(m_events.IsEmpty()); + B3_ASSERT(m_top == NULL); - listener->EndEvents(); + if (listener) + { + listener->EndEvents(); + } } \ No newline at end of file diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h index f5c2d8b..b54384d 100644 --- a/examples/testbed/framework/profiler.h +++ b/examples/testbed/framework/profiler.h @@ -59,11 +59,9 @@ public: void End(ProfilerListener* listener); // Add a profiler event to the queue. - // Return true if the even has been added to the event queue - // or false if the queue is full. // You can control the maximum number of profiler events using // MAX_PROFILER_EVENTS. - bool PushEvent(const char* name); + void PushEvent(const char* name); // Remove the top profiler event. void PopEvent(); diff --git a/examples/testbed/framework/recorder_profiler.cpp b/examples/testbed/framework/recorder_profiler.cpp index 8b126cb..886689c 100644 --- a/examples/testbed/framework/recorder_profiler.cpp +++ b/examples/testbed/framework/recorder_profiler.cpp @@ -22,18 +22,37 @@ RecorderProfiler* g_profilerRecorder = nullptr; void RecorderProfiler::BeginEvents() { + m_count = 0; for (u32 i = 0; i < m_records.Count(); ++i) { m_records[i].elapsed = 0.0; + m_records[i].call = 0; } } void RecorderProfiler::EndEvents() { + for (u32 i = 0; i < m_records.Count(); ++i) + { + ProfilerRecord& r1 = m_records[i]; + + for (u32 j = i + 1; j < m_records.Count(); ++j) + { + ProfilerRecord& r2 = m_records[j]; + + if (r2.call < r1.call) + { + b3Swap(r1, r2); + break; + } + } + } } void RecorderProfiler::Add(const char* name, float64 elapsedTime) { + m_count += 1; + for (u32 i = 0; i < m_records.Count(); ++i) { ProfilerRecord& r = m_records[i]; @@ -41,14 +60,16 @@ void RecorderProfiler::Add(const char* name, float64 elapsedTime) { r.elapsed += elapsedTime; r.maxElapsed = b3Max(r.maxElapsed, elapsedTime); + r.call = m_count; return; } } ProfilerRecord r; r.name = name; - r.elapsed = 0.0; - r.maxElapsed = 0.0; + r.elapsed = elapsedTime; + r.maxElapsed = elapsedTime; + r.call = m_count; m_records.PushBack(r); } \ No newline at end of file diff --git a/examples/testbed/framework/recorder_profiler.h b/examples/testbed/framework/recorder_profiler.h index 5291901..0c74e41 100644 --- a/examples/testbed/framework/recorder_profiler.h +++ b/examples/testbed/framework/recorder_profiler.h @@ -28,6 +28,7 @@ struct ProfilerRecord float64 elapsed; float64 maxElapsed; const char* name; + u32 call; }; // The profiler recorder simply keeps profile events in an event buffer, @@ -45,6 +46,7 @@ public: const b3Array& GetRecords() const { return m_records; } private: b3StackArray m_records; + u32 m_count; }; extern RecorderProfiler* g_profilerRecorder; diff --git a/examples/testbed/framework/test.cpp b/examples/testbed/framework/test.cpp index 39170c1..befad6c 100644 --- a/examples/testbed/framework/test.cpp +++ b/examples/testbed/framework/test.cpp @@ -25,20 +25,14 @@ extern u32 b3_convexCalls, b3_convexCacheHits; extern u32 b3_gjkCalls, b3_gjkIters, b3_gjkMaxIters; extern bool b3_convexCache; -static bool push_ok = false; - void b3BeginProfileScope(const char* name) { - push_ok = g_profiler->PushEvent(name); + g_profiler->PushEvent(name); } void b3EndProfileScope() { - if (push_ok) - { - g_profiler->PopEvent(); - push_ok = false; - } + g_profiler->PopEvent(); } Test::Test() : diff --git a/src/bounce/dynamics/cloth/cloth.cpp b/src/bounce/dynamics/cloth/cloth.cpp index 95632c0..bd526a9 100644 --- a/src/bounce/dynamics/cloth/cloth.cpp +++ b/src/bounce/dynamics/cloth/cloth.cpp @@ -509,7 +509,7 @@ bool b3Cloth::RayCast(b3RayCastOutput* output, const b3RayCastInput* input, u32 void b3Cloth::UpdateBodyContacts() { - B3_PROFILE("Update Body Contacts"); + B3_PROFILE("Cloth Update Body Contacts"); // Clear buffer b3BodyContact* c = m_bodyContactList.m_head; @@ -588,7 +588,7 @@ void b3Cloth::UpdateBodyContacts() void b3Cloth::UpdateParticleContacts() { - B3_PROFILE("Update Particle Contacts"); + B3_PROFILE("Cloth Update Particle Contacts"); // Clear buffer b3ParticleContact* c = m_particleContactList.m_head; @@ -790,7 +790,7 @@ static B3_FORCE_INLINE void b3Solve3(float32 out[3], void b3Cloth::UpdateTriangleContacts() { - B3_PROFILE("Update Triangle Contacts"); + B3_PROFILE("Cloth Update Triangle Contacts"); // Clear buffer b3TriangleContact* c = m_triangleContactList.m_head; @@ -883,7 +883,7 @@ void b3Cloth::UpdateTriangleContacts() void b3Cloth::Solve(float32 dt, const b3Vec3& gravity) { - B3_PROFILE("Solve"); + B3_PROFILE("Cloth Solve"); // Solve b3ClothSolverDef solverDef; @@ -946,7 +946,7 @@ void b3Cloth::UpdateContacts() void b3Cloth::Step(float32 dt, const b3Vec3& gravity) { - B3_PROFILE("Step"); + B3_PROFILE("Cloth Step"); // Update contacts UpdateContacts(); diff --git a/src/bounce/dynamics/cloth/cloth_solver.cpp b/src/bounce/dynamics/cloth/cloth_solver.cpp index 763031f..e4d66b2 100644 --- a/src/bounce/dynamics/cloth/cloth_solver.cpp +++ b/src/bounce/dynamics/cloth/cloth_solver.cpp @@ -359,7 +359,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) void b3ClothSolver::Solve(b3DenseVec3& x, u32& iterations, const b3SparseSymMat33& A, const b3DenseVec3& b, const b3DiagMat33& S, const b3DenseVec3& z, const b3DenseVec3& y) const { - B3_PROFILE("Solve Ax = b"); + B3_PROFILE("Cloth Solve Ax = b"); // P = diag(A) b3DiagMat33 inv_P(m_particleCount); From f98374143a44a86f563bd270115fb6a7a093908e Mon Sep 17 00:00:00 2001 From: Irlan <-> Date: Mon, 8 Oct 2018 16:18:28 -0300 Subject: [PATCH 017/198] refactoring --- examples/testbed/framework/body_dragger.cpp | 98 ++++++++ examples/testbed/framework/body_dragger.h | 72 ++++++ examples/testbed/framework/cloth_dragger.cpp | 184 ++++++++++++++ .../cloth_dragger.h} | 64 +++-- examples/testbed/framework/test.cpp | 29 +-- examples/testbed/framework/test.h | 29 +-- examples/testbed/framework/test_entries.cpp | 1 - examples/testbed/tests/pinned_cloth.h | 87 +++++-- examples/testbed/tests/ray_cast.h | 4 +- examples/testbed/tests/self_collision.h | 83 ++++++- examples/testbed/tests/shirt.h | 63 ++++- examples/testbed/tests/table_cloth.h | 82 ++++++- examples/testbed/tests/tension_mapping.h | 75 +++++- include/bounce/bounce.h | 23 +- include/bounce/{dynamics => }/cloth/cloth.h | 72 +++--- .../cloth/cloth_contact_solver.h | 0 .../bounce/{dynamics => }/cloth/cloth_mesh.h | 0 .../{dynamics => }/cloth/cloth_solver.h | 0 .../bounce/{dynamics => }/cloth/dense_vec3.h | 0 .../bounce/{dynamics => }/cloth/diag_mat33.h | 2 +- include/bounce/{dynamics => }/cloth/force.h | 2 - include/bounce/{ => cloth}/garment/garment.h | 2 +- .../bounce/{ => cloth}/garment/garment_mesh.h | 0 .../{ => cloth}/garment/sewing_pattern.h | 0 .../bounce/{dynamics => }/cloth/particle.h | 4 +- .../{dynamics => }/cloth/sparse_sym_mat33.h | 4 +- .../{dynamics => }/cloth/spring_force.h | 2 +- include/bounce/controllers/body_dragger.h | 129 ---------- include/bounce/controllers/cloth_dragger.h | 224 ------------------ include/bounce/dynamics/cloth/bend_force.h | 158 ------------ include/bounce/dynamics/world.h | 49 +--- include/bounce/dynamics/world_listeners.h | 7 - include/bounce/{dynamics => }/rope/rope.h | 0 include/bounce/{dynamics => rope}/spatial.h | 0 premake5.lua | 6 + src/bounce/{dynamics => }/cloth/cloth.cpp | 82 +++---- .../cloth/cloth_contact_solver.cpp | 8 +- .../{dynamics => }/cloth/cloth_mesh.cpp | 8 +- .../{dynamics => }/cloth/cloth_solver.cpp | 22 +- src/bounce/{dynamics => }/cloth/force.cpp | 18 +- .../{ => cloth}/garment/garment_mesh.cpp | 6 +- src/bounce/{dynamics => }/cloth/particle.cpp | 10 +- .../{dynamics => }/cloth/spring_force.cpp | 10 +- src/bounce/dynamics/cloth/bend_force.cpp | 128 ---------- src/bounce/dynamics/world.cpp | 90 +------ src/bounce/{dynamics => }/rope/rope.cpp | 4 +- test/ignore.txt | 0 47 files changed, 895 insertions(+), 1046 deletions(-) create mode 100644 examples/testbed/framework/body_dragger.cpp create mode 100644 examples/testbed/framework/body_dragger.h create mode 100644 examples/testbed/framework/cloth_dragger.cpp rename examples/testbed/{tests/cloth_test.h => framework/cloth_dragger.h} (52%) rename include/bounce/{dynamics => }/cloth/cloth.h (85%) rename include/bounce/{dynamics => }/cloth/cloth_contact_solver.h (100%) rename include/bounce/{dynamics => }/cloth/cloth_mesh.h (100%) rename include/bounce/{dynamics => }/cloth/cloth_solver.h (100%) rename include/bounce/{dynamics => }/cloth/dense_vec3.h (100%) rename include/bounce/{dynamics => }/cloth/diag_mat33.h (98%) rename include/bounce/{dynamics => }/cloth/force.h (97%) rename include/bounce/{ => cloth}/garment/garment.h (98%) rename include/bounce/{ => cloth}/garment/garment_mesh.h (100%) rename include/bounce/{ => cloth}/garment/sewing_pattern.h (100%) rename include/bounce/{dynamics => }/cloth/particle.h (99%) rename include/bounce/{dynamics => }/cloth/sparse_sym_mat33.h (98%) rename include/bounce/{dynamics => }/cloth/spring_force.h (98%) delete mode 100644 include/bounce/controllers/body_dragger.h delete mode 100644 include/bounce/controllers/cloth_dragger.h delete mode 100644 include/bounce/dynamics/cloth/bend_force.h rename include/bounce/{dynamics => }/rope/rope.h (100%) rename include/bounce/{dynamics => rope}/spatial.h (100%) rename src/bounce/{dynamics => }/cloth/cloth.cpp (92%) rename src/bounce/{dynamics => }/cloth/cloth_contact_solver.cpp (99%) rename src/bounce/{dynamics => }/cloth/cloth_mesh.cpp (95%) rename src/bounce/{dynamics => }/cloth/cloth_solver.cpp (96%) rename src/bounce/{dynamics => }/cloth/force.cpp (79%) rename src/bounce/{ => cloth}/garment/garment_mesh.cpp (97%) rename src/bounce/{dynamics => }/cloth/particle.cpp (91%) rename src/bounce/{dynamics => }/cloth/spring_force.cpp (91%) delete mode 100644 src/bounce/dynamics/cloth/bend_force.cpp rename src/bounce/{dynamics => }/rope/rope.cpp (99%) delete mode 100644 test/ignore.txt diff --git a/examples/testbed/framework/body_dragger.cpp b/examples/testbed/framework/body_dragger.cpp new file mode 100644 index 0000000..58456a5 --- /dev/null +++ b/examples/testbed/framework/body_dragger.cpp @@ -0,0 +1,98 @@ +/* +* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include + +b3BodyDragger::b3BodyDragger(b3Ray3* ray, b3World* world) +{ + m_ray = ray; + m_world = world; + m_shape = nullptr; + m_mouseJoint = nullptr; +} + +b3BodyDragger::~b3BodyDragger() +{ + +} + +bool b3BodyDragger::StartDragging() +{ + B3_ASSERT(IsDragging() == false); + + b3RayCastSingleOutput out; + if (m_world->RayCastSingle(&out, m_ray->A(), m_ray->B()) == false) + { + return false; + } + + m_x = out.fraction; + m_shape = out.shape; + + b3BodyDef bd; + b3Body* groundBody = m_world->CreateBody(bd); + + b3Body* body = m_shape->GetBody(); + body->SetAwake(true); + + b3MouseJointDef jd; + jd.bodyA = groundBody; + jd.bodyB = body; + jd.target = out.point; + jd.maxForce = 2000.0f * body->GetMass(); + + m_mouseJoint = (b3MouseJoint*)m_world->CreateJoint(jd); + + m_p = body->GetLocalPoint(out.point); + + return true; +} + +void b3BodyDragger::Drag() +{ + B3_ASSERT(IsDragging() == true); + m_mouseJoint->SetTarget(GetPointB()); +} + +void b3BodyDragger::StopDragging() +{ + B3_ASSERT(IsDragging() == true); + b3Body* groundBody = m_mouseJoint->GetBodyA(); + m_world->DestroyJoint(m_mouseJoint); + m_mouseJoint = nullptr; + m_world->DestroyBody(groundBody); + m_shape = nullptr; +} + +b3Body* b3BodyDragger::GetBody() const +{ + B3_ASSERT(IsDragging() == true); + return m_shape->GetBody(); +} + +b3Vec3 b3BodyDragger::GetPointA() const +{ + B3_ASSERT(IsDragging() == true); + return m_shape->GetBody()->GetWorldPoint(m_p); +} + +b3Vec3 b3BodyDragger::GetPointB() const +{ + B3_ASSERT(IsDragging() == true); + return (1.0f - m_x) * m_ray->A() + m_x * m_ray->B(); +} \ No newline at end of file diff --git a/examples/testbed/framework/body_dragger.h b/examples/testbed/framework/body_dragger.h new file mode 100644 index 0000000..10d5a66 --- /dev/null +++ b/examples/testbed/framework/body_dragger.h @@ -0,0 +1,72 @@ +/* +* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_BODY_DRAGGER_H +#define B3_BODY_DRAGGER_H + +#include +#include +#include +#include +#include +#include + +// A body shape dragger. +class b3BodyDragger +{ +public: + b3BodyDragger(b3Ray3* ray, b3World* world); + ~b3BodyDragger(); + + bool StartDragging(); + + void Drag(); + + void StopDragging(); + + bool IsDragging() const; + + b3Ray3* GetRay() const; + + b3Body* GetBody() const; + + b3Vec3 GetPointA() const; + + b3Vec3 GetPointB() const; +private: + b3Ray3 * m_ray; + float32 m_x; + + b3World* m_world; + + b3Shape* m_shape; + b3Vec3 m_p; + b3MouseJoint* m_mouseJoint; +}; + +inline bool b3BodyDragger::IsDragging() const +{ + return m_shape != nullptr; +} + +inline b3Ray3* b3BodyDragger::GetRay() const +{ + return m_ray; +} + +#endif \ No newline at end of file diff --git a/examples/testbed/framework/cloth_dragger.cpp b/examples/testbed/framework/cloth_dragger.cpp new file mode 100644 index 0000000..ea228ba --- /dev/null +++ b/examples/testbed/framework/cloth_dragger.cpp @@ -0,0 +1,184 @@ +/* +* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include + +b3ClothDragger::b3ClothDragger(b3Ray3* ray, b3Cloth* cloth) +{ + m_spring = false; + m_ray = ray; + m_cloth = cloth; + m_triangle = nullptr; +} + +b3ClothDragger::~b3ClothDragger() +{ + +} + +bool b3ClothDragger::StartDragging() +{ + B3_ASSERT(IsDragging() == false); + + b3ClothRayCastSingleOutput rayOut; + if (m_cloth->RayCastSingle(&rayOut, m_ray->A(), m_ray->B()) == false) + { + return false; + } + + m_mesh = m_cloth->GetMesh(); + m_triangle = m_mesh->triangles + rayOut.triangle; + m_x = rayOut.fraction; + + b3Particle* p1 = m_cloth->GetVertexParticle(m_triangle->v1); + b3Particle* p2 = m_cloth->GetVertexParticle(m_triangle->v2); + b3Particle* p3 = m_cloth->GetVertexParticle(m_triangle->v3); + + b3Vec3 v1 = p1->GetPosition(); + b3Vec3 v2 = p2->GetPosition(); + b3Vec3 v3 = p3->GetPosition(); + + b3Vec3 B = GetPointB(); + + float32 wABC[4]; + b3BarycentricCoordinates(wABC, v1, v2, v3, B); + + if (wABC[3] > B3_EPSILON) + { + m_u = wABC[0] / wABC[3]; + m_v = wABC[1] / wABC[3]; + } + else + { + m_u = m_v = 0.0f; + } + + if (m_spring) + { + b3ParticleDef pd; + pd.type = e_staticParticle; + pd.position = B; + + m_particle = m_cloth->CreateParticle(pd); + + { + b3SpringForceDef sfd; + sfd.p1 = m_particle; + sfd.p2 = p1; + sfd.restLength = 0.0f; + sfd.structural = 10000.0f; + m_s1 = (b3SpringForce*)m_cloth->CreateForce(sfd); + } + + { + b3SpringForceDef sfd; + sfd.p1 = m_particle; + sfd.p2 = p2; + sfd.restLength = 0.0f; + sfd.structural = 10000.0f; + m_s2 = (b3SpringForce*)m_cloth->CreateForce(sfd); + } + + { + b3SpringForceDef sfd; + sfd.p1 = m_particle; + sfd.p2 = p3; + sfd.restLength = 0.0f; + sfd.structural = 10000.0f; + m_s3 = (b3SpringForce*)m_cloth->CreateForce(sfd); + } + } + else + { + m_t1 = p1->GetType(); + p1->SetType(e_staticParticle); + + m_t2 = p2->GetType(); + p2->SetType(e_staticParticle); + + m_t3 = p3->GetType(); + p3->SetType(e_staticParticle); + } + + return true; +} + +void b3ClothDragger::Drag() +{ + B3_ASSERT(IsDragging() == true); + + b3Vec3 A = GetPointA(); + b3Vec3 B = GetPointB(); + + b3Vec3 dx = B - A; + + if (m_spring) + { + m_particle->SetPosition(B); + } + else + { + b3Particle* p1 = m_cloth->GetVertexParticle(m_triangle->v1); + p1->ApplyTranslation(dx); + + b3Particle* p2 = m_cloth->GetVertexParticle(m_triangle->v2); + p2->ApplyTranslation(dx); + + b3Particle* p3 = m_cloth->GetVertexParticle(m_triangle->v3); + p3->ApplyTranslation(dx); + } +} + +void b3ClothDragger::StopDragging() +{ + B3_ASSERT(IsDragging() == true); + + if (m_spring) + { + m_cloth->DestroyForce(m_s1); + m_cloth->DestroyForce(m_s2); + m_cloth->DestroyForce(m_s3); + m_cloth->DestroyParticle(m_particle); + } + else + { + m_cloth->GetVertexParticle(m_triangle->v1)->SetType(m_t1); + m_cloth->GetVertexParticle(m_triangle->v2)->SetType(m_t2); + m_cloth->GetVertexParticle(m_triangle->v3)->SetType(m_t3); + } + + m_triangle = nullptr; +} + +b3Vec3 b3ClothDragger::GetPointA() const +{ + B3_ASSERT(IsDragging() == true); + + b3Vec3 A = m_cloth->GetVertexParticle(m_triangle->v1)->GetPosition(); + b3Vec3 B = m_cloth->GetVertexParticle(m_triangle->v2)->GetPosition(); + b3Vec3 C = m_cloth->GetVertexParticle(m_triangle->v3)->GetPosition(); + + return m_u * A + m_v * B + (1.0f - m_u - m_v) * C; +} + +b3Vec3 b3ClothDragger::GetPointB() const +{ + B3_ASSERT(IsDragging() == true); + return (1.0f - m_x) * m_ray->A() + m_x * m_ray->B(); +} \ No newline at end of file diff --git a/examples/testbed/tests/cloth_test.h b/examples/testbed/framework/cloth_dragger.h similarity index 52% rename from examples/testbed/tests/cloth_test.h rename to examples/testbed/framework/cloth_dragger.h index 3f95cce..5058f20 100644 --- a/examples/testbed/tests/cloth_test.h +++ b/examples/testbed/framework/cloth_dragger.h @@ -16,37 +16,55 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef CLOTH_TESH_H -#define CLOTH_TESH_H +#ifndef B3_CLOTH_DRAGGER_H +#define B3_CLOTH_DRAGGER_H -class ClothTest : public Test +#include +#include +#include +#include +#include + +// A cloth triangle dragger. +class b3ClothDragger { public: - ClothTest() - { - m_world.SetGravity(b3Vec3(0.0f, -10.0f, 0.0f)); - m_cloth = nullptr; - } + b3ClothDragger(b3Ray3* ray, b3Cloth* cloth); + ~b3ClothDragger(); - void Step() - { - Test::Step(); + bool IsDragging() const; - m_cloth->Draw(); + bool StartDragging(); - if (m_clothDragger.IsDragging() == true) - { - g_draw->DrawSegment(m_clothDragger.GetPointA(), m_clothDragger.GetPointB(), b3Color_white); - } + void Drag(); + + void StopDragging(); + + b3Vec3 GetPointA() const; + + b3Vec3 GetPointB() const; +private: + b3Ray3* m_ray; + float32 m_x; - extern u32 b3_clothSolverIterations; - g_draw->DrawString(b3Color_white, "Iterations = %u", b3_clothSolverIterations); - - float32 E = m_cloth->GetEnergy(); - g_draw->DrawString(b3Color_white, "E = %f", E); - } - b3Cloth* m_cloth; + const b3ClothMesh* m_mesh; + b3ClothMeshTriangle* m_triangle; + float32 m_u, m_v; + + bool m_spring; + + b3Particle* m_particle; + b3SpringForce* m_s1; + b3SpringForce* m_s2; + b3SpringForce* m_s3; + + b3ParticleType m_t1, m_t2, m_t3; }; +inline bool b3ClothDragger::IsDragging() const +{ + return m_triangle != nullptr; +} + #endif \ No newline at end of file diff --git a/examples/testbed/framework/test.cpp b/examples/testbed/framework/test.cpp index befad6c..12ca6db 100644 --- a/examples/testbed/framework/test.cpp +++ b/examples/testbed/framework/test.cpp @@ -18,7 +18,6 @@ #include #include -#include extern u32 b3_allocCalls, b3_maxAllocCalls; extern u32 b3_convexCalls, b3_convexCacheHits; @@ -36,8 +35,7 @@ void b3EndProfileScope() } Test::Test() : - m_bodyDragger(&m_ray, &m_world), - m_clothDragger(&m_ray, &m_world) + m_bodyDragger(&m_ray, &m_world) { b3Draw_draw = g_draw; b3_convexCache = g_testSettings->convexCache; @@ -115,11 +113,6 @@ void Test::Step() g_draw->DrawString(b3Color_white, "Convex Cache Hits %d (%f)", b3_convexCacheHits, convexCacheHitRatio); g_draw->DrawString(b3Color_white, "Frame Allocations %d (%d)", b3_allocCalls, b3_maxAllocCalls); } - - if (m_clothDragger.IsDragging() == true) - { - g_draw->DrawSegment(m_clothDragger.GetPointA(), m_clothDragger.GetPointB(), b3Color_white); - } } void Test::MouseMove(const b3Ray3& pw) @@ -130,11 +123,6 @@ void Test::MouseMove(const b3Ray3& pw) { m_bodyDragger.Drag(); } - - if (m_clothDragger.IsDragging() == true) - { - m_clothDragger.Drag(); - } } void Test::MouseLeftDown(const b3Ray3& pw) @@ -146,14 +134,6 @@ void Test::MouseLeftDown(const b3Ray3& pw) BeginDragging(); } } - - if (m_clothDragger.IsDragging() == false) - { - if (m_clothDragger.StartDragging() == true) - { - BeginDragging(); - } - } } void Test::MouseLeftUp(const b3Ray3& pw) @@ -164,11 +144,4 @@ void Test::MouseLeftUp(const b3Ray3& pw) EndDragging(); } - - if (m_clothDragger.IsDragging() == true) - { - m_clothDragger.StopDragging(); - - EndDragging(); - } } \ No newline at end of file diff --git a/examples/testbed/framework/test.h b/examples/testbed/framework/test.h index 481c2ea..4171804 100644 --- a/examples/testbed/framework/test.h +++ b/examples/testbed/framework/test.h @@ -22,6 +22,9 @@ #include #include +#include +#include + #include #include @@ -33,27 +36,6 @@ inline float32 RandomFloat(float32 a, float32 b) return a + r; } -class RayCastListener : public b3RayCastListener -{ -public: - float32 ReportShape(b3Shape* shape, const b3Vec3& point, const b3Vec3& normal, float32 fraction) - { - hit.shape = shape; - hit.point = point; - hit.normal = normal; - hit.fraction = fraction; - return 1.0f; - } - - float32 ReportCloth(b3Shape* shape, const b3Vec3& point, const b3Vec3& normal, float32 fraction) - { - B3_ASSERT(false); - return 1.0f; - } - - b3RayCastSingleShapeOutput hit; -}; - class Test : public b3ContactListener { public: @@ -77,10 +59,9 @@ public: void EndContact(b3Contact* c) override { } void PreSolve(b3Contact* c) override { } - b3World m_world; - b3Ray3 m_ray; - b3ClothDragger m_clothDragger; + + b3World m_world; b3BodyDragger m_bodyDragger; b3BoxHull m_groundHull; diff --git a/examples/testbed/framework/test_entries.cpp b/examples/testbed/framework/test_entries.cpp index 69737b1..6da7e6f 100644 --- a/examples/testbed/framework/test_entries.cpp +++ b/examples/testbed/framework/test_entries.cpp @@ -60,7 +60,6 @@ #include #include #include -#include #include #include #include diff --git a/examples/testbed/tests/pinned_cloth.h b/examples/testbed/tests/pinned_cloth.h index dfafeab..08fe1e8 100644 --- a/examples/testbed/tests/pinned_cloth.h +++ b/examples/testbed/tests/pinned_cloth.h @@ -19,7 +19,7 @@ #ifndef PINNED_CLOTH_H #define PINNED_CLOTH_H -class PinnedCloth : public ClothTest +class PinnedCloth : public Test { public: PinnedCloth() : m_rectangleGarment(5.0f, 5.0f) @@ -30,21 +30,26 @@ public: // Create 3D mesh m_rectangleClothMesh.Set(&m_rectangleGarmentMesh); - // - b3Mat33 dq = b3Mat33RotationX(0.5f * B3_PI); + // Rotate the mesh + b3Mat33 rotation = b3Mat33RotationX(0.5f * B3_PI); for (u32 i = 0; i < m_rectangleClothMesh.vertexCount; ++i) { - m_rectangleClothMesh.vertices[i] = dq * m_rectangleClothMesh.vertices[i]; + m_rectangleClothMesh.vertices[i] = rotation * m_rectangleClothMesh.vertices[i]; } + // Create cloth b3ClothDef def; def.mesh = &m_rectangleClothMesh; def.density = 0.2f; def.structural = 100000.0f; def.damping = 0.0f; - m_cloth = m_world.CreateCloth(def); + m_cloth = new b3Cloth(def); + m_cloth->SetGravity(b3Vec3(0.0f, -9.8f, 0.0f)); + m_cloth->SetWorld(&m_world); + + // Freeze some particles b3AABB3 aabb1; aabb1.m_lower.Set(-5.0f, -1.0f, -6.0f); aabb1.m_upper.Set(5.0f, 1.0f, -4.0f); @@ -65,23 +70,70 @@ public: p->SetType(e_staticParticle); } } + + m_clothDragger = new b3ClothDragger(&m_ray, m_cloth); + } + ~PinnedCloth() + { + delete m_clothDragger; + delete m_cloth; + } + + void Step() + { + Test::Step(); + + m_cloth->Step(g_testSettings->inv_hertz); + + m_cloth->Draw(); + + if (m_clothDragger->IsDragging()) { - b3BodyDef bd; - bd.type = e_dynamicBody; - bd.position.Set(0.0f, 5.0f, 0.0f); + b3Vec3 pA = m_clothDragger->GetPointA(); + b3Vec3 pB = m_clothDragger->GetPointB(); - b3Body* b = m_world.CreateBody(bd); + g_draw->DrawPoint(pA, 2.0f, b3Color_green); - b3SphereShape sphere; - sphere.m_center.SetZero(); - sphere.m_radius = 2.0f; + g_draw->DrawPoint(pB, 2.0f, b3Color_green); - b3ShapeDef sd; - sd.shape = &sphere; - sd.density = 1.0f; + g_draw->DrawSegment(pA, pB, b3Color_white); + } - b3Shape* s = b->CreateShape(sd); + extern u32 b3_clothSolverIterations; + g_draw->DrawString(b3Color_white, "Iterations = %d", b3_clothSolverIterations); + + float32 E = m_cloth->GetEnergy(); + g_draw->DrawString(b3Color_white, "E = %f", E); + } + + void MouseMove(const b3Ray3& pw) + { + Test::MouseMove(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->Drag(); + } + } + + void MouseLeftDown(const b3Ray3& pw) + { + Test::MouseLeftDown(pw); + + if (m_clothDragger->IsDragging() == false) + { + m_clothDragger->StartDragging(); + } + } + + void MouseLeftUp(const b3Ray3& pw) + { + Test::MouseLeftUp(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->StopDragging(); } } @@ -93,6 +145,9 @@ public: b3RectangleGarment m_rectangleGarment; b3GarmentMesh m_rectangleGarmentMesh; b3GarmentClothMesh m_rectangleClothMesh; + + b3Cloth* m_cloth; + b3ClothDragger* m_clothDragger; }; #endif \ No newline at end of file diff --git a/examples/testbed/tests/ray_cast.h b/examples/testbed/tests/ray_cast.h index 3b7e7ed..610627f 100644 --- a/examples/testbed/tests/ray_cast.h +++ b/examples/testbed/tests/ray_cast.h @@ -178,8 +178,8 @@ public: void CastRay(const b3Vec3 p1, const b3Vec3 p2) const { - b3RayCastSingleShapeOutput out; - if (m_world.RayCastSingleShape(&out, p1, p2)) + b3RayCastSingleOutput out; + if (m_world.RayCastSingle(&out, p1, p2)) { g_draw->DrawSegment(p1, out.point, b3Color_green); diff --git a/examples/testbed/tests/self_collision.h b/examples/testbed/tests/self_collision.h index 9a3ed52..3135b4f 100644 --- a/examples/testbed/tests/self_collision.h +++ b/examples/testbed/tests/self_collision.h @@ -19,7 +19,7 @@ #ifndef SELF_COLLISION_H #define SELF_COLLISION_H -class SelfCollision : public ClothTest +class SelfCollision : public Test { public: SelfCollision() : m_rectangleGarment(5.0f, 5.0f) @@ -30,19 +30,24 @@ public: // Create 3D mesh m_rectangleClothMesh.Set(&m_rectangleGarmentMesh); - b3Mat33 Rx = b3Mat33RotationX(0.5f * B3_PI); + // Rotate and translate the mesh + b3Mat33 rotation = b3Mat33RotationX(0.5f * B3_PI); for (u32 i = 0; i < m_rectangleClothMesh.vertexCount; ++i) { - m_rectangleClothMesh.vertices[i] = Rx * m_rectangleClothMesh.vertices[i]; + m_rectangleClothMesh.vertices[i] = rotation * m_rectangleClothMesh.vertices[i]; m_rectangleClothMesh.vertices[i].y += 5.0f; } + // Create cloth b3ClothDef def; def.mesh = &m_rectangleClothMesh; def.density = 1.0f; def.structural = 100000.0f; - m_cloth = m_world.CreateCloth(def); + m_cloth = new b3Cloth(def); + + m_cloth->SetGravity(b3Vec3(0.0f, -9.8f, 0.0f)); + m_cloth->SetWorld(&m_world); for (b3Particle* p = m_cloth->GetParticleList().m_head; p; p = p->GetNext()) { @@ -67,6 +72,71 @@ public: b->CreateShape(sd); } + + m_clothDragger = new b3ClothDragger(&m_ray, m_cloth); + } + + ~SelfCollision() + { + delete m_cloth; + delete m_clothDragger; + } + + void Step() + { + Test::Step(); + + m_cloth->Step(g_testSettings->inv_hertz); + + m_cloth->Draw(); + + if (m_clothDragger->IsDragging()) + { + b3Vec3 pA = m_clothDragger->GetPointA(); + b3Vec3 pB = m_clothDragger->GetPointB(); + + g_draw->DrawPoint(pA, 2.0f, b3Color_green); + + g_draw->DrawPoint(pB, 2.0f, b3Color_green); + + g_draw->DrawSegment(pA, pB, b3Color_white); + } + + extern u32 b3_clothSolverIterations; + g_draw->DrawString(b3Color_white, "Iterations = %d", b3_clothSolverIterations); + + float32 E = m_cloth->GetEnergy(); + g_draw->DrawString(b3Color_white, "E = %f", E); + } + + void MouseMove(const b3Ray3& pw) + { + Test::MouseMove(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->Drag(); + } + } + + void MouseLeftDown(const b3Ray3& pw) + { + Test::MouseLeftDown(pw); + + if (m_clothDragger->IsDragging() == false) + { + m_clothDragger->StartDragging(); + } + } + + void MouseLeftUp(const b3Ray3& pw) + { + Test::MouseLeftUp(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->StopDragging(); + } } static Test* Create() @@ -77,6 +147,9 @@ public: b3RectangleGarment m_rectangleGarment; b3GarmentMesh m_rectangleGarmentMesh; b3GarmentClothMesh m_rectangleClothMesh; -}; + + b3Cloth* m_cloth; + b3ClothDragger* m_clothDragger; + }; #endif \ No newline at end of file diff --git a/examples/testbed/tests/shirt.h b/examples/testbed/tests/shirt.h index 158b8f1..1d76f1a 100644 --- a/examples/testbed/tests/shirt.h +++ b/examples/testbed/tests/shirt.h @@ -19,7 +19,7 @@ #ifndef SHIRT_H #define SHIRT_H -class Shirt : public ClothTest +class Shirt : public Test { public: Shirt() @@ -57,7 +57,63 @@ public: def.density = 0.2f; def.structural = 10000.0f; - m_cloth = m_world.CreateCloth(def); + m_cloth = new b3Cloth(def); + + m_cloth->SetGravity(b3Vec3(0.0f, -9.8f, 0.0f)); + m_cloth->SetWorld(&m_world); + + m_clothDragger = new b3ClothDragger(&m_ray, m_cloth); + } + + ~Shirt() + { + delete m_clothDragger; + delete m_cloth; + } + + void Step() + { + Test::Step(); + + m_cloth->Step(g_testSettings->inv_hertz); + + m_cloth->Draw(); + + extern u32 b3_clothSolverIterations; + g_draw->DrawString(b3Color_white, "Iterations = %d", b3_clothSolverIterations); + + float32 E = m_cloth->GetEnergy(); + g_draw->DrawString(b3Color_white, "E = %f", E); + } + + void MouseMove(const b3Ray3& pw) + { + Test::MouseMove(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->Drag(); + } + } + + void MouseLeftDown(const b3Ray3& pw) + { + Test::MouseLeftDown(pw); + + if (m_clothDragger->IsDragging() == false) + { + m_clothDragger->StartDragging(); + } + } + + void MouseLeftUp(const b3Ray3& pw) + { + Test::MouseLeftUp(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->StopDragging(); + } } static Test* Create() @@ -68,6 +124,9 @@ public: b3ShirtGarment m_shirtGarment; b3GarmentMesh m_shirtGarmentMesh; b3GarmentClothMesh m_shirtClothMesh; + + b3Cloth* m_cloth; + b3ClothDragger* m_clothDragger; }; #endif \ No newline at end of file diff --git a/examples/testbed/tests/table_cloth.h b/examples/testbed/tests/table_cloth.h index c0ec318..af43732 100644 --- a/examples/testbed/tests/table_cloth.h +++ b/examples/testbed/tests/table_cloth.h @@ -19,7 +19,7 @@ #ifndef TABLE_CLOTH_H #define TABLE_CLOTH_H -class TableCloth : public ClothTest +class TableCloth : public Test { public: TableCloth() : m_rectangleGarment(5.0f, 5.0f) @@ -30,14 +30,15 @@ public: // Create 3D mesh m_rectangleClothMesh.Set(&m_rectangleGarmentMesh); - // - b3Mat33 dq = b3Mat33RotationX(0.5f * B3_PI); + // Rotate the mesh + b3Mat33 rotation = b3Mat33RotationX(0.5f * B3_PI); for (u32 i = 0; i < m_rectangleClothMesh.vertexCount; ++i) { - m_rectangleClothMesh.vertices[i] = dq * m_rectangleClothMesh.vertices[i]; + m_rectangleClothMesh.vertices[i] = rotation * m_rectangleClothMesh.vertices[i]; m_rectangleClothMesh.vertices[i].y += 5.0f; } + // Create cloth b3ClothDef def; def.mesh = &m_rectangleClothMesh; def.density = 0.2f; @@ -45,7 +46,10 @@ public: def.structural = 10000.0f; def.damping = 0.0f; - m_cloth = m_world.CreateCloth(def); + m_cloth = new b3Cloth(def); + + m_cloth->SetGravity(b3Vec3(0.0f, -9.8f, 0.0f)); + m_cloth->SetWorld(&m_world); for (b3Particle* p = m_cloth->GetParticleList().m_head; p; p = p->GetNext()) { @@ -71,6 +75,71 @@ public: b->CreateShape(sd); } + + m_clothDragger = new b3ClothDragger(&m_ray, m_cloth); + } + + ~TableCloth() + { + delete m_clothDragger; + delete m_cloth; + } + + void Step() + { + Test::Step(); + + m_cloth->Step(g_testSettings->inv_hertz); + + m_cloth->Draw(); + + if (m_clothDragger->IsDragging()) + { + b3Vec3 pA = m_clothDragger->GetPointA(); + b3Vec3 pB = m_clothDragger->GetPointB(); + + g_draw->DrawPoint(pA, 2.0f, b3Color_green); + + g_draw->DrawPoint(pB, 2.0f, b3Color_green); + + g_draw->DrawSegment(pA, pB, b3Color_white); + } + + extern u32 b3_clothSolverIterations; + g_draw->DrawString(b3Color_white, "Iterations = %d", b3_clothSolverIterations); + + float32 E = m_cloth->GetEnergy(); + g_draw->DrawString(b3Color_white, "E = %f", E); + } + + void MouseMove(const b3Ray3& pw) + { + Test::MouseMove(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->Drag(); + } + } + + void MouseLeftDown(const b3Ray3& pw) + { + Test::MouseLeftDown(pw); + + if (m_clothDragger->IsDragging() == false) + { + m_clothDragger->StartDragging(); + } + } + + void MouseLeftUp(const b3Ray3& pw) + { + Test::MouseLeftUp(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->StopDragging(); + } } static Test* Create() @@ -82,6 +151,9 @@ public: b3GarmentMesh m_rectangleGarmentMesh; b3GarmentClothMesh m_rectangleClothMesh; + b3Cloth* m_cloth; + b3ClothDragger* m_clothDragger; + b3QHull m_tableHull; }; diff --git a/examples/testbed/tests/tension_mapping.h b/examples/testbed/tests/tension_mapping.h index 463113d..49d8d23 100644 --- a/examples/testbed/tests/tension_mapping.h +++ b/examples/testbed/tests/tension_mapping.h @@ -55,7 +55,7 @@ static inline b3Color Color(float32 x, float32 a, float32 b) return c; } -class TensionMapping : public ClothTest +class TensionMapping : public Test { public: TensionMapping() : m_rectangleGarment(5.0f, 5.0f) @@ -66,20 +66,25 @@ public: // Create 3D mesh m_rectangleClothMesh.Set(&m_rectangleGarmentMesh); - // - b3Mat33 dq = b3Mat33RotationX(0.5f * B3_PI); + // Rotate the mesh + b3Mat33 rotation = b3Mat33RotationX(0.5f * B3_PI); for (u32 i = 0; i < m_rectangleClothMesh.vertexCount; ++i) { - m_rectangleClothMesh.vertices[i] = dq * m_rectangleClothMesh.vertices[i]; + m_rectangleClothMesh.vertices[i] = rotation * m_rectangleClothMesh.vertices[i]; } + // Create cloth b3ClothDef def; def.mesh = &m_rectangleClothMesh; def.density = 0.2f; def.structural = 10000.0f; - m_cloth = m_world.CreateCloth(def); + m_cloth = new b3Cloth(def); + m_cloth->SetGravity(b3Vec3(0.0f, -9.8f, 0.0f)); + m_cloth->SetWorld(&m_world); + + // Freeze some particles b3AABB3 aabb; aabb.m_lower.Set(-5.0f, -1.0f, -6.0f); aabb.m_upper.Set(5.0f, 1.0f, -4.0f); @@ -91,12 +96,22 @@ public: p->SetType(e_staticParticle); } } + + m_clothDragger = new b3ClothDragger(&m_ray, m_cloth); + } + + ~TensionMapping() + { + delete m_clothDragger; + delete m_cloth; } void Step() { Test::Step(); + m_cloth->Step(g_testSettings->inv_hertz); + const b3ClothMesh* mesh = m_cloth->GetMesh(); b3StackArray tension; @@ -120,9 +135,9 @@ public: } } - for (u32 i = 0; i < m_rectangleClothMesh.triangleCount; ++i) + for (u32 i = 0; i < mesh->triangleCount; ++i) { - b3ClothMeshTriangle* t = m_rectangleClothMesh.triangles + i; + b3ClothMeshTriangle* t = mesh->triangles + i; b3Vec3 v1 = m_cloth->GetVertexParticle(t->v1)->GetPosition(); b3Vec3 v2 = m_cloth->GetVertexParticle(t->v2)->GetPosition(); @@ -154,18 +169,55 @@ public: g_draw->DrawSolidTriangle(n2, v1, v3, v2, color); } - if (m_clothDragger.IsDragging() == true) + if (m_clothDragger->IsDragging()) { - g_draw->DrawSegment(m_clothDragger.GetPointA(), m_clothDragger.GetPointB(), b3Color_white); + b3Vec3 pA = m_clothDragger->GetPointA(); + b3Vec3 pB = m_clothDragger->GetPointB(); + + g_draw->DrawPoint(pA, 2.0f, b3Color_green); + + g_draw->DrawPoint(pB, 2.0f, b3Color_green); + + g_draw->DrawSegment(pA, pB, b3Color_white); } extern u32 b3_clothSolverIterations; - g_draw->DrawString(b3Color_white, "Iterations = %u", b3_clothSolverIterations); + g_draw->DrawString(b3Color_white, "Iterations = %d", b3_clothSolverIterations); float32 E = m_cloth->GetEnergy(); g_draw->DrawString(b3Color_white, "E = %f", E); } + void MouseMove(const b3Ray3& pw) + { + Test::MouseMove(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->Drag(); + } + } + + void MouseLeftDown(const b3Ray3& pw) + { + Test::MouseLeftDown(pw); + + if (m_clothDragger->IsDragging() == false) + { + m_clothDragger->StartDragging(); + } + } + + void MouseLeftUp(const b3Ray3& pw) + { + Test::MouseLeftUp(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->StopDragging(); + } + } + static Test* Create() { return new TensionMapping(); @@ -174,6 +226,9 @@ public: b3RectangleGarment m_rectangleGarment; b3GarmentMesh m_rectangleGarmentMesh; b3GarmentClothMesh m_rectangleClothMesh; + + b3Cloth* m_cloth; + b3ClothDragger* m_clothDragger; }; #endif \ No newline at end of file diff --git a/include/bounce/bounce.h b/include/bounce/bounce.h index 1c4162f..ba4633e 100644 --- a/include/bounce/bounce.h +++ b/include/bounce/bounce.h @@ -56,23 +56,20 @@ #include #include -#include - -#include -#include -#include - -#include -#include -#include -#include - #include #include #include -#include -#include +#include + +#include +#include +#include +#include + +#include +#include +#include #endif \ No newline at end of file diff --git a/include/bounce/dynamics/cloth/cloth.h b/include/bounce/cloth/cloth.h similarity index 85% rename from include/bounce/dynamics/cloth/cloth.h rename to include/bounce/cloth/cloth.h index 6f2d6f5..b8b6a7c 100644 --- a/include/bounce/dynamics/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -21,6 +21,7 @@ #include #include +#include #include class b3World; @@ -83,7 +84,20 @@ struct b3ClothDef class b3Cloth { public: - // Get the world the cloth belongs to. + b3Cloth(const b3ClothDef& def); + ~b3Cloth(); + + // Set the acceleration of gravity. + void SetGravity(const b3Vec3& gravity); + + // Get the acceleration of gravity. + b3Vec3 GetGravity() const; + + // Attach a world to this cloth. + // The cloth will be able to respond to collisions with the static shapes in the attached world. + void SetWorld(b3World* world); + + // Get the world attached to this cloth. const b3World* GetWorld() const; b3World* GetWorld(); @@ -99,9 +113,6 @@ public: // Destroy a given force. void DestroyForce(b3Force* force); - // Perform a ray cast with the cloth. - void RayCast(b3RayCastListener* listener, const b3Vec3& p1, const b3Vec3& p2); - // Perform a ray cast with the cloth. bool RayCastSingle(b3ClothRayCastSingleOutput* output, const b3Vec3& p1, const b3Vec3& p2) const; @@ -129,20 +140,14 @@ public: // Get the next cloth in the world cloth list. b3Cloth* GetNext(); + // Perform a time step. + void Step(float32 dt); + // Debug draw the cloth using the associated cloth mesh. void Draw() const; private: - friend class b3World; - friend class b3List2; - b3Cloth(const b3ClothDef& def, b3World* world); - ~b3Cloth(); - - // Perform a time step. - // Called only inside b3World. - void Step(float32 dt, const b3Vec3& gravity); - // Compute mass of each particle. void ComputeMass(); @@ -161,6 +166,15 @@ private: // Solve void Solve(float32 dt, const b3Vec3& gravity); + // Stack allocator + b3StackAllocator m_stackAllocator; + + // The world attached to this cloth + b3World* m_world; + + // Gravity acceleration + b3Vec3 m_gravity; + // Proxy mesh const b3ClothMesh* m_mesh; @@ -196,15 +210,23 @@ private: // List of triangle contacts b3List2 m_triangleContactList; - - // The parent world of this cloth. - b3World* m_world; - - // Links to the world cloth list. - b3Cloth* m_prev; - b3Cloth* m_next; }; +inline void b3Cloth::SetGravity(const b3Vec3& gravity) +{ + m_gravity = gravity; +} + +inline b3Vec3 b3Cloth::GetGravity() const +{ + return m_gravity; +} + +inline void b3Cloth::SetWorld(b3World* world) +{ + m_world = world; +} + inline const b3World* b3Cloth::GetWorld() const { return m_world; @@ -230,14 +252,4 @@ inline const b3List2& b3Cloth::GetForceList() const return m_forceList; } -inline const b3Cloth* b3Cloth::GetNext() const -{ - return m_next; -} - -inline b3Cloth* b3Cloth::GetNext() -{ - return m_next; -} - #endif \ No newline at end of file diff --git a/include/bounce/dynamics/cloth/cloth_contact_solver.h b/include/bounce/cloth/cloth_contact_solver.h similarity index 100% rename from include/bounce/dynamics/cloth/cloth_contact_solver.h rename to include/bounce/cloth/cloth_contact_solver.h diff --git a/include/bounce/dynamics/cloth/cloth_mesh.h b/include/bounce/cloth/cloth_mesh.h similarity index 100% rename from include/bounce/dynamics/cloth/cloth_mesh.h rename to include/bounce/cloth/cloth_mesh.h diff --git a/include/bounce/dynamics/cloth/cloth_solver.h b/include/bounce/cloth/cloth_solver.h similarity index 100% rename from include/bounce/dynamics/cloth/cloth_solver.h rename to include/bounce/cloth/cloth_solver.h diff --git a/include/bounce/dynamics/cloth/dense_vec3.h b/include/bounce/cloth/dense_vec3.h similarity index 100% rename from include/bounce/dynamics/cloth/dense_vec3.h rename to include/bounce/cloth/dense_vec3.h diff --git a/include/bounce/dynamics/cloth/diag_mat33.h b/include/bounce/cloth/diag_mat33.h similarity index 98% rename from include/bounce/dynamics/cloth/diag_mat33.h rename to include/bounce/cloth/diag_mat33.h index 34272af..24b62ff 100644 --- a/include/bounce/dynamics/cloth/diag_mat33.h +++ b/include/bounce/cloth/diag_mat33.h @@ -20,7 +20,7 @@ #define B3_DIAG_MAT_33_H #include -#include +#include // Diagonal matrix storing only the diagonal elements of the // original matrix. diff --git a/include/bounce/dynamics/cloth/force.h b/include/bounce/cloth/force.h similarity index 97% rename from include/bounce/dynamics/cloth/force.h rename to include/bounce/cloth/force.h index 87bcb5b..78a18ba 100644 --- a/include/bounce/dynamics/cloth/force.h +++ b/include/bounce/cloth/force.h @@ -29,9 +29,7 @@ class b3Particle; // Force types enum b3ForceType { - e_frictionForce, e_springForce, - e_bendForce, }; struct b3ForceDef diff --git a/include/bounce/garment/garment.h b/include/bounce/cloth/garment/garment.h similarity index 98% rename from include/bounce/garment/garment.h rename to include/bounce/cloth/garment/garment.h index 742ace0..3605584 100644 --- a/include/bounce/garment/garment.h +++ b/include/bounce/cloth/garment/garment.h @@ -19,7 +19,7 @@ #ifndef B3_GARMENT_H #define B3_GARMENT_H -#include +#include struct b3SewingLine { diff --git a/include/bounce/garment/garment_mesh.h b/include/bounce/cloth/garment/garment_mesh.h similarity index 100% rename from include/bounce/garment/garment_mesh.h rename to include/bounce/cloth/garment/garment_mesh.h diff --git a/include/bounce/garment/sewing_pattern.h b/include/bounce/cloth/garment/sewing_pattern.h similarity index 100% rename from include/bounce/garment/sewing_pattern.h rename to include/bounce/cloth/garment/sewing_pattern.h diff --git a/include/bounce/dynamics/cloth/particle.h b/include/bounce/cloth/particle.h similarity index 99% rename from include/bounce/dynamics/cloth/particle.h rename to include/bounce/cloth/particle.h index 11b93d0..7d531dc 100644 --- a/include/bounce/dynamics/cloth/particle.h +++ b/include/bounce/cloth/particle.h @@ -20,9 +20,9 @@ #define B3_PARTICLE_H #include -#include -#include #include +#include +#include class b3Shape; class b3Cloth; diff --git a/include/bounce/dynamics/cloth/sparse_sym_mat33.h b/include/bounce/cloth/sparse_sym_mat33.h similarity index 98% rename from include/bounce/dynamics/cloth/sparse_sym_mat33.h rename to include/bounce/cloth/sparse_sym_mat33.h index e84f69b..6b688d9 100644 --- a/include/bounce/dynamics/cloth/sparse_sym_mat33.h +++ b/include/bounce/cloth/sparse_sym_mat33.h @@ -20,8 +20,8 @@ #define B3_SPARSE_SYM_MAT_33_H #include -#include -#include +#include +#include // An element in a sparse symmetric matrix. struct b3RowValue diff --git a/include/bounce/dynamics/cloth/spring_force.h b/include/bounce/cloth/spring_force.h similarity index 98% rename from include/bounce/dynamics/cloth/spring_force.h rename to include/bounce/cloth/spring_force.h index c5d1bba..c0825b7 100644 --- a/include/bounce/dynamics/cloth/spring_force.h +++ b/include/bounce/cloth/spring_force.h @@ -19,7 +19,7 @@ #ifndef B3_SPRING_FORCE_H #define B3_SPRING_FORCE_H -#include +#include struct b3SpringForceDef : public b3ForceDef { diff --git a/include/bounce/controllers/body_dragger.h b/include/bounce/controllers/body_dragger.h deleted file mode 100644 index 37b28dd..0000000 --- a/include/bounce/controllers/body_dragger.h +++ /dev/null @@ -1,129 +0,0 @@ -/* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef B3_BODY_DRAGGER_H -#define B3_BODY_DRAGGER_H - -#include -#include - -// A body dragger. -class b3BodyDragger -{ -public: - b3BodyDragger(b3Ray3* ray, b3World* world) - { - m_ray = ray; - m_world = world; - m_shape = nullptr; - m_mouseJoint = nullptr; - } - - ~b3BodyDragger() - { - - } - - bool IsDragging() const - { - return m_shape != nullptr; - } - - bool StartDragging() - { - B3_ASSERT(IsDragging() == false); - - b3RayCastSingleShapeOutput out; - if (m_world->RayCastSingleShape(&out, m_ray->A(), m_ray->B()) == false) - { - return false; - } - - m_x = out.fraction; - m_shape = out.shape; - - b3BodyDef bd; - b3Body* groundBody = m_world->CreateBody(bd); - - b3Body* body = m_shape->GetBody(); - body->SetAwake(true); - - b3MouseJointDef jd; - jd.bodyA = groundBody; - jd.bodyB = body; - jd.target = out.point; - jd.maxForce = 2000.0f * body->GetMass(); - - m_mouseJoint = (b3MouseJoint*)m_world->CreateJoint(jd); - - m_p = body->GetLocalPoint(out.point); - - return true; - } - - void Drag() - { - B3_ASSERT(IsDragging() == true); - m_mouseJoint->SetTarget(GetPointB()); - } - - void StopDragging() - { - B3_ASSERT(IsDragging() == true); - b3Body* groundBody = m_mouseJoint->GetBodyA(); - m_world->DestroyJoint(m_mouseJoint); - m_mouseJoint = nullptr; - m_world->DestroyBody(groundBody); - m_shape = nullptr; - } - - b3Ray3* GetRay() const - { - return m_ray; - } - - b3Body* GetBody() const - { - B3_ASSERT(IsDragging() == true); - return m_shape->GetBody(); - } - - b3Vec3 GetPointA() const - { - B3_ASSERT(IsDragging() == true); - return m_shape->GetBody()->GetWorldPoint(m_p); - } - - b3Vec3 GetPointB() const - { - B3_ASSERT(IsDragging() == true); - return (1.0f - m_x) * m_ray->A() + m_x * m_ray->B(); - } - -private: - b3Ray3 * m_ray; - float32 m_x; - - b3World* m_world; - - b3Shape* m_shape; - b3Vec3 m_p; - b3MouseJoint* m_mouseJoint; -}; - -#endif \ No newline at end of file diff --git a/include/bounce/controllers/cloth_dragger.h b/include/bounce/controllers/cloth_dragger.h deleted file mode 100644 index b77060a..0000000 --- a/include/bounce/controllers/cloth_dragger.h +++ /dev/null @@ -1,224 +0,0 @@ -/* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef B3_CLOTH_DRAGGER_H -#define B3_CLOTH_DRAGGER_H - -#include -#include -#include -#include -#include -#include - -// A cloth triangle dragger. -class b3ClothDragger -{ -public: - b3ClothDragger(b3Ray3* ray, b3World* world) - { - m_spring = false; - m_ray = ray; - m_world = world; - m_cloth = nullptr; - } - - ~b3ClothDragger() - { - - } - - bool IsDragging() const - { - return m_cloth != nullptr; - } - - bool StartDragging() - { - B3_ASSERT(IsDragging() == false); - - b3RayCastSingleClothOutput rayOut; - if (m_world->RayCastSingleCloth(&rayOut, m_ray->A(), m_ray->B()) == false) - { - return false; - } - - m_cloth = rayOut.cloth; - m_mesh = m_cloth->GetMesh(); - m_triangle = m_mesh->triangles + rayOut.triangle; - m_x = rayOut.fraction; - - b3Particle* p1 = m_cloth->GetVertexParticle(m_triangle->v1); - b3Particle* p2 = m_cloth->GetVertexParticle(m_triangle->v2); - b3Particle* p3 = m_cloth->GetVertexParticle(m_triangle->v3); - - b3Vec3 v1 = p1->GetPosition(); - b3Vec3 v2 = p2->GetPosition(); - b3Vec3 v3 = p3->GetPosition(); - - b3Vec3 B = GetPointB(); - - float32 wABC[4]; - b3BarycentricCoordinates(wABC, v1, v2, v3, B); - - if (wABC[3] > B3_EPSILON) - { - m_u = wABC[0] / wABC[3]; - m_v = wABC[1] / wABC[3]; - } - else - { - m_u = m_v = 0.0f; - } - - if (m_spring) - { - b3ParticleDef pd; - pd.type = e_staticParticle; - pd.position = B; - - m_particle = m_cloth->CreateParticle(pd); - - { - b3SpringForceDef sfd; - sfd.p1 = m_particle; - sfd.p2 = p1; - sfd.restLength = 0.0f; - sfd.structural = 10000.0f; - m_s1 = (b3SpringForce*)m_cloth->CreateForce(sfd); - } - - { - b3SpringForceDef sfd; - sfd.p1 = m_particle; - sfd.p2 = p2; - sfd.restLength = 0.0f; - sfd.structural = 10000.0f; - m_s2 = (b3SpringForce*)m_cloth->CreateForce(sfd); - } - - { - b3SpringForceDef sfd; - sfd.p1 = m_particle; - sfd.p2 = p3; - sfd.restLength = 0.0f; - sfd.structural = 10000.0f; - m_s3 = (b3SpringForce*)m_cloth->CreateForce(sfd); - } - } - else - { - m_t1 = p1->GetType(); - p1->SetType(e_staticParticle); - - m_t2 = p2->GetType(); - p2->SetType(e_staticParticle); - - m_t3 = p3->GetType(); - p3->SetType(e_staticParticle); - } - - return true; - } - - void Drag() - { - B3_ASSERT(IsDragging() == true); - - b3Vec3 A = GetPointA(); - b3Vec3 B = GetPointB(); - - b3Vec3 dx = B - A; - - if (m_spring) - { - m_particle->SetPosition(B); - } - else - { - b3Particle* p1 = m_cloth->GetVertexParticle(m_triangle->v1); - p1->ApplyTranslation(dx); - - b3Particle* p2 = m_cloth->GetVertexParticle(m_triangle->v2); - p2->ApplyTranslation(dx); - - b3Particle* p3 = m_cloth->GetVertexParticle(m_triangle->v3); - p3->ApplyTranslation(dx); - } - } - - void StopDragging() - { - B3_ASSERT(IsDragging() == true); - - if (m_spring) - { - m_cloth->DestroyForce(m_s1); - m_cloth->DestroyForce(m_s2); - m_cloth->DestroyForce(m_s3); - m_cloth->DestroyParticle(m_particle); - } - else - { - m_cloth->GetVertexParticle(m_triangle->v1)->SetType(m_t1); - m_cloth->GetVertexParticle(m_triangle->v2)->SetType(m_t2); - m_cloth->GetVertexParticle(m_triangle->v3)->SetType(m_t3); - } - - m_cloth = nullptr; - } - - b3Vec3 GetPointA() const - { - B3_ASSERT(IsDragging() == true); - - b3Vec3 A = m_cloth->GetVertexParticle(m_triangle->v1)->GetPosition(); - b3Vec3 B = m_cloth->GetVertexParticle(m_triangle->v2)->GetPosition(); - b3Vec3 C = m_cloth->GetVertexParticle(m_triangle->v3)->GetPosition(); - - return m_u * A + m_v * B + (1.0f - m_u - m_v) * C; - } - - b3Vec3 GetPointB() const - { - B3_ASSERT(IsDragging() == true); - - return (1.0f - m_x) * m_ray->A() + m_x * m_ray->B(); - } -private: - b3Ray3* m_ray; - float32 m_x; - - b3World* m_world; - - b3Cloth* m_cloth; - const b3ClothMesh* m_mesh; - b3ClothMeshTriangle* m_triangle; - float32 m_u, m_v; - - bool m_spring; - - b3Particle* m_particle; - b3SpringForce* m_s1; - b3SpringForce* m_s2; - b3SpringForce* m_s3; - - b3ParticleType m_t1, m_t2, m_t3; -}; - -#endif \ No newline at end of file diff --git a/include/bounce/dynamics/cloth/bend_force.h b/include/bounce/dynamics/cloth/bend_force.h deleted file mode 100644 index 069a88e..0000000 --- a/include/bounce/dynamics/cloth/bend_force.h +++ /dev/null @@ -1,158 +0,0 @@ -/* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef B3_BEND_FORCE_H -#define B3_BEND_FORCE_H - -#include - -struct b3BendForceDef : public b3ForceDef -{ - b3BendForceDef() - { - type = e_bendForce; - p1 = nullptr; - p2 = nullptr; - p3 = nullptr; - p4 = nullptr; - restDistance = 0.0f; - restAngle = 0.0f; - structural = 0.0f; - damping = 0.0f; - } - - // - void Initialize(b3Particle* particle1, b3Particle* particle2, b3Particle* particle3, b3Particle* particle4, - float32 structuralStiffness, float32 dampingStiffness); - - // Particle 1 - b3Particle* p1; - - // Particle 2 - b3Particle* p2; - - // Particle 3 - b3Particle* p3; - - // Particle 4 - b3Particle* p4; - - // Rest distance - float32 restDistance; - - // Rest angle - float32 restAngle; - - // Structural stiffness - float32 structural; - - // Damping stiffness - float32 damping; -}; - -// -class b3BendForce : public b3Force -{ -public: - b3Particle* GetParticle1(); - - b3Particle* GetParticle2(); - - b3Particle* GetParticle3(); - - b3Particle* GetParticle4(); - - float32 GetRestDistance() const; - - float32 GetRestAngle() const; - - float32 GetStructuralStiffness() const; - - float32 GetDampingStiffness() const; -private: - friend class b3Force; - friend class b3Cloth; - - b3BendForce(const b3BendForceDef* def); - ~b3BendForce(); - - void Apply(const b3ClothSolverData* data); - - // Solver shared - - // Particle 1 - b3Particle* m_p1; - - // Particle 2 - b3Particle* m_p2; - - // Particle 3 - b3Particle* m_p3; - - // Particle 4 - b3Particle* m_p4; - - // Rest distance - float32 m_L0; - - // Rest angle - float32 m_angle0; - - // Structural stiffness - float32 m_ks; - - // Structural stiffness - float32 m_kd; -}; - -inline b3Particle* b3BendForce::GetParticle1() -{ - return m_p1; -} - -inline b3Particle* b3BendForce::GetParticle2() -{ - return m_p2; -} - -inline b3Particle* b3BendForce::GetParticle3() -{ - return m_p3; -} - -inline b3Particle* b3BendForce::GetParticle4() -{ - return m_p4; -} - -inline float32 b3BendForce::GetRestAngle() const -{ - return m_angle0; -} - -inline float32 b3BendForce::GetStructuralStiffness() const -{ - return m_ks; -} - -inline float32 b3BendForce::GetDampingStiffness() const -{ - return m_kd; -} - -#endif \ No newline at end of file diff --git a/include/bounce/dynamics/world.h b/include/bounce/dynamics/world.h index 2d30247..88a7510 100644 --- a/include/bounce/dynamics/world.h +++ b/include/bounce/dynamics/world.h @@ -26,17 +26,15 @@ #include #include -struct b3ClothDef; struct b3BodyDef; -class b3Cloth; class b3Body; class b3QueryListener; class b3RayCastListener; class b3ContactListener; class b3ContactFilter; -struct b3RayCastSingleShapeOutput +struct b3RayCastSingleOutput { b3Shape* shape; // shape b3Vec3 point; // intersection point on surface @@ -44,15 +42,6 @@ struct b3RayCastSingleShapeOutput float32 fraction; // time of intersection on segment }; -struct b3RayCastSingleClothOutput -{ - b3Cloth* cloth; // cloth - u32 triangle; // triangle - b3Vec3 point; // intersection point on surface - b3Vec3 normal; // surface normal of intersection - float32 fraction; // time of intersection on segment -}; - // Use a physics world to create/destroy rigid bodies, execute ray cast and volume queries. class b3World { @@ -79,12 +68,6 @@ public: // The acceleration has units of m/s^2. void SetGravity(const b3Vec3& gravity); - // Create a new deformable cloth. - b3Cloth* CreateCloth(const b3ClothDef& def); - - // Destroy an existing deformable cloth. - void DestroyCloth(b3Cloth* cloth); - // Create a new rigid body. b3Body* CreateBody(const b3BodyDef& def); @@ -108,29 +91,14 @@ public: // The ray cast output is the intercepted shape, the intersection // point in world space, the face normal on the shape associated with the point, // and the intersection fraction. - void RayCastShape(b3RayCastListener* listener, const b3Vec3& p1, const b3Vec3& p2) const; + void RayCast(b3RayCastListener* listener, const b3Vec3& p1, const b3Vec3& p2) const; // Perform a ray cast with the world. // If the ray doesn't intersect with a shape in the world then return false. // The ray cast output is the intercepted shape, the intersection // point in world space, the face normal on the shape associated with the point, // and the intersection fraction. - bool RayCastSingleShape(b3RayCastSingleShapeOutput* output, const b3Vec3& p1, const b3Vec3& p2) const; - - // Perform a ray cast with the world. - // The given ray cast listener will be notified when a ray intersects a shape - // in the world. - // The ray cast output is the intercepted cloth, the intersection - // point in world space, the face normal on the cloth associated with the point, - // and the intersection fraction. - void RayCastCloth(b3RayCastListener* listener, const b3Vec3& p1, const b3Vec3& p2) const; - - // Perform a ray cast with the world. - // If the ray doesn't intersect with a cloth in the world then return false. - // The ray cast output is the intercepted cloth, the intersection - // point in world space, the face normal on the cloth associated with the point, - // and the intersection fraction. - bool RayCastSingleCloth(b3RayCastSingleClothOutput* output, const b3Vec3& p1, const b3Vec3& p2) const; + bool RayCastSingle(b3RayCastSingleOutput* output, const b3Vec3& p1, const b3Vec3& p2) const; // Perform a AABB query with the world. // The query listener will be notified when two shape AABBs are overlapping. @@ -161,8 +129,7 @@ private : e_shapeAddedFlag = 0x0001, e_clearForcesFlag = 0x0002, }; - - friend class b3Cloth; + friend class b3Body; friend class b3Shape; friend class b3Contact; @@ -172,20 +139,16 @@ private : void Solve(float32 dt, u32 velocityIterations, u32 positionIterations); - void StepCloth(float32 dt); - bool m_sleeping; bool m_warmStarting; u32 m_flags; b3Vec3 m_gravity; b3StackAllocator m_stackAllocator; - b3BlockPool m_clothBlocks; + + // Pool of bodies b3BlockPool m_bodyBlocks; - // List of clothes - b3List2 m_clothList; - // List of bodies b3List2 m_bodyList; diff --git a/include/bounce/dynamics/world_listeners.h b/include/bounce/dynamics/world_listeners.h index cc9fc02..4b87efa 100644 --- a/include/bounce/dynamics/world_listeners.h +++ b/include/bounce/dynamics/world_listeners.h @@ -22,7 +22,6 @@ #include class b3Shape; -class b3Cloth; class b3Contact; class b3QueryListener @@ -47,12 +46,6 @@ public: // the intersection point on the shape, the surface normal associated with the point, and the // intersection fraction for the ray. virtual float32 ReportShape(b3Shape* shape, const b3Vec3& point, const b3Vec3& normal, float32 fraction) = 0; - - // Report that a cloth was hit by the ray to this contact listener. - // The reported information are the shape hit by the ray, - // the intersection point on the cloth, the surface normal associated with the point, the - // intersection fraction for the ray, and the triangle. - virtual float32 ReportCloth(b3Cloth* cloth, const b3Vec3& point, const b3Vec3& normal, float32 fraction, u32 triangle) = 0; }; class b3ContactListener diff --git a/include/bounce/dynamics/rope/rope.h b/include/bounce/rope/rope.h similarity index 100% rename from include/bounce/dynamics/rope/rope.h rename to include/bounce/rope/rope.h diff --git a/include/bounce/dynamics/spatial.h b/include/bounce/rope/spatial.h similarity index 100% rename from include/bounce/dynamics/spatial.h rename to include/bounce/rope/spatial.h diff --git a/premake5.lua b/premake5.lua index 3c43015..625618c 100644 --- a/premake5.lua +++ b/premake5.lua @@ -274,6 +274,12 @@ workspace(solution_name) examples_src_dir .. "/testbed/framework/test.h", + examples_src_dir .. "/testbed/framework/body_dragger.h", + examples_src_dir .. "/testbed/framework/cloth_dragger.h", + + examples_src_dir .. "/testbed/framework/body_dragger.cpp", + examples_src_dir .. "/testbed/framework/cloth_dragger.cpp", + examples_inc_dir .. "/testbed/tests/**.h", examples_src_dir .. "/testbed/framework/draw.cpp", diff --git a/src/bounce/dynamics/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp similarity index 92% rename from src/bounce/dynamics/cloth/cloth.cpp rename to src/bounce/cloth/cloth.cpp index bd526a9..cc301e3 100644 --- a/src/bounce/dynamics/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -16,19 +16,16 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include -#include #include #include #include -#include #include static B3_FORCE_INLINE u32 b3NextIndex(u32 i) @@ -149,7 +146,7 @@ static u32 b3FindSharedEdges(b3SharedEdge* sharedEdges, const b3ClothMesh* m) return sharedCount; } -b3Cloth::b3Cloth(const b3ClothDef& def, b3World* world) : +b3Cloth::b3Cloth(const b3ClothDef& def) : m_particleBlocks(sizeof(b3Particle)), m_bodyContactBlocks(sizeof(b3BodyContact)), m_particleContactBlocks(sizeof(b3ParticleContact)), @@ -158,7 +155,6 @@ b3Cloth::b3Cloth(const b3ClothDef& def, b3World* world) : B3_ASSERT(def.mesh); B3_ASSERT(def.density > 0.0f); - m_world = world; m_mesh = def.mesh; m_density = def.density; @@ -184,7 +180,7 @@ b3Cloth::b3Cloth(const b3ClothDef& def, b3World* world) : ComputeMass(); // Create forces - b3StackAllocator* allocator = &m_world->m_stackAllocator; + b3StackAllocator* allocator = &m_stackAllocator; // Worst-case edge memory u32 edgeCount = 3 * m->triangleCount; @@ -218,8 +214,8 @@ b3Cloth::b3Cloth(const b3ClothDef& def, b3World* world) : b3Particle* p3 = m_vertexParticles[e->nsv1]; b3Particle* p4 = m_vertexParticles[e->nsv2]; - b3BendForceDef fd; - fd.Initialize(p1, p2, p3, p4, def.bending, def.damping); + b3SpringForceDef fd; + fd.Initialize(p3, p4, def.bending, def.damping); CreateForce(fd); } @@ -240,6 +236,9 @@ b3Cloth::b3Cloth(const b3ClothDef& def, b3World* world) : CreateForce(fd); } + + m_gravity.SetZero(); + m_world = nullptr; } b3Cloth::~b3Cloth() @@ -354,33 +353,6 @@ void b3Cloth::ComputeMass() } } -void b3Cloth::RayCast(b3RayCastListener* listener, const b3Vec3& p1, const b3Vec3& p2) -{ - b3RayCastInput input; - input.p1 = p1; - input.p2 = p2; - input.maxFraction = 1.0f; - - for (u32 i = 0; i < m_mesh->triangleCount; ++i) - { - b3RayCastOutput subOutput; - if (RayCast(&subOutput, &input, i)) - { - float32 fraction = subOutput.fraction; - b3Vec3 point = (1.0f - fraction) * input.p1 + fraction * input.p2; - b3Vec3 normal = subOutput.normal; - - float32 newFraction = listener->ReportCloth(this, point, normal, fraction, i); - - if (newFraction == 0.0f) - { - // The client has stopped the query. - return; - } - } - } -} - bool b3Cloth::RayCastSingle(b3ClothRayCastSingleOutput* output, const b3Vec3& p1, const b3Vec3& p2) const { b3RayCastInput input; @@ -440,7 +412,6 @@ bool b3Cloth::RayCast(b3RayCastOutput* output, const b3RayCastInput* input, u32 return false; } - B3_ASSERT(len > B3_EPSILON); n /= len; float32 numerator = b3Dot(n, v1 - p1); @@ -483,11 +454,8 @@ bool b3Cloth::RayCast(b3RayCastOutput* output, const b3RayCastInput* input, u32 float32 v = b3Dot(QC_x_QA, AB_x_AC); float32 w = b3Dot(QA_x_QB, AB_x_AC); - // Characteristic length of triangle - const float32 kTol = -B3_EPSILON; - // Is the intersection on the triangle? - if (u > kTol && v > kTol && w > kTol) + if (u >= 0.0f && v >= 0.0f && w >= 0.0f) { output->fraction = fraction; @@ -509,6 +477,12 @@ bool b3Cloth::RayCast(b3RayCastOutput* output, const b3RayCastInput* input, u32 void b3Cloth::UpdateBodyContacts() { + // Is there a world attached to this cloth? + if (m_world == nullptr) + { + return; + } + B3_PROFILE("Cloth Update Body Contacts"); // Clear buffer @@ -537,9 +511,13 @@ void b3Cloth::UpdateBodyContacts() for (b3Body* body = m_world->GetBodyList().m_head; body; body = body->GetNext()) { - if (p->m_type != e_dynamicParticle && body->GetType() != e_dynamicBody) + if (p->m_type != e_dynamicParticle) + { + continue; + } + + if (body->GetType() != e_staticBody) { - // At least one body should be kinematic or dynamic. continue; } @@ -887,7 +865,7 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity) // Solve b3ClothSolverDef solverDef; - solverDef.stack = &m_world->m_stackAllocator; + solverDef.stack = &m_stackAllocator; solverDef.particleCapacity = m_particleList.m_count; solverDef.forceCapacity = m_forceList.m_count; solverDef.bodyContactCapacity = m_bodyContactList.m_count; @@ -937,14 +915,16 @@ void b3Cloth::UpdateContacts() // Update body contacts UpdateBodyContacts(); +#if 0 // Update particle contacts UpdateParticleContacts(); // Update triangle contacts UpdateTriangleContacts(); +#endif } -void b3Cloth::Step(float32 dt, const b3Vec3& gravity) +void b3Cloth::Step(float32 dt) { B3_PROFILE("Cloth Step"); @@ -954,7 +934,7 @@ void b3Cloth::Step(float32 dt, const b3Vec3& gravity) // Solve constraints, integrate state, clear forces and translations. if (dt > 0.0f) { - Solve(dt, gravity); + Solve(dt, m_gravity); } } diff --git a/src/bounce/dynamics/cloth/cloth_contact_solver.cpp b/src/bounce/cloth/cloth_contact_solver.cpp similarity index 99% rename from src/bounce/dynamics/cloth/cloth_contact_solver.cpp rename to src/bounce/cloth/cloth_contact_solver.cpp index cf05921..e553df2 100644 --- a/src/bounce/dynamics/cloth/cloth_contact_solver.cpp +++ b/src/bounce/cloth/cloth_contact_solver.cpp @@ -16,12 +16,12 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include -#include -#include +#include +#include +#include +#include #include #include -#include b3ClothContactSolver::b3ClothContactSolver(const b3ClothContactSolverDef& def) { diff --git a/src/bounce/dynamics/cloth/cloth_mesh.cpp b/src/bounce/cloth/cloth_mesh.cpp similarity index 95% rename from src/bounce/dynamics/cloth/cloth_mesh.cpp rename to src/bounce/cloth/cloth_mesh.cpp index f51c408..5018104 100644 --- a/src/bounce/dynamics/cloth/cloth_mesh.cpp +++ b/src/bounce/cloth/cloth_mesh.cpp @@ -16,10 +16,10 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include -#include -#include -#include +#include +#include +#include +#include b3GarmentClothMesh::b3GarmentClothMesh() { diff --git a/src/bounce/dynamics/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp similarity index 96% rename from src/bounce/dynamics/cloth/cloth_solver.cpp rename to src/bounce/cloth/cloth_solver.cpp index e4d66b2..2684966 100644 --- a/src/bounce/dynamics/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -16,18 +16,18 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include +#include // Here, we solve Ax = b using the Modified Preconditioned Conjugate Gradient (MPCG) algorithm. // described in the paper: @@ -298,7 +298,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) // Solve velocity constraints { - const u32 kVelocityIterations = 5; + const u32 kVelocityIterations = 10; for (u32 i = 0; i < kVelocityIterations; ++i) { diff --git a/src/bounce/dynamics/cloth/force.cpp b/src/bounce/cloth/force.cpp similarity index 79% rename from src/bounce/dynamics/cloth/force.cpp rename to src/bounce/cloth/force.cpp index 0365c66..53e4fce 100644 --- a/src/bounce/dynamics/cloth/force.cpp +++ b/src/bounce/cloth/force.cpp @@ -16,9 +16,8 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include -#include -#include +#include +#include b3Force* b3Force::Create(const b3ForceDef* def) { @@ -31,12 +30,6 @@ b3Force* b3Force::Create(const b3ForceDef* def) force = new (block) b3SpringForce((b3SpringForceDef*)def); break; } - case e_bendForce: - { - void* block = b3Alloc(sizeof(b3BendForce)); - force = new (block) b3BendForce((b3BendForceDef*)def); - break; - } default: { B3_ASSERT(false); @@ -60,13 +53,6 @@ void b3Force::Destroy(b3Force* force) b3Free(force); break; } - case e_bendForce: - { - b3BendForce* o = (b3BendForce*)force; - o->~b3BendForce(); - b3Free(force); - break; - } default: { B3_ASSERT(false); diff --git a/src/bounce/garment/garment_mesh.cpp b/src/bounce/cloth/garment/garment_mesh.cpp similarity index 97% rename from src/bounce/garment/garment_mesh.cpp rename to src/bounce/cloth/garment/garment_mesh.cpp index ffc2a5e..473b5f0 100644 --- a/src/bounce/garment/garment_mesh.cpp +++ b/src/bounce/cloth/garment/garment_mesh.cpp @@ -16,9 +16,9 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include -#include -#include +#include +#include +#include #define ANSI_DECLARATORS #define REAL double diff --git a/src/bounce/dynamics/cloth/particle.cpp b/src/bounce/cloth/particle.cpp similarity index 91% rename from src/bounce/dynamics/cloth/particle.cpp rename to src/bounce/cloth/particle.cpp index cd4c2fe..0da1c6d 100644 --- a/src/bounce/dynamics/cloth/particle.cpp +++ b/src/bounce/cloth/particle.cpp @@ -16,11 +16,11 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include void b3BodyContactWorldPoint::Initialize(const b3BodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB) { diff --git a/src/bounce/dynamics/cloth/spring_force.cpp b/src/bounce/cloth/spring_force.cpp similarity index 91% rename from src/bounce/dynamics/cloth/spring_force.cpp rename to src/bounce/cloth/spring_force.cpp index 25ba831..4a634fb 100644 --- a/src/bounce/dynamics/cloth/spring_force.cpp +++ b/src/bounce/cloth/spring_force.cpp @@ -16,11 +16,11 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include void b3SpringForceDef::Initialize(b3Particle* particle1, b3Particle* particle2, float32 structuralStiffness, float32 dampingStiffness) { diff --git a/src/bounce/dynamics/cloth/bend_force.cpp b/src/bounce/dynamics/cloth/bend_force.cpp deleted file mode 100644 index d540706..0000000 --- a/src/bounce/dynamics/cloth/bend_force.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#include -#include -#include -#include -#include - -void b3BendForceDef::Initialize(b3Particle* particle1, b3Particle* particle2, b3Particle* particle3, b3Particle* particle4, float32 structuralStiffness, float32 dampingStiffness) -{ - type = e_bendForce; - p1 = particle1; - p2 = particle2; - p3 = particle3; - p4 = particle4; - - b3Vec3 x1 = p1->GetPosition(); - b3Vec3 x2 = p2->GetPosition(); - b3Vec3 x3 = p3->GetPosition(); - b3Vec3 x4 = p4->GetPosition(); - - restDistance = b3Distance(x2, x3); - structural = structuralStiffness; - damping = dampingStiffness; -} - -b3BendForce::b3BendForce(const b3BendForceDef* def) -{ - m_type = e_bendForce; - m_p1 = def->p1; - m_p2 = def->p2; - m_p3 = def->p3; - m_p4 = def->p4; - m_L0 = def->restDistance; - m_angle0 = def->restAngle; - m_ks = def->structural; - m_kd = def->damping; -} - -b3BendForce::~b3BendForce() -{ - -} - -void b3BendForce::Apply(const b3ClothSolverData* data) -{ - b3DenseVec3& x = *data->x; - b3DenseVec3& v = *data->v; - b3DenseVec3& f = *data->f; - b3SparseSymMat33& dfdx = *data->dfdx; - b3SparseSymMat33& dfdv = *data->dfdv; - - u32 i1 = m_p2->m_solverId; - u32 i2 = m_p3->m_solverId; - - b3Vec3 x1 = x[i1]; - b3Vec3 v1 = v[i1]; - - b3Vec3 x2 = x[i2]; - b3Vec3 v2 = v[i2]; - - b3Mat33 I; I.SetIdentity(); - - b3Vec3 ef; ef.SetZero(); - - if (m_ks > 0.0f) - { - b3Vec3 dx = x1 - x2; - - float32 L = b3Length(dx); - - if (L >= m_L0) - { - // Apply tension - b3Vec3 n = dx / L; - - ef += -m_ks * (L - m_L0) * n; - - // Jacobian - b3Mat33 Jx11 = -m_ks * (b3Outer(dx, dx) + (1.0f - m_L0 / L) * (I - b3Outer(dx, dx))); - b3Mat33 Jx12 = -Jx11; - //b3Mat33 Jx21 = Jx12; - b3Mat33 Jx22 = Jx11; - - dfdx(i1, i1) += Jx11; - dfdx(i1, i2) += Jx12; - //dfdx(i2, i1) += Jx21; - dfdx(i2, i2) += Jx22; - } - } - - if (m_kd > 0.0f) - { - // Apply damping - b3Vec3 dv = v1 - v2; - - ef += -m_kd * dv; - - b3Mat33 Jv11 = -m_kd * I; - b3Mat33 Jv12 = -Jv11; - //b3Mat33 Jv21 = Jv12; - b3Mat33 Jv22 = Jv11; - - dfdv(i1, i1) += Jv11; - dfdv(i1, i2) += Jv12; - //dfdv(i2, i1) += Jv21; - dfdv(i2, i2) += Jv22; - } - - f[i1] += ef; - f[i2] -= ef; -} \ No newline at end of file diff --git a/src/bounce/dynamics/world.cpp b/src/bounce/dynamics/world.cpp index aa9647a..bc688b7 100644 --- a/src/bounce/dynamics/world.cpp +++ b/src/bounce/dynamics/world.cpp @@ -17,7 +17,6 @@ */ #include -#include #include #include #include @@ -32,7 +31,6 @@ extern u32 b3_gjkCalls, b3_gjkIters, b3_gjkMaxIters; extern bool b3_convexCache; b3World::b3World() : - m_clothBlocks(sizeof(b3Cloth)), m_bodyBlocks(sizeof(b3Body)) { b3_allocCalls = 0; @@ -54,14 +52,6 @@ b3World::b3World() : b3World::~b3World() { - b3Cloth* c = m_clothList.m_head; - while (c) - { - b3Cloth* c0 = c; - c = c->m_next; - c0->~b3Cloth(); - } - b3Body* b = m_bodyList.m_head; while (b) { @@ -93,21 +83,6 @@ void b3World::SetSleeping(bool flag) } } -b3Cloth* b3World::CreateCloth(const b3ClothDef& def) -{ - void* mem = m_clothBlocks.Allocate(); - b3Cloth* c = new(mem) b3Cloth(def, this); - m_clothList.PushFront(c); - return c; -} - -void b3World::DestroyCloth(b3Cloth* c) -{ - m_clothList.Remove(c); - c->~b3Cloth(); - m_clothBlocks.Free(c); -} - b3Body* b3World::CreateBody(const b3BodyDef& def) { void* mem = m_bodyBlocks.Allocate(); @@ -166,11 +141,6 @@ void b3World::Step(float32 dt, u32 velocityIterations, u32 positionIterations) { Solve(dt, velocityIterations, positionIterations); } - - //SolveTOI - - // Step cloth dynamics - StepCloth(dt); } void b3World::Solve(float32 dt, u32 velocityIterations, u32 positionIterations) @@ -366,16 +336,6 @@ void b3World::Solve(float32 dt, u32 velocityIterations, u32 positionIterations) } } -void b3World::StepCloth(float32 dt) -{ - B3_PROFILE("Step Cloth"); - - for (b3Cloth* c = m_clothList.m_head; c; c = c->GetNext()) - { - c->Step(dt, m_gravity); - } -} - struct b3ShapeRayCastCallback { float32 Report(const b3RayCastInput& input, u32 proxyId) @@ -411,7 +371,7 @@ struct b3ShapeRayCastCallback const b3BroadPhase* broadPhase; }; -void b3World::RayCastShape(b3RayCastListener* listener, const b3Vec3& p1, const b3Vec3& p2) const +void b3World::RayCast(b3RayCastListener* listener, const b3Vec3& p1, const b3Vec3& p2) const { b3RayCastInput input; input.p1 = p1; @@ -456,7 +416,7 @@ struct b3RayCastSingleShapeCallback const b3BroadPhase* broadPhase; }; -bool b3World::RayCastSingleShape(b3RayCastSingleShapeOutput* output, const b3Vec3& p1, const b3Vec3& p2) const +bool b3World::RayCastSingle(b3RayCastSingleOutput* output, const b3Vec3& p1, const b3Vec3& p2) const { b3RayCastInput input; input.p1 = p1; @@ -489,52 +449,6 @@ bool b3World::RayCastSingleShape(b3RayCastSingleShapeOutput* output, const b3Vec return false; } -void b3World::RayCastCloth(b3RayCastListener* listener, const b3Vec3& p1, const b3Vec3& p2) const -{ - for (b3Cloth* c = m_clothList.m_head; c; c = c->m_next) - { - c->RayCast(listener, p1, p2); - } -} - -bool b3World::RayCastSingleCloth(b3RayCastSingleClothOutput* output, const b3Vec3& p1, const b3Vec3& p2) const -{ - b3Cloth* cloth0 = NULL; - b3ClothRayCastSingleOutput output0; - output0.fraction = B3_MAX_FLOAT; - - for (b3Cloth* c = m_clothList.m_head; c; c = c->m_next) - { - b3ClothRayCastSingleOutput subOutput; - if (c->RayCastSingle(&subOutput, p1, p2)) - { - if (subOutput.fraction < output0.fraction) - { - cloth0 = c; - output0 = subOutput; - } - } - } - - if (cloth0 != NULL) - { - u32 triangle = output0.triangle; - float32 fraction = output0.fraction; - b3Vec3 point = (1.0f - fraction) * p1 + fraction * p2; - b3Vec3 normal = output0.normal; - - output->cloth = cloth0; - output->triangle = triangle; - output->point = point; - output->normal = normal; - output->fraction = fraction; - - return true; - } - - return false; -} - struct b3QueryAABBCallback { bool Report(u32 proxyID) diff --git a/src/bounce/dynamics/rope/rope.cpp b/src/bounce/rope/rope.cpp similarity index 99% rename from src/bounce/dynamics/rope/rope.cpp rename to src/bounce/rope/rope.cpp index 6ddec52..d839180 100644 --- a/src/bounce/dynamics/rope/rope.cpp +++ b/src/bounce/rope/rope.cpp @@ -16,8 +16,8 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include -#include +#include +#include #include struct b3RopeBody diff --git a/test/ignore.txt b/test/ignore.txt deleted file mode 100644 index e69de29..0000000 From be3912c7626d85524a258a4eef99a2c3b325d923 Mon Sep 17 00:00:00 2001 From: Irlan Robson Date: Mon, 8 Oct 2018 16:19:56 -0300 Subject: [PATCH 018/198] Update readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 8879dfa..cd2eedb 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ ## Bounce -**Welcome! Bounce is a 3D physics engine.** +**Welcome! Bounce is a 3D physics engine for games.** Feel free to ask questions, give feedback and suggestions using the issue tracker. From 1f65a9f79d493e633b51cd04c03c17810aa11ba0 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 26 Feb 2019 16:41:28 -0300 Subject: [PATCH 019/198] update license headers --- examples/hello_world/main.cpp | 2 +- examples/testbed/framework/body_dragger.cpp | 2 +- examples/testbed/framework/body_dragger.h | 2 +- examples/testbed/framework/cloth_dragger.cpp | 2 +- examples/testbed/framework/cloth_dragger.h | 2 +- examples/testbed/framework/draw.cpp | 2 +- examples/testbed/framework/draw.h | 2 +- examples/testbed/framework/draw_gl2.h | 2 +- examples/testbed/framework/json_profiler.cpp | 2 +- examples/testbed/framework/json_profiler.h | 2 +- examples/testbed/framework/main.cpp | 2 +- examples/testbed/framework/model.cpp | 2 +- examples/testbed/framework/model.h | 2 +- examples/testbed/framework/profiler.cpp | 2 +- examples/testbed/framework/profiler.h | 2 +- examples/testbed/framework/recorder_profiler.cpp | 2 +- examples/testbed/framework/recorder_profiler.h | 2 +- examples/testbed/framework/test.cpp | 2 +- examples/testbed/framework/test.h | 2 +- examples/testbed/framework/test_entries.cpp | 2 +- examples/testbed/framework/testbed_listener.h | 2 +- examples/testbed/framework/view.cpp | 2 +- examples/testbed/framework/view.h | 2 +- examples/testbed/framework/view_model.cpp | 2 +- examples/testbed/framework/view_model.h | 2 +- examples/testbed/tests/angular_motion.h | 2 +- examples/testbed/tests/body_types.h | 2 +- examples/testbed/tests/box_edge_contact.h | 2 +- examples/testbed/tests/box_face_contact.h | 2 +- examples/testbed/tests/box_stack.h | 2 +- examples/testbed/tests/capsule_collision.h | 2 +- examples/testbed/tests/capsule_spin.h | 2 +- examples/testbed/tests/capsule_stack.h | 2 +- examples/testbed/tests/character_test.h | 2 +- examples/testbed/tests/cluster.h | 2 +- examples/testbed/tests/collide_test.h | 2 +- examples/testbed/tests/compound_body.h | 2 +- examples/testbed/tests/cone_test.h | 2 +- examples/testbed/tests/convex_hull.h | 2 +- examples/testbed/tests/deep_capsule.h | 2 +- examples/testbed/tests/degenerate_capsule.h | 2 +- examples/testbed/tests/distance_test.h | 2 +- examples/testbed/tests/gyro_motion.h | 2 +- examples/testbed/tests/hinge_chain.h | 2 +- examples/testbed/tests/hinge_motor.h | 2 +- examples/testbed/tests/hull_collision.h | 2 +- examples/testbed/tests/hull_contact_test.h | 2 +- examples/testbed/tests/initial_overlap.h | 2 +- examples/testbed/tests/jenga.h | 2 +- examples/testbed/tests/linear_motion.h | 2 +- examples/testbed/tests/mass_spring.h | 2 +- examples/testbed/tests/mesh_contact_test.h | 2 +- examples/testbed/tests/multiple_pendulum.h | 2 +- examples/testbed/tests/newton_cradle.h | 2 +- examples/testbed/tests/particle_types.h | 2 +- examples/testbed/tests/pinned_cloth.h | 2 +- examples/testbed/tests/pyramid.h | 2 +- examples/testbed/tests/pyramids.h | 2 +- examples/testbed/tests/quadric_shapes.h | 2 +- examples/testbed/tests/ragdoll.h | 2 +- examples/testbed/tests/ray_cast.h | 2 +- examples/testbed/tests/rope_test.h | 2 +- examples/testbed/tests/self_collision.h | 2 +- examples/testbed/tests/sensor_test.h | 2 +- examples/testbed/tests/shape_stack.h | 2 +- examples/testbed/tests/sheet_stack.h | 2 +- examples/testbed/tests/shirt.h | 2 +- examples/testbed/tests/single_pendulum.h | 2 +- examples/testbed/tests/sphere_stack.h | 2 +- examples/testbed/tests/spring.h | 2 +- examples/testbed/tests/table_cloth.h | 2 +- examples/testbed/tests/tension_mapping.h | 2 +- examples/testbed/tests/tumbler.h | 2 +- examples/testbed/tests/varying_friction.h | 2 +- examples/testbed/tests/varying_restitution.h | 2 +- examples/testbed/tests/weld_test.h | 2 +- include/bounce/bounce.h | 2 +- include/bounce/cloth/cloth.h | 2 +- include/bounce/cloth/cloth_contact_solver.h | 2 +- include/bounce/cloth/cloth_mesh.h | 2 +- include/bounce/cloth/cloth_solver.h | 2 +- include/bounce/cloth/dense_vec3.h | 2 +- include/bounce/cloth/diag_mat33.h | 2 +- include/bounce/cloth/force.h | 2 +- include/bounce/cloth/garment/garment.h | 2 +- include/bounce/cloth/garment/garment_mesh.h | 2 +- include/bounce/cloth/garment/sewing_pattern.h | 2 +- include/bounce/cloth/particle.h | 2 +- include/bounce/cloth/sparse_sym_mat33.h | 2 +- include/bounce/cloth/spring_force.h | 2 +- include/bounce/collision/broad_phase.h | 2 +- include/bounce/collision/collision.h | 2 +- include/bounce/collision/gjk/gjk.h | 2 +- include/bounce/collision/gjk/gjk_proxy.h | 2 +- include/bounce/collision/sat/sat.h | 2 +- include/bounce/collision/sat/sat_edge_and_hull.h | 2 +- include/bounce/collision/sat/sat_vertex_and_hull.h | 2 +- include/bounce/collision/shapes/aabb3.h | 2 +- include/bounce/collision/shapes/box_hull.h | 2 +- include/bounce/collision/shapes/capsule.h | 2 +- include/bounce/collision/shapes/grid_mesh.h | 2 +- include/bounce/collision/shapes/hull.h | 2 +- include/bounce/collision/shapes/mesh.h | 2 +- include/bounce/collision/shapes/qhull.h | 2 +- include/bounce/collision/shapes/sphere.h | 2 +- include/bounce/collision/shapes/triangle_hull.h | 2 +- include/bounce/collision/trees/dynamic_tree.h | 2 +- include/bounce/collision/trees/static_tree.h | 2 +- include/bounce/common/draw.h | 2 +- include/bounce/common/geometry.h | 2 +- include/bounce/common/math/mat.h | 2 +- include/bounce/common/math/mat22.h | 2 +- include/bounce/common/math/mat33.h | 2 +- include/bounce/common/math/mat44.h | 2 +- include/bounce/common/math/math.h | 2 +- include/bounce/common/math/quat.h | 2 +- include/bounce/common/math/transform.h | 2 +- include/bounce/common/math/vec2.h | 2 +- include/bounce/common/math/vec3.h | 2 +- include/bounce/common/math/vec4.h | 2 +- include/bounce/common/memory/block_pool.h | 2 +- include/bounce/common/memory/stack_allocator.h | 2 +- include/bounce/common/settings.h | 2 +- include/bounce/common/template/array.h | 2 +- include/bounce/common/template/list.h | 2 +- include/bounce/common/template/object_array.h | 2 +- include/bounce/common/template/queue.h | 2 +- include/bounce/common/template/stack.h | 2 +- include/bounce/common/time.h | 2 +- include/bounce/dynamics/body.h | 2 +- include/bounce/dynamics/contact_manager.h | 2 +- include/bounce/dynamics/contacts/collide/clip.h | 2 +- include/bounce/dynamics/contacts/collide/collide.h | 2 +- include/bounce/dynamics/contacts/contact.h | 2 +- include/bounce/dynamics/contacts/contact_cluster.h | 2 +- include/bounce/dynamics/contacts/contact_solver.h | 2 +- include/bounce/dynamics/contacts/convex_contact.h | 2 +- include/bounce/dynamics/contacts/manifold.h | 2 +- include/bounce/dynamics/contacts/mesh_contact.h | 2 +- include/bounce/dynamics/island.h | 2 +- include/bounce/dynamics/joint_manager.h | 2 +- include/bounce/dynamics/joints/cone_joint.h | 2 +- include/bounce/dynamics/joints/joint.h | 2 +- include/bounce/dynamics/joints/joint_solver.h | 2 +- include/bounce/dynamics/joints/mouse_joint.h | 2 +- include/bounce/dynamics/joints/revolute_joint.h | 2 +- include/bounce/dynamics/joints/sphere_joint.h | 2 +- include/bounce/dynamics/joints/spring_joint.h | 2 +- include/bounce/dynamics/joints/weld_joint.h | 2 +- include/bounce/dynamics/shapes/capsule_shape.h | 2 +- include/bounce/dynamics/shapes/hull_shape.h | 2 +- include/bounce/dynamics/shapes/mesh_shape.h | 2 +- include/bounce/dynamics/shapes/shape.h | 2 +- include/bounce/dynamics/shapes/sphere_shape.h | 2 +- include/bounce/dynamics/time_step.h | 2 +- include/bounce/dynamics/world.h | 2 +- include/bounce/dynamics/world_listeners.h | 2 +- include/bounce/quickhull/qh_hull.h | 2 +- include/bounce/rope/rope.h | 2 +- include/bounce/rope/spatial.h | 2 +- src/bounce/cloth/cloth.cpp | 2 +- src/bounce/cloth/cloth_contact_solver.cpp | 2 +- src/bounce/cloth/cloth_mesh.cpp | 2 +- src/bounce/cloth/cloth_solver.cpp | 2 +- src/bounce/cloth/force.cpp | 2 +- src/bounce/cloth/garment/garment_mesh.cpp | 2 +- src/bounce/cloth/particle.cpp | 2 +- src/bounce/cloth/spring_force.cpp | 2 +- src/bounce/collision/broad_phase.cpp | 2 +- src/bounce/collision/collision.cpp | 2 +- src/bounce/collision/gjk/gjk.cpp | 2 +- src/bounce/collision/gjk/gjk_feature_pair.cpp | 2 +- src/bounce/collision/sat/sat.cpp | 2 +- src/bounce/collision/sat/sat_edge_and_hull.cpp | 2 +- src/bounce/collision/sat/sat_vertex_and_hull.cpp | 2 +- src/bounce/collision/shapes/hull.cpp | 2 +- src/bounce/collision/shapes/qhull.cpp | 2 +- src/bounce/collision/trees/dynamic_tree.cpp | 2 +- src/bounce/collision/trees/static_tree.cpp | 2 +- src/bounce/common/math/math.cpp | 2 +- src/bounce/common/memory/block_pool.cpp | 2 +- src/bounce/common/memory/stack_allocator.cpp | 2 +- src/bounce/common/settings.cpp | 2 +- src/bounce/dynamics/body.cpp | 2 +- src/bounce/dynamics/contact_manager.cpp | 2 +- src/bounce/dynamics/contacts/collide/clip.cpp | 2 +- src/bounce/dynamics/contacts/collide/collide.cpp | 2 +- src/bounce/dynamics/contacts/collide/collide_capsule_hull.cpp | 2 +- src/bounce/dynamics/contacts/collide/collide_capsules.cpp | 2 +- src/bounce/dynamics/contacts/collide/collide_hulls.cpp | 2 +- src/bounce/dynamics/contacts/collide/collide_hulls_cache.cpp | 2 +- src/bounce/dynamics/contacts/collide/collide_sphere_capsule.cpp | 2 +- src/bounce/dynamics/contacts/collide/collide_sphere_hull.cpp | 2 +- src/bounce/dynamics/contacts/collide/collide_spheres.cpp | 2 +- src/bounce/dynamics/contacts/contact.cpp | 2 +- src/bounce/dynamics/contacts/contact_cluster.cpp | 2 +- src/bounce/dynamics/contacts/contact_solver.cpp | 2 +- src/bounce/dynamics/contacts/convex_contact.cpp | 2 +- src/bounce/dynamics/contacts/manifold.cpp | 2 +- src/bounce/dynamics/contacts/mesh_contact.cpp | 2 +- src/bounce/dynamics/draw_world.cpp | 2 +- src/bounce/dynamics/island.cpp | 2 +- src/bounce/dynamics/joint_manager.cpp | 2 +- src/bounce/dynamics/joints/cone_joint.cpp | 2 +- src/bounce/dynamics/joints/joint.cpp | 2 +- src/bounce/dynamics/joints/joint_solver.cpp | 2 +- src/bounce/dynamics/joints/mouse_joint.cpp | 2 +- src/bounce/dynamics/joints/revolute_joint.cpp | 2 +- src/bounce/dynamics/joints/sphere_joint.cpp | 2 +- src/bounce/dynamics/joints/spring_joint.cpp | 2 +- src/bounce/dynamics/joints/weld_joint.cpp | 2 +- src/bounce/dynamics/shapes/capsule_shape.cpp | 2 +- src/bounce/dynamics/shapes/hull_shape.cpp | 2 +- src/bounce/dynamics/shapes/mesh_shape.cpp | 2 +- src/bounce/dynamics/shapes/shape.cpp | 2 +- src/bounce/dynamics/shapes/sphere_shape.cpp | 2 +- src/bounce/dynamics/world.cpp | 2 +- src/bounce/quickhull/qh_hull.cpp | 2 +- src/bounce/rope/rope.cpp | 2 +- 219 files changed, 219 insertions(+), 219 deletions(-) diff --git a/examples/hello_world/main.cpp b/examples/hello_world/main.cpp index 5715060..d68db49 100644 --- a/examples/hello_world/main.cpp +++ b/examples/hello_world/main.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/body_dragger.cpp b/examples/testbed/framework/body_dragger.cpp index 58456a5..5e0be78 100644 --- a/examples/testbed/framework/body_dragger.cpp +++ b/examples/testbed/framework/body_dragger.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/body_dragger.h b/examples/testbed/framework/body_dragger.h index 10d5a66..d7f4858 100644 --- a/examples/testbed/framework/body_dragger.h +++ b/examples/testbed/framework/body_dragger.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/cloth_dragger.cpp b/examples/testbed/framework/cloth_dragger.cpp index ea228ba..47ee7fc 100644 --- a/examples/testbed/framework/cloth_dragger.cpp +++ b/examples/testbed/framework/cloth_dragger.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/cloth_dragger.h b/examples/testbed/framework/cloth_dragger.h index 5058f20..d0e49de 100644 --- a/examples/testbed/framework/cloth_dragger.h +++ b/examples/testbed/framework/cloth_dragger.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/draw.cpp b/examples/testbed/framework/draw.cpp index 480e66f..1ed646c 100644 --- a/examples/testbed/framework/draw.cpp +++ b/examples/testbed/framework/draw.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/draw.h b/examples/testbed/framework/draw.h index e1e6755..b4db22b 100644 --- a/examples/testbed/framework/draw.h +++ b/examples/testbed/framework/draw.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/draw_gl2.h b/examples/testbed/framework/draw_gl2.h index d4ab35c..3cf7810 100644 --- a/examples/testbed/framework/draw_gl2.h +++ b/examples/testbed/framework/draw_gl2.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/json_profiler.cpp b/examples/testbed/framework/json_profiler.cpp index d11fa5a..1f4a38a 100644 --- a/examples/testbed/framework/json_profiler.cpp +++ b/examples/testbed/framework/json_profiler.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/json_profiler.h b/examples/testbed/framework/json_profiler.h index 3ff6fb8..15bcc2c 100644 --- a/examples/testbed/framework/json_profiler.h +++ b/examples/testbed/framework/json_profiler.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/main.cpp b/examples/testbed/framework/main.cpp index 9cd3863..dad7c74 100644 --- a/examples/testbed/framework/main.cpp +++ b/examples/testbed/framework/main.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/model.cpp b/examples/testbed/framework/model.cpp index 581616a..df3357d 100644 --- a/examples/testbed/framework/model.cpp +++ b/examples/testbed/framework/model.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/model.h b/examples/testbed/framework/model.h index e41d97b..310e6f2 100644 --- a/examples/testbed/framework/model.h +++ b/examples/testbed/framework/model.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/profiler.cpp b/examples/testbed/framework/profiler.cpp index 8a47078..81b6aec 100644 --- a/examples/testbed/framework/profiler.cpp +++ b/examples/testbed/framework/profiler.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h index b54384d..5428ee1 100644 --- a/examples/testbed/framework/profiler.h +++ b/examples/testbed/framework/profiler.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/recorder_profiler.cpp b/examples/testbed/framework/recorder_profiler.cpp index 886689c..3b21a1a 100644 --- a/examples/testbed/framework/recorder_profiler.cpp +++ b/examples/testbed/framework/recorder_profiler.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/recorder_profiler.h b/examples/testbed/framework/recorder_profiler.h index 0c74e41..49f2edf 100644 --- a/examples/testbed/framework/recorder_profiler.h +++ b/examples/testbed/framework/recorder_profiler.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/test.cpp b/examples/testbed/framework/test.cpp index 12ca6db..3b83bb6 100644 --- a/examples/testbed/framework/test.cpp +++ b/examples/testbed/framework/test.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/test.h b/examples/testbed/framework/test.h index 4171804..8e21ca7 100644 --- a/examples/testbed/framework/test.h +++ b/examples/testbed/framework/test.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/test_entries.cpp b/examples/testbed/framework/test_entries.cpp index 6da7e6f..8526371 100644 --- a/examples/testbed/framework/test_entries.cpp +++ b/examples/testbed/framework/test_entries.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/testbed_listener.h b/examples/testbed/framework/testbed_listener.h index 35dc5d3..ee50dba 100644 --- a/examples/testbed/framework/testbed_listener.h +++ b/examples/testbed/framework/testbed_listener.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/view.cpp b/examples/testbed/framework/view.cpp index 23b03a1..d2d68dc 100644 --- a/examples/testbed/framework/view.cpp +++ b/examples/testbed/framework/view.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/view.h b/examples/testbed/framework/view.h index 26c4e57..edb0be1 100644 --- a/examples/testbed/framework/view.h +++ b/examples/testbed/framework/view.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/view_model.cpp b/examples/testbed/framework/view_model.cpp index 370a231..ffb3372 100644 --- a/examples/testbed/framework/view_model.cpp +++ b/examples/testbed/framework/view_model.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/framework/view_model.h b/examples/testbed/framework/view_model.h index 7d5925f..8b10a0c 100644 --- a/examples/testbed/framework/view_model.h +++ b/examples/testbed/framework/view_model.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/angular_motion.h b/examples/testbed/tests/angular_motion.h index a5674cc..ae3578e 100644 --- a/examples/testbed/tests/angular_motion.h +++ b/examples/testbed/tests/angular_motion.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/body_types.h b/examples/testbed/tests/body_types.h index ab3449a..ac71468 100644 --- a/examples/testbed/tests/body_types.h +++ b/examples/testbed/tests/body_types.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/box_edge_contact.h b/examples/testbed/tests/box_edge_contact.h index 9847880..5e90f7f 100644 --- a/examples/testbed/tests/box_edge_contact.h +++ b/examples/testbed/tests/box_edge_contact.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/box_face_contact.h b/examples/testbed/tests/box_face_contact.h index 99d6b9f..71243e3 100644 --- a/examples/testbed/tests/box_face_contact.h +++ b/examples/testbed/tests/box_face_contact.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/box_stack.h b/examples/testbed/tests/box_stack.h index 6cf636a..1d1e9ef 100644 --- a/examples/testbed/tests/box_stack.h +++ b/examples/testbed/tests/box_stack.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/capsule_collision.h b/examples/testbed/tests/capsule_collision.h index 8551b03..00a51ac 100644 --- a/examples/testbed/tests/capsule_collision.h +++ b/examples/testbed/tests/capsule_collision.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/capsule_spin.h b/examples/testbed/tests/capsule_spin.h index 0d6cc5a..94be411 100644 --- a/examples/testbed/tests/capsule_spin.h +++ b/examples/testbed/tests/capsule_spin.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/capsule_stack.h b/examples/testbed/tests/capsule_stack.h index f0462b8..d4b370c 100644 --- a/examples/testbed/tests/capsule_stack.h +++ b/examples/testbed/tests/capsule_stack.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/character_test.h b/examples/testbed/tests/character_test.h index 762bf0c..ec6e322 100644 --- a/examples/testbed/tests/character_test.h +++ b/examples/testbed/tests/character_test.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/cluster.h b/examples/testbed/tests/cluster.h index bd7b0b2..74bf446 100644 --- a/examples/testbed/tests/cluster.h +++ b/examples/testbed/tests/cluster.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/collide_test.h b/examples/testbed/tests/collide_test.h index 4818df3..560df43 100644 --- a/examples/testbed/tests/collide_test.h +++ b/examples/testbed/tests/collide_test.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/compound_body.h b/examples/testbed/tests/compound_body.h index 0445d03..a1d1922 100644 --- a/examples/testbed/tests/compound_body.h +++ b/examples/testbed/tests/compound_body.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/cone_test.h b/examples/testbed/tests/cone_test.h index dfa6691..a75a598 100644 --- a/examples/testbed/tests/cone_test.h +++ b/examples/testbed/tests/cone_test.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/convex_hull.h b/examples/testbed/tests/convex_hull.h index a9b0e3c..1ddb27d 100644 --- a/examples/testbed/tests/convex_hull.h +++ b/examples/testbed/tests/convex_hull.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/deep_capsule.h b/examples/testbed/tests/deep_capsule.h index c4cb36e..98eb010 100644 --- a/examples/testbed/tests/deep_capsule.h +++ b/examples/testbed/tests/deep_capsule.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/degenerate_capsule.h b/examples/testbed/tests/degenerate_capsule.h index 0ca046c..7a5b575 100644 --- a/examples/testbed/tests/degenerate_capsule.h +++ b/examples/testbed/tests/degenerate_capsule.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/distance_test.h b/examples/testbed/tests/distance_test.h index 44cbb20..e5ea4ac 100644 --- a/examples/testbed/tests/distance_test.h +++ b/examples/testbed/tests/distance_test.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/gyro_motion.h b/examples/testbed/tests/gyro_motion.h index cfbd897..2d35f1b 100644 --- a/examples/testbed/tests/gyro_motion.h +++ b/examples/testbed/tests/gyro_motion.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/hinge_chain.h b/examples/testbed/tests/hinge_chain.h index 3cc2e6b..7c0e297 100644 --- a/examples/testbed/tests/hinge_chain.h +++ b/examples/testbed/tests/hinge_chain.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/hinge_motor.h b/examples/testbed/tests/hinge_motor.h index 829f42b..0e8d01f 100644 --- a/examples/testbed/tests/hinge_motor.h +++ b/examples/testbed/tests/hinge_motor.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/hull_collision.h b/examples/testbed/tests/hull_collision.h index 8aad10e..1a24a06 100644 --- a/examples/testbed/tests/hull_collision.h +++ b/examples/testbed/tests/hull_collision.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/hull_contact_test.h b/examples/testbed/tests/hull_contact_test.h index 51fc123..186f387 100644 --- a/examples/testbed/tests/hull_contact_test.h +++ b/examples/testbed/tests/hull_contact_test.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/initial_overlap.h b/examples/testbed/tests/initial_overlap.h index b3e7156..fbfeec9 100644 --- a/examples/testbed/tests/initial_overlap.h +++ b/examples/testbed/tests/initial_overlap.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/jenga.h b/examples/testbed/tests/jenga.h index 8695def..7079d82 100644 --- a/examples/testbed/tests/jenga.h +++ b/examples/testbed/tests/jenga.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/linear_motion.h b/examples/testbed/tests/linear_motion.h index cd70d40..b94b5ac 100644 --- a/examples/testbed/tests/linear_motion.h +++ b/examples/testbed/tests/linear_motion.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/mass_spring.h b/examples/testbed/tests/mass_spring.h index 4c10924..dac0e58 100644 --- a/examples/testbed/tests/mass_spring.h +++ b/examples/testbed/tests/mass_spring.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/mesh_contact_test.h b/examples/testbed/tests/mesh_contact_test.h index d81f05f..6589eb4 100644 --- a/examples/testbed/tests/mesh_contact_test.h +++ b/examples/testbed/tests/mesh_contact_test.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/multiple_pendulum.h b/examples/testbed/tests/multiple_pendulum.h index 487b73f..6bc43b7 100644 --- a/examples/testbed/tests/multiple_pendulum.h +++ b/examples/testbed/tests/multiple_pendulum.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/newton_cradle.h b/examples/testbed/tests/newton_cradle.h index 445cd1c..865cb3a 100644 --- a/examples/testbed/tests/newton_cradle.h +++ b/examples/testbed/tests/newton_cradle.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/particle_types.h b/examples/testbed/tests/particle_types.h index 5aa4c71..55f51f6 100644 --- a/examples/testbed/tests/particle_types.h +++ b/examples/testbed/tests/particle_types.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/pinned_cloth.h b/examples/testbed/tests/pinned_cloth.h index 08fe1e8..b2a28ef 100644 --- a/examples/testbed/tests/pinned_cloth.h +++ b/examples/testbed/tests/pinned_cloth.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/pyramid.h b/examples/testbed/tests/pyramid.h index ecb3235..d7528e3 100644 --- a/examples/testbed/tests/pyramid.h +++ b/examples/testbed/tests/pyramid.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/pyramids.h b/examples/testbed/tests/pyramids.h index 60f4cff..e440e9d 100644 --- a/examples/testbed/tests/pyramids.h +++ b/examples/testbed/tests/pyramids.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/quadric_shapes.h b/examples/testbed/tests/quadric_shapes.h index 8bb1395..f00133a 100644 --- a/examples/testbed/tests/quadric_shapes.h +++ b/examples/testbed/tests/quadric_shapes.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/ragdoll.h b/examples/testbed/tests/ragdoll.h index 4942ebb..5dac8cb 100644 --- a/examples/testbed/tests/ragdoll.h +++ b/examples/testbed/tests/ragdoll.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/ray_cast.h b/examples/testbed/tests/ray_cast.h index 610627f..579bdd8 100644 --- a/examples/testbed/tests/ray_cast.h +++ b/examples/testbed/tests/ray_cast.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/rope_test.h b/examples/testbed/tests/rope_test.h index de9252d..389f517 100644 --- a/examples/testbed/tests/rope_test.h +++ b/examples/testbed/tests/rope_test.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be hebd liable for any damages diff --git a/examples/testbed/tests/self_collision.h b/examples/testbed/tests/self_collision.h index 3135b4f..3051748 100644 --- a/examples/testbed/tests/self_collision.h +++ b/examples/testbed/tests/self_collision.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/sensor_test.h b/examples/testbed/tests/sensor_test.h index 63bc2c4..3d8e64f 100644 --- a/examples/testbed/tests/sensor_test.h +++ b/examples/testbed/tests/sensor_test.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/shape_stack.h b/examples/testbed/tests/shape_stack.h index a22b6ba..8d6097c 100644 --- a/examples/testbed/tests/shape_stack.h +++ b/examples/testbed/tests/shape_stack.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/sheet_stack.h b/examples/testbed/tests/sheet_stack.h index afdbba9..7a32e93 100644 --- a/examples/testbed/tests/sheet_stack.h +++ b/examples/testbed/tests/sheet_stack.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/shirt.h b/examples/testbed/tests/shirt.h index 1d76f1a..5e3e0c9 100644 --- a/examples/testbed/tests/shirt.h +++ b/examples/testbed/tests/shirt.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/single_pendulum.h b/examples/testbed/tests/single_pendulum.h index 4746d3e..3808548 100644 --- a/examples/testbed/tests/single_pendulum.h +++ b/examples/testbed/tests/single_pendulum.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/sphere_stack.h b/examples/testbed/tests/sphere_stack.h index 1800e56..b5f48b6 100644 --- a/examples/testbed/tests/sphere_stack.h +++ b/examples/testbed/tests/sphere_stack.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/spring.h b/examples/testbed/tests/spring.h index 4d4f8be..ad5df0b 100644 --- a/examples/testbed/tests/spring.h +++ b/examples/testbed/tests/spring.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/table_cloth.h b/examples/testbed/tests/table_cloth.h index af43732..71ee69d 100644 --- a/examples/testbed/tests/table_cloth.h +++ b/examples/testbed/tests/table_cloth.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/tension_mapping.h b/examples/testbed/tests/tension_mapping.h index 49d8d23..74d93ae 100644 --- a/examples/testbed/tests/tension_mapping.h +++ b/examples/testbed/tests/tension_mapping.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/tumbler.h b/examples/testbed/tests/tumbler.h index 9f79bf0..471fb2c 100644 --- a/examples/testbed/tests/tumbler.h +++ b/examples/testbed/tests/tumbler.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/varying_friction.h b/examples/testbed/tests/varying_friction.h index 0a1c2ab..30726a7 100644 --- a/examples/testbed/tests/varying_friction.h +++ b/examples/testbed/tests/varying_friction.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/varying_restitution.h b/examples/testbed/tests/varying_restitution.h index d6ffeb6..b25e9fc 100644 --- a/examples/testbed/tests/varying_restitution.h +++ b/examples/testbed/tests/varying_restitution.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/examples/testbed/tests/weld_test.h b/examples/testbed/tests/weld_test.h index b35c825..1ca7aac 100644 --- a/examples/testbed/tests/weld_test.h +++ b/examples/testbed/tests/weld_test.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/bounce.h b/include/bounce/bounce.h index ba4633e..b44bacc 100644 --- a/include/bounce/bounce.h +++ b/include/bounce/bounce.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index b8b6a7c..c78e921 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/cloth/cloth_contact_solver.h b/include/bounce/cloth/cloth_contact_solver.h index a4a03f5..4752adc 100644 --- a/include/bounce/cloth/cloth_contact_solver.h +++ b/include/bounce/cloth/cloth_contact_solver.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/cloth/cloth_mesh.h b/include/bounce/cloth/cloth_mesh.h index 593f6dc..391f88e 100644 --- a/include/bounce/cloth/cloth_mesh.h +++ b/include/bounce/cloth/cloth_mesh.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/cloth/cloth_solver.h b/include/bounce/cloth/cloth_solver.h index ed56b82..2c53294 100644 --- a/include/bounce/cloth/cloth_solver.h +++ b/include/bounce/cloth/cloth_solver.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/cloth/dense_vec3.h b/include/bounce/cloth/dense_vec3.h index 34820e1..78a5b52 100644 --- a/include/bounce/cloth/dense_vec3.h +++ b/include/bounce/cloth/dense_vec3.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/cloth/diag_mat33.h b/include/bounce/cloth/diag_mat33.h index 24b62ff..b08065e 100644 --- a/include/bounce/cloth/diag_mat33.h +++ b/include/bounce/cloth/diag_mat33.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/cloth/force.h b/include/bounce/cloth/force.h index 78a18ba..04c456f 100644 --- a/include/bounce/cloth/force.h +++ b/include/bounce/cloth/force.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/cloth/garment/garment.h b/include/bounce/cloth/garment/garment.h index 3605584..8e16f93 100644 --- a/include/bounce/cloth/garment/garment.h +++ b/include/bounce/cloth/garment/garment.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/cloth/garment/garment_mesh.h b/include/bounce/cloth/garment/garment_mesh.h index 66e97ca..9057d24 100644 --- a/include/bounce/cloth/garment/garment_mesh.h +++ b/include/bounce/cloth/garment/garment_mesh.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/cloth/garment/sewing_pattern.h b/include/bounce/cloth/garment/sewing_pattern.h index 3d2ee57..635bed9 100644 --- a/include/bounce/cloth/garment/sewing_pattern.h +++ b/include/bounce/cloth/garment/sewing_pattern.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/cloth/particle.h b/include/bounce/cloth/particle.h index 7d531dc..7db88a4 100644 --- a/include/bounce/cloth/particle.h +++ b/include/bounce/cloth/particle.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/cloth/sparse_sym_mat33.h b/include/bounce/cloth/sparse_sym_mat33.h index 6b688d9..daf2664 100644 --- a/include/bounce/cloth/sparse_sym_mat33.h +++ b/include/bounce/cloth/sparse_sym_mat33.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/cloth/spring_force.h b/include/bounce/cloth/spring_force.h index c0825b7..aee0425 100644 --- a/include/bounce/cloth/spring_force.h +++ b/include/bounce/cloth/spring_force.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/broad_phase.h b/include/bounce/collision/broad_phase.h index 8e703f6..b9483a2 100644 --- a/include/bounce/collision/broad_phase.h +++ b/include/bounce/collision/broad_phase.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/collision.h b/include/bounce/collision/collision.h index 1fde28f..9eda6de 100644 --- a/include/bounce/collision/collision.h +++ b/include/bounce/collision/collision.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/gjk/gjk.h b/include/bounce/collision/gjk/gjk.h index 6261de6..0d85e50 100644 --- a/include/bounce/collision/gjk/gjk.h +++ b/include/bounce/collision/gjk/gjk.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/gjk/gjk_proxy.h b/include/bounce/collision/gjk/gjk_proxy.h index 5a4766e..71638ea 100644 --- a/include/bounce/collision/gjk/gjk_proxy.h +++ b/include/bounce/collision/gjk/gjk_proxy.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/sat/sat.h b/include/bounce/collision/sat/sat.h index 8f45ee2..a21fa12 100644 --- a/include/bounce/collision/sat/sat.h +++ b/include/bounce/collision/sat/sat.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/sat/sat_edge_and_hull.h b/include/bounce/collision/sat/sat_edge_and_hull.h index 60ecb2f..8998a40 100644 --- a/include/bounce/collision/sat/sat_edge_and_hull.h +++ b/include/bounce/collision/sat/sat_edge_and_hull.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/sat/sat_vertex_and_hull.h b/include/bounce/collision/sat/sat_vertex_and_hull.h index 52a59d5..4f78c32 100644 --- a/include/bounce/collision/sat/sat_vertex_and_hull.h +++ b/include/bounce/collision/sat/sat_vertex_and_hull.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/shapes/aabb3.h b/include/bounce/collision/shapes/aabb3.h index 1ffd8b4..a9e562e 100644 --- a/include/bounce/collision/shapes/aabb3.h +++ b/include/bounce/collision/shapes/aabb3.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/shapes/box_hull.h b/include/bounce/collision/shapes/box_hull.h index 5d384fa..b655c18 100644 --- a/include/bounce/collision/shapes/box_hull.h +++ b/include/bounce/collision/shapes/box_hull.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/shapes/capsule.h b/include/bounce/collision/shapes/capsule.h index 3c5464b..ea434e5 100644 --- a/include/bounce/collision/shapes/capsule.h +++ b/include/bounce/collision/shapes/capsule.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/shapes/grid_mesh.h b/include/bounce/collision/shapes/grid_mesh.h index 4561277..358416d 100644 --- a/include/bounce/collision/shapes/grid_mesh.h +++ b/include/bounce/collision/shapes/grid_mesh.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/shapes/hull.h b/include/bounce/collision/shapes/hull.h index 46a7f11..48f152d 100644 --- a/include/bounce/collision/shapes/hull.h +++ b/include/bounce/collision/shapes/hull.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/shapes/mesh.h b/include/bounce/collision/shapes/mesh.h index a5395dc..3fbde6d 100644 --- a/include/bounce/collision/shapes/mesh.h +++ b/include/bounce/collision/shapes/mesh.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/shapes/qhull.h b/include/bounce/collision/shapes/qhull.h index c900135..42f3c66 100644 --- a/include/bounce/collision/shapes/qhull.h +++ b/include/bounce/collision/shapes/qhull.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/shapes/sphere.h b/include/bounce/collision/shapes/sphere.h index 7156fb0..b0bc4bb 100644 --- a/include/bounce/collision/shapes/sphere.h +++ b/include/bounce/collision/shapes/sphere.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/shapes/triangle_hull.h b/include/bounce/collision/shapes/triangle_hull.h index 1ed5964..92c67ef 100644 --- a/include/bounce/collision/shapes/triangle_hull.h +++ b/include/bounce/collision/shapes/triangle_hull.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/trees/dynamic_tree.h b/include/bounce/collision/trees/dynamic_tree.h index 761c28c..969f46d 100644 --- a/include/bounce/collision/trees/dynamic_tree.h +++ b/include/bounce/collision/trees/dynamic_tree.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/collision/trees/static_tree.h b/include/bounce/collision/trees/static_tree.h index 069b855..4a07ba1 100644 --- a/include/bounce/collision/trees/static_tree.h +++ b/include/bounce/collision/trees/static_tree.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/draw.h b/include/bounce/common/draw.h index cbb2b51..f6790b8 100644 --- a/include/bounce/common/draw.h +++ b/include/bounce/common/draw.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/geometry.h b/include/bounce/common/geometry.h index 7cb1582..212a17f 100644 --- a/include/bounce/common/geometry.h +++ b/include/bounce/common/geometry.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/math/mat.h b/include/bounce/common/math/mat.h index 75eab32..0f9cd93 100644 --- a/include/bounce/common/math/mat.h +++ b/include/bounce/common/math/mat.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/math/mat22.h b/include/bounce/common/math/mat22.h index bc66b82..b976cf1 100644 --- a/include/bounce/common/math/mat22.h +++ b/include/bounce/common/math/mat22.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/math/mat33.h b/include/bounce/common/math/mat33.h index 3696e22..598210c 100644 --- a/include/bounce/common/math/mat33.h +++ b/include/bounce/common/math/mat33.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/math/mat44.h b/include/bounce/common/math/mat44.h index 0285422..57691e2 100644 --- a/include/bounce/common/math/mat44.h +++ b/include/bounce/common/math/mat44.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/math/math.h b/include/bounce/common/math/math.h index 4bbc091..3521d30 100644 --- a/include/bounce/common/math/math.h +++ b/include/bounce/common/math/math.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/math/quat.h b/include/bounce/common/math/quat.h index 7f745ac..a027a99 100644 --- a/include/bounce/common/math/quat.h +++ b/include/bounce/common/math/quat.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/math/transform.h b/include/bounce/common/math/transform.h index 13a9101..da6530e 100644 --- a/include/bounce/common/math/transform.h +++ b/include/bounce/common/math/transform.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/math/vec2.h b/include/bounce/common/math/vec2.h index 4fcaac9..b833016 100644 --- a/include/bounce/common/math/vec2.h +++ b/include/bounce/common/math/vec2.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/math/vec3.h b/include/bounce/common/math/vec3.h index 0b04bda..67c2524 100644 --- a/include/bounce/common/math/vec3.h +++ b/include/bounce/common/math/vec3.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/math/vec4.h b/include/bounce/common/math/vec4.h index 304e45f..8b44ec8 100644 --- a/include/bounce/common/math/vec4.h +++ b/include/bounce/common/math/vec4.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/memory/block_pool.h b/include/bounce/common/memory/block_pool.h index 583b6b6..3f81955 100644 --- a/include/bounce/common/memory/block_pool.h +++ b/include/bounce/common/memory/block_pool.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/memory/stack_allocator.h b/include/bounce/common/memory/stack_allocator.h index db64bb8..be70249 100644 --- a/include/bounce/common/memory/stack_allocator.h +++ b/include/bounce/common/memory/stack_allocator.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/settings.h b/include/bounce/common/settings.h index 7aeba36..431ab59 100644 --- a/include/bounce/common/settings.h +++ b/include/bounce/common/settings.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/template/array.h b/include/bounce/common/template/array.h index 028dfd2..c6d09b4 100644 --- a/include/bounce/common/template/array.h +++ b/include/bounce/common/template/array.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/template/list.h b/include/bounce/common/template/list.h index 58c00b6..8d83141 100644 --- a/include/bounce/common/template/list.h +++ b/include/bounce/common/template/list.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/template/object_array.h b/include/bounce/common/template/object_array.h index 8f4a885..f691a6e 100644 --- a/include/bounce/common/template/object_array.h +++ b/include/bounce/common/template/object_array.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/template/queue.h b/include/bounce/common/template/queue.h index 327f169..0992b4d 100644 --- a/include/bounce/common/template/queue.h +++ b/include/bounce/common/template/queue.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/template/stack.h b/include/bounce/common/template/stack.h index 46d28bd..ca938d0 100644 --- a/include/bounce/common/template/stack.h +++ b/include/bounce/common/template/stack.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/common/time.h b/include/bounce/common/time.h index 4625535..b6b7900 100644 --- a/include/bounce/common/time.h +++ b/include/bounce/common/time.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/body.h b/include/bounce/dynamics/body.h index 692201b..24ba1c8 100644 --- a/include/bounce/dynamics/body.h +++ b/include/bounce/dynamics/body.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/contact_manager.h b/include/bounce/dynamics/contact_manager.h index a32ffc9..5be9470 100644 --- a/include/bounce/dynamics/contact_manager.h +++ b/include/bounce/dynamics/contact_manager.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/contacts/collide/clip.h b/include/bounce/dynamics/contacts/collide/clip.h index d87ccbb..ced23e1 100644 --- a/include/bounce/dynamics/contacts/collide/clip.h +++ b/include/bounce/dynamics/contacts/collide/clip.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/contacts/collide/collide.h b/include/bounce/dynamics/contacts/collide/collide.h index 12ec99a..f3741b9 100644 --- a/include/bounce/dynamics/contacts/collide/collide.h +++ b/include/bounce/dynamics/contacts/collide/collide.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/contacts/contact.h b/include/bounce/dynamics/contacts/contact.h index 3988f38..c934c5f 100644 --- a/include/bounce/dynamics/contacts/contact.h +++ b/include/bounce/dynamics/contacts/contact.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/contacts/contact_cluster.h b/include/bounce/dynamics/contacts/contact_cluster.h index 4d316e9..f2a0de7 100644 --- a/include/bounce/dynamics/contacts/contact_cluster.h +++ b/include/bounce/dynamics/contacts/contact_cluster.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/contacts/contact_solver.h b/include/bounce/dynamics/contacts/contact_solver.h index e8d0c6c..51a3f4e 100644 --- a/include/bounce/dynamics/contacts/contact_solver.h +++ b/include/bounce/dynamics/contacts/contact_solver.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/contacts/convex_contact.h b/include/bounce/dynamics/contacts/convex_contact.h index 22eb9df..0610b3e 100644 --- a/include/bounce/dynamics/contacts/convex_contact.h +++ b/include/bounce/dynamics/contacts/convex_contact.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/contacts/manifold.h b/include/bounce/dynamics/contacts/manifold.h index 66b4d47..b47e763 100644 --- a/include/bounce/dynamics/contacts/manifold.h +++ b/include/bounce/dynamics/contacts/manifold.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/contacts/mesh_contact.h b/include/bounce/dynamics/contacts/mesh_contact.h index 2b3df78..0a626e4 100644 --- a/include/bounce/dynamics/contacts/mesh_contact.h +++ b/include/bounce/dynamics/contacts/mesh_contact.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/island.h b/include/bounce/dynamics/island.h index efdb6e0..2a74da7 100644 --- a/include/bounce/dynamics/island.h +++ b/include/bounce/dynamics/island.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/joint_manager.h b/include/bounce/dynamics/joint_manager.h index af66e73..d399bb2 100644 --- a/include/bounce/dynamics/joint_manager.h +++ b/include/bounce/dynamics/joint_manager.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/joints/cone_joint.h b/include/bounce/dynamics/joints/cone_joint.h index 663d8aa..1e092b7 100644 --- a/include/bounce/dynamics/joints/cone_joint.h +++ b/include/bounce/dynamics/joints/cone_joint.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/joints/joint.h b/include/bounce/dynamics/joints/joint.h index f973d99..6130f3f 100644 --- a/include/bounce/dynamics/joints/joint.h +++ b/include/bounce/dynamics/joints/joint.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/joints/joint_solver.h b/include/bounce/dynamics/joints/joint_solver.h index 4ccb9d5..ef36fa9 100644 --- a/include/bounce/dynamics/joints/joint_solver.h +++ b/include/bounce/dynamics/joints/joint_solver.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/joints/mouse_joint.h b/include/bounce/dynamics/joints/mouse_joint.h index e044495..a2a35e0 100644 --- a/include/bounce/dynamics/joints/mouse_joint.h +++ b/include/bounce/dynamics/joints/mouse_joint.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/joints/revolute_joint.h b/include/bounce/dynamics/joints/revolute_joint.h index 336c5c9..12e707d 100644 --- a/include/bounce/dynamics/joints/revolute_joint.h +++ b/include/bounce/dynamics/joints/revolute_joint.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/joints/sphere_joint.h b/include/bounce/dynamics/joints/sphere_joint.h index 086b366..16d82ab 100644 --- a/include/bounce/dynamics/joints/sphere_joint.h +++ b/include/bounce/dynamics/joints/sphere_joint.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/joints/spring_joint.h b/include/bounce/dynamics/joints/spring_joint.h index cbd16e2..32f57db 100644 --- a/include/bounce/dynamics/joints/spring_joint.h +++ b/include/bounce/dynamics/joints/spring_joint.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/joints/weld_joint.h b/include/bounce/dynamics/joints/weld_joint.h index e6ced48..29cfb47 100644 --- a/include/bounce/dynamics/joints/weld_joint.h +++ b/include/bounce/dynamics/joints/weld_joint.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/shapes/capsule_shape.h b/include/bounce/dynamics/shapes/capsule_shape.h index 579e48f..2c3d089 100644 --- a/include/bounce/dynamics/shapes/capsule_shape.h +++ b/include/bounce/dynamics/shapes/capsule_shape.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/shapes/hull_shape.h b/include/bounce/dynamics/shapes/hull_shape.h index 193798f..7cc3f7e 100644 --- a/include/bounce/dynamics/shapes/hull_shape.h +++ b/include/bounce/dynamics/shapes/hull_shape.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/shapes/mesh_shape.h b/include/bounce/dynamics/shapes/mesh_shape.h index 7436e3c..e75775c 100644 --- a/include/bounce/dynamics/shapes/mesh_shape.h +++ b/include/bounce/dynamics/shapes/mesh_shape.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/shapes/shape.h b/include/bounce/dynamics/shapes/shape.h index c62df71..a11619c 100644 --- a/include/bounce/dynamics/shapes/shape.h +++ b/include/bounce/dynamics/shapes/shape.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/shapes/sphere_shape.h b/include/bounce/dynamics/shapes/sphere_shape.h index be5514b..7e398d6 100644 --- a/include/bounce/dynamics/shapes/sphere_shape.h +++ b/include/bounce/dynamics/shapes/sphere_shape.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/time_step.h b/include/bounce/dynamics/time_step.h index 670c074..6736ffe 100644 --- a/include/bounce/dynamics/time_step.h +++ b/include/bounce/dynamics/time_step.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/world.h b/include/bounce/dynamics/world.h index 88a7510..d33a5a8 100644 --- a/include/bounce/dynamics/world.h +++ b/include/bounce/dynamics/world.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/dynamics/world_listeners.h b/include/bounce/dynamics/world_listeners.h index 4b87efa..275f505 100644 --- a/include/bounce/dynamics/world_listeners.h +++ b/include/bounce/dynamics/world_listeners.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/quickhull/qh_hull.h b/include/bounce/quickhull/qh_hull.h index 108f236..877097f 100644 --- a/include/bounce/quickhull/qh_hull.h +++ b/include/bounce/quickhull/qh_hull.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/rope/rope.h b/include/bounce/rope/rope.h index 4e04763..92e782a 100644 --- a/include/bounce/rope/rope.h +++ b/include/bounce/rope/rope.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/bounce/rope/spatial.h b/include/bounce/rope/spatial.h index 2c944ca..a1b6bd6 100644 --- a/include/bounce/rope/spatial.h +++ b/include/bounce/rope/spatial.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index cc301e3..3ca1e95 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/cloth/cloth_contact_solver.cpp b/src/bounce/cloth/cloth_contact_solver.cpp index e553df2..a01de49 100644 --- a/src/bounce/cloth/cloth_contact_solver.cpp +++ b/src/bounce/cloth/cloth_contact_solver.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/cloth/cloth_mesh.cpp b/src/bounce/cloth/cloth_mesh.cpp index 5018104..59b106e 100644 --- a/src/bounce/cloth/cloth_mesh.cpp +++ b/src/bounce/cloth/cloth_mesh.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index 2684966..9e3b325 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/cloth/force.cpp b/src/bounce/cloth/force.cpp index 53e4fce..4f658df 100644 --- a/src/bounce/cloth/force.cpp +++ b/src/bounce/cloth/force.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/cloth/garment/garment_mesh.cpp b/src/bounce/cloth/garment/garment_mesh.cpp index 473b5f0..1d54f89 100644 --- a/src/bounce/cloth/garment/garment_mesh.cpp +++ b/src/bounce/cloth/garment/garment_mesh.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/cloth/particle.cpp b/src/bounce/cloth/particle.cpp index 0da1c6d..169e5bc 100644 --- a/src/bounce/cloth/particle.cpp +++ b/src/bounce/cloth/particle.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/cloth/spring_force.cpp b/src/bounce/cloth/spring_force.cpp index 4a634fb..3e68424 100644 --- a/src/bounce/cloth/spring_force.cpp +++ b/src/bounce/cloth/spring_force.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/collision/broad_phase.cpp b/src/bounce/collision/broad_phase.cpp index 83372bd..58c1212 100644 --- a/src/bounce/collision/broad_phase.cpp +++ b/src/bounce/collision/broad_phase.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/collision/collision.cpp b/src/bounce/collision/collision.cpp index 9826e31..30fbc81 100644 --- a/src/bounce/collision/collision.cpp +++ b/src/bounce/collision/collision.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/collision/gjk/gjk.cpp b/src/bounce/collision/gjk/gjk.cpp index 03ea554..a6a9a85 100644 --- a/src/bounce/collision/gjk/gjk.cpp +++ b/src/bounce/collision/gjk/gjk.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/collision/gjk/gjk_feature_pair.cpp b/src/bounce/collision/gjk/gjk_feature_pair.cpp index 34ada7d..42a0283 100644 --- a/src/bounce/collision/gjk/gjk_feature_pair.cpp +++ b/src/bounce/collision/gjk/gjk_feature_pair.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/collision/sat/sat.cpp b/src/bounce/collision/sat/sat.cpp index 565cabf..6d38811 100644 --- a/src/bounce/collision/sat/sat.cpp +++ b/src/bounce/collision/sat/sat.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/collision/sat/sat_edge_and_hull.cpp b/src/bounce/collision/sat/sat_edge_and_hull.cpp index fe639df..3df7dc5 100644 --- a/src/bounce/collision/sat/sat_edge_and_hull.cpp +++ b/src/bounce/collision/sat/sat_edge_and_hull.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/collision/sat/sat_vertex_and_hull.cpp b/src/bounce/collision/sat/sat_vertex_and_hull.cpp index 96a4b81..10a90ce 100644 --- a/src/bounce/collision/sat/sat_vertex_and_hull.cpp +++ b/src/bounce/collision/sat/sat_vertex_and_hull.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/collision/shapes/hull.cpp b/src/bounce/collision/shapes/hull.cpp index 873604a..74cecd7 100644 --- a/src/bounce/collision/shapes/hull.cpp +++ b/src/bounce/collision/shapes/hull.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/collision/shapes/qhull.cpp b/src/bounce/collision/shapes/qhull.cpp index 1db841b..06ff300 100644 --- a/src/bounce/collision/shapes/qhull.cpp +++ b/src/bounce/collision/shapes/qhull.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/collision/trees/dynamic_tree.cpp b/src/bounce/collision/trees/dynamic_tree.cpp index c609677..99d482f 100644 --- a/src/bounce/collision/trees/dynamic_tree.cpp +++ b/src/bounce/collision/trees/dynamic_tree.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/collision/trees/static_tree.cpp b/src/bounce/collision/trees/static_tree.cpp index 490cfa0..7dd0436 100644 --- a/src/bounce/collision/trees/static_tree.cpp +++ b/src/bounce/collision/trees/static_tree.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/common/math/math.cpp b/src/bounce/common/math/math.cpp index f50ff18..793e5c5 100644 --- a/src/bounce/common/math/math.cpp +++ b/src/bounce/common/math/math.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/common/memory/block_pool.cpp b/src/bounce/common/memory/block_pool.cpp index 307c39f..e6a0619 100644 --- a/src/bounce/common/memory/block_pool.cpp +++ b/src/bounce/common/memory/block_pool.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/common/memory/stack_allocator.cpp b/src/bounce/common/memory/stack_allocator.cpp index f158ff9..1077899 100644 --- a/src/bounce/common/memory/stack_allocator.cpp +++ b/src/bounce/common/memory/stack_allocator.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/common/settings.cpp b/src/bounce/common/settings.cpp index 8da10c4..9c663c4 100644 --- a/src/bounce/common/settings.cpp +++ b/src/bounce/common/settings.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/body.cpp b/src/bounce/dynamics/body.cpp index 9116d14..e7e010d 100644 --- a/src/bounce/dynamics/body.cpp +++ b/src/bounce/dynamics/body.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/contact_manager.cpp b/src/bounce/dynamics/contact_manager.cpp index b6d5ddc..715298d 100644 --- a/src/bounce/dynamics/contact_manager.cpp +++ b/src/bounce/dynamics/contact_manager.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/contacts/collide/clip.cpp b/src/bounce/dynamics/contacts/collide/clip.cpp index ded4e26..78be3c8 100644 --- a/src/bounce/dynamics/contacts/collide/clip.cpp +++ b/src/bounce/dynamics/contacts/collide/clip.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/contacts/collide/collide.cpp b/src/bounce/dynamics/contacts/collide/collide.cpp index 9292145..3e716ec 100644 --- a/src/bounce/dynamics/contacts/collide/collide.cpp +++ b/src/bounce/dynamics/contacts/collide/collide.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/contacts/collide/collide_capsule_hull.cpp b/src/bounce/dynamics/contacts/collide/collide_capsule_hull.cpp index 8a5d586..5d66029 100644 --- a/src/bounce/dynamics/contacts/collide/collide_capsule_hull.cpp +++ b/src/bounce/dynamics/contacts/collide/collide_capsule_hull.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/contacts/collide/collide_capsules.cpp b/src/bounce/dynamics/contacts/collide/collide_capsules.cpp index 58463f7..51d70c2 100644 --- a/src/bounce/dynamics/contacts/collide/collide_capsules.cpp +++ b/src/bounce/dynamics/contacts/collide/collide_capsules.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/contacts/collide/collide_hulls.cpp b/src/bounce/dynamics/contacts/collide/collide_hulls.cpp index 70005e1..af2a549 100644 --- a/src/bounce/dynamics/contacts/collide/collide_hulls.cpp +++ b/src/bounce/dynamics/contacts/collide/collide_hulls.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/contacts/collide/collide_hulls_cache.cpp b/src/bounce/dynamics/contacts/collide/collide_hulls_cache.cpp index a7cada8..b4d02f2 100644 --- a/src/bounce/dynamics/contacts/collide/collide_hulls_cache.cpp +++ b/src/bounce/dynamics/contacts/collide/collide_hulls_cache.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/contacts/collide/collide_sphere_capsule.cpp b/src/bounce/dynamics/contacts/collide/collide_sphere_capsule.cpp index ae8e843..0ffc545 100644 --- a/src/bounce/dynamics/contacts/collide/collide_sphere_capsule.cpp +++ b/src/bounce/dynamics/contacts/collide/collide_sphere_capsule.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/contacts/collide/collide_sphere_hull.cpp b/src/bounce/dynamics/contacts/collide/collide_sphere_hull.cpp index 5b425a0..15ebb47 100644 --- a/src/bounce/dynamics/contacts/collide/collide_sphere_hull.cpp +++ b/src/bounce/dynamics/contacts/collide/collide_sphere_hull.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/contacts/collide/collide_spheres.cpp b/src/bounce/dynamics/contacts/collide/collide_spheres.cpp index 3826736..66d8c4f 100644 --- a/src/bounce/dynamics/contacts/collide/collide_spheres.cpp +++ b/src/bounce/dynamics/contacts/collide/collide_spheres.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/contacts/contact.cpp b/src/bounce/dynamics/contacts/contact.cpp index 973ad49..82da917 100644 --- a/src/bounce/dynamics/contacts/contact.cpp +++ b/src/bounce/dynamics/contacts/contact.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/contacts/contact_cluster.cpp b/src/bounce/dynamics/contacts/contact_cluster.cpp index 1c4be98..bdc4109 100644 --- a/src/bounce/dynamics/contacts/contact_cluster.cpp +++ b/src/bounce/dynamics/contacts/contact_cluster.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/contacts/contact_solver.cpp b/src/bounce/dynamics/contacts/contact_solver.cpp index ee3d7ec..4d33001 100644 --- a/src/bounce/dynamics/contacts/contact_solver.cpp +++ b/src/bounce/dynamics/contacts/contact_solver.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/contacts/convex_contact.cpp b/src/bounce/dynamics/contacts/convex_contact.cpp index 472e997..fd4bc3d 100644 --- a/src/bounce/dynamics/contacts/convex_contact.cpp +++ b/src/bounce/dynamics/contacts/convex_contact.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/contacts/manifold.cpp b/src/bounce/dynamics/contacts/manifold.cpp index 4164e8c..bbe0380 100644 --- a/src/bounce/dynamics/contacts/manifold.cpp +++ b/src/bounce/dynamics/contacts/manifold.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/contacts/mesh_contact.cpp b/src/bounce/dynamics/contacts/mesh_contact.cpp index e5a69e7..f3aaf99 100644 --- a/src/bounce/dynamics/contacts/mesh_contact.cpp +++ b/src/bounce/dynamics/contacts/mesh_contact.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/draw_world.cpp b/src/bounce/dynamics/draw_world.cpp index 3b674d4..1d197c8 100644 --- a/src/bounce/dynamics/draw_world.cpp +++ b/src/bounce/dynamics/draw_world.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/island.cpp b/src/bounce/dynamics/island.cpp index 305c66f..d0e831a 100644 --- a/src/bounce/dynamics/island.cpp +++ b/src/bounce/dynamics/island.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/joint_manager.cpp b/src/bounce/dynamics/joint_manager.cpp index b81c579..fb29016 100644 --- a/src/bounce/dynamics/joint_manager.cpp +++ b/src/bounce/dynamics/joint_manager.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/joints/cone_joint.cpp b/src/bounce/dynamics/joints/cone_joint.cpp index 15f41ac..fcf54c2 100644 --- a/src/bounce/dynamics/joints/cone_joint.cpp +++ b/src/bounce/dynamics/joints/cone_joint.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/joints/joint.cpp b/src/bounce/dynamics/joints/joint.cpp index 2d1593c..1f380b7 100644 --- a/src/bounce/dynamics/joints/joint.cpp +++ b/src/bounce/dynamics/joints/joint.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/joints/joint_solver.cpp b/src/bounce/dynamics/joints/joint_solver.cpp index 234b74a..7336a80 100644 --- a/src/bounce/dynamics/joints/joint_solver.cpp +++ b/src/bounce/dynamics/joints/joint_solver.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/joints/mouse_joint.cpp b/src/bounce/dynamics/joints/mouse_joint.cpp index dc38de1..e816c1f 100644 --- a/src/bounce/dynamics/joints/mouse_joint.cpp +++ b/src/bounce/dynamics/joints/mouse_joint.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/joints/revolute_joint.cpp b/src/bounce/dynamics/joints/revolute_joint.cpp index 07dbec8..550c47e 100644 --- a/src/bounce/dynamics/joints/revolute_joint.cpp +++ b/src/bounce/dynamics/joints/revolute_joint.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/joints/sphere_joint.cpp b/src/bounce/dynamics/joints/sphere_joint.cpp index ecbdd16..72bc346 100644 --- a/src/bounce/dynamics/joints/sphere_joint.cpp +++ b/src/bounce/dynamics/joints/sphere_joint.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/joints/spring_joint.cpp b/src/bounce/dynamics/joints/spring_joint.cpp index 93d2541..8ce5759 100644 --- a/src/bounce/dynamics/joints/spring_joint.cpp +++ b/src/bounce/dynamics/joints/spring_joint.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/joints/weld_joint.cpp b/src/bounce/dynamics/joints/weld_joint.cpp index 22e15b0..ccd21ae 100644 --- a/src/bounce/dynamics/joints/weld_joint.cpp +++ b/src/bounce/dynamics/joints/weld_joint.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/shapes/capsule_shape.cpp b/src/bounce/dynamics/shapes/capsule_shape.cpp index 76af86d..06da598 100644 --- a/src/bounce/dynamics/shapes/capsule_shape.cpp +++ b/src/bounce/dynamics/shapes/capsule_shape.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/shapes/hull_shape.cpp b/src/bounce/dynamics/shapes/hull_shape.cpp index 1c1333e..78da7bc 100644 --- a/src/bounce/dynamics/shapes/hull_shape.cpp +++ b/src/bounce/dynamics/shapes/hull_shape.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/shapes/mesh_shape.cpp b/src/bounce/dynamics/shapes/mesh_shape.cpp index 012028e..4331b1d 100644 --- a/src/bounce/dynamics/shapes/mesh_shape.cpp +++ b/src/bounce/dynamics/shapes/mesh_shape.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/shapes/shape.cpp b/src/bounce/dynamics/shapes/shape.cpp index 4fe4ef5..da720b5 100644 --- a/src/bounce/dynamics/shapes/shape.cpp +++ b/src/bounce/dynamics/shapes/shape.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/shapes/sphere_shape.cpp b/src/bounce/dynamics/shapes/sphere_shape.cpp index 8d3819c..ba600c2 100644 --- a/src/bounce/dynamics/shapes/sphere_shape.cpp +++ b/src/bounce/dynamics/shapes/sphere_shape.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/dynamics/world.cpp b/src/bounce/dynamics/world.cpp index bc688b7..700f284 100644 --- a/src/bounce/dynamics/world.cpp +++ b/src/bounce/dynamics/world.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/quickhull/qh_hull.cpp b/src/bounce/quickhull/qh_hull.cpp index b2fb740..2b11141 100644 --- a/src/bounce/quickhull/qh_hull.cpp +++ b/src/bounce/quickhull/qh_hull.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/src/bounce/rope/rope.cpp b/src/bounce/rope/rope.cpp index d839180..38824d7 100644 --- a/src/bounce/rope/rope.cpp +++ b/src/bounce/rope/rope.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages From 4984fa2a2eb465183253023174556c0831936f18 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 27 Mar 2019 10:14:53 -0300 Subject: [PATCH 020/198] grow event buffer --- examples/testbed/framework/profiler.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h index 5428ee1..38165cd 100644 --- a/examples/testbed/framework/profiler.h +++ b/examples/testbed/framework/profiler.h @@ -25,8 +25,8 @@ #include // This defines the maximum number of profiler events that can be -// queued per frame until the function Profiler::Flush is called. -#define MAX_PROFILER_EVENTS 256 +// queued per frame until the function Profiler::End is called. +#define MAX_PROFILER_EVENTS 2048 class ProfilerListener; From 812ee84d7ecbca0c6dbf55ff265b233982c5dabc Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 30 Mar 2019 10:48:24 -0300 Subject: [PATCH 021/198] bugfix --- examples/testbed/framework/json_profiler.cpp | 12 +++---- examples/testbed/framework/json_profiler.h | 4 +-- examples/testbed/framework/main.cpp | 5 ++- examples/testbed/framework/profiler.cpp | 34 +++++++++---------- examples/testbed/framework/profiler.h | 18 +++------- .../testbed/framework/recorder_profiler.cpp | 32 +++++++++++------ .../testbed/framework/recorder_profiler.h | 8 +++-- examples/testbed/framework/testbed_listener.h | 23 ++++++------- 8 files changed, 69 insertions(+), 67 deletions(-) diff --git a/examples/testbed/framework/json_profiler.cpp b/examples/testbed/framework/json_profiler.cpp index 1f4a38a..ac2415c 100644 --- a/examples/testbed/framework/json_profiler.cpp +++ b/examples/testbed/framework/json_profiler.cpp @@ -75,7 +75,7 @@ void JsonProfiler::EndEvents() m_file = nullptr; } -void JsonProfiler::BeginEvent(i32 tid, i32 pid, const char* name, float64 t) +void JsonProfiler::BeginEvent(const char* name, float64 t) { if (!m_writer) { @@ -87,8 +87,8 @@ void JsonProfiler::BeginEvent(i32 tid, i32 pid, const char* name, float64 t) float64 scale = 1000.0; m_writer->StartObject(); - m_writer->STRING("pid"); m_writer->Int(pid); - m_writer->STRING("tid"); m_writer->Int(tid); + m_writer->STRING("pid"); m_writer->Int(0); + m_writer->STRING("tid"); m_writer->Int(0); m_writer->STRING("ts"); m_writer->Int64((u64)(t * scale)); m_writer->STRING("ph"); m_writer->String(phase, 1); m_writer->STRING("cat"); m_writer->STRING("physics"); @@ -97,7 +97,7 @@ void JsonProfiler::BeginEvent(i32 tid, i32 pid, const char* name, float64 t) m_writer->EndObject(); } -void JsonProfiler::EndEvent(i32 tid, i32 pid, const char* name, float64 t) +void JsonProfiler::EndEvent(const char* name, float64 t) { if (!m_writer) { @@ -109,8 +109,8 @@ void JsonProfiler::EndEvent(i32 tid, i32 pid, const char* name, float64 t) float64 scale = 1000.0; m_writer->StartObject(); - m_writer->STRING("pid"); m_writer->Int(pid); - m_writer->STRING("tid"); m_writer->Int(tid); + m_writer->STRING("pid"); m_writer->Int(0); + m_writer->STRING("tid"); m_writer->Int(0); m_writer->STRING("ts"); m_writer->Int64((u64)(t * scale)); m_writer->STRING("ph"); m_writer->String(phase, 1); m_writer->STRING("cat"); m_writer->STRING("physics"); diff --git a/examples/testbed/framework/json_profiler.h b/examples/testbed/framework/json_profiler.h index 15bcc2c..dbca993 100644 --- a/examples/testbed/framework/json_profiler.h +++ b/examples/testbed/framework/json_profiler.h @@ -43,9 +43,9 @@ public: void EndEvents(); - void BeginEvent(i32 tid, i32 pid, const char* name, float64 time); + void BeginEvent(const char* name, float64 time); - void EndEvent(i32 tid, i32 pid, const char* name, float64 time); + void EndEvent(const char* name, float64 time); private: FILE * m_file; FileWriteStream* m_stream; diff --git a/examples/testbed/framework/main.cpp b/examples/testbed/framework/main.cpp index dad7c74..f47c194 100644 --- a/examples/testbed/framework/main.cpp +++ b/examples/testbed/framework/main.cpp @@ -124,8 +124,7 @@ static void Run() for (u32 i = 0; i < records.Count(); ++i) { const ProfilerRecord& r = records[i]; - - if (r.elapsed > 0.0) + if (r.call != 0) { g_draw->DrawString(b3Color_white, "%s %.4f (%.4f) [ms]", r.name, r.elapsed, r.maxElapsed); } @@ -140,7 +139,7 @@ static void Run() g_profiler->PopEvent(); - g_profiler->End(g_profilerListener); + g_profiler->End(); glfwSwapBuffers(g_window); glfwPollEvents(); diff --git a/examples/testbed/framework/profiler.cpp b/examples/testbed/framework/profiler.cpp index 81b6aec..296db7f 100644 --- a/examples/testbed/framework/profiler.cpp +++ b/examples/testbed/framework/profiler.cpp @@ -35,26 +35,24 @@ void Profiler::PushEvent(const char* name) m_time.Update(); ProfilerEvent e; - e.tid = -1; - e.pid = -1; e.t0 = m_time.GetCurrentMilis(); - e.t1 = 0.0; + e.t1 = -1.0; e.name = name; e.parent = m_top; - ProfilerEvent* back = m_events.Push(e); - B3_ASSERT(back); - m_top = back; + m_events.PushBack(e); + + m_top = &m_events.Back(); } void Profiler::PopEvent() { - B3_ASSERT(m_top); - B3_ASSERT(m_top->t1 == 0.0); - m_time.Update(); + m_top->t1 = m_time.GetCurrentMilis(); - B3_ASSERT(m_top->t1 != 0.0); + + B3_ASSERT(m_top->t1 > m_top->t0); + m_top = m_top->parent; } @@ -65,29 +63,31 @@ void Profiler::Begin() B3_ASSERT(m_top == NULL); } -void Profiler::End(ProfilerListener* listener) +void Profiler::End() { + ProfilerListener* listener = g_profilerListener; + if (listener) { listener->BeginEvents(); } - while (m_events.IsEmpty() == false) + for (u32 i = 0; i < m_events.Count(); ++i) { - ProfilerEvent e = m_events.Front(); - - m_events.Pop(); + ProfilerEvent e = m_events[i]; if (listener) { - listener->BeginEvent(e.tid, e.pid, e.name, e.t0); + listener->BeginEvent(e.name, e.t0); - listener->EndEvent(e.tid, e.pid, e.name, e.t1); + listener->EndEvent(e.name, e.t1); listener->Duration(e.name, e.t1 - e.t0); } } + m_events.Resize(0); + B3_ASSERT(m_events.IsEmpty()); B3_ASSERT(m_top == NULL); diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h index 38165cd..aa18bba 100644 --- a/examples/testbed/framework/profiler.h +++ b/examples/testbed/framework/profiler.h @@ -24,17 +24,11 @@ #include #include -// This defines the maximum number of profiler events that can be -// queued per frame until the function Profiler::End is called. -#define MAX_PROFILER_EVENTS 2048 - class ProfilerListener; // A time-stamped profiler event. struct ProfilerEvent { - i32 tid; - i32 pid; const char* name; float64 t0; float64 t1; @@ -56,7 +50,7 @@ public: // The function will report all events in this profiler // to the given event listener in the correct calling order. // This function also flushes the profiler. - void End(ProfilerListener* listener); + void End(); // Add a profiler event to the queue. // You can control the maximum number of profiler events using @@ -67,7 +61,7 @@ public: void PopEvent(); private: b3Time m_time; - b3BoundedQueue m_events; + b3StackArray m_events; ProfilerEvent* m_top; }; @@ -86,19 +80,15 @@ public: virtual void EndEvents() { } // This function is called when a profiler event begins. - virtual void BeginEvent(i32 tid, i32 pid, const char* name, float64 time) + virtual void BeginEvent(const char* name, float64 time) { - B3_NOT_USED(tid); - B3_NOT_USED(pid); B3_NOT_USED(name); B3_NOT_USED(time); } // This function is called when a profiler event ends. - virtual void EndEvent(i32 tid, i32 pid, const char* name, float64 time) + virtual void EndEvent(const char* name, float64 time) { - B3_NOT_USED(tid); - B3_NOT_USED(pid); B3_NOT_USED(name); B3_NOT_USED(time); } diff --git a/examples/testbed/framework/recorder_profiler.cpp b/examples/testbed/framework/recorder_profiler.cpp index 3b21a1a..d597a49 100644 --- a/examples/testbed/framework/recorder_profiler.cpp +++ b/examples/testbed/framework/recorder_profiler.cpp @@ -22,7 +22,7 @@ RecorderProfiler* g_profilerRecorder = nullptr; void RecorderProfiler::BeginEvents() { - m_count = 0; + m_call = 0; for (u32 i = 0; i < m_records.Count(); ++i) { m_records[i].elapsed = 0.0; @@ -43,33 +43,45 @@ void RecorderProfiler::EndEvents() if (r2.call < r1.call) { b3Swap(r1, r2); - break; } } } } -void RecorderProfiler::Add(const char* name, float64 elapsedTime) +ProfilerRecord* RecorderProfiler::FindRecord(const char* name) { - m_count += 1; - for (u32 i = 0; i < m_records.Count(); ++i) { ProfilerRecord& r = m_records[i]; if (r.name == name) { - r.elapsed += elapsedTime; - r.maxElapsed = b3Max(r.maxElapsed, elapsedTime); - r.call = m_count; - return; + return &r; } } + return nullptr; +} + +void RecorderProfiler::Add(const char* name, float64 elapsedTime) +{ + B3_ASSERT(elapsedTime >= 0.0); + + ++m_call; + + ProfilerRecord* fr = FindRecord(name); + if (fr) + { + fr->elapsed += elapsedTime; + fr->maxElapsed = b3Max(fr->maxElapsed, elapsedTime); + fr->call = m_call; + + return; + } ProfilerRecord r; r.name = name; r.elapsed = elapsedTime; r.maxElapsed = elapsedTime; - r.call = m_count; + r.call = m_call; m_records.PushBack(r); } \ No newline at end of file diff --git a/examples/testbed/framework/recorder_profiler.h b/examples/testbed/framework/recorder_profiler.h index 49f2edf..c623851 100644 --- a/examples/testbed/framework/recorder_profiler.h +++ b/examples/testbed/framework/recorder_profiler.h @@ -25,9 +25,9 @@ // An event in the profiler event recorder. struct ProfilerRecord { + const char* name; float64 elapsed; float64 maxElapsed; - const char* name; u32 call; }; @@ -43,10 +43,12 @@ public: void Add(const char* name, float64 elapsedTime); + ProfilerRecord* FindRecord(const char* name); + const b3Array& GetRecords() const { return m_records; } private: - b3StackArray m_records; - u32 m_count; + b3StackArray m_records; // persistent records sorted by call order + u32 m_call; }; extern RecorderProfiler* g_profilerRecorder; diff --git a/examples/testbed/framework/testbed_listener.h b/examples/testbed/framework/testbed_listener.h index ee50dba..847b700 100644 --- a/examples/testbed/framework/testbed_listener.h +++ b/examples/testbed/framework/testbed_listener.h @@ -54,25 +54,24 @@ public: } - void BeginEvent(i32 tid, i32 pid, const char* name, float64 time) override + void BeginEvent(const char* name, float64 time) override { #if (PROFILE_JSON == 1) - m_jsonListener.BeginEvent(tid, pid, name, time); + m_jsonListener.BeginEvent(name, time); +#endif + } + + void EndEvent(const char* name, float64 time) override + { +#if (PROFILE_JSON == 1) + m_jsonListener.EndEvent(name, time); #endif } - void EndEvent(i32 tid, i32 pid, const char* name, float64 time) override + void Duration(const char* name, float64 duration) override { -#if (PROFILE_JSON == 1) - m_jsonListener.EndEvent(tid, pid, name, time); -#endif - - } - - void Duration(const char* name, float64 time) override - { - m_recorderProfiler.Add(name, time); + m_recorderProfiler.Add(name, duration); } RecorderProfiler m_recorderProfiler; From f59df50fbd4c8c0c4ebdd4f15b926fea5a8ea31e Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 30 Mar 2019 11:51:55 -0300 Subject: [PATCH 022/198] consistency --- examples/testbed/framework/model.cpp | 2 +- examples/testbed/framework/profiler.cpp | 2 -- examples/testbed/framework/profiler.h | 8 ------ .../testbed/framework/recorder_profiler.cpp | 26 ++++++++++++------- .../testbed/framework/recorder_profiler.h | 5 +++- examples/testbed/framework/testbed_listener.h | 16 +++++------- 6 files changed, 29 insertions(+), 30 deletions(-) diff --git a/examples/testbed/framework/model.cpp b/examples/testbed/framework/model.cpp index df3357d..df07d7f 100644 --- a/examples/testbed/framework/model.cpp +++ b/examples/testbed/framework/model.cpp @@ -26,7 +26,7 @@ Model::Model() g_draw = &m_draw; g_camera = &m_camera; g_profiler = &m_profiler; - g_profilerRecorder = &m_profilerListener.m_recorderProfiler; + g_profilerRecorder = &m_profilerListener.m_recorderListener; g_profilerListener = &m_profilerListener; m_test = nullptr; diff --git a/examples/testbed/framework/profiler.cpp b/examples/testbed/framework/profiler.cpp index 296db7f..3678fe9 100644 --- a/examples/testbed/framework/profiler.cpp +++ b/examples/testbed/framework/profiler.cpp @@ -81,8 +81,6 @@ void Profiler::End() listener->BeginEvent(e.name, e.t0); listener->EndEvent(e.name, e.t1); - - listener->Duration(e.name, e.t1 - e.t0); } } diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h index aa18bba..6189092 100644 --- a/examples/testbed/framework/profiler.h +++ b/examples/testbed/framework/profiler.h @@ -92,14 +92,6 @@ public: B3_NOT_USED(name); B3_NOT_USED(time); } - - // This function is called when a profiler event ends. - // However it supplies the duration of the last begin and end events. - virtual void Duration(const char* name, float64 duration) - { - B3_NOT_USED(name); - B3_NOT_USED(duration); - } }; extern ProfilerListener* g_profilerListener; diff --git a/examples/testbed/framework/recorder_profiler.cpp b/examples/testbed/framework/recorder_profiler.cpp index d597a49..016dbf0 100644 --- a/examples/testbed/framework/recorder_profiler.cpp +++ b/examples/testbed/framework/recorder_profiler.cpp @@ -61,27 +61,35 @@ ProfilerRecord* RecorderProfiler::FindRecord(const char* name) return nullptr; } -void RecorderProfiler::Add(const char* name, float64 elapsedTime) +void RecorderProfiler::BeginEvent(const char* name, float64 time) { - B3_ASSERT(elapsedTime >= 0.0); - ++m_call; - + ProfilerRecord* fr = FindRecord(name); if (fr) { - fr->elapsed += elapsedTime; - fr->maxElapsed = b3Max(fr->maxElapsed, elapsedTime); + fr->time = time; fr->call = m_call; - return; } ProfilerRecord r; r.name = name; - r.elapsed = elapsedTime; - r.maxElapsed = elapsedTime; + r.time = time; + r.elapsed = 0.0; + r.maxElapsed = 0.0; r.call = m_call; m_records.PushBack(r); +} + +void RecorderProfiler::EndEvent(const char* name, float64 time) +{ + ProfilerRecord* fr = FindRecord(name); + B3_ASSERT(fr != nullptr); + + float64 elapsedTime = time - fr->time; + + fr->elapsed += elapsedTime; + fr->maxElapsed = b3Max(fr->maxElapsed, elapsedTime); } \ No newline at end of file diff --git a/examples/testbed/framework/recorder_profiler.h b/examples/testbed/framework/recorder_profiler.h index c623851..f7b1fde 100644 --- a/examples/testbed/framework/recorder_profiler.h +++ b/examples/testbed/framework/recorder_profiler.h @@ -26,6 +26,7 @@ struct ProfilerRecord { const char* name; + float64 time; float64 elapsed; float64 maxElapsed; u32 call; @@ -41,7 +42,9 @@ public: void EndEvents(); - void Add(const char* name, float64 elapsedTime); + void BeginEvent(const char* name, float64 time); + + void EndEvent(const char* name, float64 time); ProfilerRecord* FindRecord(const char* name); diff --git a/examples/testbed/framework/testbed_listener.h b/examples/testbed/framework/testbed_listener.h index 847b700..9545384 100644 --- a/examples/testbed/framework/testbed_listener.h +++ b/examples/testbed/framework/testbed_listener.h @@ -36,7 +36,7 @@ class TestbedListener : public ProfilerListener public: void BeginEvents() override { - m_recorderProfiler.BeginEvents(); + m_recorderListener.BeginEvents(); #if (PROFILE_JSON == 1) m_jsonListener.BeginEvents(); @@ -46,7 +46,7 @@ public: void EndEvents() override { - m_recorderProfiler.EndEvents(); + m_recorderListener.EndEvents(); #if (PROFILE_JSON == 1) m_jsonListener.EndEvents(); @@ -56,6 +56,8 @@ public: void BeginEvent(const char* name, float64 time) override { + m_recorderListener.BeginEvent(name, time); + #if (PROFILE_JSON == 1) m_jsonListener.BeginEvent(name, time); #endif @@ -63,18 +65,14 @@ public: void EndEvent(const char* name, float64 time) override { + m_recorderListener.EndEvent(name, time); + #if (PROFILE_JSON == 1) m_jsonListener.EndEvent(name, time); #endif - } - void Duration(const char* name, float64 duration) override - { - m_recorderProfiler.Add(name, duration); - } - - RecorderProfiler m_recorderProfiler; + RecorderProfiler m_recorderListener; #if (PROFILE_JSON == 1) JsonProfiler m_jsonListener; From 117f9846980f8c0b99e34ce87c439c6534ce8f5a Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 30 Mar 2019 11:59:20 -0300 Subject: [PATCH 023/198] consistency --- examples/testbed/framework/main.cpp | 2 +- examples/testbed/framework/model.cpp | 4 ++-- examples/testbed/framework/model.h | 4 ++-- examples/testbed/framework/recorder_profiler.cpp | 2 +- examples/testbed/framework/recorder_profiler.h | 6 +++--- .../framework/{testbed_listener.h => testbed_profiler.h} | 5 ++--- 6 files changed, 11 insertions(+), 12 deletions(-) rename examples/testbed/framework/{testbed_listener.h => testbed_profiler.h} (94%) diff --git a/examples/testbed/framework/main.cpp b/examples/testbed/framework/main.cpp index f47c194..749c6f1 100644 --- a/examples/testbed/framework/main.cpp +++ b/examples/testbed/framework/main.cpp @@ -120,7 +120,7 @@ static void Run() if (g_settings->drawProfile) { - const b3Array& records = g_profilerRecorder->GetRecords(); + const b3Array& records = g_recorderProfiler->GetRecords(); for (u32 i = 0; i < records.Count(); ++i) { const ProfilerRecord& r = records[i]; diff --git a/examples/testbed/framework/model.cpp b/examples/testbed/framework/model.cpp index df07d7f..37c9aab 100644 --- a/examples/testbed/framework/model.cpp +++ b/examples/testbed/framework/model.cpp @@ -26,7 +26,7 @@ Model::Model() g_draw = &m_draw; g_camera = &m_camera; g_profiler = &m_profiler; - g_profilerRecorder = &m_profilerListener.m_recorderListener; + g_recorderProfiler = &m_profilerListener.m_recorderListener; g_profilerListener = &m_profilerListener; m_test = nullptr; @@ -53,7 +53,7 @@ Model::~Model() g_draw = nullptr; g_camera = nullptr; g_profiler = nullptr; - g_profilerRecorder = nullptr; + g_recorderProfiler = nullptr; g_profilerListener = nullptr; delete m_test; diff --git a/examples/testbed/framework/model.h b/examples/testbed/framework/model.h index 310e6f2..2580055 100644 --- a/examples/testbed/framework/model.h +++ b/examples/testbed/framework/model.h @@ -20,7 +20,7 @@ #define MODEL_H #include -#include +#include class Test; @@ -63,7 +63,7 @@ private: Draw m_draw; Camera m_camera; Profiler m_profiler; - TestbedListener m_profilerListener; + TestbedProfiler m_profilerListener; Test* m_test; bool m_setTest; bool m_pause; diff --git a/examples/testbed/framework/recorder_profiler.cpp b/examples/testbed/framework/recorder_profiler.cpp index 016dbf0..4edfaed 100644 --- a/examples/testbed/framework/recorder_profiler.cpp +++ b/examples/testbed/framework/recorder_profiler.cpp @@ -18,7 +18,7 @@ #include -RecorderProfiler* g_profilerRecorder = nullptr; +RecorderProfiler* g_recorderProfiler = nullptr; void RecorderProfiler::BeginEvents() { diff --git a/examples/testbed/framework/recorder_profiler.h b/examples/testbed/framework/recorder_profiler.h index f7b1fde..3934ae7 100644 --- a/examples/testbed/framework/recorder_profiler.h +++ b/examples/testbed/framework/recorder_profiler.h @@ -46,14 +46,14 @@ public: void EndEvent(const char* name, float64 time); - ProfilerRecord* FindRecord(const char* name); - const b3Array& GetRecords() const { return m_records; } private: + ProfilerRecord* FindRecord(const char* name); + b3StackArray m_records; // persistent records sorted by call order u32 m_call; }; -extern RecorderProfiler* g_profilerRecorder; +extern RecorderProfiler* g_recorderProfiler; #endif \ No newline at end of file diff --git a/examples/testbed/framework/testbed_listener.h b/examples/testbed/framework/testbed_profiler.h similarity index 94% rename from examples/testbed/framework/testbed_listener.h rename to examples/testbed/framework/testbed_profiler.h index 9545384..0e19f3b 100644 --- a/examples/testbed/framework/testbed_listener.h +++ b/examples/testbed/framework/testbed_profiler.h @@ -22,7 +22,7 @@ #include #include -// Set to 1 then the testbed listener will write profile events into a .json file. +// Set to 1 then the testbed profiler will write profile events into a .json file. // Set to 0 otherwise. #define PROFILE_JSON 0 @@ -30,8 +30,7 @@ #include #endif - -class TestbedListener : public ProfilerListener +class TestbedProfiler : public ProfilerListener { public: void BeginEvents() override From 5c6a7a967aec6c3ed08146e9a8d260e19e4a6d99 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 30 Mar 2019 12:01:28 -0300 Subject: [PATCH 024/198] remove include --- examples/testbed/framework/profiler.h | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h index 6189092..e1f2225 100644 --- a/examples/testbed/framework/profiler.h +++ b/examples/testbed/framework/profiler.h @@ -21,7 +21,6 @@ #include #include -#include #include class ProfilerListener; From f446f90266b4de161595f46c82cf7f2c9d79f8b2 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 31 Mar 2019 10:20:01 -0300 Subject: [PATCH 025/198] rename header --- examples/testbed/framework/testbed_profiler.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/testbed/framework/testbed_profiler.h b/examples/testbed/framework/testbed_profiler.h index 0e19f3b..a770937 100644 --- a/examples/testbed/framework/testbed_profiler.h +++ b/examples/testbed/framework/testbed_profiler.h @@ -16,8 +16,8 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef TESTBED_LISTENER_H -#define TESTBED_LISTENER_H +#ifndef TESTBED_PROFILER_H +#define TESTBED_PROFILER_H #include #include From 701e4c5b15bb144cd79406bd32d2d96513b92323 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 31 Mar 2019 10:20:17 -0300 Subject: [PATCH 026/198] profiler hierarchy --- examples/testbed/framework/profiler.cpp | 97 ++++++++++++++++++------- examples/testbed/framework/profiler.h | 28 ++++--- 2 files changed, 88 insertions(+), 37 deletions(-) diff --git a/examples/testbed/framework/profiler.cpp b/examples/testbed/framework/profiler.cpp index 3678fe9..579c968 100644 --- a/examples/testbed/framework/profiler.cpp +++ b/examples/testbed/framework/profiler.cpp @@ -21,36 +21,62 @@ Profiler* g_profiler = nullptr; ProfilerListener* g_profilerListener = nullptr; -Profiler::Profiler() +Profiler::Profiler() : m_pool(sizeof(ProfilerNode)) { + m_root = nullptr; m_top = nullptr; } Profiler::~Profiler() { + B3_ASSERT(m_root == nullptr); + B3_ASSERT(m_top == nullptr); +} + +ProfilerNode* Profiler::CreateNode() +{ + void* block = m_pool.Allocate(); + ProfilerNode* n = new (block) ProfilerNode(); + return n; +} + +void Profiler::DestroyNode(ProfilerNode* node) +{ + node->~ProfilerNode(); + m_pool.Free(node); } void Profiler::PushEvent(const char* name) { m_time.Update(); - ProfilerEvent e; - e.t0 = m_time.GetCurrentMilis(); - e.t1 = -1.0; - e.name = name; - e.parent = m_top; + ProfilerNode* n = CreateNode(); + n->name = name; + n->t0 = m_time.GetCurrentMilis(); + n->t1 = 0.0; + n->parent = m_top; - m_events.PushBack(e); + if (m_root == nullptr) + { + m_root = n; + m_top = n; + return; + } - m_top = &m_events.Back(); + if (m_top) + { + m_top->children.PushBack(n); + } + + m_top = n; } void Profiler::PopEvent() { m_time.Update(); + B3_ASSERT(m_top != nullptr); m_top->t1 = m_time.GetCurrentMilis(); - B3_ASSERT(m_top->t1 > m_top->t0); m_top = m_top->parent; @@ -59,12 +85,41 @@ void Profiler::PopEvent() void Profiler::Begin() { // If this assert is hit then it means Profiler::End hasn't been called. - B3_ASSERT(m_events.IsEmpty()); - B3_ASSERT(m_top == NULL); + B3_ASSERT(m_root == nullptr); + B3_ASSERT(m_top == nullptr); +} + +static inline void RecurseEvents(ProfilerNode* node) +{ + ProfilerListener* listener = g_profilerListener; + + if (listener) + { + listener->BeginEvent(node->name, node->t0); + + listener->EndEvent(node->name, node->t1); + } + + for (u32 i = 0; i < node->children.Count(); ++i) + { + RecurseEvents(node->children[i]); + } +} + +void Profiler::RecurseDestroy(ProfilerNode* node) +{ + for (u32 i = 0; i < node->children.Count(); ++i) + { + RecurseDestroy(node->children[i]); + } + + DestroyNode(node); } void Profiler::End() { + B3_ASSERT(m_top == nullptr); + ProfilerListener* listener = g_profilerListener; if (listener) @@ -72,22 +127,12 @@ void Profiler::End() listener->BeginEvents(); } - for (u32 i = 0; i < m_events.Count(); ++i) - { - ProfilerEvent e = m_events[i]; + RecurseEvents(m_root); + + RecurseDestroy(m_root); + m_root = nullptr; - if (listener) - { - listener->BeginEvent(e.name, e.t0); - - listener->EndEvent(e.name, e.t1); - } - } - - m_events.Resize(0); - - B3_ASSERT(m_events.IsEmpty()); - B3_ASSERT(m_top == NULL); + B3_ASSERT(m_root == nullptr); if (listener) { diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h index e1f2225..3a463ed 100644 --- a/examples/testbed/framework/profiler.h +++ b/examples/testbed/framework/profiler.h @@ -20,21 +20,23 @@ #define PROFILER_H #include -#include +#include #include +#include class ProfilerListener; -// A time-stamped profiler event. -struct ProfilerEvent +// Profiler node +struct ProfilerNode { const char* name; float64 t0; float64 t1; - ProfilerEvent* parent; + ProfilerNode* parent; + b3StackArray children; }; -// A single-threaded event-based profiler. +// A single-threaded profiler. class Profiler { public: @@ -51,17 +53,21 @@ public: // This function also flushes the profiler. void End(); - // Add a profiler event to the queue. - // You can control the maximum number of profiler events using - // MAX_PROFILER_EVENTS. + // Push an event. void PushEvent(const char* name); // Remove the top profiler event. void PopEvent(); private: - b3Time m_time; - b3StackArray m_events; - ProfilerEvent* m_top; + ProfilerNode* CreateNode(); + void DestroyNode(ProfilerNode* node); + + void RecurseDestroy(ProfilerNode* node); + + b3BlockPool m_pool; // pool of nodes + b3Time m_time; // timer + ProfilerNode* m_root; // tree root node + ProfilerNode* m_top; // top node }; extern Profiler* g_profiler; From 725ca3ce31bda5cc6d5f5343a337021d605c2a23 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 31 Mar 2019 10:21:53 -0300 Subject: [PATCH 027/198] reduce stack size --- examples/testbed/framework/profiler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h index 3a463ed..18f2a96 100644 --- a/examples/testbed/framework/profiler.h +++ b/examples/testbed/framework/profiler.h @@ -33,7 +33,7 @@ struct ProfilerNode float64 t0; float64 t1; ProfilerNode* parent; - b3StackArray children; + b3StackArray children; }; // A single-threaded profiler. From fb895192e7971625669d288920d1e7ed6bdce8d4 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 31 Mar 2019 11:02:26 -0300 Subject: [PATCH 028/198] rename --- examples/testbed/framework/profiler.cpp | 4 ++-- examples/testbed/framework/profiler.h | 8 ++++---- examples/testbed/framework/test.cpp | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/testbed/framework/profiler.cpp b/examples/testbed/framework/profiler.cpp index 579c968..fd50f61 100644 --- a/examples/testbed/framework/profiler.cpp +++ b/examples/testbed/framework/profiler.cpp @@ -46,7 +46,7 @@ void Profiler::DestroyNode(ProfilerNode* node) m_pool.Free(node); } -void Profiler::PushEvent(const char* name) +void Profiler::BeginScope(const char* name) { m_time.Update(); @@ -71,7 +71,7 @@ void Profiler::PushEvent(const char* name) m_top = n; } -void Profiler::PopEvent() +void Profiler::EndScope() { m_time.Update(); diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h index 18f2a96..004f878 100644 --- a/examples/testbed/framework/profiler.h +++ b/examples/testbed/framework/profiler.h @@ -53,11 +53,11 @@ public: // This function also flushes the profiler. void End(); - // Push an event. - void PushEvent(const char* name); + // Begin a new scope. + void BeginScope(const char* name); - // Remove the top profiler event. - void PopEvent(); + // End the top scope. + void EndScope(); private: ProfilerNode* CreateNode(); void DestroyNode(ProfilerNode* node); diff --git a/examples/testbed/framework/test.cpp b/examples/testbed/framework/test.cpp index 3b83bb6..f5779aa 100644 --- a/examples/testbed/framework/test.cpp +++ b/examples/testbed/framework/test.cpp @@ -26,12 +26,12 @@ extern bool b3_convexCache; void b3BeginProfileScope(const char* name) { - g_profiler->PushEvent(name); + g_profiler->BeginScope(name); } void b3EndProfileScope() { - g_profiler->PopEvent(); + g_profiler->EndScope(); } Test::Test() : From 1c99a105368ff79b17407013b97bf8329e6b739b Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 31 Mar 2019 11:03:02 -0300 Subject: [PATCH 029/198] bugfix --- examples/testbed/framework/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/testbed/framework/main.cpp b/examples/testbed/framework/main.cpp index 749c6f1..7b466ba 100644 --- a/examples/testbed/framework/main.cpp +++ b/examples/testbed/framework/main.cpp @@ -105,7 +105,7 @@ static void Run() { g_profiler->Begin(); - g_profiler->PushEvent("Frame"); + g_profiler->BeginScope("Frame"); g_view->BeginInterface(); @@ -137,7 +137,7 @@ static void Run() g_view->EndInterface(); - g_profiler->PopEvent(); + g_profiler->EndScope(); g_profiler->End(); From 3f55504a913289055f9d7722f153667ecbbb3cae Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 31 Mar 2019 11:07:05 -0300 Subject: [PATCH 030/198] use assert instead of B3_ASSERT --- examples/testbed/framework/profiler.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/testbed/framework/profiler.cpp b/examples/testbed/framework/profiler.cpp index fd50f61..92521eb 100644 --- a/examples/testbed/framework/profiler.cpp +++ b/examples/testbed/framework/profiler.cpp @@ -29,8 +29,8 @@ Profiler::Profiler() : m_pool(sizeof(ProfilerNode)) Profiler::~Profiler() { - B3_ASSERT(m_root == nullptr); - B3_ASSERT(m_top == nullptr); + assert(m_root == nullptr); + assert(m_top == nullptr); } ProfilerNode* Profiler::CreateNode() @@ -75,9 +75,9 @@ void Profiler::EndScope() { m_time.Update(); - B3_ASSERT(m_top != nullptr); + assert(m_top != nullptr); m_top->t1 = m_time.GetCurrentMilis(); - B3_ASSERT(m_top->t1 > m_top->t0); + assert(m_top->t1 > m_top->t0); m_top = m_top->parent; } @@ -85,8 +85,8 @@ void Profiler::EndScope() void Profiler::Begin() { // If this assert is hit then it means Profiler::End hasn't been called. - B3_ASSERT(m_root == nullptr); - B3_ASSERT(m_top == nullptr); + assert(m_root == nullptr); + assert(m_top == nullptr); } static inline void RecurseEvents(ProfilerNode* node) @@ -118,7 +118,7 @@ void Profiler::RecurseDestroy(ProfilerNode* node) void Profiler::End() { - B3_ASSERT(m_top == nullptr); + assert(m_top == nullptr); ProfilerListener* listener = g_profilerListener; @@ -132,7 +132,7 @@ void Profiler::End() RecurseDestroy(m_root); m_root = nullptr; - B3_ASSERT(m_root == nullptr); + assert(m_root == nullptr); if (listener) { From f195e77be7ab1ca6d6b66127842745aeb3321ba4 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 1 Apr 2019 11:03:12 -0300 Subject: [PATCH 031/198] improve profiler --- examples/testbed/framework/json_profiler.h | 12 +- examples/testbed/framework/main.cpp | 18 ++- examples/testbed/framework/model.cpp | 10 +- examples/testbed/framework/model.h | 17 ++- examples/testbed/framework/profiler.h | 2 + .../testbed/framework/recorder_profiler.cpp | 121 +++++++++++------- .../testbed/framework/recorder_profiler.h | 33 +++-- examples/testbed/framework/testbed_profiler.h | 82 ------------ 8 files changed, 133 insertions(+), 162 deletions(-) delete mode 100644 examples/testbed/framework/testbed_profiler.h diff --git a/examples/testbed/framework/json_profiler.h b/examples/testbed/framework/json_profiler.h index dbca993..8266302 100644 --- a/examples/testbed/framework/json_profiler.h +++ b/examples/testbed/framework/json_profiler.h @@ -19,6 +19,8 @@ #ifndef JSON_PROFILER_H #define JSON_PROFILER_H +#include + #include #include @@ -33,19 +35,19 @@ using namespace rapidjson; // Say chrome://tracing to the web browser and load the file // This file is by default called "profile.json". Any name can be given. // For implementation details, see json_profile.cpp. -class JsonProfiler +class JsonProfiler : public ProfilerListener { public: JsonProfiler(); ~JsonProfiler(); - void BeginEvents(); + void BeginEvents() override; - void EndEvents(); + void EndEvents() override; - void BeginEvent(const char* name, float64 time); + void BeginEvent(const char* name, float64 time) override; - void EndEvent(const char* name, float64 time); + void EndEvent(const char* name, float64 time) override; private: FILE * m_file; FileWriteStream* m_stream; diff --git a/examples/testbed/framework/main.cpp b/examples/testbed/framework/main.cpp index 7b466ba..1ff1c07 100644 --- a/examples/testbed/framework/main.cpp +++ b/examples/testbed/framework/main.cpp @@ -101,6 +101,8 @@ static void Run() glfwGetWindowSize(g_window, &w, &h); g_view->Event_SetWindowSize(u32(w), u32(h)); + b3StackArray records; + while (glfwWindowShouldClose(g_window) == 0) { g_profiler->Begin(); @@ -120,14 +122,10 @@ static void Run() if (g_settings->drawProfile) { - const b3Array& records = g_recorderProfiler->GetRecords(); for (u32 i = 0; i < records.Count(); ++i) { - const ProfilerRecord& r = records[i]; - if (r.call != 0) - { - g_draw->DrawString(b3Color_white, "%s %.4f (%.4f) [ms]", r.name, r.elapsed, r.maxElapsed); - } + ProfilerRecord* r = records[i]; + g_draw->DrawString(b3Color_white, "%s %.4f (min = %.4f) (max = %.4f) (calls = %d) [ms]", r->name, r->elapsed, r->minElapsed, r->maxElapsed, r->callCount); } } @@ -138,6 +136,14 @@ static void Run() g_view->EndInterface(); g_profiler->EndScope(); + + g_recorderProfiler->BuildRecords(); + + if (g_settings->drawProfile) + { + records.Resize(0); + g_recorderProfiler->BuildSortedRecords(records); + } g_profiler->End(); diff --git a/examples/testbed/framework/model.cpp b/examples/testbed/framework/model.cpp index 37c9aab..f3f2c8c 100644 --- a/examples/testbed/framework/model.cpp +++ b/examples/testbed/framework/model.cpp @@ -26,8 +26,11 @@ Model::Model() g_draw = &m_draw; g_camera = &m_camera; g_profiler = &m_profiler; - g_recorderProfiler = &m_profilerListener.m_recorderListener; - g_profilerListener = &m_profilerListener; + g_recorderProfiler = &m_recorderProfiler; + +#if (PROFILE_JSON == 1) + g_profilerListener = &m_jsonListener; +#endif m_test = nullptr; @@ -54,7 +57,10 @@ Model::~Model() g_camera = nullptr; g_profiler = nullptr; g_recorderProfiler = nullptr; + +#if (PROFILE_JSON == 1) g_profilerListener = nullptr; +#endif delete m_test; } diff --git a/examples/testbed/framework/model.h b/examples/testbed/framework/model.h index 2580055..6371131 100644 --- a/examples/testbed/framework/model.h +++ b/examples/testbed/framework/model.h @@ -20,7 +20,15 @@ #define MODEL_H #include -#include +#include +#include + +// Set to 1 to write profile events into a .json file. Set to 0 otherwise. +#define PROFILE_JSON 1 + +#if (PROFILE_JSON == 1) +#include +#endif class Test; @@ -63,7 +71,12 @@ private: Draw m_draw; Camera m_camera; Profiler m_profiler; - TestbedProfiler m_profilerListener; + RecorderProfiler m_recorderProfiler; + +#if (PROFILE_JSON == 1) + JsonProfiler m_jsonListener; +#endif + Test* m_test; bool m_setTest; bool m_pause; diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h index 004f878..aa70633 100644 --- a/examples/testbed/framework/profiler.h +++ b/examples/testbed/framework/profiler.h @@ -59,6 +59,8 @@ public: // End the top scope. void EndScope(); private: + friend class RecorderProfiler; + ProfilerNode* CreateNode(); void DestroyNode(ProfilerNode* node); diff --git a/examples/testbed/framework/recorder_profiler.cpp b/examples/testbed/framework/recorder_profiler.cpp index 4edfaed..143ab83 100644 --- a/examples/testbed/framework/recorder_profiler.cpp +++ b/examples/testbed/framework/recorder_profiler.cpp @@ -17,37 +17,10 @@ */ #include +#include RecorderProfiler* g_recorderProfiler = nullptr; -void RecorderProfiler::BeginEvents() -{ - m_call = 0; - for (u32 i = 0; i < m_records.Count(); ++i) - { - m_records[i].elapsed = 0.0; - m_records[i].call = 0; - } -} - -void RecorderProfiler::EndEvents() -{ - for (u32 i = 0; i < m_records.Count(); ++i) - { - ProfilerRecord& r1 = m_records[i]; - - for (u32 j = i + 1; j < m_records.Count(); ++j) - { - ProfilerRecord& r2 = m_records[j]; - - if (r2.call < r1.call) - { - b3Swap(r1, r2); - } - } - } -} - ProfilerRecord* RecorderProfiler::FindRecord(const char* name) { for (u32 i = 0; i < m_records.Count(); ++i) @@ -61,35 +34,87 @@ ProfilerRecord* RecorderProfiler::FindRecord(const char* name) return nullptr; } -void RecorderProfiler::BeginEvent(const char* name, float64 time) +void RecorderProfiler::RecurseBuildRecords(ProfilerNode* node) { - ++m_call; - - ProfilerRecord* fr = FindRecord(name); + ProfilerRecord* fr = FindRecord(node->name); if (fr) { - fr->time = time; - fr->call = m_call; - return; + float64 elapsedTime = node->t1 - node->t0; + + fr->elapsed += elapsedTime; + fr->minElapsed = b3Min(fr->minElapsed, elapsedTime); + fr->maxElapsed = b3Max(fr->maxElapsed, elapsedTime); + ++fr->callCount; } + else + { + float64 elapsedTime = node->t1 - node->t0; - ProfilerRecord r; - r.name = name; - r.time = time; - r.elapsed = 0.0; - r.maxElapsed = 0.0; - r.call = m_call; + ProfilerRecord r; + r.name = node->name; + r.elapsed = elapsedTime; + r.minElapsed = elapsedTime; + r.maxElapsed = elapsedTime; + r.callCount = 1; - m_records.PushBack(r); + m_records.PushBack(r); + } + + for (u32 i = 0; i < node->children.Count(); ++i) + { + RecurseBuildRecords(node->children[i]); + } } -void RecorderProfiler::EndEvent(const char* name, float64 time) +void RecorderProfiler::BuildRecords() { - ProfilerRecord* fr = FindRecord(name); - B3_ASSERT(fr != nullptr); + for (u32 i = 0; i < m_records.Count(); ++i) + { + m_records[i].elapsed = 0.0; + m_records[i].callCount = 0; + } - float64 elapsedTime = time - fr->time; + RecurseBuildRecords(g_profiler->m_root); +} - fr->elapsed += elapsedTime; - fr->maxElapsed = b3Max(fr->maxElapsed, elapsedTime); +static ProfilerRecord* FindSortedRecord(b3Array& records, const char* name) +{ + for (u32 i = 0; i < records.Count(); ++i) + { + ProfilerRecord* r = records[i]; + if (r->name == name) + { + return r; + } + } + return nullptr; +} + +void RecorderProfiler::RecurseBuildSortedRecords(ProfilerNode* node, b3Array& output) +{ + ProfilerRecord* fsr = FindSortedRecord(output, node->name); + + if (fsr == nullptr) + { + ProfilerRecord* fr = FindRecord(node->name); + + assert(fr != nullptr); + + // Push back the first ocurrence of call in calling order + output.PushBack(fr); + } + + for (u32 i = 0; i < node->children.Count(); ++i) + { + RecurseBuildSortedRecords(node->children[i], output); + } +} + +void RecorderProfiler::BuildSortedRecords(b3Array& output) +{ + assert(output.Count() == 0); + + output.Reserve(m_records.Count()); + + RecurseBuildSortedRecords(g_profiler->m_root, output); } \ No newline at end of file diff --git a/examples/testbed/framework/recorder_profiler.h b/examples/testbed/framework/recorder_profiler.h index 3934ae7..9d6eae6 100644 --- a/examples/testbed/framework/recorder_profiler.h +++ b/examples/testbed/framework/recorder_profiler.h @@ -19,39 +19,38 @@ #ifndef RECORDER_PROFILER_H #define RECORDER_PROFILER_H -#include #include +#include -// An event in the profiler event recorder. +struct ProfilerNode; + +// A persistent profiler record struct ProfilerRecord { const char* name; - float64 time; float64 elapsed; + float64 minElapsed; float64 maxElapsed; - u32 call; + u32 callCount; }; -// The profiler recorder simply keeps profile events in an event buffer, -// so that they can be rendered, saved to a file, etc. later in a -// particular position in code. +// This class maintains persistent profiler records class RecorderProfiler { public: - void BeginEvents(); - - void EndEvents(); - - void BeginEvent(const char* name, float64 time); + void BuildRecords(); + + void BuildSortedRecords(b3Array& output); - void EndEvent(const char* name, float64 time); - const b3Array& GetRecords() const { return m_records; } private: - ProfilerRecord* FindRecord(const char* name); + void RecurseBuildRecords(ProfilerNode* node); - b3StackArray m_records; // persistent records sorted by call order - u32 m_call; + void RecurseBuildSortedRecords(ProfilerNode* node, b3Array& output); + + ProfilerRecord* FindRecord(const char* name); + + b3StackArray m_records; // persistent profiler records }; extern RecorderProfiler* g_recorderProfiler; diff --git a/examples/testbed/framework/testbed_profiler.h b/examples/testbed/framework/testbed_profiler.h deleted file mode 100644 index a770937..0000000 --- a/examples/testbed/framework/testbed_profiler.h +++ /dev/null @@ -1,82 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef TESTBED_PROFILER_H -#define TESTBED_PROFILER_H - -#include -#include - -// Set to 1 then the testbed profiler will write profile events into a .json file. -// Set to 0 otherwise. -#define PROFILE_JSON 0 - -#if (PROFILE_JSON == 1) - #include -#endif - -class TestbedProfiler : public ProfilerListener -{ -public: - void BeginEvents() override - { - m_recorderListener.BeginEvents(); - -#if (PROFILE_JSON == 1) - m_jsonListener.BeginEvents(); -#endif - - } - - void EndEvents() override - { - m_recorderListener.EndEvents(); - -#if (PROFILE_JSON == 1) - m_jsonListener.EndEvents(); -#endif - - } - - void BeginEvent(const char* name, float64 time) override - { - m_recorderListener.BeginEvent(name, time); - -#if (PROFILE_JSON == 1) - m_jsonListener.BeginEvent(name, time); -#endif - } - - void EndEvent(const char* name, float64 time) override - { - m_recorderListener.EndEvent(name, time); - -#if (PROFILE_JSON == 1) - m_jsonListener.EndEvent(name, time); -#endif - } - - RecorderProfiler m_recorderListener; - -#if (PROFILE_JSON == 1) - JsonProfiler m_jsonListener; -#endif - -}; - -#endif \ No newline at end of file From 2e4b7004c9fc1a7d69dbb8870a77463e7c252656 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 1 Apr 2019 11:15:39 -0300 Subject: [PATCH 032/198] some renaming --- examples/testbed/framework/main.cpp | 4 ++-- examples/testbed/framework/model.cpp | 4 ++-- examples/testbed/framework/model.h | 6 +++--- examples/testbed/framework/profiler.h | 2 +- ...recorder_profiler.cpp => profiler_recorder.cpp} | 14 +++++++------- .../{recorder_profiler.h => profiler_recorder.h} | 8 ++++---- 6 files changed, 19 insertions(+), 19 deletions(-) rename examples/testbed/framework/{recorder_profiler.cpp => profiler_recorder.cpp} (86%) rename examples/testbed/framework/{recorder_profiler.h => profiler_recorder.h} (92%) diff --git a/examples/testbed/framework/main.cpp b/examples/testbed/framework/main.cpp index 1ff1c07..bd498ff 100644 --- a/examples/testbed/framework/main.cpp +++ b/examples/testbed/framework/main.cpp @@ -137,12 +137,12 @@ static void Run() g_profiler->EndScope(); - g_recorderProfiler->BuildRecords(); + g_profilerRecorder->BuildRecords(); if (g_settings->drawProfile) { records.Resize(0); - g_recorderProfiler->BuildSortedRecords(records); + g_profilerRecorder->BuildSortedRecords(records); } g_profiler->End(); diff --git a/examples/testbed/framework/model.cpp b/examples/testbed/framework/model.cpp index f3f2c8c..325bfa0 100644 --- a/examples/testbed/framework/model.cpp +++ b/examples/testbed/framework/model.cpp @@ -26,7 +26,7 @@ Model::Model() g_draw = &m_draw; g_camera = &m_camera; g_profiler = &m_profiler; - g_recorderProfiler = &m_recorderProfiler; + g_profilerRecorder = &m_profilerRecorder; #if (PROFILE_JSON == 1) g_profilerListener = &m_jsonListener; @@ -56,7 +56,7 @@ Model::~Model() g_draw = nullptr; g_camera = nullptr; g_profiler = nullptr; - g_recorderProfiler = nullptr; + g_profilerRecorder = nullptr; #if (PROFILE_JSON == 1) g_profilerListener = nullptr; diff --git a/examples/testbed/framework/model.h b/examples/testbed/framework/model.h index 6371131..22dc4cc 100644 --- a/examples/testbed/framework/model.h +++ b/examples/testbed/framework/model.h @@ -21,10 +21,10 @@ #include #include -#include +#include // Set to 1 to write profile events into a .json file. Set to 0 otherwise. -#define PROFILE_JSON 1 +#define PROFILE_JSON 0 #if (PROFILE_JSON == 1) #include @@ -71,7 +71,7 @@ private: Draw m_draw; Camera m_camera; Profiler m_profiler; - RecorderProfiler m_recorderProfiler; + ProfilerRecorder m_profilerRecorder; #if (PROFILE_JSON == 1) JsonProfiler m_jsonListener; diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h index aa70633..bcc978a 100644 --- a/examples/testbed/framework/profiler.h +++ b/examples/testbed/framework/profiler.h @@ -59,7 +59,7 @@ public: // End the top scope. void EndScope(); private: - friend class RecorderProfiler; + friend class ProfilerRecorder; ProfilerNode* CreateNode(); void DestroyNode(ProfilerNode* node); diff --git a/examples/testbed/framework/recorder_profiler.cpp b/examples/testbed/framework/profiler_recorder.cpp similarity index 86% rename from examples/testbed/framework/recorder_profiler.cpp rename to examples/testbed/framework/profiler_recorder.cpp index 143ab83..94b0dc7 100644 --- a/examples/testbed/framework/recorder_profiler.cpp +++ b/examples/testbed/framework/profiler_recorder.cpp @@ -16,12 +16,12 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include +#include #include -RecorderProfiler* g_recorderProfiler = nullptr; +ProfilerRecorder* g_profilerRecorder = nullptr; -ProfilerRecord* RecorderProfiler::FindRecord(const char* name) +ProfilerRecord* ProfilerRecorder::FindRecord(const char* name) { for (u32 i = 0; i < m_records.Count(); ++i) { @@ -34,7 +34,7 @@ ProfilerRecord* RecorderProfiler::FindRecord(const char* name) return nullptr; } -void RecorderProfiler::RecurseBuildRecords(ProfilerNode* node) +void ProfilerRecorder::RecurseBuildRecords(ProfilerNode* node) { ProfilerRecord* fr = FindRecord(node->name); if (fr) @@ -66,7 +66,7 @@ void RecorderProfiler::RecurseBuildRecords(ProfilerNode* node) } } -void RecorderProfiler::BuildRecords() +void ProfilerRecorder::BuildRecords() { for (u32 i = 0; i < m_records.Count(); ++i) { @@ -90,7 +90,7 @@ static ProfilerRecord* FindSortedRecord(b3Array& records, const return nullptr; } -void RecorderProfiler::RecurseBuildSortedRecords(ProfilerNode* node, b3Array& output) +void ProfilerRecorder::RecurseBuildSortedRecords(ProfilerNode* node, b3Array& output) { ProfilerRecord* fsr = FindSortedRecord(output, node->name); @@ -110,7 +110,7 @@ void RecorderProfiler::RecurseBuildSortedRecords(ProfilerNode* node, b3Array& output) +void ProfilerRecorder::BuildSortedRecords(b3Array& output) { assert(output.Count() == 0); diff --git a/examples/testbed/framework/recorder_profiler.h b/examples/testbed/framework/profiler_recorder.h similarity index 92% rename from examples/testbed/framework/recorder_profiler.h rename to examples/testbed/framework/profiler_recorder.h index 9d6eae6..b3ea5b4 100644 --- a/examples/testbed/framework/recorder_profiler.h +++ b/examples/testbed/framework/profiler_recorder.h @@ -16,8 +16,8 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef RECORDER_PROFILER_H -#define RECORDER_PROFILER_H +#ifndef PROFILER_RECORDER_H +#define PROFILER_RECORDER_H #include #include @@ -35,7 +35,7 @@ struct ProfilerRecord }; // This class maintains persistent profiler records -class RecorderProfiler +class ProfilerRecorder { public: void BuildRecords(); @@ -53,6 +53,6 @@ private: b3StackArray m_records; // persistent profiler records }; -extern RecorderProfiler* g_recorderProfiler; +extern ProfilerRecorder* g_profilerRecorder; #endif \ No newline at end of file From cca8ccad3204090542f874d765fbb42858dc353d Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 1 Apr 2019 13:25:25 -0300 Subject: [PATCH 033/198] use forward slashes --- examples/testbed/framework/model.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/testbed/framework/model.h b/examples/testbed/framework/model.h index 22dc4cc..0354f56 100644 --- a/examples/testbed/framework/model.h +++ b/examples/testbed/framework/model.h @@ -27,7 +27,7 @@ #define PROFILE_JSON 0 #if (PROFILE_JSON == 1) -#include +#include #endif class Test; From 16421febc43bdb85061493c6b74b2fde79db3845 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 2 Apr 2019 09:21:43 -0300 Subject: [PATCH 034/198] update main --- examples/testbed/framework/main.cpp | 31 ++++++++++++----------------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/examples/testbed/framework/main.cpp b/examples/testbed/framework/main.cpp index bd498ff..4c68c4e 100644 --- a/examples/testbed/framework/main.cpp +++ b/examples/testbed/framework/main.cpp @@ -100,8 +100,6 @@ static void Run() int w, h; glfwGetWindowSize(g_window, &w, &h); g_view->Event_SetWindowSize(u32(w), u32(h)); - - b3StackArray records; while (glfwWindowShouldClose(g_window) == 0) { @@ -120,8 +118,19 @@ static void Run() g_draw->DrawString(b3Color_white, "*PLAYING*"); } + g_view->Interface(); + + g_model->Update(); + + g_profiler->EndScope(); + + g_profilerRecorder->BuildRecords(); + if (g_settings->drawProfile) { + b3StackArray records; + g_profilerRecorder->BuildSortedRecords(records); + for (u32 i = 0; i < records.Count(); ++i) { ProfilerRecord* r = records[i]; @@ -129,23 +138,9 @@ static void Run() } } - g_view->Interface(); - - g_model->Update(); - - g_view->EndInterface(); - - g_profiler->EndScope(); - - g_profilerRecorder->BuildRecords(); - - if (g_settings->drawProfile) - { - records.Resize(0); - g_profilerRecorder->BuildSortedRecords(records); - } - g_profiler->End(); + + g_view->EndInterface(); glfwSwapBuffers(g_window); glfwPollEvents(); From c638a0a52cb736162e39efeb92c8f06255546be1 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 2 Apr 2019 10:26:54 -0300 Subject: [PATCH 035/198] renaming --- examples/testbed/framework/profiler.cpp | 6 +++--- examples/testbed/framework/profiler.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/testbed/framework/profiler.cpp b/examples/testbed/framework/profiler.cpp index 92521eb..0422084 100644 --- a/examples/testbed/framework/profiler.cpp +++ b/examples/testbed/framework/profiler.cpp @@ -106,11 +106,11 @@ static inline void RecurseEvents(ProfilerNode* node) } } -void Profiler::RecurseDestroy(ProfilerNode* node) +void Profiler::RecurseDestroyNode(ProfilerNode* node) { for (u32 i = 0; i < node->children.Count(); ++i) { - RecurseDestroy(node->children[i]); + RecurseDestroyNode(node->children[i]); } DestroyNode(node); @@ -129,7 +129,7 @@ void Profiler::End() RecurseEvents(m_root); - RecurseDestroy(m_root); + RecurseDestroyNode(m_root); m_root = nullptr; assert(m_root == nullptr); diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h index bcc978a..d87ed3e 100644 --- a/examples/testbed/framework/profiler.h +++ b/examples/testbed/framework/profiler.h @@ -64,7 +64,7 @@ private: ProfilerNode* CreateNode(); void DestroyNode(ProfilerNode* node); - void RecurseDestroy(ProfilerNode* node); + void RecurseDestroyNode(ProfilerNode* node); b3BlockPool m_pool; // pool of nodes b3Time m_time; // timer From 756c4d354ab722efe11f306096c882a562d42fd3 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 2 Apr 2019 12:30:29 -0300 Subject: [PATCH 036/198] remove some inefficient code, add some test code --- include/bounce/cloth/cloth.h | 8 +- src/bounce/cloth/cloth.cpp | 241 +----------------------------- src/bounce/cloth/cloth_solver.cpp | 51 +++++++ 3 files changed, 53 insertions(+), 247 deletions(-) diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index c78e921..17c25c0 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -156,9 +156,6 @@ private: // Update particle contacts. void UpdateParticleContacts(); - - // Update triangle contacts. - void UpdateTriangleContacts(); // Update contacts void UpdateContacts(); @@ -192,10 +189,7 @@ private: // Pool of particle contacts b3BlockPool m_particleContactBlocks; - - // Pool of triangle contacts - b3BlockPool m_triangleContactBlocks; - + // List of particles b3List2 m_particleList; diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 3ca1e95..7e36935 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -149,8 +149,7 @@ static u32 b3FindSharedEdges(b3SharedEdge* sharedEdges, const b3ClothMesh* m) b3Cloth::b3Cloth(const b3ClothDef& def) : m_particleBlocks(sizeof(b3Particle)), m_bodyContactBlocks(sizeof(b3BodyContact)), - m_particleContactBlocks(sizeof(b3ParticleContact)), - m_triangleContactBlocks(sizeof(b3TriangleContact)) + m_particleContactBlocks(sizeof(b3ParticleContact)) { B3_ASSERT(def.mesh); B3_ASSERT(def.density > 0.0f); @@ -624,241 +623,6 @@ void b3Cloth::UpdateParticleContacts() } } -static B3_FORCE_INLINE void b3Barycentric(float32 out[3], - const b3Vec3& A, const b3Vec3& B, - const b3Vec3& Q) -{ - b3Vec3 AB = B - A; - b3Vec3 QA = A - Q; - b3Vec3 QB = B - Q; - - //float32 divisor = b3Dot(AB, AB); - - out[0] = b3Dot(QB, AB); - out[1] = -b3Dot(QA, AB); - out[2] = out[0] + out[1]; -} - -// Convert a point Q from Cartesian coordinates to Barycentric coordinates (u, v, w) -// with respect to a triangle ABC. -// The last output value is the divisor. -static B3_FORCE_INLINE void b3Barycentric(float32 out[4], - const b3Vec3& A, const b3Vec3& B, const b3Vec3& C, - const b3Vec3& Q) -{ - b3Vec3 AB = B - A; - b3Vec3 AC = C - A; - - b3Vec3 QA = A - Q; - b3Vec3 QB = B - Q; - b3Vec3 QC = C - Q; - - b3Vec3 QB_x_QC = b3Cross(QB, QC); - b3Vec3 QC_x_QA = b3Cross(QC, QA); - b3Vec3 QA_x_QB = b3Cross(QA, QB); - - b3Vec3 AB_x_AC = b3Cross(AB, AC); - - //float32 divisor = b3Dot(AB_x_AC, AB_x_AC); - - out[0] = b3Dot(QB_x_QC, AB_x_AC); - out[1] = b3Dot(QC_x_QA, AB_x_AC); - out[2] = b3Dot(QA_x_QB, AB_x_AC); - out[3] = out[0] + out[1] + out[2]; -} - -static B3_FORCE_INLINE void b3Solve3(float32 out[3], - const b3Vec3& A, const b3Vec3& B, const b3Vec3& C, - const b3Vec3& Q) -{ - // Test vertex regions - float32 wAB[3], wBC[3], wCA[3]; - b3Barycentric(wAB, A, B, Q); - b3Barycentric(wBC, B, C, Q); - b3Barycentric(wCA, C, A, Q); - - // R A - if (wAB[1] <= 0.0f && wCA[0] <= 0.0f) - { - out[0] = 1.0f; - out[1] = 0.0f; - out[2] = 0.0f; - return; - } - - // R B - if (wAB[0] <= 0.0f && wBC[1] <= 0.0f) - { - out[0] = 0.0f; - out[1] = 1.0f; - out[2] = 0.0f; - return; - } - - // R C - if (wBC[0] <= 0.0f && wCA[1] <= 0.0f) - { - out[0] = 0.0f; - out[1] = 0.0f; - out[2] = 1.0f; - return; - } - - // Test edge regions - float32 wABC[4]; - b3Barycentric(wABC, A, B, C, Q); - - // This is used to help testing if the face degenerates - // into an edge. - float32 area = wABC[3]; - - // R AB - if (wAB[0] > 0.0f && wAB[1] > 0.0f && area * wABC[2] <= 0.0f) - { - float32 divisor = wAB[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; - out[0] = s * wAB[0]; - out[1] = s * wAB[1]; - out[2] = 0.0f; - return; - } - - // R BC - if (wBC[0] > 0.0f && wBC[1] > 0.0f && area * wABC[0] <= 0.0f) - { - float32 divisor = wBC[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; - out[0] = 0.0f; - out[1] = s * wBC[0]; - out[2] = s * wBC[1]; - return; - } - - // R CA - if (wCA[0] > 0.0f && wCA[1] > 0.0f && area * wABC[1] <= 0.0f) - { - float32 divisor = wCA[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; - out[0] = 0.0f; - out[2] = s * wCA[0]; - out[1] = s * wCA[1]; - return; - } - - // R ABC/ACB - float32 divisor = wABC[3]; - if (divisor <= 0.0f) - { - float32 s = 1.0f / 3.0f; - out[0] = s; - out[2] = s; - out[1] = s; - return; - } - - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; - out[0] = s * wABC[0]; - out[1] = s * wABC[1]; - out[2] = s * wABC[2]; -} - -void b3Cloth::UpdateTriangleContacts() -{ - B3_PROFILE("Cloth Update Triangle Contacts"); - - // Clear buffer - b3TriangleContact* c = m_triangleContactList.m_head; - while (c) - { - b3TriangleContact* c0 = c; - c = c->m_next; - m_triangleContactList.Remove(c0); - c0->~b3TriangleContact(); - m_triangleContactBlocks.Free(c0); - } - - // Create triangle contacts - for (b3Particle* p1 = m_particleList.m_head; p1; p1 = p1->m_next) - { - for (u32 i = 0; i < m_mesh->triangleCount; ++i) - { - b3ClothMeshTriangle* triangle = m_mesh->triangles + i; - - b3Particle* p2 = m_vertexParticles[triangle->v1]; - b3Particle* p3 = m_vertexParticles[triangle->v2]; - b3Particle* p4 = m_vertexParticles[triangle->v3]; - - if (p1 == p2 || p1 == p3 || p1 == p4) - { - continue; - } - - float32 r1 = p1->m_radius; - float32 r2 = 0.0f; - - float32 totalRadius = r1 + r2; - - b3Vec3 A = p2->m_position; - b3Vec3 B = p3->m_position; - b3Vec3 C = p4->m_position; - - b3Vec3 P = p1->m_position; - - b3Vec3 n = b3Cross(B - A, C - A); - float32 n_len = n.Normalize(); - - float32 distance = b3Dot(n, P - A); - - if (distance < -totalRadius) - { - continue; - } - - if (distance > totalRadius) - { - continue; - } - - // Project particle on triangle plane - b3Vec3 Q = P - distance * n; - - // Barycentric coordinates for Q - float32 wABC[3]; - b3Solve3(wABC, A, B, C, Q); - - b3Vec3 c1 = p1->m_position; - b3Vec3 c2 = wABC[0] * A + wABC[1] * B + wABC[2] * C; - - if (b3DistanceSquared(c1, c2) > totalRadius * totalRadius) - { - continue; - } - - bool front = false; - - // Is the the other point in front of the triangle plane? - if (distance >= 0.0f) - { - front = true; - } - - b3TriangleContact* c = (b3TriangleContact*)m_triangleContactBlocks.Allocate(); - c->p1 = p1; - c->p2 = p2; - c->p3 = p3; - c->p4 = p4; - c->front = front; - c->normalImpulse = 0.0f; - - m_triangleContactList.PushFront(c); - } - } -} - void b3Cloth::Solve(float32 dt, const b3Vec3& gravity) { B3_PROFILE("Cloth Solve"); @@ -918,9 +682,6 @@ void b3Cloth::UpdateContacts() #if 0 // Update particle contacts UpdateParticleContacts(); - - // Update triangle contacts - UpdateTriangleContacts(); #endif } diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index 9e3b325..95c2c2f 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -155,6 +155,7 @@ void b3ClothSolver::ApplyConstraints() { b3DiagMat33& S = *m_solverData.S; b3DenseVec3& z = *m_solverData.z; + b3DenseVec3& x = *m_solverData.x; S.SetIdentity(); z.SetZero(); @@ -172,6 +173,35 @@ void b3ClothSolver::ApplyConstraints() } } +#if 0 + for (u32 i = 0; i < m_bodyContactCount; ++i) + { + b3BodyContact* bc = m_bodyContacts[i]; + + b3Particle* p1 = bc->p1; + + B3_ASSERT(p1->m_type == e_dynamicParticle); + + b3Transform xf1; + xf1.position = x[p1->m_solverId]; + xf1.rotation.SetIdentity(); + + b3Shape* s2 = bc->s2; + b3Body* b2 = s2->GetBody(); + b3Transform xf2 = b2->GetTransform(); + + b3BodyContactWorldPoint bcwp; + bcwp.Initialize(bc, p1->m_radius, xf1, s2->m_radius, xf2); + + b3AccelerationConstraint* ac = m_constraints + m_constraintCount; + ++m_constraintCount; + ac->i1 = p1->m_solverId; + ac->ndof = 2; + ac->p = bcwp.normal; + ac->z.SetZero(); + } +#endif + for (u32 i = 0; i < m_constraintCount; ++i) { m_constraints[i].Apply(&m_solverData); @@ -223,6 +253,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) m_solverData.S = &S; m_solverData.z = &z; + // Apply position correction for (u32 i = 0; i < m_particleCount; ++i) { b3Particle* p = m_particles[i]; @@ -230,7 +261,27 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) sy[i] = p->m_translation; sx0[i] = p->m_x; } + +#if 0 + for (u32 i = 0; i < m_bodyContactCount; ++i) + { + b3BodyContact* bc = m_bodyContacts[i]; + b3Particle* p1 = bc->p1; + b3Transform xf1; + xf1.position = sx[p1->m_solverId]; + xf1.rotation.SetIdentity(); + + b3Shape* s2 = bc->s2; + b3Body* b2 = s2->GetBody(); + b3Transform xf2 = b2->GetTransform(); + + b3BodyContactWorldPoint bcwp; + bcwp.Initialize(bc, p1->m_radius, xf1, s2->m_radius, xf2); + + sy[p1->m_solverId] += bcwp.separation * bcwp.normal; + } +#endif // Apply internal forces ApplyForces(); From f86ec88adfbbf234bb072f85af4230bfba116dc7 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 2 Apr 2019 12:48:49 -0300 Subject: [PATCH 037/198] removed unused code --- include/bounce/cloth/cloth.h | 6 +- include/bounce/cloth/cloth_contact_solver.h | 33 --- include/bounce/cloth/cloth_solver.h | 9 +- include/bounce/cloth/particle.h | 21 -- src/bounce/cloth/cloth.cpp | 6 - src/bounce/cloth/cloth_contact_solver.cpp | 278 -------------------- src/bounce/cloth/cloth_solver.cpp | 22 +- 7 files changed, 4 insertions(+), 371 deletions(-) diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index 17c25c0..3d5535f 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -31,7 +31,6 @@ class b3Particle; class b3Force; class b3BodyContact; class b3ParticleContact; -class b3TriangleContact; struct b3ParticleDef; struct b3ForceDef; @@ -186,7 +185,7 @@ private: // Pool of body contacts b3BlockPool m_bodyContactBlocks; - + // Pool of particle contacts b3BlockPool m_particleContactBlocks; @@ -201,9 +200,6 @@ private: // List of particle contacts b3List2 m_particleContactList; - - // List of triangle contacts - b3List2 m_triangleContactList; }; inline void b3Cloth::SetGravity(const b3Vec3& gravity) diff --git a/include/bounce/cloth/cloth_contact_solver.h b/include/bounce/cloth/cloth_contact_solver.h index 4752adc..30b1e80 100644 --- a/include/bounce/cloth/cloth_contact_solver.h +++ b/include/bounce/cloth/cloth_contact_solver.h @@ -29,7 +29,6 @@ class b3Body; class b3BodyContact; class b3ParticleContact; -class b3TriangleContact; struct b3DenseVec3; @@ -136,23 +135,6 @@ struct b3ClothSolverTriangleContactVelocityConstraint float32 normalImpulse; }; -struct b3ClothSolverTriangleContactPositionConstraint -{ - u32 indexA; - float32 invMassA; - float32 radiusA; - - u32 indexB; - float32 invMassB; - u32 indexC; - float32 invMassC; - u32 indexD; - float32 invMassD; - float32 triangleRadius; - - bool front; -}; - struct b3ClothContactSolverDef { b3StackAllocator* allocator; @@ -165,9 +147,6 @@ struct b3ClothContactSolverDef u32 particleContactCount; b3ParticleContact** particleContacts; - - u32 triangleContactCount; - b3TriangleContact** triangleContacts; }; inline float32 b3MixFriction(float32 u1, float32 u2) @@ -185,24 +164,17 @@ public: void InitializeParticleContactConstraints(); - void InitializeTriangleContactConstraints(); - void WarmStart(); void SolveBodyContactVelocityConstraints(); void SolveParticleContactVelocityConstraints(); - void SolveTriangleContactVelocityConstraints(); - void StoreImpulses(); bool SolveBodyContactPositionConstraints(); bool SolveParticleContactPositionConstraints(); - - bool SolveTriangleContactPositionConstraints(); - protected: b3StackAllocator* m_allocator; @@ -218,11 +190,6 @@ protected: b3ParticleContact** m_particleContacts; b3ClothSolverParticleContactVelocityConstraint* m_particleVelocityConstraints; b3ClothSolverParticleContactPositionConstraint* m_particlePositionConstraints; - - u32 m_triangleContactCount; - b3TriangleContact** m_triangleContacts; - b3ClothSolverTriangleContactVelocityConstraint* m_triangleVelocityConstraints; - b3ClothSolverTriangleContactPositionConstraint* m_trianglePositionConstraints; }; #endif \ No newline at end of file diff --git a/include/bounce/cloth/cloth_solver.h b/include/bounce/cloth/cloth_solver.h index 2c53294..6b0b896 100644 --- a/include/bounce/cloth/cloth_solver.h +++ b/include/bounce/cloth/cloth_solver.h @@ -34,7 +34,6 @@ struct b3SparseSymMat33; class b3BodyContact; class b3ParticleContact; -class b3TriangleContact; struct b3ClothSolverDef { @@ -43,7 +42,6 @@ struct b3ClothSolverDef u32 forceCapacity; u32 bodyContactCapacity; u32 particleContactCapacity; - u32 triangleContactCapacity; }; struct b3ClothSolverData @@ -79,7 +77,6 @@ public: void Add(b3Force* f); void Add(b3BodyContact* c); void Add(b3ParticleContact* c); - void Add(b3TriangleContact* c); void Solve(float32 dt, const b3Vec3& gravity); private: @@ -113,11 +110,7 @@ private: u32 m_particleContactCapacity; u32 m_particleContactCount; b3ParticleContact** m_particleContacts; - - u32 m_triangleContactCapacity; - u32 m_triangleContactCount; - b3TriangleContact** m_triangleContacts; - + b3ClothSolverData m_solverData; }; diff --git a/include/bounce/cloth/particle.h b/include/bounce/cloth/particle.h index 7db88a4..afa6318 100644 --- a/include/bounce/cloth/particle.h +++ b/include/bounce/cloth/particle.h @@ -123,27 +123,6 @@ struct b3ParticleContactWorldPoint float32 separation; }; -// A contact between a particle and a triangle -class b3TriangleContact -{ -public: - b3TriangleContact() { } - ~b3TriangleContact() { } - - b3Particle* p1; - b3Particle* p2; - b3Particle* p3; - b3Particle* p4; - - bool front; - - // Contact constraint - float32 normalImpulse; - - b3TriangleContact* m_prev; - b3TriangleContact* m_next; -}; - // A cloth particle. class b3Particle { diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 7e36935..7844fdf 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -634,7 +634,6 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity) solverDef.forceCapacity = m_forceList.m_count; solverDef.bodyContactCapacity = m_bodyContactList.m_count; solverDef.particleContactCapacity = m_particleContactList.m_count; - solverDef.triangleContactCapacity = m_triangleContactList.m_count; b3ClothSolver solver(solverDef); @@ -658,11 +657,6 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity) solver.Add(c); } - for (b3TriangleContact* c = m_triangleContactList.m_head; c; c = c->m_next) - { - solver.Add(c); - } - // Solve solver.Solve(dt, gravity); diff --git a/src/bounce/cloth/cloth_contact_solver.cpp b/src/bounce/cloth/cloth_contact_solver.cpp index a01de49..5052198 100644 --- a/src/bounce/cloth/cloth_contact_solver.cpp +++ b/src/bounce/cloth/cloth_contact_solver.cpp @@ -39,18 +39,10 @@ b3ClothContactSolver::b3ClothContactSolver(const b3ClothContactSolverDef& def) m_particleContacts = def.particleContacts; m_particleVelocityConstraints = (b3ClothSolverParticleContactVelocityConstraint*)m_allocator->Allocate(m_particleContactCount * sizeof(b3ClothSolverParticleContactVelocityConstraint)); m_particlePositionConstraints = (b3ClothSolverParticleContactPositionConstraint*)m_allocator->Allocate(m_particleContactCount * sizeof(b3ClothSolverParticleContactPositionConstraint)); - - m_triangleContactCount = def.triangleContactCount; - m_triangleContacts = def.triangleContacts; - m_triangleVelocityConstraints = (b3ClothSolverTriangleContactVelocityConstraint*)m_allocator->Allocate(m_triangleContactCount * sizeof(b3ClothSolverTriangleContactVelocityConstraint)); - m_trianglePositionConstraints = (b3ClothSolverTriangleContactPositionConstraint*)m_allocator->Allocate(m_triangleContactCount * sizeof(b3ClothSolverTriangleContactPositionConstraint)); } b3ClothContactSolver::~b3ClothContactSolver() { - m_allocator->Free(m_trianglePositionConstraints); - m_allocator->Free(m_triangleVelocityConstraints); - m_allocator->Free(m_particlePositionConstraints); m_allocator->Free(m_particleVelocityConstraints); @@ -270,93 +262,6 @@ void b3ClothContactSolver::InitializeParticleContactConstraints() } } -void b3ClothContactSolver::InitializeTriangleContactConstraints() -{ - b3DenseVec3& x = *m_positions; - - for (u32 i = 0; i < m_triangleContactCount; ++i) - { - b3TriangleContact* c = m_triangleContacts[i]; - b3ClothSolverTriangleContactVelocityConstraint* vc = m_triangleVelocityConstraints + i; - b3ClothSolverTriangleContactPositionConstraint* pc = m_trianglePositionConstraints + i; - - vc->indexA = c->p1->m_solverId; - vc->invMassA = c->p1->m_type == e_staticParticle ? 0.0f : c->p1->m_invMass; - - vc->indexB = c->p2->m_solverId; - vc->invMassB = c->p2->m_type == e_staticParticle ? 0.0f : c->p2->m_invMass; - - vc->indexC = c->p3->m_solverId; - vc->invMassC = c->p3->m_type == e_staticParticle ? 0.0f : c->p3->m_invMass; - - vc->indexD = c->p4->m_solverId; - vc->invMassD = c->p4->m_type == e_staticParticle ? 0.0f : c->p4->m_invMass; - - pc->indexA = c->p1->m_solverId; - pc->invMassA = c->p1->m_type == e_staticParticle ? 0.0f : c->p1->m_invMass; - pc->radiusA = c->p1->m_radius; - - pc->indexB = c->p2->m_solverId; - pc->invMassB = c->p2->m_type == e_staticParticle ? 0.0f : c->p2->m_invMass; - - pc->indexC = c->p3->m_solverId; - pc->invMassC = c->p3->m_type == e_staticParticle ? 0.0f : c->p3->m_invMass; - - pc->indexD = c->p4->m_solverId; - pc->invMassD = c->p4->m_type == e_staticParticle ? 0.0f : c->p4->m_invMass; - - pc->triangleRadius = 0.0f; - pc->front = c->front; - - u32 indexA = pc->indexA; - float32 mA = pc->invMassA; - - u32 indexB = pc->indexB; - float32 mB = pc->invMassB; - - u32 indexC = pc->indexC; - float32 mC = pc->invMassC; - - u32 indexD = pc->indexD; - float32 mD = pc->invMassD; - - b3Vec3 xA = x[indexA]; - b3Vec3 xB = x[indexB]; - b3Vec3 xC = x[indexC]; - b3Vec3 xD = x[indexD]; - - b3Vec3 n = b3Cross(xC - xB, xD - xB); - - if (pc->front == false) - { - n = -n; - } - - float32 n_len = n.Normalize(); - - b3Mat33 I; I.SetIdentity(); - - b3Mat33 N = I - b3Outer(n, n); - if (n_len > B3_EPSILON) - { - N = (1.0f / n_len) * N; - } - - b3Vec3 N_n = N * n; - - vc->JA = n; - vc->JC = b3Cross(xD - xB, N_n); - vc->JD = b3Cross(xC - xB, N_n); - vc->JB = b3Cross(xC - xD, N_n) - n; - - // Compute effective mass. - float32 K = mA + mB + mC + mD; - - vc->normalMass = K > 0.0f ? 1.0f / K : 0.0f; - vc->normalImpulse = c->normalImpulse; - } -} - void b3ClothContactSolver::WarmStart() { b3DenseVec3& v = *m_velocities; @@ -430,41 +335,6 @@ void b3ClothContactSolver::WarmStart() v[indexA] = vA; v[indexB] = vB; } - - for (u32 i = 0; i < m_triangleContactCount; ++i) - { - b3ClothSolverTriangleContactVelocityConstraint* vc = m_triangleVelocityConstraints + i; - - u32 indexA = vc->indexA; - u32 indexB = vc->indexB; - u32 indexC = vc->indexC; - u32 indexD = vc->indexD; - - b3Vec3 vA = v[indexA]; - b3Vec3 vB = v[indexB]; - b3Vec3 vC = v[indexC]; - b3Vec3 vD = v[indexD]; - - float32 mA = vc->invMassA; - float32 mB = vc->invMassB; - float32 mC = vc->invMassC; - float32 mD = vc->invMassD; - - b3Vec3 PA = vc->normalImpulse * vc->JA; - b3Vec3 PB = vc->normalImpulse * vc->JB; - b3Vec3 PC = vc->normalImpulse * vc->JC; - b3Vec3 PD = vc->normalImpulse * vc->JD; - - vA += mA * PA; - vB += mB * PB; - vC += mC * PC; - vD += mD * PD; - - v[indexA] = vA; - v[indexB] = vB; - v[indexC] = vC; - v[indexD] = vD; - } } void b3ClothContactSolver::SolveBodyContactVelocityConstraints() @@ -626,59 +496,6 @@ void b3ClothContactSolver::SolveParticleContactVelocityConstraints() } } -void b3ClothContactSolver::SolveTriangleContactVelocityConstraints() -{ - b3DenseVec3& v = *m_velocities; - - for (u32 i = 0; i < m_triangleContactCount; ++i) - { - b3ClothSolverTriangleContactVelocityConstraint* vc = m_triangleVelocityConstraints + i; - - u32 indexA = vc->indexA; - float32 mA = vc->invMassA; - - u32 indexB = vc->indexB; - float32 mB = vc->invMassB; - - u32 indexC = vc->indexC; - float32 mC = vc->invMassC; - - u32 indexD = vc->indexD; - float32 mD = vc->invMassD; - - b3Vec3 vA = v[indexA]; - b3Vec3 vB = v[indexB]; - b3Vec3 vC = v[indexC]; - b3Vec3 vD = v[indexD]; - - b3Vec3 n = vc->JA; - - // Allow some slop and prevent large corrections. - float32 Cdot = b3Dot(n, vA - vB); - - float32 impulse = -vc->normalMass * Cdot; - - float32 oldImpulse = vc->normalImpulse; - vc->normalImpulse = b3Max(vc->normalImpulse + impulse, 0.0f); - impulse = vc->normalImpulse - oldImpulse; - - b3Vec3 PA = impulse * vc->JA; - b3Vec3 PB = impulse * vc->JB; - b3Vec3 PC = impulse * vc->JC; - b3Vec3 PD = impulse * vc->JD; - - vA += mA * PA; - vB += mB * PB; - vC += mC * PC; - vD += mD * PD; - - v[indexA] = vA; - v[indexB] = vB; - v[indexC] = vC; - v[indexD] = vD; - } -} - void b3ClothContactSolver::StoreImpulses() { for (u32 i = 0; i < m_bodyContactCount; ++i) @@ -698,14 +515,6 @@ void b3ClothContactSolver::StoreImpulses() c->normalImpulse = vc->normalImpulse; c->tangentImpulse = vc->tangentImpulse; } - - for (u32 i = 0; i < m_triangleContactCount; ++i) - { - b3TriangleContact* c = m_triangleContacts[i]; - b3ClothSolverTriangleContactVelocityConstraint* vc = m_triangleVelocityConstraints + i; - - c->normalImpulse = vc->normalImpulse; - } } struct b3ClothSolverBodyContactSolverPoint @@ -891,92 +700,5 @@ bool b3ClothContactSolver::SolveParticleContactPositionConstraints() x[indexB] = xB; } - return minSeparation >= -3.0f * B3_LINEAR_SLOP; -} - -bool b3ClothContactSolver::SolveTriangleContactPositionConstraints() -{ - b3DenseVec3& x = *m_positions; - - float32 minSeparation = 0.0f; - - for (u32 i = 0; i < m_triangleContactCount; ++i) - { - b3ClothSolverTriangleContactPositionConstraint* pc = m_trianglePositionConstraints + i; - - u32 indexA = pc->indexA; - float32 mA = pc->invMassA; - float32 radiusA = pc->radiusA; - - u32 indexB = pc->indexB; - float32 mB = pc->invMassB; - - u32 indexC = pc->indexC; - float32 mC = pc->invMassC; - - u32 indexD = pc->indexD; - float32 mD = pc->invMassD; - - float32 triangleRadius = pc->triangleRadius; - - b3Vec3 xA = x[indexA]; - b3Vec3 xB = x[indexB]; - b3Vec3 xC = x[indexC]; - b3Vec3 xD = x[indexD]; - - b3Vec3 n = b3Cross(xC - xB, xD - xB); - if (pc->front == false) - { - n = -n; - } - - float32 n_len = n.Normalize(); - - float32 distance = b3Dot(n, xA - xB); - float32 separation = distance - radiusA - triangleRadius; - - // Update max constraint error. - minSeparation = b3Min(minSeparation, separation); - - // Allow some slop and prevent large corrections. - float32 C = b3Clamp(B3_BAUMGARTE * (separation + B3_LINEAR_SLOP), -B3_MAX_LINEAR_CORRECTION, 0.0f); - - b3Mat33 I; I.SetIdentity(); - - b3Mat33 N = I - b3Outer(n, n); - if (n_len > B3_EPSILON) - { - N = (1.0f / n_len) * N; - } - - b3Vec3 N_n = N * n; - - b3Vec3 JA = n; - b3Vec3 JC = b3Cross(xD - xB, N_n); - b3Vec3 JD = b3Cross(xC - xB, N_n); - b3Vec3 JB = b3Cross(xC - xD, N_n) - n; - - // Compute effective mass. - float32 K = mA + mB + mC + mD; - - // Compute normal impulse. - float32 impulse = K > 0.0f ? -C / K : 0.0f; - - b3Vec3 PA = impulse * JA; - b3Vec3 PB = impulse * JB; - b3Vec3 PC = impulse * JC; - b3Vec3 PD = impulse * JD; - - xA += mA * PA; - xB += mB * PB; - xC += mC * PC; - xD += mD * PD; - - x[indexA] = xA; - x[indexB] = xB; - x[indexC] = xC; - x[indexD] = xD; - } - return minSeparation >= -3.0f * B3_LINEAR_SLOP; } \ No newline at end of file diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index 95c2c2f..737ffd6 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -61,15 +61,10 @@ b3ClothSolver::b3ClothSolver(const b3ClothSolverDef& def) m_particleContactCapacity = def.particleContactCapacity; m_particleContactCount = 0; m_particleContacts = (b3ParticleContact**)m_allocator->Allocate(m_particleContactCapacity * sizeof(b3ParticleContact*));; - - m_triangleContactCapacity = def.triangleContactCapacity; - m_triangleContactCount = 0; - m_triangleContacts = (b3TriangleContact**)m_allocator->Allocate(m_triangleContactCapacity * sizeof(b3TriangleContact*));; } b3ClothSolver::~b3ClothSolver() { - m_allocator->Free(m_triangleContacts); m_allocator->Free(m_particleContacts); m_allocator->Free(m_bodyContacts); @@ -99,11 +94,6 @@ void b3ClothSolver::Add(b3ParticleContact* c) m_particleContacts[m_particleContactCount++] = c; } -void b3ClothSolver::Add(b3TriangleContact* c) -{ - m_triangleContacts[m_triangleContactCount++] = c; -} - void b3ClothSolver::ApplyForces() { for (u32 i = 0; i < m_forceCount; ++i) @@ -173,7 +163,6 @@ void b3ClothSolver::ApplyConstraints() } } -#if 0 for (u32 i = 0; i < m_bodyContactCount; ++i) { b3BodyContact* bc = m_bodyContacts[i]; @@ -200,7 +189,6 @@ void b3ClothSolver::ApplyConstraints() ac->p = bcwp.normal; ac->z.SetZero(); } -#endif for (u32 i = 0; i < m_constraintCount; ++i) { @@ -262,7 +250,6 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) sx0[i] = p->m_x; } -#if 0 for (u32 i = 0; i < m_bodyContactCount; ++i) { b3BodyContact* bc = m_bodyContacts[i]; @@ -281,7 +268,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) sy[p1->m_solverId] += bcwp.separation * bcwp.normal; } -#endif + // Apply internal forces ApplyForces(); @@ -332,15 +319,12 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) contactSolverDef.bodyContacts = m_bodyContacts; contactSolverDef.particleContactCount = m_particleContactCount; contactSolverDef.particleContacts = m_particleContacts; - contactSolverDef.triangleContactCount = m_triangleContactCount; - contactSolverDef.triangleContacts = m_triangleContacts; b3ClothContactSolver contactSolver(contactSolverDef); { contactSolver.InitializeBodyContactConstraints(); contactSolver.InitializeParticleContactConstraints(); - contactSolver.InitializeTriangleContactConstraints(); } { @@ -355,7 +339,6 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) { contactSolver.SolveBodyContactVelocityConstraints(); contactSolver.SolveParticleContactVelocityConstraints(); - contactSolver.SolveTriangleContactVelocityConstraints(); } } @@ -375,9 +358,8 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) { bool bodyContactsSolved = contactSolver.SolveBodyContactPositionConstraints(); bool particleContactsSolved = contactSolver.SolveParticleContactPositionConstraints(); - bool triangleContactsSolved = contactSolver.SolveTriangleContactPositionConstraints(); - if (bodyContactsSolved && particleContactsSolved && triangleContactsSolved) + if (bodyContactsSolved && particleContactsSolved) { positionSolved = true; break; From 397a8eeee3184d1145b3418956918fc1f13896a5 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 2 Apr 2019 12:52:13 -0300 Subject: [PATCH 038/198] remove dead structure --- include/bounce/cloth/cloth_contact_solver.h | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/include/bounce/cloth/cloth_contact_solver.h b/include/bounce/cloth/cloth_contact_solver.h index 30b1e80..011d3fb 100644 --- a/include/bounce/cloth/cloth_contact_solver.h +++ b/include/bounce/cloth/cloth_contact_solver.h @@ -114,27 +114,6 @@ struct b3ClothSolverParticleContactPositionConstraint float32 radiusB; }; -struct b3ClothSolverTriangleContactVelocityConstraint -{ - u32 indexA; - float32 invMassA; - - u32 indexB; - float32 invMassB; - u32 indexC; - float32 invMassC; - u32 indexD; - float32 invMassD; - - b3Vec3 JA; - b3Vec3 JB; - b3Vec3 JC; - b3Vec3 JD; - - float32 normalMass; - float32 normalImpulse; -}; - struct b3ClothContactSolverDef { b3StackAllocator* allocator; From b4d33305797d7433f8413becaf7c97cc0fd50fd2 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 3 Apr 2019 11:05:21 -0300 Subject: [PATCH 039/198] Caching impulses for b3BodyContact has been added. Also added some code for completeness. --- include/bounce/cloth/cloth.h | 6 -- include/bounce/cloth/particle.h | 18 +++- src/bounce/cloth/cloth.cpp | 137 ++++++++++++++++++++++++------ src/bounce/cloth/cloth_solver.cpp | 92 +++++++++++--------- src/bounce/cloth/particle.cpp | 2 +- 5 files changed, 176 insertions(+), 79 deletions(-) diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index 3d5535f..e02e6a1 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -183,9 +183,6 @@ private: // Pool of particles b3BlockPool m_particleBlocks; - // Pool of body contacts - b3BlockPool m_bodyContactBlocks; - // Pool of particle contacts b3BlockPool m_particleContactBlocks; @@ -195,9 +192,6 @@ private: // List of forces b3List2 m_forceList; - // List of particle contacts - b3List2 m_bodyContactList; - // List of particle contacts b3List2 m_particleContactList; }; diff --git a/include/bounce/cloth/particle.h b/include/bounce/cloth/particle.h index afa6318..abe1b20 100644 --- a/include/bounce/cloth/particle.h +++ b/include/bounce/cloth/particle.h @@ -71,6 +71,16 @@ public: b3Particle* p1; b3Shape* s2; + // Contact constraint + float32 s; + b3Vec3 p; + b3Vec3 n; + float32 fn0; + float32 fn; + float32 ft1, ft2; + bool t1Active; + bool t2Active; + // Contact constraint b3Vec3 localPoint1; b3Vec3 localPoint2; @@ -80,8 +90,7 @@ public: b3Vec3 t1, t2; b3Vec2 tangentImpulse; - b3BodyContact* m_prev; - b3BodyContact* m_next; + bool active; }; struct b3BodyContactWorldPoint @@ -198,7 +207,7 @@ private: // Applied external force b3Vec3 m_force; - // Applied external translation + // Applied translation b3Vec3 m_translation; // Mass @@ -230,6 +239,9 @@ private: // b3Cloth* m_cloth; + // + b3BodyContact m_bodyContact; + // b3Particle* m_prev; diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 7844fdf..30cfe2f 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -148,7 +148,6 @@ static u32 b3FindSharedEdges(b3SharedEdge* sharedEdges, const b3ClothMesh* m) b3Cloth::b3Cloth(const b3ClothDef& def) : m_particleBlocks(sizeof(b3Particle)), - m_bodyContactBlocks(sizeof(b3BodyContact)), m_particleContactBlocks(sizeof(b3ParticleContact)) { B3_ASSERT(def.mesh); @@ -484,17 +483,6 @@ void b3Cloth::UpdateBodyContacts() B3_PROFILE("Cloth Update Body Contacts"); - // Clear buffer - b3BodyContact* c = m_bodyContactList.m_head; - while (c) - { - b3BodyContact* c0 = c; - c = c->m_next; - m_bodyContactList.Remove(c0); - c0->~b3BodyContact(); - m_bodyContactBlocks.Free(c0); - } - // Create contacts for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) { @@ -539,6 +527,7 @@ void b3Cloth::UpdateBodyContacts() if (bestShape == nullptr) { + p->m_bodyContact.active = false; continue; } @@ -549,17 +538,108 @@ void b3Cloth::UpdateBodyContacts() b3Vec3 point = bestPoint; b3Vec3 normal = -bestNormal; - b3BodyContact* c = (b3BodyContact*)m_bodyContactBlocks.Allocate(); + b3BodyContact* c = &p->m_bodyContact; + + b3BodyContact c0 = *c; + + c->active = true; c->p1 = p; c->s2 = shape; + c->s = separation; + c->p = point; + c->n = normal; + c->fn0 = 0.0f; + c->fn = 0.0f; + c->ft1 = 0.0f; + c->ft2 = 0.0f; + c->t1Active = false; + c->t2Active = false; c->localPoint1.SetZero(); c->localPoint2 = body->GetLocalPoint(point); c->t1 = b3Perp(normal); c->t2 = b3Cross(c->t1, normal); c->normalImpulse = 0.0f; c->tangentImpulse.SetZero(); + +#if 0 + // Apply position correction + b3Vec3 dx = separation * normal; + p->m_translation += dx; +#endif - m_bodyContactList.PushFront(c); + // Update contact state + if (c0.active == true) + { + c->fn0 = c0.fn0; + c->fn = c0.fn; + c->ft1 = c0.ft1; + c->ft2 = c0.ft2; + + c->normalImpulse = c0.normalImpulse; + c->tangentImpulse = c0.tangentImpulse; +#if 0 + const float32 kForceTol = 0.0f; + + // Allow the contact to release when the constraint force + // switches from a repulsive force to an attractive one. + // if (c0.fn0 < kForceTol && c0.fn > kForceTol) + if (c0.fn > kForceTol) + { + // Contact force is attractive. + c->active = false; + continue; + } +#endif + } +#if 0 + // A friction force requires an associated normal force. + if (c0.active == false) + { + continue; + } + b3Vec3 v1; v1.SetZero(); + b3Vec3 v2 = p->m_velocity; + b3Vec3 dv = v2 - v1; + + const float32 kVelTol = B3_EPSILON * B3_EPSILON; + + // Lock particle on surface + float32 dvt1 = b3Dot(dv, c->t1); + if (dvt1 * dvt1 < kVelTol * kVelTol) + { + c->t1Active = true; + } + + float32 dvt2 = b3Dot(dv, c->t2); + if (dvt2 * dvt2 < kVelTol * kVelTol) + { + c->t2Active = true; + } + + // Unlock particle off surface + float32 normalForce = c->fn; + + float32 friction = shape->GetFriction(); + float32 maxFrictionForce = friction * normalForce; + + if (c0.t1Active == true) + { + float32 tangentForce1 = c0.ft1; + if (tangentForce1 * tangentForce1 > maxFrictionForce * maxFrictionForce) + { + c->t1Active = false; + } + } + + if (c0.t2Active == true) + { + float32 tangentForce2 = c0.ft2; + if (tangentForce2 * tangentForce2 > maxFrictionForce* maxFrictionForce) + { + c->t2Active = false; + } + } +#endif } } @@ -632,7 +712,7 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity) solverDef.stack = &m_stackAllocator; solverDef.particleCapacity = m_particleList.m_count; solverDef.forceCapacity = m_forceList.m_count; - solverDef.bodyContactCapacity = m_bodyContactList.m_count; + solverDef.bodyContactCapacity = m_particleList.m_count; solverDef.particleContactCapacity = m_particleContactList.m_count; b3ClothSolver solver(solverDef); @@ -646,26 +726,22 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity) { solver.Add(f); } - - for (b3BodyContact* c = m_bodyContactList.m_head; c; c = c->m_next) + + for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) { - solver.Add(c); + if (p->m_bodyContact.active) + { + solver.Add(&p->m_bodyContact); + } } - + for (b3ParticleContact* c = m_particleContactList.m_head; c; c = c->m_next) { solver.Add(c); } - + // Solve solver.Solve(dt, gravity); - - // Clear external applied forces and translations - for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) - { - p->m_force.SetZero(); - p->m_translation.SetZero(); - } } void b3Cloth::UpdateContacts() @@ -691,6 +767,13 @@ void b3Cloth::Step(float32 dt) { Solve(dt, m_gravity); } + + // Clear external applied forces and translations + for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) + { + p->m_force.SetZero(); + p->m_translation.SetZero(); + } } void b3Cloth::Draw() const diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index 737ffd6..cc46370 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -149,7 +149,7 @@ void b3ClothSolver::ApplyConstraints() S.SetIdentity(); z.SetZero(); - + for (u32 i = 0; i < m_particleCount; ++i) { b3Particle* p = m_particles[i]; @@ -162,33 +162,42 @@ void b3ClothSolver::ApplyConstraints() ac->z.SetZero(); } } - +#if 0 for (u32 i = 0; i < m_bodyContactCount; ++i) { b3BodyContact* bc = m_bodyContacts[i]; - + b3Particle* p1 = bc->p1; - + B3_ASSERT(p1->m_type == e_dynamicParticle); - b3Transform xf1; - xf1.position = x[p1->m_solverId]; - xf1.rotation.SetIdentity(); - - b3Shape* s2 = bc->s2; - b3Body* b2 = s2->GetBody(); - b3Transform xf2 = b2->GetTransform(); - - b3BodyContactWorldPoint bcwp; - bcwp.Initialize(bc, p1->m_radius, xf1, s2->m_radius, xf2); - b3AccelerationConstraint* ac = m_constraints + m_constraintCount; ++m_constraintCount; ac->i1 = p1->m_solverId; ac->ndof = 2; - ac->p = bcwp.normal; + ac->p = bc->n; ac->z.SetZero(); + + if (bc->t1Active && bc->t2Active) + { + ac->ndof = 0; + } + else + { + if (bc->t1Active) + { + ac->ndof = 1; + ac->q = bc->t1; + } + + if (bc->t2Active) + { + ac->ndof = 1; + ac->q = bc->t2; + } + } } +#endif for (u32 i = 0; i < m_constraintCount; ++i) { @@ -210,7 +219,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) m_solverData.x = &sx; m_solverData.v = &sv; m_solverData.f = &sf; - + for (u32 i = 0; i < m_particleCount; ++i) { b3Particle* p = m_particles[i]; @@ -249,25 +258,6 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) sy[i] = p->m_translation; sx0[i] = p->m_x; } - - for (u32 i = 0; i < m_bodyContactCount; ++i) - { - b3BodyContact* bc = m_bodyContacts[i]; - - b3Particle* p1 = bc->p1; - b3Transform xf1; - xf1.position = sx[p1->m_solverId]; - xf1.rotation.SetIdentity(); - - b3Shape* s2 = bc->s2; - b3Body* b2 = s2->GetBody(); - b3Transform xf2 = b2->GetTransform(); - - b3BodyContactWorldPoint bcwp; - bcwp.Initialize(bc, p1->m_radius, xf1, s2->m_radius, xf2); - - sy[p1->m_solverId] += bcwp.separation * bcwp.normal; - } // Apply internal forces ApplyForces(); @@ -290,16 +280,33 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) // b b3DenseVec3 b = h * (sf + h * (dfdx * sv) + dfdx * sy); - + // x b3DenseVec3 x(m_particleCount); u32 iterations = 0; Solve(x, iterations, A, b, S, z, sx0); b3_clothSolverIterations = iterations; +#if 0 + // Copy constraint forces to the contacts + b3DenseVec3 f = A * x - b; + for (u32 i = 0; i < m_bodyContactCount; ++i) + { + b3BodyContact* c = m_bodyContacts[i]; + + b3Vec3 cf = f[i]; + + c->fn0 = c->fn; + c->fn = b3Dot(cf, c->n); + c->ft1 = b3Dot(cf, c->t1); + c->ft2 = b3Dot(cf, c->t2); + } +#endif + // sv = sv + x; sx = sx + sy; + // Copy state buffers back to the particle for (u32 i = 0; i < m_particleCount; ++i) { b3Particle* p = m_particles[i]; @@ -309,7 +316,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) p->m_velocity = sv[i]; } } - +#if 1 // Solve constraints b3ClothContactSolverDef contactSolverDef; contactSolverDef.allocator = m_allocator; @@ -341,10 +348,11 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) contactSolver.SolveParticleContactVelocityConstraints(); } } - + { contactSolver.StoreImpulses(); } +#endif // Integrate positions sx = sx + h * sv; @@ -358,7 +366,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) { bool bodyContactsSolved = contactSolver.SolveBodyContactPositionConstraints(); bool particleContactsSolved = contactSolver.SolveParticleContactPositionConstraints(); - + if (bodyContactsSolved && particleContactsSolved) { positionSolved = true; @@ -366,7 +374,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) } } } - +#if 0 // Synchronize bodies for (u32 i = 0; i < m_bodyContactCount; ++i) { @@ -378,7 +386,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) body->SynchronizeShapes(); } - +#endif // Copy state buffers back to the particles for (u32 i = 0; i < m_particleCount; ++i) { diff --git a/src/bounce/cloth/particle.cpp b/src/bounce/cloth/particle.cpp index 169e5bc..7dde875 100644 --- a/src/bounce/cloth/particle.cpp +++ b/src/bounce/cloth/particle.cpp @@ -73,7 +73,6 @@ b3Particle::b3Particle(const b3ParticleDef& def, b3Cloth* cloth) { m_cloth = cloth; m_type = def.type; - m_position = def.position; m_velocity = def.velocity; m_force = def.force; @@ -85,6 +84,7 @@ b3Particle::b3Particle(const b3ParticleDef& def, b3Cloth* cloth) m_userData = nullptr; m_x.SetZero(); m_vertex = ~0; + m_bodyContact.active = false; } b3Particle::~b3Particle() From dbc5b189c777e548488087f5312ee668c7b11609 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 3 Apr 2019 14:53:10 -0300 Subject: [PATCH 040/198] Add sewing lines to pattern mesh for completeness. --- src/bounce/cloth/garment/garment_mesh.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/bounce/cloth/garment/garment_mesh.cpp b/src/bounce/cloth/garment/garment_mesh.cpp index 1d54f89..3d107ba 100644 --- a/src/bounce/cloth/garment/garment_mesh.cpp +++ b/src/bounce/cloth/garment/garment_mesh.cpp @@ -186,4 +186,21 @@ void b3GarmentMesh::Set(b3Garment* g, float32 desiredArea) { b3Set(meshes + i, desiredArea, garment->patterns[i]); } + + // It's okay to do run the following code because + // the first vertices of a sewing pattern mesh are the vertices of its + // corresponding sewing pattern. + sewingCount = garment->sewingCount; + sewingLines = (b3GarmentMeshSewingLine*)b3Alloc(garment->sewingCount * sizeof(b3GarmentMeshSewingLine)); + for (u32 i = 0; i < garment->sewingCount; ++i) + { + b3SewingLine* sewingLine = garment->sewingLines + i; + b3GarmentMeshSewingLine* line = sewingLines + i; + + line->s1 = sewingLine->p1; + line->v1 = sewingLine->v1; + + line->s2 = sewingLine->p2; + line->v2 = sewingLine->v2; + } } \ No newline at end of file From 62838a4887aa00ea4ee9a17f9a631dfb332c568f Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 3 Apr 2019 15:33:56 -0300 Subject: [PATCH 041/198] Assert some code --- src/bounce/cloth/garment/garment_mesh.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/bounce/cloth/garment/garment_mesh.cpp b/src/bounce/cloth/garment/garment_mesh.cpp index 3d107ba..b42be19 100644 --- a/src/bounce/cloth/garment/garment_mesh.cpp +++ b/src/bounce/cloth/garment/garment_mesh.cpp @@ -65,7 +65,12 @@ static void b3Set(b3SewingPatternMesh* mesh, float32 desiredArea, const b3Sewing in.pointlist[i] = (REAL)fp[i]; } in.pointattributelist = NULL; - in.pointmarkerlist = NULL; + in.pointmarkerlist = (int*)malloc(pattern->vertexCount * sizeof(int)); + for (u32 i = 0; i < pattern->vertexCount; ++i) + { + in.pointmarkerlist[i] = 10 + int(i); + } + in.numberofpoints = pattern->vertexCount; in.numberofpointattributes = 0; @@ -132,6 +137,18 @@ static void b3Set(b3SewingPatternMesh* mesh, float32 desiredArea, const b3Sewing // r - read triangles triangulate("zpcra", &mid, &out, NULL); + // The first vertices of the output structure must be the vertices of the input structure. + for (int i = 0; i < in.numberofpoints; ++i) + { + B3_ASSERT(in.pointmarkerlist[i] == out.pointmarkerlist[i]); + } + + // Note: comparing doubles + for (int i = 0; i < in.numberofpoints * 2; ++i) + { + B3_ASSERT(in.pointlist[i] == out.pointlist[i]); + } + // Convert the output structure // The mesh must be empty @@ -159,6 +176,7 @@ static void b3Set(b3SewingPatternMesh* mesh, float32 desiredArea, const b3Sewing // Free the input structure free(in.pointlist); + free(in.pointmarkerlist); // Free the middle structure free(mid.pointlist); From 7c15a8eaf80be12b9e2ac840b347f9a5e2e16cc8 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 3 Apr 2019 19:02:21 -0300 Subject: [PATCH 042/198] Fit windows to content --- examples/testbed/framework/draw.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/testbed/framework/draw.cpp b/examples/testbed/framework/draw.cpp index 1ed646c..3640a76 100644 --- a/examples/testbed/framework/draw.cpp +++ b/examples/testbed/framework/draw.cpp @@ -480,13 +480,17 @@ void Draw::DrawString(const b3Color& color, const b3Vec3& pw, const char* text, va_list args; va_start(args, text); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::SetNextWindowBgAlpha(0.0f); ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f)); - ImGui::SetNextWindowSize(ImVec2(g_camera->m_width, g_camera->m_height)); + ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f)); ImGui::Begin("Superlay", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar); ImGui::SetCursorPos(ImVec2(ps.x, ps.y)); ImGui::TextColoredV(ImVec4(color.r, color.g, color.b, color.a), text, args); ImGui::End(); + ImGui::PopStyleVar(); + va_end(args); } @@ -495,13 +499,15 @@ void Draw::DrawString(const b3Color& color, const char* text, ...) va_list args; va_start(args, text); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::SetNextWindowBgAlpha(0.0f); ImGui::SetNextWindowPos(ImVec2(0.0f, 40.0f)); - ImGui::SetNextWindowSize(ImVec2(g_camera->m_width, g_camera->m_height)); + ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f)); ImGui::Begin("Overlay", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar); ImGui::TextColoredV(ImVec4(color.r, color.g, color.b, color.a), text, args); ImGui::End(); - + ImGui::PopStyleVar(); + va_end(args); } From 4407e31d8aee33dabf9d55e90605fca729e9b175 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 3 Apr 2019 19:12:17 -0300 Subject: [PATCH 043/198] Draw the profiler tree. --- examples/testbed/framework/main.cpp | 5 +++ examples/testbed/framework/profiler.h | 3 ++ examples/testbed/framework/view.cpp | 49 +++++++++++++++++++++++++ examples/testbed/framework/view.h | 1 + examples/testbed/framework/view_model.h | 2 + 5 files changed, 60 insertions(+) diff --git a/examples/testbed/framework/main.cpp b/examples/testbed/framework/main.cpp index 4c68c4e..03dcebd 100644 --- a/examples/testbed/framework/main.cpp +++ b/examples/testbed/framework/main.cpp @@ -124,6 +124,11 @@ static void Run() g_profiler->EndScope(); + if (g_settings->drawProfileTree) + { + g_view->InterfaceProfileTree(); + } + g_profilerRecorder->BuildRecords(); if (g_settings->drawProfile) diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h index d87ed3e..7c27dac 100644 --- a/examples/testbed/framework/profiler.h +++ b/examples/testbed/framework/profiler.h @@ -58,6 +58,9 @@ public: // End the top scope. void EndScope(); + + // Get the root profiler node. + ProfilerNode* GetRoot() { return m_root; } private: friend class ProfilerRecorder; diff --git a/examples/testbed/framework/view.cpp b/examples/testbed/framework/view.cpp index d2d68dc..adaea83 100644 --- a/examples/testbed/framework/view.cpp +++ b/examples/testbed/framework/view.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #if defined (U_OPENGL_2) @@ -218,6 +219,7 @@ void View::Interface() if (ImGui::BeginMenu("View")) { ImGui::MenuItem("Profile", "", &settings.drawProfile); + ImGui::MenuItem("Profile Tree", "", &settings.drawProfileTree); ImGui::MenuItem("Statistics", "", &settings.drawStats); ImGui::Separator(); @@ -400,6 +402,53 @@ void View::Interface() ImGui::End(); } +static void TreeNode(ProfilerNode* node, u32& index) +{ + ImGui::PushID(index); + ++index; + + if (ImGui::TreeNode(node->name)) + { + float64 elapsedTime = node->t1 - node->t0; + ImGui::Text("%.4f [ms]", elapsedTime); + + for (u32 i = 0; i < node->children.Count(); ++i) + { + TreeNode(node->children[i], index); + } + ImGui::TreePop(); + } + + ImGui::PopID(); +} + +void View::InterfaceProfileTree() +{ + ImGui::Begin("Overlay", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar); + ImVec2 ws = ImGui::GetWindowSize(); + ImVec2 wp = ImGui::GetWindowPos(); + ImGui::End(); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + + ImGui::SetNextWindowBgAlpha(0.0f); + ImGui::SetNextWindowPos(ImVec2(0.0f, wp.y + ws.y)); + ImGui::SetNextWindowSize(ImVec2(g_camera->m_width - 250.0f, 0.0f)); + + ImGui::Begin("##ProfileTree", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize); + + ProfilerNode* root = g_profiler->GetRoot(); + if (root) + { + u32 index = 0; + TreeNode(root, index); + } + + ImGui::End(); + + ImGui::PopStyleVar(); +} + void View::EndInterface() { ImGui::PopStyleVar(); diff --git a/examples/testbed/framework/view.h b/examples/testbed/framework/view.h index edb0be1..2650e81 100644 --- a/examples/testbed/framework/view.h +++ b/examples/testbed/framework/view.h @@ -41,6 +41,7 @@ public: void BeginInterface(); void Interface(); + void InterfaceProfileTree(); void EndInterface(); private: friend class ViewModel; diff --git a/examples/testbed/framework/view_model.h b/examples/testbed/framework/view_model.h index 8b10a0c..2d133ef 100644 --- a/examples/testbed/framework/view_model.h +++ b/examples/testbed/framework/view_model.h @@ -33,6 +33,7 @@ struct Settings drawTriangles = true; drawGrid = true; drawProfile = false; + drawProfileTree = false; drawStats = false; } @@ -43,6 +44,7 @@ struct Settings bool drawTriangles; bool drawGrid; bool drawProfile; + bool drawProfileTree; bool drawStats; }; From 00ae0296e7fda30f00028d49ae08651c2c8f51b5 Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 4 Apr 2019 10:12:56 -0300 Subject: [PATCH 044/198] Applied a bugfix. --- src/bounce/cloth/spring_force.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bounce/cloth/spring_force.cpp b/src/bounce/cloth/spring_force.cpp index 3e68424..723b1ac 100644 --- a/src/bounce/cloth/spring_force.cpp +++ b/src/bounce/cloth/spring_force.cpp @@ -77,7 +77,7 @@ void b3SpringForce::Apply(const b3ClothSolverData* data) float32 L = b3Length(dx); - if (L >= m_L0) + if (L > m_L0) { // Apply tension b3Vec3 n = dx / L; From e32c2e61ee45dd43194d3fdaead6900ddd6748af Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 4 Apr 2019 10:35:13 -0300 Subject: [PATCH 045/198] Update shirt.h --- examples/testbed/tests/shirt.h | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/examples/testbed/tests/shirt.h b/examples/testbed/tests/shirt.h index 5e3e0c9..f081326 100644 --- a/examples/testbed/tests/shirt.h +++ b/examples/testbed/tests/shirt.h @@ -30,6 +30,14 @@ public: // Create 3D mesh m_shirtClothMesh.Set(&m_shirtGarmentMesh); + // Create cloth + b3ClothDef def; + def.mesh = &m_shirtClothMesh; + def.density = 0.2f; + def.structural = 1000.0f; + + m_cloth = new b3Cloth(def); + // Perform fitting for (u32 i = 0; i < 3; ++i) { @@ -37,7 +45,12 @@ public: for (u32 j = 0; j < front->vertexCount; ++j) { u32 v = front->startVertex + j; - m_shirtClothMesh.vertices[v].z = -1.0f; + b3Particle* p = m_cloth->GetVertexParticle(v); + + b3Vec3 x = p->GetPosition(); + x.z = -1.0f; + + p->SetPosition(x); } } @@ -47,18 +60,15 @@ public: for (u32 j = 0; j < back->vertexCount; ++j) { u32 v = back->startVertex + j; - m_shirtClothMesh.vertices[v].z = 1.0f; + b3Particle* p = m_cloth->GetVertexParticle(v); + + b3Vec3 x = p->GetPosition(); + x.z = 1.0f; + + p->SetPosition(x); } } - // Create cloth - b3ClothDef def; - def.mesh = &m_shirtClothMesh; - def.density = 0.2f; - def.structural = 10000.0f; - - m_cloth = new b3Cloth(def); - m_cloth->SetGravity(b3Vec3(0.0f, -9.8f, 0.0f)); m_cloth->SetWorld(&m_world); From d8637415873705b6669cafa3ffcbbc69e25ebbea Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 4 Apr 2019 14:00:40 -0300 Subject: [PATCH 046/198] Add more code for completeness --- include/bounce/cloth/particle.h | 1 + src/bounce/cloth/cloth.cpp | 55 ++++++++++++++++++------------- src/bounce/cloth/cloth_solver.cpp | 8 ++++- 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/include/bounce/cloth/particle.h b/include/bounce/cloth/particle.h index abe1b20..91480c2 100644 --- a/include/bounce/cloth/particle.h +++ b/include/bounce/cloth/particle.h @@ -78,6 +78,7 @@ public: float32 fn0; float32 fn; float32 ft1, ft2; + bool nActive; bool t1Active; bool t2Active; diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 30cfe2f..5d85cc8 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -552,6 +552,7 @@ void b3Cloth::UpdateBodyContacts() c->fn = 0.0f; c->ft1 = 0.0f; c->ft2 = 0.0f; + c->nActive = true; c->t1Active = false; c->t2Active = false; c->localPoint1.SetZero(); @@ -560,48 +561,56 @@ void b3Cloth::UpdateBodyContacts() c->t2 = b3Cross(c->t1, normal); c->normalImpulse = 0.0f; c->tangentImpulse.SetZero(); - + #if 0 // Apply position correction - b3Vec3 dx = separation * normal; - p->m_translation += dx; + p->m_translation += separation * normal; #endif // Update contact state if (c0.active == true) { - c->fn0 = c0.fn0; - c->fn = c0.fn; - c->ft1 = c0.ft1; - c->ft2 = c0.ft2; - - c->normalImpulse = c0.normalImpulse; - c->tangentImpulse = c0.tangentImpulse; -#if 0 - const float32 kForceTol = 0.0f; - - // Allow the contact to release when the constraint force - // switches from a repulsive force to an attractive one. - // if (c0.fn0 < kForceTol && c0.fn > kForceTol) - if (c0.fn > kForceTol) + if (c0.nActive == true) { - // Contact force is attractive. - c->active = false; - continue; - } + c->fn0 = c0.fn0; + c->fn = c0.fn; + c->ft1 = c0.ft1; + c->ft2 = c0.ft2; + + c->normalImpulse = c0.normalImpulse; + c->tangentImpulse = c0.tangentImpulse; +#if 1 + const float32 kForceTol = 0.0f; + + // Allow the contact to release when the constraint force + // switches from a repulsive force to an attractive one. + // if (c0.fn0 < kForceTol && c0.fn > kForceTol) + if (c0.fn > kForceTol) + { + // Contact force is attractive. + c->nActive = false; + continue; + } #endif + } } #if 0 - // A friction force requires an associated normal force. if (c0.active == false) { continue; } + + // A friction force requires an associated normal force. + if (c0.nActive == false) + { + continue; + } + b3Vec3 v1; v1.SetZero(); b3Vec3 v2 = p->m_velocity; b3Vec3 dv = v2 - v1; - const float32 kVelTol = B3_EPSILON * B3_EPSILON; + const float32 kVelTol = 2.0f * B3_EPSILON; // Lock particle on surface float32 dvt1 = b3Dot(dv, c->t1); diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index cc46370..46a7da7 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -166,6 +166,11 @@ void b3ClothSolver::ApplyConstraints() for (u32 i = 0; i < m_bodyContactCount; ++i) { b3BodyContact* bc = m_bodyContacts[i]; + + if (bc->nActive == false) + { + continue; + } b3Particle* p1 = bc->p1; @@ -357,6 +362,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) // Integrate positions sx = sx + h * sv; +#if 1 // Solve position constraints { const u32 kPositionIterations = 2; @@ -374,7 +380,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) } } } -#if 0 + // Synchronize bodies for (u32 i = 0; i < m_bodyContactCount; ++i) { From ff535f9f7b3163eb7bdef7d174931d3bf6a3c2fa Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 4 Apr 2019 14:02:37 -0300 Subject: [PATCH 047/198] Unset if --- src/bounce/cloth/cloth.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 5d85cc8..baffc6b 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -579,7 +579,7 @@ void b3Cloth::UpdateBodyContacts() c->normalImpulse = c0.normalImpulse; c->tangentImpulse = c0.tangentImpulse; -#if 1 +#if 0 const float32 kForceTol = 0.0f; // Allow the contact to release when the constraint force From 1ef6d46d33646957c36f406f898b62814f8d86c0 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 6 Apr 2019 18:06:20 -0300 Subject: [PATCH 048/198] Add a statistical profiler. Also applied a bugfix b3Profiler. - This is a compact hierarchical profiler which also stores node statistics - Might need to use a hash-table to lookup statistic for node because each frame trees are build --- examples/testbed/framework/main.cpp | 23 +- examples/testbed/framework/model.cpp | 4 +- examples/testbed/framework/model.h | 4 +- examples/testbed/framework/profiler.cpp | 11 +- examples/testbed/framework/profiler.h | 2 - .../testbed/framework/profiler_recorder.cpp | 120 ----------- .../testbed/framework/profiler_recorder.h | 58 ----- examples/testbed/framework/profiler_st.cpp | 199 ++++++++++++++++++ examples/testbed/framework/profiler_st.h | 94 +++++++++ examples/testbed/framework/test.cpp | 3 + examples/testbed/framework/view.cpp | 63 +++++- examples/testbed/framework/view.h | 1 + examples/testbed/framework/view_model.h | 4 +- 13 files changed, 381 insertions(+), 205 deletions(-) delete mode 100644 examples/testbed/framework/profiler_recorder.cpp delete mode 100644 examples/testbed/framework/profiler_recorder.h create mode 100644 examples/testbed/framework/profiler_st.cpp create mode 100644 examples/testbed/framework/profiler_st.h diff --git a/examples/testbed/framework/main.cpp b/examples/testbed/framework/main.cpp index 03dcebd..3917fd1 100644 --- a/examples/testbed/framework/main.cpp +++ b/examples/testbed/framework/main.cpp @@ -104,9 +104,13 @@ static void Run() while (glfwWindowShouldClose(g_window) == 0) { g_profiler->Begin(); + + g_profilerSt->Begin(); g_profiler->BeginScope("Frame"); - + + g_profilerSt->BeginScope("Frame"); + g_view->BeginInterface(); if (g_model->IsPaused()) @@ -122,6 +126,8 @@ static void Run() g_model->Update(); + g_profilerSt->EndScope(); + g_profiler->EndScope(); if (g_settings->drawProfileTree) @@ -129,20 +135,13 @@ static void Run() g_view->InterfaceProfileTree(); } - g_profilerRecorder->BuildRecords(); - - if (g_settings->drawProfile) + if (g_settings->drawProfileTreeStats) { - b3StackArray records; - g_profilerRecorder->BuildSortedRecords(records); - - for (u32 i = 0; i < records.Count(); ++i) - { - ProfilerRecord* r = records[i]; - g_draw->DrawString(b3Color_white, "%s %.4f (min = %.4f) (max = %.4f) (calls = %d) [ms]", r->name, r->elapsed, r->minElapsed, r->maxElapsed, r->callCount); - } + g_view->InterfaceProfileTreeStats(); } + g_profilerSt->End(); + g_profiler->End(); g_view->EndInterface(); diff --git a/examples/testbed/framework/model.cpp b/examples/testbed/framework/model.cpp index 325bfa0..d15067b 100644 --- a/examples/testbed/framework/model.cpp +++ b/examples/testbed/framework/model.cpp @@ -26,7 +26,7 @@ Model::Model() g_draw = &m_draw; g_camera = &m_camera; g_profiler = &m_profiler; - g_profilerRecorder = &m_profilerRecorder; + g_profilerSt = &m_profilerSt; #if (PROFILE_JSON == 1) g_profilerListener = &m_jsonListener; @@ -56,7 +56,7 @@ Model::~Model() g_draw = nullptr; g_camera = nullptr; g_profiler = nullptr; - g_profilerRecorder = nullptr; + g_profilerSt = nullptr; #if (PROFILE_JSON == 1) g_profilerListener = nullptr; diff --git a/examples/testbed/framework/model.h b/examples/testbed/framework/model.h index 0354f56..017c7ce 100644 --- a/examples/testbed/framework/model.h +++ b/examples/testbed/framework/model.h @@ -21,7 +21,7 @@ #include #include -#include +#include // Set to 1 to write profile events into a .json file. Set to 0 otherwise. #define PROFILE_JSON 0 @@ -71,7 +71,7 @@ private: Draw m_draw; Camera m_camera; Profiler m_profiler; - ProfilerRecorder m_profilerRecorder; + ProfilerSt m_profilerSt; #if (PROFILE_JSON == 1) JsonProfiler m_jsonListener; diff --git a/examples/testbed/framework/profiler.cpp b/examples/testbed/framework/profiler.cpp index 0422084..faef74e 100644 --- a/examples/testbed/framework/profiler.cpp +++ b/examples/testbed/framework/profiler.cpp @@ -127,12 +127,13 @@ void Profiler::End() listener->BeginEvents(); } - RecurseEvents(m_root); - - RecurseDestroyNode(m_root); - m_root = nullptr; + if (m_root) + { + RecurseEvents(m_root); - assert(m_root == nullptr); + RecurseDestroyNode(m_root); + m_root = nullptr; + } if (listener) { diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h index 7c27dac..75861f8 100644 --- a/examples/testbed/framework/profiler.h +++ b/examples/testbed/framework/profiler.h @@ -62,8 +62,6 @@ public: // Get the root profiler node. ProfilerNode* GetRoot() { return m_root; } private: - friend class ProfilerRecorder; - ProfilerNode* CreateNode(); void DestroyNode(ProfilerNode* node); diff --git a/examples/testbed/framework/profiler_recorder.cpp b/examples/testbed/framework/profiler_recorder.cpp deleted file mode 100644 index 94b0dc7..0000000 --- a/examples/testbed/framework/profiler_recorder.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#include -#include - -ProfilerRecorder* g_profilerRecorder = nullptr; - -ProfilerRecord* ProfilerRecorder::FindRecord(const char* name) -{ - for (u32 i = 0; i < m_records.Count(); ++i) - { - ProfilerRecord& r = m_records[i]; - if (r.name == name) - { - return &r; - } - } - return nullptr; -} - -void ProfilerRecorder::RecurseBuildRecords(ProfilerNode* node) -{ - ProfilerRecord* fr = FindRecord(node->name); - if (fr) - { - float64 elapsedTime = node->t1 - node->t0; - - fr->elapsed += elapsedTime; - fr->minElapsed = b3Min(fr->minElapsed, elapsedTime); - fr->maxElapsed = b3Max(fr->maxElapsed, elapsedTime); - ++fr->callCount; - } - else - { - float64 elapsedTime = node->t1 - node->t0; - - ProfilerRecord r; - r.name = node->name; - r.elapsed = elapsedTime; - r.minElapsed = elapsedTime; - r.maxElapsed = elapsedTime; - r.callCount = 1; - - m_records.PushBack(r); - } - - for (u32 i = 0; i < node->children.Count(); ++i) - { - RecurseBuildRecords(node->children[i]); - } -} - -void ProfilerRecorder::BuildRecords() -{ - for (u32 i = 0; i < m_records.Count(); ++i) - { - m_records[i].elapsed = 0.0; - m_records[i].callCount = 0; - } - - RecurseBuildRecords(g_profiler->m_root); -} - -static ProfilerRecord* FindSortedRecord(b3Array& records, const char* name) -{ - for (u32 i = 0; i < records.Count(); ++i) - { - ProfilerRecord* r = records[i]; - if (r->name == name) - { - return r; - } - } - return nullptr; -} - -void ProfilerRecorder::RecurseBuildSortedRecords(ProfilerNode* node, b3Array& output) -{ - ProfilerRecord* fsr = FindSortedRecord(output, node->name); - - if (fsr == nullptr) - { - ProfilerRecord* fr = FindRecord(node->name); - - assert(fr != nullptr); - - // Push back the first ocurrence of call in calling order - output.PushBack(fr); - } - - for (u32 i = 0; i < node->children.Count(); ++i) - { - RecurseBuildSortedRecords(node->children[i], output); - } -} - -void ProfilerRecorder::BuildSortedRecords(b3Array& output) -{ - assert(output.Count() == 0); - - output.Reserve(m_records.Count()); - - RecurseBuildSortedRecords(g_profiler->m_root, output); -} \ No newline at end of file diff --git a/examples/testbed/framework/profiler_recorder.h b/examples/testbed/framework/profiler_recorder.h deleted file mode 100644 index b3ea5b4..0000000 --- a/examples/testbed/framework/profiler_recorder.h +++ /dev/null @@ -1,58 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef PROFILER_RECORDER_H -#define PROFILER_RECORDER_H - -#include -#include - -struct ProfilerNode; - -// A persistent profiler record -struct ProfilerRecord -{ - const char* name; - float64 elapsed; - float64 minElapsed; - float64 maxElapsed; - u32 callCount; -}; - -// This class maintains persistent profiler records -class ProfilerRecorder -{ -public: - void BuildRecords(); - - void BuildSortedRecords(b3Array& output); - - const b3Array& GetRecords() const { return m_records; } -private: - void RecurseBuildRecords(ProfilerNode* node); - - void RecurseBuildSortedRecords(ProfilerNode* node, b3Array& output); - - ProfilerRecord* FindRecord(const char* name); - - b3StackArray m_records; // persistent profiler records -}; - -extern ProfilerRecorder* g_profilerRecorder; - -#endif \ No newline at end of file diff --git a/examples/testbed/framework/profiler_st.cpp b/examples/testbed/framework/profiler_st.cpp new file mode 100644 index 0000000..bb3523a --- /dev/null +++ b/examples/testbed/framework/profiler_st.cpp @@ -0,0 +1,199 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include + +ProfilerSt* g_profilerSt = nullptr; + +ProfilerSt::ProfilerSt() : m_pool(sizeof(ProfilerStNode)) +{ + m_root = nullptr; + m_top = nullptr; +} + +ProfilerSt::~ProfilerSt() +{ + if (m_root) + { + RecurseDestroyNode(m_root); + } +} + +ProfilerStNodeStat* ProfilerSt::FindStat(const char* name) +{ + for (u32 i = 0; i < m_stats.Count(); ++i) + { + if (m_stats[i].name == name) + { + return &m_stats[i]; + } + } + + return nullptr; +} + +ProfilerStNodeStat* ProfilerSt::CreateStat() +{ + m_stats.PushBack(ProfilerStNodeStat()); + return &m_stats.Back(); +} + +ProfilerStNode* ProfilerSt::CreateNode() +{ + void* block = m_pool.Allocate(); + return new (block) ProfilerStNode(); +} + +void ProfilerSt::DestroyNode(ProfilerStNode* node) +{ + node->~ProfilerStNode(); + m_pool.Free(node); +} + +void ProfilerSt::RecurseDestroyNode(ProfilerStNode* node) +{ + for (u32 i = 0; i < node->children.Count(); ++i) + { + return RecurseDestroyNode(node->children[i]); + } + + DestroyNode(node); +} + +static ProfilerStNode* RecurseFindNode(ProfilerStNode* node, const char* name) +{ + if (node->name == name) + { + return node; + } + + ProfilerStNode* result = nullptr; + for (u32 i = 0; result == nullptr && i < node->children.Count(); ++i) + { + result = RecurseFindNode(node->children[i], name); + } + + return result; +} + +ProfilerStNode* ProfilerSt::FindNode(const char* name) +{ + if (m_top) + { + return RecurseFindNode(m_top, name); + } + + if (m_root) + { + return RecurseFindNode(m_root, name); + } + + return nullptr; +} + +void ProfilerSt::BeginScope(const char* name) +{ + ProfilerStNode* fn = FindNode(name); + + if (fn) + { + m_time.Update(); + fn->t0 = m_time.GetCurrentMilis(); + ++fn->callCount; + m_top = fn; + return; + } + + m_time.Update(); + + ProfilerStNode* n = CreateNode(); + n->name = name; + n->t0 = m_time.GetCurrentMilis(); + n->elapsed = 0.0f; + n->callCount = 1; + n->parent = m_top; + n->stat = nullptr; + + if (m_root == nullptr) + { + m_root = n; + m_top = n; + + return; + } + + if (m_top) + { + m_top->children.PushBack(n); + } + + m_top = n; +} + +void ProfilerSt::EndScope() +{ + assert(m_top != nullptr); + + m_time.Update(); + + m_top->t1 = m_time.GetCurrentMilis(); + + float64 elapsedTime = m_top->t1 - m_top->t0; + + m_top->elapsed += elapsedTime; + + ProfilerStNodeStat* stat = FindStat(m_top->name); + + if (stat == nullptr) + { + stat = CreateStat(); + stat->name = m_top->name; + stat->minElapsed = elapsedTime; + stat->maxElapsed = elapsedTime; + } + else + { + stat->minElapsed = b3Min(stat->minElapsed, elapsedTime); + stat->maxElapsed = b3Max(stat->maxElapsed, elapsedTime); + } + + if (m_top->stat == nullptr) + { + m_top->stat = stat; + } + + assert(m_top->stat == stat); + + m_top = m_top->parent; +} + +void ProfilerSt::Begin() +{ + assert(m_top == nullptr); +} + +void ProfilerSt::End() +{ + assert(m_top == nullptr); + + if (m_root) + { + RecurseDestroyNode(m_root); + m_root = nullptr; + } +} \ No newline at end of file diff --git a/examples/testbed/framework/profiler_st.h b/examples/testbed/framework/profiler_st.h new file mode 100644 index 0000000..9cb1290 --- /dev/null +++ b/examples/testbed/framework/profiler_st.h @@ -0,0 +1,94 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef PROFILER_ST_H +#define PROFILER_ST_H + +#include +#include +#include +#include + +// Profiler tree node statistics +struct ProfilerStNodeStat +{ + const char* name; + float64 minElapsed; + float64 maxElapsed; +}; + +// A profiler tree node +struct ProfilerStNode +{ + const char* name; + float64 t0; + float64 t1; + + float64 elapsed; + u32 callCount; + + ProfilerStNode* parent; + b3StackArray children; + + ProfilerStNodeStat* stat; +}; + +// A profiler tree +class ProfilerSt +{ +public: + ProfilerSt(); + + ~ProfilerSt(); + + // Must be called before profiling. + void Begin(); + + // Must be called after profiling. + void End(); + + // Begin a new scope. + void BeginScope(const char* name); + + // End the top scope. + void EndScope(); + + ProfilerStNode* GetRoot() { return m_root; } +private: + ProfilerStNode* CreateNode(); + void DestroyNode(ProfilerStNode* node); + + void RecurseDestroyNode(ProfilerStNode* node); + + ProfilerStNode* FindNode(const char* name); + + ProfilerStNodeStat* CreateStat(); + + ProfilerStNodeStat* FindStat(const char* name); + + b3BlockPool m_pool; // pool of nodes + b3Time m_time; // timer + ProfilerStNode* m_root; // tree root node + ProfilerStNode* m_top; // top node + + b3StackArray m_stats; // node statistics +}; + +extern ProfilerSt* g_profilerSt; + +#endif \ No newline at end of file diff --git a/examples/testbed/framework/test.cpp b/examples/testbed/framework/test.cpp index f5779aa..d6dc8b0 100644 --- a/examples/testbed/framework/test.cpp +++ b/examples/testbed/framework/test.cpp @@ -18,6 +18,7 @@ #include #include +#include extern u32 b3_allocCalls, b3_maxAllocCalls; extern u32 b3_convexCalls, b3_convexCacheHits; @@ -27,11 +28,13 @@ extern bool b3_convexCache; void b3BeginProfileScope(const char* name) { g_profiler->BeginScope(name); + g_profilerSt->BeginScope(name); } void b3EndProfileScope() { g_profiler->EndScope(); + g_profilerSt->EndScope(); } Test::Test() : diff --git a/examples/testbed/framework/view.cpp b/examples/testbed/framework/view.cpp index adaea83..0f4cffb 100644 --- a/examples/testbed/framework/view.cpp +++ b/examples/testbed/framework/view.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #if defined (U_OPENGL_2) @@ -218,8 +219,8 @@ void View::Interface() if (ImGui::BeginMenu("View")) { - ImGui::MenuItem("Profile", "", &settings.drawProfile); ImGui::MenuItem("Profile Tree", "", &settings.drawProfileTree); + ImGui::MenuItem("Profile Tree Statistics", "", &settings.drawProfileTreeStats); ImGui::MenuItem("Statistics", "", &settings.drawStats); ImGui::Separator(); @@ -435,7 +436,7 @@ void View::InterfaceProfileTree() ImGui::SetNextWindowPos(ImVec2(0.0f, wp.y + ws.y)); ImGui::SetNextWindowSize(ImVec2(g_camera->m_width - 250.0f, 0.0f)); - ImGui::Begin("##ProfileTree", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize); + ImGui::Begin("Profile Tree", NULL, ImGuiWindowFlags_AlwaysAutoResize); ProfilerNode* root = g_profiler->GetRoot(); if (root) @@ -449,6 +450,64 @@ void View::InterfaceProfileTree() ImGui::PopStyleVar(); } +static void TreeNode(ProfilerStNode* node, u32& index) +{ + ImGui::PushID(index); + ++index; + + if (ImGui::TreeNode(node->name)) + { + ImGui::Text("%.4f (min = %.4f) (max = %.4f) (calls = %d) [ms]", node->elapsed, node->stat->minElapsed, node->stat->maxElapsed, node->callCount); + + for (u32 i = 0; i < node->children.Count(); ++i) + { + TreeNode(node->children[i], index); + } + ImGui::TreePop(); + } + + ImGui::PopID(); +} + +void View::InterfaceProfileTreeStats() +{ + ImGui::Begin("Overlay", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar); + ImVec2 wp = ImGui::GetWindowPos(); + ImVec2 ws = ImGui::GetWindowSize(); + ImGui::End(); + + wp.y = wp.y + ws.y; + + if (g_settings->drawProfileTree) + { + ImGui::Begin("Profile Tree", NULL, ImGuiWindowFlags_AlwaysAutoResize); + ImVec2 ptwp = ImGui::GetWindowPos(); + ImVec2 ptws = ImGui::GetWindowSize(); + ImGui::End(); + + wp.y = ptwp.y + ptws.y; + } + + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + + ImGui::SetNextWindowBgAlpha(0.0f); + ImGui::SetNextWindowPos(ImVec2(0.0f, wp.y)); + ImGui::SetNextWindowSize(ImVec2(g_camera->m_width - 250.0f, 0.0f)); + + ImGui::Begin("Profile Tree Statistics", NULL, ImGuiWindowFlags_AlwaysAutoResize); + + ProfilerStNode* root = g_profilerSt->GetRoot(); + if (root) + { + u32 index = 0; + TreeNode(root, index); + } + + ImGui::End(); + + ImGui::PopStyleVar(); +} + void View::EndInterface() { ImGui::PopStyleVar(); diff --git a/examples/testbed/framework/view.h b/examples/testbed/framework/view.h index 2650e81..3851e0c 100644 --- a/examples/testbed/framework/view.h +++ b/examples/testbed/framework/view.h @@ -42,6 +42,7 @@ public: void BeginInterface(); void Interface(); void InterfaceProfileTree(); + void InterfaceProfileTreeStats(); void EndInterface(); private: friend class ViewModel; diff --git a/examples/testbed/framework/view_model.h b/examples/testbed/framework/view_model.h index 2d133ef..09f46ab 100644 --- a/examples/testbed/framework/view_model.h +++ b/examples/testbed/framework/view_model.h @@ -32,8 +32,8 @@ struct Settings drawLines = true; drawTriangles = true; drawGrid = true; - drawProfile = false; drawProfileTree = false; + drawProfileTreeStats = false; drawStats = false; } @@ -43,8 +43,8 @@ struct Settings bool drawLines; bool drawTriangles; bool drawGrid; - bool drawProfile; bool drawProfileTree; + bool drawProfileTreeStats; bool drawStats; }; From 7ed4166f0ca2de3c2adccd8a3f46ea6da5efbd3b Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 7 Apr 2019 08:07:34 -0300 Subject: [PATCH 049/198] Remove code --- examples/testbed/framework/profiler_st.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/testbed/framework/profiler_st.cpp b/examples/testbed/framework/profiler_st.cpp index bb3523a..3866e84 100644 --- a/examples/testbed/framework/profiler_st.cpp +++ b/examples/testbed/framework/profiler_st.cpp @@ -28,10 +28,8 @@ ProfilerSt::ProfilerSt() : m_pool(sizeof(ProfilerStNode)) ProfilerSt::~ProfilerSt() { - if (m_root) - { - RecurseDestroyNode(m_root); - } + assert(m_root == nullptr); + assert(m_top == nullptr); } ProfilerStNodeStat* ProfilerSt::FindStat(const char* name) From 6aa677e1331070c384d964e00704710ec374a0c9 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 8 Apr 2019 13:53:35 -0300 Subject: [PATCH 050/198] Removed profiler listener --- examples/testbed/framework/json_profiler.cpp | 2 ++ examples/testbed/framework/json_profiler.h | 18 +++++----- examples/testbed/framework/main.cpp | 4 +++ examples/testbed/framework/model.cpp | 36 ++++++++++++++++++-- examples/testbed/framework/model.h | 8 +++-- examples/testbed/framework/profiler.cpp | 32 ----------------- examples/testbed/framework/profiler.h | 31 ----------------- 7 files changed, 54 insertions(+), 77 deletions(-) diff --git a/examples/testbed/framework/json_profiler.cpp b/examples/testbed/framework/json_profiler.cpp index ac2415c..bce7c98 100644 --- a/examples/testbed/framework/json_profiler.cpp +++ b/examples/testbed/framework/json_profiler.cpp @@ -18,6 +18,8 @@ #include +JsonProfiler* g_jsonProfiler = nullptr; + #define STRING(x) String(x, sizeof(x) - 1) JsonProfiler::JsonProfiler() diff --git a/examples/testbed/framework/json_profiler.h b/examples/testbed/framework/json_profiler.h index 8266302..dc547a5 100644 --- a/examples/testbed/framework/json_profiler.h +++ b/examples/testbed/framework/json_profiler.h @@ -19,8 +19,6 @@ #ifndef JSON_PROFILER_H #define JSON_PROFILER_H -#include - #include #include @@ -28,30 +26,32 @@ using namespace rapidjson; -// The following profiler listener is notified by a profiler when events are initiated +// The following profiler is notified when events are initiated // or terminated. // When it receives the notification it immediately saves its data into a .json file format. // The .json file can be read and interpreted by the Google Chrome Tracing. // Say chrome://tracing to the web browser and load the file // This file is by default called "profile.json". Any name can be given. // For implementation details, see json_profile.cpp. -class JsonProfiler : public ProfilerListener +class JsonProfiler { public: JsonProfiler(); ~JsonProfiler(); - void BeginEvents() override; + void BeginEvents(); - void EndEvents() override; + void EndEvents(); - void BeginEvent(const char* name, float64 time) override; + void BeginEvent(const char* name, float64 time); - void EndEvent(const char* name, float64 time) override; + void EndEvent(const char* name, float64 time); private: - FILE * m_file; + FILE* m_file; FileWriteStream* m_stream; Writer* m_writer; }; +extern JsonProfiler* g_jsonProfiler; + #endif \ No newline at end of file diff --git a/examples/testbed/framework/main.cpp b/examples/testbed/framework/main.cpp index 3917fd1..823170d 100644 --- a/examples/testbed/framework/main.cpp +++ b/examples/testbed/framework/main.cpp @@ -142,6 +142,10 @@ static void Run() g_profilerSt->End(); +#if PROFILE_JSON == 1 + g_model->UpdateJson(); +#endif + g_profiler->End(); g_view->EndInterface(); diff --git a/examples/testbed/framework/model.cpp b/examples/testbed/framework/model.cpp index d15067b..ce522d8 100644 --- a/examples/testbed/framework/model.cpp +++ b/examples/testbed/framework/model.cpp @@ -29,7 +29,7 @@ Model::Model() g_profilerSt = &m_profilerSt; #if (PROFILE_JSON == 1) - g_profilerListener = &m_jsonListener; + g_jsonProfiler = &m_jsonProfiler; #endif m_test = nullptr; @@ -59,7 +59,7 @@ Model::~Model() g_profilerSt = nullptr; #if (PROFILE_JSON == 1) - g_profilerListener = nullptr; + g_jsonProfiler = nullptr; #endif delete m_test; @@ -229,4 +229,34 @@ void Model::Update() m_test->Step(); m_draw.Flush(); -} \ No newline at end of file +} + +#if (PROFILE_JSON == 1) + +static inline void RecurseEvents(ProfilerNode* node) +{ + g_jsonProfiler->BeginEvent(node->name, node->t0); + + g_jsonProfiler->EndEvent(node->name, node->t1); + + for (u32 i = 0; i < node->children.Count(); ++i) + { + RecurseEvents(node->children[i]); + } +} + +void Model::UpdateJson() +{ + m_jsonProfiler.BeginEvents(); + + ProfilerNode* root = m_profiler.GetRoot(); + + if (root) + { + RecurseEvents(root); + } + + m_jsonProfiler.EndEvents(); +} + +#endif \ No newline at end of file diff --git a/examples/testbed/framework/model.h b/examples/testbed/framework/model.h index 017c7ce..f586afa 100644 --- a/examples/testbed/framework/model.h +++ b/examples/testbed/framework/model.h @@ -61,7 +61,11 @@ public: void Command_ZoomCamera(float32 d); void Update(); - + +#if (PROFILE_JSON == 1) + void UpdateJson(); +#endif + bool IsPaused() const { return m_pause; } private: friend class ViewModel; @@ -74,7 +78,7 @@ private: ProfilerSt m_profilerSt; #if (PROFILE_JSON == 1) - JsonProfiler m_jsonListener; + JsonProfiler m_jsonProfiler; #endif Test* m_test; diff --git a/examples/testbed/framework/profiler.cpp b/examples/testbed/framework/profiler.cpp index faef74e..fea9077 100644 --- a/examples/testbed/framework/profiler.cpp +++ b/examples/testbed/framework/profiler.cpp @@ -19,7 +19,6 @@ #include Profiler* g_profiler = nullptr; -ProfilerListener* g_profilerListener = nullptr; Profiler::Profiler() : m_pool(sizeof(ProfilerNode)) { @@ -89,23 +88,6 @@ void Profiler::Begin() assert(m_top == nullptr); } -static inline void RecurseEvents(ProfilerNode* node) -{ - ProfilerListener* listener = g_profilerListener; - - if (listener) - { - listener->BeginEvent(node->name, node->t0); - - listener->EndEvent(node->name, node->t1); - } - - for (u32 i = 0; i < node->children.Count(); ++i) - { - RecurseEvents(node->children[i]); - } -} - void Profiler::RecurseDestroyNode(ProfilerNode* node) { for (u32 i = 0; i < node->children.Count(); ++i) @@ -119,24 +101,10 @@ void Profiler::RecurseDestroyNode(ProfilerNode* node) void Profiler::End() { assert(m_top == nullptr); - - ProfilerListener* listener = g_profilerListener; - - if (listener) - { - listener->BeginEvents(); - } if (m_root) { - RecurseEvents(m_root); - RecurseDestroyNode(m_root); m_root = nullptr; } - - if (listener) - { - listener->EndEvents(); - } } \ No newline at end of file diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h index 75861f8..3940537 100644 --- a/examples/testbed/framework/profiler.h +++ b/examples/testbed/framework/profiler.h @@ -24,8 +24,6 @@ #include #include -class ProfilerListener; - // Profiler node struct ProfilerNode { @@ -75,33 +73,4 @@ private: extern Profiler* g_profiler; -// Any implementation of this interface passed to Profiler::End will listen to profile events. -class ProfilerListener -{ -public: - virtual ~ProfilerListener() { } - - // This function is called when profiling has began. - virtual void BeginEvents() { } - - // This function is called when profiling has ended. - virtual void EndEvents() { } - - // This function is called when a profiler event begins. - virtual void BeginEvent(const char* name, float64 time) - { - B3_NOT_USED(name); - B3_NOT_USED(time); - } - - // This function is called when a profiler event ends. - virtual void EndEvent(const char* name, float64 time) - { - B3_NOT_USED(name); - B3_NOT_USED(time); - } -}; - -extern ProfilerListener* g_profilerListener; - #endif \ No newline at end of file From daacb4fde748517b4837b4058bcada533bc33eee Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 8 Apr 2019 15:38:41 -0300 Subject: [PATCH 051/198] Update premake5.lua --- premake5.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/premake5.lua b/premake5.lua index 625618c..f0ef7e9 100644 --- a/premake5.lua +++ b/premake5.lua @@ -264,9 +264,8 @@ workspace(solution_name) { examples_inc_dir .. "/testbed/framework/draw.h", examples_inc_dir .. "/testbed/framework/profiler.h", - examples_inc_dir .. "/testbed/framework/recorder_profiler.h", + examples_inc_dir .. "/testbed/framework/profiler_st.h", examples_inc_dir .. "/testbed/framework/json_profiler.h", - examples_inc_dir .. "/testbed/framework/testbed_listener.h", examples_inc_dir .. "/testbed/framework/model.h", examples_inc_dir .. "/testbed/framework/view.h", @@ -284,7 +283,7 @@ workspace(solution_name) examples_src_dir .. "/testbed/framework/draw.cpp", examples_src_dir .. "/testbed/framework/profiler.cpp", - examples_src_dir .. "/testbed/framework/recorder_profiler.cpp", + examples_src_dir .. "/testbed/framework/profiler_st.cpp", examples_src_dir .. "/testbed/framework/json_profiler.cpp", examples_inc_dir .. "/testbed/framework/model.cpp", From 8352e29847e02922b3b84640090eadd79df8d446 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 9 Apr 2019 16:26:14 -0300 Subject: [PATCH 052/198] Remove comment --- examples/testbed/framework/profiler.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h index 3940537..070ae7e 100644 --- a/examples/testbed/framework/profiler.h +++ b/examples/testbed/framework/profiler.h @@ -46,9 +46,6 @@ public: void Begin(); // Must be called after profiling. - // The function will report all events in this profiler - // to the given event listener in the correct calling order. - // This function also flushes the profiler. void End(); // Begin a new scope. From 8950df2ef6a5743a5e9701c82d9196b98d477a3b Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 10 Apr 2019 08:03:06 -0300 Subject: [PATCH 053/198] Fix #48 --- include/bounce/common/template/object_array.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/bounce/common/template/object_array.h b/include/bounce/common/template/object_array.h index f691a6e..348f9e2 100644 --- a/include/bounce/common/template/object_array.h +++ b/include/bounce/common/template/object_array.h @@ -93,6 +93,9 @@ public: { B3_ASSERT(m_count > 0); --m_count; + + T* e = m_elements + m_count; + e->~T(); } const T& Back() const From d34fce1e4d3860ff92e3c60c81fb89f2939e7b3e Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 10 Apr 2019 08:15:55 -0300 Subject: [PATCH 054/198] Just equal instead of less than or equal --- src/bounce/dynamics/contacts/collide/collide_hulls.cpp | 2 +- src/bounce/dynamics/contacts/collide/collide_hulls_cache.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bounce/dynamics/contacts/collide/collide_hulls.cpp b/src/bounce/dynamics/contacts/collide/collide_hulls.cpp index af2a549..63ae512 100644 --- a/src/bounce/dynamics/contacts/collide/collide_hulls.cpp +++ b/src/bounce/dynamics/contacts/collide/collide_hulls.cpp @@ -54,7 +54,7 @@ void b3BuildEdgeContact(b3Manifold& manifold, // Compute the closest points on the two lines. float32 b = b3Dot(N1, N2); float32 den = 1.0f - b * b; - if (den <= 0.0f) + if (den == 0.0f) { return; } diff --git a/src/bounce/dynamics/contacts/collide/collide_hulls_cache.cpp b/src/bounce/dynamics/contacts/collide/collide_hulls_cache.cpp index b4d02f2..bcdc59e 100644 --- a/src/bounce/dynamics/contacts/collide/collide_hulls_cache.cpp +++ b/src/bounce/dynamics/contacts/collide/collide_hulls_cache.cpp @@ -63,7 +63,7 @@ static void b3RebuildEdgeContact(b3Manifold& manifold, // Compute the closest points on the two lines. float32 b = b3Dot(N1, N2); float32 den = 1.0f - b * b; - if (den <= 0.0f) + if (den == 0.0f) { return; } From 5b95cb4e1ffef9c8bb28dcddb1e1162ee9696625 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 10 Apr 2019 08:26:19 -0300 Subject: [PATCH 055/198] Equal instad of less than or equal --- src/bounce/dynamics/contacts/collide/collide_capsule_hull.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bounce/dynamics/contacts/collide/collide_capsule_hull.cpp b/src/bounce/dynamics/contacts/collide/collide_capsule_hull.cpp index 5d66029..fef0862 100644 --- a/src/bounce/dynamics/contacts/collide/collide_capsule_hull.cpp +++ b/src/bounce/dynamics/contacts/collide/collide_capsule_hull.cpp @@ -52,7 +52,7 @@ static void b3BuildEdgeContact(b3Manifold& manifold, // Compute the closest points on the two lines. float32 b = b3Dot(N1, N2); float32 den = 1.0f - b * b; - if (den <= 0.0f) + if (den == 0.0f) { return; } From 255f7363cb3507e03ec7526441d8ed2181ef8cc4 Mon Sep 17 00:00:00 2001 From: Irlan Robson Date: Sun, 14 Apr 2019 11:23:29 -0300 Subject: [PATCH 056/198] Update license.txt --- license.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/license.txt b/license.txt index a99dc41..f92063f 100644 --- a/license.txt +++ b/license.txt @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson http://www.irlan.net * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -14,4 +14,4 @@ * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. -*/ \ No newline at end of file +*/ From 5c2cc41254ce801421cee0a17ca8301a51f690b7 Mon Sep 17 00:00:00 2001 From: Irlan Robson Date: Sun, 14 Apr 2019 11:24:16 -0300 Subject: [PATCH 057/198] Update license.txt --- license.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/license.txt b/license.txt index f92063f..afe08e9 100644 --- a/license.txt +++ b/license.txt @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016-2019 Irlan Robson http://www.irlan.net +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages From df33dd5cab88cf900087f008ae06d7806cec89fb Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 18 Apr 2019 11:02:04 -0300 Subject: [PATCH 058/198] Setter and getter for spring mode cloth dragging --- examples/testbed/framework/cloth_dragger.cpp | 15 +++++++++++++++ examples/testbed/framework/cloth_dragger.h | 9 +++++++++ 2 files changed, 24 insertions(+) diff --git a/examples/testbed/framework/cloth_dragger.cpp b/examples/testbed/framework/cloth_dragger.cpp index 47ee7fc..e05d447 100644 --- a/examples/testbed/framework/cloth_dragger.cpp +++ b/examples/testbed/framework/cloth_dragger.cpp @@ -145,6 +145,21 @@ void b3ClothDragger::Drag() } } +void b3ClothDragger::SetSpring(bool bit) +{ + if (bit == m_spring) + { + return; + } + + if (IsDragging()) + { + StopDragging(); + } + + m_spring = bit; +} + void b3ClothDragger::StopDragging() { B3_ASSERT(IsDragging() == true); diff --git a/examples/testbed/framework/cloth_dragger.h b/examples/testbed/framework/cloth_dragger.h index d0e49de..6bbb797 100644 --- a/examples/testbed/framework/cloth_dragger.h +++ b/examples/testbed/framework/cloth_dragger.h @@ -32,6 +32,10 @@ public: b3ClothDragger(b3Ray3* ray, b3Cloth* cloth); ~b3ClothDragger(); + void SetSpring(bool bit); + + bool GetSpring() const; + bool IsDragging() const; bool StartDragging(); @@ -62,6 +66,11 @@ private: b3ParticleType m_t1, m_t2, m_t3; }; +inline bool b3ClothDragger::GetSpring() const +{ + return m_spring; +} + inline bool b3ClothDragger::IsDragging() const { return m_triangle != nullptr; From 36f2484663d1e3070f09220eaf48e56820ae1786 Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 18 Apr 2019 11:03:28 -0300 Subject: [PATCH 059/198] Assert triangle area against zero instead of epsilon --- src/bounce/cloth/cloth.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index baffc6b..efbfd10 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -330,7 +330,7 @@ void b3Cloth::ComputeMass() b3Vec3 v3 = m_mesh->vertices[triangle->v3]; float32 area = b3Area(v1, v2, v3); - B3_ASSERT(area > B3_EPSILON); + B3_ASSERT(area > 0.0f); float32 mass = rho * area; From 923a069408058c83babd9030363750607bf2f01e Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 18 Apr 2019 11:05:13 -0300 Subject: [PATCH 060/198] Allow moving while jumping. Disable gravity when grounded. Change some parameters. --- examples/testbed/tests/character_test.h | 84 ++++++++++++++----------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/examples/testbed/tests/character_test.h b/examples/testbed/tests/character_test.h index ec6e322..93a8f02 100644 --- a/examples/testbed/tests/character_test.h +++ b/examples/testbed/tests/character_test.h @@ -68,7 +68,7 @@ private: sphere.radius = m_characterShape->m_radius; return sphere; } - + void UpdateGrounded() { b3StackArray shapes; @@ -278,6 +278,7 @@ private: b3StackArray shapes; CollectStaticShapes(shapes); + // Collect collision planes b3StackArray planes; CollectOverlapPlanes(planes, shapes); @@ -293,8 +294,9 @@ private: for (;;) { + // Collect collision planes b3Vec3 translation = solvePosition - startPosition; - + CollectSweepPlanes(planes, shapes, translation); solvePosition = SolvePositionConstraints(planes, targetPosition); @@ -309,7 +311,7 @@ private: oldSolvePosition = solvePosition; } - // Update body + // Synchronize body b3Quat orientation = m_characterBody->GetOrientation(); b3Vec3 axis; @@ -543,42 +545,48 @@ public: float32 dt = g_testSettings->inv_hertz; const float32 walkSpeed = 10.0f; - const float32 jumpSpeed = 300.0f; - const float32 gravity = 50.0f * 9.8f; - b3Vec3 velocity = b3Vec3_zero; - - bool isGounded = m_characterController->IsGrounded(); + const float32 jumpSpeed = 10.0f; + const float32 gravityAcc = 9.8f; - if (isGounded) + static b3Vec3 velocity = b3Vec3_zero; + + velocity.x = 0.0f; + velocity.z = 0.0f; + + extern GLFWwindow* g_window; + + bool leftDown = glfwGetKey(g_window, GLFW_KEY_LEFT); + bool rightDown = glfwGetKey(g_window, GLFW_KEY_RIGHT); + bool downDown = glfwGetKey(g_window, GLFW_KEY_DOWN); + bool upDown = glfwGetKey(g_window, GLFW_KEY_UP); + bool spaceDown = glfwGetKey(g_window, GLFW_KEY_SPACE); + + bool isGrounded = m_characterController->IsGrounded(); + + // Walk + if (leftDown) { - extern GLFWwindow* g_window; + velocity.x -= walkSpeed; + } - bool leftDown = glfwGetKey(g_window, GLFW_KEY_LEFT); - bool rightDown = glfwGetKey(g_window, GLFW_KEY_RIGHT); - bool downDown = glfwGetKey(g_window, GLFW_KEY_DOWN); - bool upDown = glfwGetKey(g_window, GLFW_KEY_UP); - bool spaceDown = glfwGetKey(g_window, GLFW_KEY_SPACE); + if (rightDown) + { + velocity.x += walkSpeed; + } - // Walk - if (leftDown) - { - velocity.x -= walkSpeed; - } + if (upDown) + { + velocity.z -= walkSpeed; + } - if (rightDown) - { - velocity.x += walkSpeed; - } + if (downDown) + { + velocity.z += walkSpeed; + } - if (upDown) - { - velocity.z -= walkSpeed; - } - - if (downDown) - { - velocity.z += walkSpeed; - } + if (isGrounded) + { + velocity.y = 0.0f; // Jump if (spaceDown) @@ -586,10 +594,12 @@ public: velocity.y += jumpSpeed; } } - - // Integrate gravity - velocity.y -= dt * gravity; - + else + { + // Integrate gravity + velocity.y -= dt * gravityAcc; + } + // Compute translation b3Vec3 translation = dt * velocity; From 2f9abcbe9264d541465ebe7b1a654a4d4d528e73 Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 18 Apr 2019 18:48:10 -0300 Subject: [PATCH 061/198] Applied a bugfix on constraint solver and removed some frozen code --- include/bounce/cloth/cloth.h | 9 - include/bounce/cloth/cloth_contact_solver.h | 50 +--- include/bounce/cloth/cloth_solver.h | 1 - include/bounce/cloth/particle.h | 30 -- src/bounce/cloth/cloth.cpp | 69 +---- src/bounce/cloth/cloth_contact_solver.cpp | 293 +------------------- src/bounce/cloth/cloth_solver.cpp | 12 +- src/bounce/cloth/particle.cpp | 36 +-- 8 files changed, 10 insertions(+), 490 deletions(-) diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index e02e6a1..3c1a5b4 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -153,9 +153,6 @@ private: // Update body contacts. void UpdateBodyContacts(); - // Update particle contacts. - void UpdateParticleContacts(); - // Update contacts void UpdateContacts(); @@ -183,17 +180,11 @@ private: // Pool of particles b3BlockPool m_particleBlocks; - // Pool of particle contacts - b3BlockPool m_particleContactBlocks; - // List of particles b3List2 m_particleList; // List of forces b3List2 m_forceList; - - // List of particle contacts - b3List2 m_particleContactList; }; inline void b3Cloth::SetGravity(const b3Vec3& gravity) diff --git a/include/bounce/cloth/cloth_contact_solver.h b/include/bounce/cloth/cloth_contact_solver.h index 011d3fb..1fe3070 100644 --- a/include/bounce/cloth/cloth_contact_solver.h +++ b/include/bounce/cloth/cloth_contact_solver.h @@ -28,7 +28,6 @@ class b3Particle; class b3Body; class b3BodyContact; -class b3ParticleContact; struct b3DenseVec3; @@ -76,44 +75,11 @@ struct b3ClothSolverBodyContactPositionConstraint b3Vec3 rA; b3Vec3 rB; + b3Vec3 normalA; b3Vec3 localPointA; b3Vec3 localPointB; }; -struct b3ClothSolverParticleContactVelocityConstraint -{ - u32 indexA; - float32 invMassA; - - u32 indexB; - float32 invMassB; - - float32 friction; - - b3Vec3 point; - - b3Vec3 normal; - float32 normalMass; - float32 normalImpulse; - float32 velocityBias; - - b3Vec3 tangent1; - b3Vec3 tangent2; - b3Mat22 tangentMass; - b3Vec2 tangentImpulse; -}; - -struct b3ClothSolverParticleContactPositionConstraint -{ - u32 indexA; - float32 invMassA; - float32 radiusA; - - u32 indexB; - float32 invMassB; - float32 radiusB; -}; - struct b3ClothContactSolverDef { b3StackAllocator* allocator; @@ -123,9 +89,6 @@ struct b3ClothContactSolverDef u32 bodyContactCount; b3BodyContact** bodyContacts; - - u32 particleContactCount; - b3ParticleContact** particleContacts; }; inline float32 b3MixFriction(float32 u1, float32 u2) @@ -141,19 +104,13 @@ public: void InitializeBodyContactConstraints(); - void InitializeParticleContactConstraints(); - void WarmStart(); void SolveBodyContactVelocityConstraints(); - void SolveParticleContactVelocityConstraints(); - void StoreImpulses(); bool SolveBodyContactPositionConstraints(); - - bool SolveParticleContactPositionConstraints(); protected: b3StackAllocator* m_allocator; @@ -164,11 +121,6 @@ protected: b3BodyContact** m_bodyContacts; b3ClothSolverBodyContactVelocityConstraint* m_bodyVelocityConstraints; b3ClothSolverBodyContactPositionConstraint* m_bodyPositionConstraints; - - u32 m_particleContactCount; - b3ParticleContact** m_particleContacts; - b3ClothSolverParticleContactVelocityConstraint* m_particleVelocityConstraints; - b3ClothSolverParticleContactPositionConstraint* m_particlePositionConstraints; }; #endif \ No newline at end of file diff --git a/include/bounce/cloth/cloth_solver.h b/include/bounce/cloth/cloth_solver.h index 6b0b896..52b6e66 100644 --- a/include/bounce/cloth/cloth_solver.h +++ b/include/bounce/cloth/cloth_solver.h @@ -41,7 +41,6 @@ struct b3ClothSolverDef u32 particleCapacity; u32 forceCapacity; u32 bodyContactCapacity; - u32 particleContactCapacity; }; struct b3ClothSolverData diff --git a/include/bounce/cloth/particle.h b/include/bounce/cloth/particle.h index 91480c2..bec7da6 100644 --- a/include/bounce/cloth/particle.h +++ b/include/bounce/cloth/particle.h @@ -103,36 +103,6 @@ struct b3BodyContactWorldPoint float32 separation; }; -// A contact between two particles -class b3ParticleContact -{ -public: - b3ParticleContact() { } - ~b3ParticleContact() { } - - b3Particle* p1; - b3Particle* p2; - - // Contact constraint - float32 normalImpulse; - - // Friction constraint - b3Vec3 t1, t2; - b3Vec2 tangentImpulse; - - b3ParticleContact* m_prev; - b3ParticleContact* m_next; -}; - -struct b3ParticleContactWorldPoint -{ - void Initialize(const b3ParticleContact* c); - - b3Vec3 point; - b3Vec3 normal; - float32 separation; -}; - // A cloth particle. class b3Particle { diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index efbfd10..2866748 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -147,8 +147,7 @@ static u32 b3FindSharedEdges(b3SharedEdge* sharedEdges, const b3ClothMesh* m) } b3Cloth::b3Cloth(const b3ClothDef& def) : - m_particleBlocks(sizeof(b3Particle)), - m_particleContactBlocks(sizeof(b3ParticleContact)) + m_particleBlocks(sizeof(b3Particle)) { B3_ASSERT(def.mesh); B3_ASSERT(def.density > 0.0f); @@ -652,66 +651,6 @@ void b3Cloth::UpdateBodyContacts() } } -void b3Cloth::UpdateParticleContacts() -{ - B3_PROFILE("Cloth Update Particle Contacts"); - - // Clear buffer - b3ParticleContact* c = m_particleContactList.m_head; - while (c) - { - b3ParticleContact* c0 = c; - c = c->m_next; - m_particleContactList.Remove(c0); - c0->~b3ParticleContact(); - m_particleContactBlocks.Free(c0); - } - - // Create particle contacts - for (b3Particle* p1 = m_particleList.m_head; p1; p1 = p1->m_next) - { - for (b3Particle* p2 = p1->m_next; p2; p2 = p2->m_next) - { - if (p1->m_type != e_dynamicParticle && p2->m_type != e_dynamicBody) - { - // At least one particle should be kinematic or dynamic. - continue; - } - - b3Vec3 c1 = p1->m_position; - float32 r1 = p1->m_radius; - - b3Vec3 c2 = p2->m_position; - float32 r2 = p2->m_radius; - - b3Vec3 d = c2 - c1; - float32 dd = b3Dot(d, d); - float32 totalRadius = r1 + r2; - if (dd > totalRadius * totalRadius) - { - continue; - } - - b3Vec3 n(0.0f, 1.0f, 0.0f); - if (dd > B3_EPSILON * B3_EPSILON) - { - float32 distance = b3Sqrt(dd); - n = d / distance; - } - - b3ParticleContact* c = (b3ParticleContact*)m_particleContactBlocks.Allocate(); - c->p1 = p1; - c->p2 = p2; - c->normalImpulse = 0.0f; - c->t1 = b3Perp(n); - c->t2 = b3Cross(c->t1, n); - c->tangentImpulse.SetZero(); - - m_particleContactList.PushFront(c); - } - } -} - void b3Cloth::Solve(float32 dt, const b3Vec3& gravity) { B3_PROFILE("Cloth Solve"); @@ -722,7 +661,6 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity) solverDef.particleCapacity = m_particleList.m_count; solverDef.forceCapacity = m_forceList.m_count; solverDef.bodyContactCapacity = m_particleList.m_count; - solverDef.particleContactCapacity = m_particleContactList.m_count; b3ClothSolver solver(solverDef); @@ -744,11 +682,6 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity) } } - for (b3ParticleContact* c = m_particleContactList.m_head; c; c = c->m_next) - { - solver.Add(c); - } - // Solve solver.Solve(dt, gravity); } diff --git a/src/bounce/cloth/cloth_contact_solver.cpp b/src/bounce/cloth/cloth_contact_solver.cpp index 5052198..ac97dfc 100644 --- a/src/bounce/cloth/cloth_contact_solver.cpp +++ b/src/bounce/cloth/cloth_contact_solver.cpp @@ -34,18 +34,10 @@ b3ClothContactSolver::b3ClothContactSolver(const b3ClothContactSolverDef& def) m_bodyContacts = def.bodyContacts; m_bodyVelocityConstraints = (b3ClothSolverBodyContactVelocityConstraint*)m_allocator->Allocate(m_bodyContactCount * sizeof(b3ClothSolverBodyContactVelocityConstraint)); m_bodyPositionConstraints = (b3ClothSolverBodyContactPositionConstraint*)m_allocator->Allocate(m_bodyContactCount * sizeof(b3ClothSolverBodyContactPositionConstraint)); - - m_particleContactCount = def.particleContactCount; - m_particleContacts = def.particleContacts; - m_particleVelocityConstraints = (b3ClothSolverParticleContactVelocityConstraint*)m_allocator->Allocate(m_particleContactCount * sizeof(b3ClothSolverParticleContactVelocityConstraint)); - m_particlePositionConstraints = (b3ClothSolverParticleContactPositionConstraint*)m_allocator->Allocate(m_particleContactCount * sizeof(b3ClothSolverParticleContactPositionConstraint)); } b3ClothContactSolver::~b3ClothContactSolver() { - m_allocator->Free(m_particlePositionConstraints); - m_allocator->Free(m_particleVelocityConstraints); - m_allocator->Free(m_bodyPositionConstraints); m_allocator->Free(m_bodyVelocityConstraints); } @@ -87,6 +79,7 @@ void b3ClothContactSolver::InitializeBodyContactConstraints() pc->localCenterA.SetZero(); pc->localCenterB = pc->bodyB->m_sweep.localCenter; + pc->normalA = c->n; pc->localPointA = c->localPoint1; pc->localPointB = c->localPoint2; } @@ -178,90 +171,6 @@ void b3ClothContactSolver::InitializeBodyContactConstraints() } } -void b3ClothContactSolver::InitializeParticleContactConstraints() -{ - b3DenseVec3& x = *m_positions; - b3DenseVec3& v = *m_velocities; - - for (u32 i = 0; i < m_particleContactCount; ++i) - { - b3ParticleContact* c = m_particleContacts[i]; - b3ClothSolverParticleContactVelocityConstraint* vc = m_particleVelocityConstraints + i; - b3ClothSolverParticleContactPositionConstraint* pc = m_particlePositionConstraints + i; - - vc->indexA = c->p1->m_solverId; - vc->indexB = c->p2->m_solverId; - - vc->invMassA = c->p1->m_type == e_staticParticle ? 0.0f : c->p1->m_invMass; - vc->invMassB = c->p2->m_type == e_staticParticle ? 0.0f : c->p2->m_invMass; - - vc->friction = b3MixFriction(c->p1->m_friction, c->p2->m_friction); - - pc->indexA = c->p1->m_solverId; - pc->indexB = c->p2->m_solverId; - - pc->invMassA = c->p1->m_type == e_staticParticle ? 0.0f : c->p1->m_invMass; - pc->invMassB = c->p2->m_type == e_staticParticle ? 0.0f : c->p2->m_invMass; - - pc->radiusA = c->p1->m_radius; - pc->radiusB = c->p2->m_radius; - } - - for (u32 i = 0; i < m_particleContactCount; ++i) - { - b3ParticleContact* c = m_particleContacts[i]; - b3ClothSolverParticleContactVelocityConstraint* vc = m_particleVelocityConstraints + i; - b3ClothSolverParticleContactPositionConstraint* pc = m_particlePositionConstraints + i; - - u32 indexA = vc->indexA; - u32 indexB = vc->indexB; - - float32 mA = vc->invMassA; - float32 mB = vc->invMassB; - - b3Vec3 xA = x[indexA]; - b3Vec3 xB = x[indexB]; - - b3ParticleContactWorldPoint wp; - wp.Initialize(c); - - vc->normal = wp.normal; - vc->tangent1 = c->t1; - vc->tangent2 = c->t2; - vc->point = wp.point; - - b3Vec3 point = vc->point; - - vc->normalImpulse = c->normalImpulse; - vc->tangentImpulse = c->tangentImpulse; - - { - b3Vec3 n = vc->normal; - - float32 K = mA + mB; - - vc->normalMass = K > 0.0f ? 1.0f / K : 0.0f; - - vc->velocityBias = 0.0f; - } - - { - b3Vec3 t1 = vc->tangent1; - b3Vec3 t2 = vc->tangent2; - - float32 k11 = mA + mB; - float32 k12 = 0.0f; - float32 k22 = mA + mB; - - b3Mat22 K; - K.x.Set(k11, k12); - K.y.Set(k12, k22); - - vc->tangentMass = b3Inverse(K); - } - } -} - void b3ClothContactSolver::WarmStart() { b3DenseVec3& v = *m_velocities; @@ -307,34 +216,6 @@ void b3ClothContactSolver::WarmStart() bodyB->SetLinearVelocity(vB); bodyB->SetAngularVelocity(wB); } - - for (u32 i = 0; i < m_particleContactCount; ++i) - { - b3ClothSolverParticleContactVelocityConstraint* vc = m_particleVelocityConstraints + i; - - u32 indexA = vc->indexA; - u32 indexB = vc->indexB; - - b3Vec3 vA = v[indexA]; - b3Vec3 vB = v[indexB]; - - float32 mA = vc->invMassA; - float32 mB = vc->invMassB; - - b3Vec3 P = vc->normalImpulse * vc->normal; - - vA -= mA * P; - vB += mB * P; - - b3Vec3 P1 = vc->tangentImpulse.x * vc->tangent1; - b3Vec3 P2 = vc->tangentImpulse.y * vc->tangent2; - - vA -= mA * (P1 + P2); - vB += mB * (P1 + P2); - - v[indexA] = vA; - v[indexB] = vB; - } } void b3ClothContactSolver::SolveBodyContactVelocityConstraints() @@ -425,77 +306,6 @@ void b3ClothContactSolver::SolveBodyContactVelocityConstraints() } } -void b3ClothContactSolver::SolveParticleContactVelocityConstraints() -{ - b3DenseVec3& v = *m_velocities; - - for (u32 i = 0; i < m_particleContactCount; ++i) - { - b3ClothSolverParticleContactVelocityConstraint* vc = m_particleVelocityConstraints + i; - - u32 indexA = vc->indexA; - u32 indexB = vc->indexB; - - b3Vec3 vA = v[indexA]; - b3Vec3 vB = v[indexB]; - - float32 mA = vc->invMassA; - float32 mB = vc->invMassB; - - b3Vec3 normal = vc->normal; - b3Vec3 point = vc->point; - - // Solve normal constraint. - { - b3Vec3 dv = vB - vA; - float32 Cdot = b3Dot(normal, dv); - - float32 impulse = vc->normalMass * (-Cdot + vc->velocityBias); - - float32 oldImpulse = vc->normalImpulse; - vc->normalImpulse = b3Max(vc->normalImpulse + impulse, 0.0f); - impulse = vc->normalImpulse - oldImpulse; - - b3Vec3 P = impulse * normal; - - vA -= mA * P; - vB += mB * P; - } - - // Solve tangent constraints. - { - b3Vec3 dv = vB - vA; - - b3Vec2 Cdot; - Cdot.x = b3Dot(dv, vc->tangent1); - Cdot.y = b3Dot(dv, vc->tangent2); - - b3Vec2 impulse = vc->tangentMass * -Cdot; - b3Vec2 oldImpulse = vc->tangentImpulse; - vc->tangentImpulse += impulse; - - float32 maxImpulse = vc->friction * vc->normalImpulse; - if (b3Dot(vc->tangentImpulse, vc->tangentImpulse) > maxImpulse * maxImpulse) - { - vc->tangentImpulse.Normalize(); - vc->tangentImpulse *= maxImpulse; - } - - impulse = vc->tangentImpulse - oldImpulse; - - b3Vec3 P1 = impulse.x * vc->tangent1; - b3Vec3 P2 = impulse.y * vc->tangent2; - b3Vec3 P = P1 + P2; - - vA -= mA * P; - vB += mB * P; - } - - v[indexA] = vA; - v[indexB] = vB; - } -} - void b3ClothContactSolver::StoreImpulses() { for (u32 i = 0; i < m_bodyContactCount; ++i) @@ -506,15 +316,6 @@ void b3ClothContactSolver::StoreImpulses() c->normalImpulse = vc->normalImpulse; c->tangentImpulse = vc->tangentImpulse; } - - for (u32 i = 0; i < m_particleContactCount; ++i) - { - b3ParticleContact* c = m_particleContacts[i]; - b3ClothSolverParticleContactVelocityConstraint* vc = m_particleVelocityConstraints + i; - - c->normalImpulse = vc->normalImpulse; - c->tangentImpulse = vc->tangentImpulse; - } } struct b3ClothSolverBodyContactSolverPoint @@ -526,22 +327,15 @@ struct b3ClothSolverBodyContactSolverPoint float32 rA = pc->radiusA; float32 rB = pc->radiusB; - - b3Vec3 d = cB - cA; - float32 distance = b3Length(d); - - b3Vec3 nA(0.0f, 1.0f, 0.0f); - if (distance > B3_EPSILON) - { - nA = d / distance; - } + + b3Vec3 nA = pc->normalA; b3Vec3 pA = cA + rA * nA; b3Vec3 pB = cB - rB * nA; - point = 0.5f * (pA + pB); + point = cB; normal = nA; - separation = distance - rA - rB; + separation = b3Dot(cB - cA, nA) - rA - rB; } b3Vec3 normal; @@ -623,82 +417,5 @@ bool b3ClothContactSolver::SolveBodyContactPositionConstraints() bodyB->m_sweep.orientation = qB; } - return minSeparation >= -3.0f * B3_LINEAR_SLOP; -} - -struct b3ClothSolverParticleContactSolverPoint -{ - void Initialize(const b3Vec3& cA, float32 rA, const b3Vec3& cB, float32 rB) - { - b3Vec3 d = cB - cA; - float32 distance = b3Length(d); - - b3Vec3 nA(0.0f, 1.0f, 0.0f); - if (distance > B3_EPSILON) - { - nA = d / distance; - } - - b3Vec3 pA = cA + rA * nA; - b3Vec3 pB = cB - rB * nA; - - point = 0.5f * (pA + pB); - normal = nA; - separation = distance - rA - rB; - } - - b3Vec3 point; - b3Vec3 normal; - float32 separation; -}; - -bool b3ClothContactSolver::SolveParticleContactPositionConstraints() -{ - b3DenseVec3& x = *m_positions; - - float32 minSeparation = 0.0f; - - for (u32 i = 0; i < m_particleContactCount; ++i) - { - b3ClothSolverParticleContactPositionConstraint* pc = m_particlePositionConstraints + i; - - u32 indexA = pc->indexA; - float32 mA = pc->invMassA; - float32 rA = pc->radiusA; - - u32 indexB = pc->indexB; - float32 mB = pc->invMassB; - float32 rB = pc->radiusB; - - b3Vec3 xA = x[indexA]; - b3Vec3 xB = x[indexB]; - - b3ClothSolverParticleContactSolverPoint cpcp; - cpcp.Initialize(xA, rA, xB, rB); - - b3Vec3 normal = cpcp.normal; - b3Vec3 point = cpcp.point; - float32 separation = cpcp.separation; - - // Update max constraint error. - minSeparation = b3Min(minSeparation, separation); - - // Allow some slop and prevent large corrections. - float32 C = b3Clamp(B3_BAUMGARTE * (separation + B3_LINEAR_SLOP), -B3_MAX_LINEAR_CORRECTION, 0.0f); - - // Compute effective mass. - float32 K = mA + mB; - - // Compute normal impulse. - float32 impulse = K > 0.0f ? -C / K : 0.0f; - b3Vec3 P = impulse * normal; - - xA -= mA * P; - xB += mB * P; - - x[indexA] = xA; - x[indexB] = xB; - } - return minSeparation >= -3.0f * B3_LINEAR_SLOP; } \ No newline at end of file diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index 46a7da7..1fc7718 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -57,15 +57,10 @@ b3ClothSolver::b3ClothSolver(const b3ClothSolverDef& def) m_bodyContactCapacity = def.bodyContactCapacity; m_bodyContactCount = 0; m_bodyContacts = (b3BodyContact**)m_allocator->Allocate(m_bodyContactCapacity * sizeof(b3BodyContact*));; - - m_particleContactCapacity = def.particleContactCapacity; - m_particleContactCount = 0; - m_particleContacts = (b3ParticleContact**)m_allocator->Allocate(m_particleContactCapacity * sizeof(b3ParticleContact*));; } b3ClothSolver::~b3ClothSolver() { - m_allocator->Free(m_particleContacts); m_allocator->Free(m_bodyContacts); m_allocator->Free(m_constraints); @@ -329,14 +324,11 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) contactSolverDef.velocities = m_solverData.v; contactSolverDef.bodyContactCount = m_bodyContactCount; contactSolverDef.bodyContacts = m_bodyContacts; - contactSolverDef.particleContactCount = m_particleContactCount; - contactSolverDef.particleContacts = m_particleContacts; b3ClothContactSolver contactSolver(contactSolverDef); { contactSolver.InitializeBodyContactConstraints(); - contactSolver.InitializeParticleContactConstraints(); } { @@ -350,7 +342,6 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) for (u32 i = 0; i < kVelocityIterations; ++i) { contactSolver.SolveBodyContactVelocityConstraints(); - contactSolver.SolveParticleContactVelocityConstraints(); } } @@ -371,9 +362,8 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) for (u32 i = 0; i < kPositionIterations; ++i) { bool bodyContactsSolved = contactSolver.SolveBodyContactPositionConstraints(); - bool particleContactsSolved = contactSolver.SolveParticleContactPositionConstraints(); - if (bodyContactsSolved && particleContactsSolved) + if (bodyContactsSolved) { positionSolved = true; break; diff --git a/src/bounce/cloth/particle.cpp b/src/bounce/cloth/particle.cpp index 7dde875..1b3fa6f 100644 --- a/src/bounce/cloth/particle.cpp +++ b/src/bounce/cloth/particle.cpp @@ -27,46 +27,14 @@ void b3BodyContactWorldPoint::Initialize(const b3BodyContact* c, float32 rA, con b3Vec3 cA = b3Mul(xfA, c->localPoint1); b3Vec3 cB = b3Mul(xfB, c->localPoint2); - b3Vec3 d = cB - cA; - float32 distance = b3Length(d); - - b3Vec3 nA(0.0f, 1.0f, 0.0f); - if (distance > B3_EPSILON) - { - nA = d / distance; - } - - b3Vec3 pA = cA + rA * nA; - b3Vec3 pB = cB - rB * nA; - - point = 0.5f * (pA + pB); - normal = nA; - separation = distance - rA - rB; -} - -void b3ParticleContactWorldPoint::Initialize(const b3ParticleContact* c) -{ - b3Vec3 cA = c->p1->GetPosition(); - float32 rA = c->p1->GetRadius(); - - b3Vec3 cB = c->p2->GetPosition(); - float32 rB = c->p2->GetRadius(); - - b3Vec3 d = cB - cA; - float32 distance = b3Length(d); - - b3Vec3 nA(0.0f, 1.0f, 0.0f); - if (distance > B3_EPSILON) - { - nA = d / distance; - } + b3Vec3 nA = c->n; b3Vec3 pA = cA + rA * nA; b3Vec3 pB = cB - rB * nA; point = 0.5f * (pA + pB); normal = nA; - separation = distance - rA - rB; + separation = b3Dot(cB - cA, nA) - rA - rB; } b3Particle::b3Particle(const b3ParticleDef& def, b3Cloth* cloth) From 695514989eab1578d9b21580b49370dd2ca236ec Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 18 Apr 2019 18:51:49 -0300 Subject: [PATCH 062/198] Removed a large cloth radius. The particle radius should be used to avoid visual artifacts and is a small value in typical situations. --- examples/testbed/tests/table_cloth.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/testbed/tests/table_cloth.h b/examples/testbed/tests/table_cloth.h index 71ee69d..32fd016 100644 --- a/examples/testbed/tests/table_cloth.h +++ b/examples/testbed/tests/table_cloth.h @@ -53,7 +53,7 @@ public: for (b3Particle* p = m_cloth->GetParticleList().m_head; p; p = p->GetNext()) { - p->SetRadius(0.2f); + p->SetRadius(0.05f); p->SetFriction(0.2f); } @@ -67,7 +67,6 @@ public: b3HullShape tableShape; tableShape.m_hull = &m_tableHull; - tableShape.m_radius = 0.2f; b3ShapeDef sd; sd.shape = &tableShape; From 399a6efc7200edb7933b09ed0c81fe9cd64dde70 Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 18 Apr 2019 19:44:12 -0300 Subject: [PATCH 063/198] Use sphere mesh. Add soft body test. --- examples/testbed/framework/draw_gl2.h | 145 ++-------- examples/testbed/framework/draw_gl4.h | 145 ++-------- examples/testbed/framework/sphere_mesh.cpp | 149 +++++++++++ examples/testbed/framework/sphere_mesh.h | 113 ++++++++ examples/testbed/framework/test_entries.cpp | 8 +- examples/testbed/tests/mass_spring.h | 158 ----------- examples/testbed/tests/shirt.h | 142 ---------- examples/testbed/tests/single_pendulum.h | 99 ------- examples/testbed/tests/soft_body.h | 278 ++++++++++++++++++++ premake5.lua | 4 + 10 files changed, 588 insertions(+), 653 deletions(-) create mode 100644 examples/testbed/framework/sphere_mesh.cpp create mode 100644 examples/testbed/framework/sphere_mesh.h delete mode 100644 examples/testbed/tests/mass_spring.h delete mode 100644 examples/testbed/tests/shirt.h delete mode 100644 examples/testbed/tests/single_pendulum.h create mode 100644 examples/testbed/tests/soft_body.h diff --git a/examples/testbed/framework/draw_gl2.h b/examples/testbed/framework/draw_gl2.h index 3cf7810..20ed762 100644 --- a/examples/testbed/framework/draw_gl2.h +++ b/examples/testbed/framework/draw_gl2.h @@ -28,6 +28,8 @@ #include #include +#include + #define BUFFER_OFFSET(i) ((char*)NULL + (i)) extern bool g_glDrawPoints; @@ -535,72 +537,22 @@ struct DrawTriangles struct DrawWireSphere { - enum - { - e_rings = 12, - e_sectors = 12, - e_vertexCount = e_rings * e_sectors, - e_indexCount = (e_rings - 1) * (e_sectors - 1) * 8 - }; - DrawWireSphere() { - float32 R = 1.0f / float32(e_rings - 1); - float32 S = 1.0f / float32(e_sectors - 1); + smMesh mesh; + smCreateMesh(mesh, 1); - b3Vec3 vs[e_vertexCount]; - b3Vec3 ns[e_vertexCount]; - b3Color cs[e_vertexCount]; - - u32 vc = 0; - for (u32 r = 0; r < e_rings; r++) - { - for (u32 s = 0; s < e_sectors; s++) - { - float32 y = sin(-0.5f * B3_PI + B3_PI * r * R); - float32 x = cos(2.0f * B3_PI * s * S) * sin(B3_PI * r * R); - float32 z = sin(2.0f * B3_PI * s * S) * sin(B3_PI * r * R); - - vs[vc].Set(x, y, z); - cs[vc] = b3Color(1.0f, 1.0f, 1.0f, 1.0f); - ++vc; - } - } - - u32 is[e_indexCount]; - - u32 ic = 0; - for (u32 r = 0; r < e_rings - 1; r++) - { - for (u32 s = 0; s < e_sectors - 1; s++) - { - u32 i1 = r * e_sectors + s; - u32 i2 = (r + 1) * e_sectors + s; - u32 i3 = (r + 1) * e_sectors + (s + 1); - u32 i4 = r * e_sectors + (s + 1); - - is[ic++] = i1; - is[ic++] = i2; - - is[ic++] = i2; - is[ic++] = i3; - - is[ic++] = i3; - is[ic++] = i4; - - is[ic++] = i4; - is[ic++] = i1; - } - } + m_vertexCount = mesh.vertices.Count(); + m_indexCount = mesh.triangleIndices.Count(); glGenBuffers(1, &m_vboId); glGenBuffers(1, &m_iboId); glBindBuffer(GL_ARRAY_BUFFER, m_vboId); - glBufferData(GL_ARRAY_BUFFER, vc * sizeof(b3Vec3), vs, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, mesh.vertices.Count() * sizeof(b3Vec3), mesh.vertices.Begin(), GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_iboId); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, ic * sizeof(u32), is, GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.triangleIndices.Count() * sizeof(u32), mesh.triangleIndices.Begin(), GL_STATIC_DRAW); AssertGL(); @@ -616,6 +568,8 @@ struct DrawWireSphere GLuint m_vboId; GLuint m_iboId; + u32 m_vertexCount; + u32 m_indexCount; }; struct DrawWire @@ -677,7 +631,7 @@ struct DrawWire glEnableVertexAttribArray(m_vertexAttribute); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_sphere.m_iboId); - glDrawElements(GL_LINES, m_sphere.e_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0)); + glDrawElements(GL_LINES, m_sphere.m_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0)); glDisableVertexAttribArray(m_vertexAttribute); @@ -697,84 +651,25 @@ struct DrawWire struct DrawSolidSphere { - enum - { - e_rings = 18, - e_sectors = 18, - e_vertexCount = e_rings * e_sectors, - e_indexCount = (e_rings - 1) * (e_sectors - 1) * 6, - e_faceCount = e_indexCount / 3 - }; - DrawSolidSphere() { - float32 R = 1.0f / float32(e_rings - 1); - float32 S = 1.0f / float32(e_sectors - 1); + smMesh mesh; + smCreateMesh(mesh, 1); - b3Vec3 vs[e_vertexCount]; - b3Vec3 ns[e_vertexCount]; - - u32 vc = 0; - for (u32 r = 0; r < e_rings; r++) - { - for (u32 s = 0; s < e_sectors; s++) - { - float32 a1 = 2.0f * B3_PI * float32(s) * S; - float32 c1 = cos(a1); - float32 s1 = sin(a1); - - float32 a2 = -0.5f * B3_PI + B3_PI * float32(r) * R; - float32 s2 = sin(a2); - - float32 a3 = B3_PI * float32(r) * R; - float32 s3 = sin(a3); - - float32 x = c1 * s3; - float32 y = s2; - float32 z = s1 * s3; - - b3Vec3 v(x, y, z); - v.Normalize(); - - vs[vc] = v; - ns[vc] = v; - ++vc; - } - } - - u32 is[e_indexCount]; - - u32 ic = 0; - for (u32 r = 0; r < e_rings - 1; r++) - { - for (u32 s = 0; s < e_sectors - 1; s++) - { - u32 i1 = r * e_sectors + s; - u32 i2 = (r + 1) * e_sectors + s; - u32 i3 = (r + 1) * e_sectors + (s + 1); - u32 i4 = r * e_sectors + (s + 1); - - is[ic++] = i1; - is[ic++] = i2; - is[ic++] = i3; - - is[ic++] = i1; - is[ic++] = i3; - is[ic++] = i4; - } - } + m_vertexCount = mesh.vertices.Count(); + m_indexCount = mesh.triangleIndices.Count(); glGenBuffers(3, m_vboIds); glGenBuffers(1, &m_iboId); glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]); - glBufferData(GL_ARRAY_BUFFER, vc * sizeof(b3Vec3), vs, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, mesh.vertices.Count() * sizeof(b3Vec3), mesh.vertices.Begin(), GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]); - glBufferData(GL_ARRAY_BUFFER, vc * sizeof(b3Vec3), ns, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, mesh.vertices.Count() * sizeof(b3Vec3), mesh.vertices.Begin(), GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_iboId); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, ic * sizeof(u32), is, GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.triangleIndices.Count() * sizeof(u32), mesh.triangleIndices.Begin(), GL_STATIC_DRAW); AssertGL(); @@ -790,6 +685,8 @@ struct DrawSolidSphere GLuint m_vboIds[2]; GLuint m_iboId; + u32 m_vertexCount; + u32 m_indexCount; }; struct DrawSolidCylinder @@ -1021,7 +918,7 @@ struct DrawSolid glEnableVertexAttribArray(m_normalAttribute); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_sphere.m_iboId); - glDrawElements(GL_TRIANGLES, m_sphere.e_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0)); + glDrawElements(GL_TRIANGLES, m_sphere.m_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0)); glDisableVertexAttribArray(m_normalAttribute); diff --git a/examples/testbed/framework/draw_gl4.h b/examples/testbed/framework/draw_gl4.h index 1cc6261..0838b3c 100644 --- a/examples/testbed/framework/draw_gl4.h +++ b/examples/testbed/framework/draw_gl4.h @@ -28,6 +28,8 @@ #include #include +#include + #define BUFFER_OFFSET(i) ((char*)NULL + (i)) extern bool g_glDrawPoints; @@ -551,72 +553,22 @@ struct DrawTriangles struct DrawWireSphere { - enum - { - e_rings = 12, - e_sectors = 12, - e_vertexCount = e_rings * e_sectors, - e_indexCount = (e_rings - 1) * (e_sectors - 1) * 8 - }; - DrawWireSphere() { - float32 R = 1.0f / float32(e_rings - 1); - float32 S = 1.0f / float32(e_sectors - 1); + smMesh mesh; + smCreateMesh(mesh, 1); - b3Vec3 vs[e_vertexCount]; - b3Vec3 ns[e_vertexCount]; - b3Color cs[e_vertexCount]; - - u32 vc = 0; - for (u32 r = 0; r < e_rings; r++) - { - for (u32 s = 0; s < e_sectors; s++) - { - float32 y = sin(-0.5f * B3_PI + B3_PI * r * R); - float32 x = cos(2.0f * B3_PI * s * S) * sin(B3_PI * r * R); - float32 z = sin(2.0f * B3_PI * s * S) * sin(B3_PI * r * R); - - vs[vc].Set(x, y, z); - cs[vc] = b3Color(1.0f, 1.0f, 1.0f, 1.0f); - ++vc; - } - } - - u32 is[e_indexCount]; - - u32 ic = 0; - for (u32 r = 0; r < e_rings - 1; r++) - { - for (u32 s = 0; s < e_sectors - 1; s++) - { - u32 i1 = r * e_sectors + s; - u32 i2 = (r + 1) * e_sectors + s; - u32 i3 = (r + 1) * e_sectors + (s + 1); - u32 i4 = r * e_sectors + (s + 1); - - is[ic++] = i1; - is[ic++] = i2; - - is[ic++] = i2; - is[ic++] = i3; - - is[ic++] = i3; - is[ic++] = i4; - - is[ic++] = i4; - is[ic++] = i1; - } - } + m_vertexCount = mesh.vertices.Count(); + m_indexCount = mesh.triangleIndices.Count(); glGenBuffers(1, &m_vboId); glGenBuffers(1, &m_iboId); glBindBuffer(GL_ARRAY_BUFFER, m_vboId); - glBufferData(GL_ARRAY_BUFFER, vc * sizeof(b3Vec3), vs, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.vertices.Begin(), GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_iboId); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, ic * sizeof(u32), is, GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(u32), mesh.triangleIndices.Begin(), GL_STATIC_DRAW); AssertGL(); @@ -632,6 +584,8 @@ struct DrawWireSphere GLuint m_vboId; GLuint m_iboId; + u32 m_vertexCount; + u32 m_indexCount; }; struct DrawWire @@ -694,7 +648,7 @@ struct DrawWire glEnableVertexAttribArray(m_vertexAttribute); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_sphere.m_iboId); - glDrawElements(GL_LINES, m_sphere.e_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0)); + glDrawElements(GL_LINES, m_sphere.m_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -712,84 +666,25 @@ struct DrawWire struct DrawSolidSphere { - enum - { - e_rings = 18, - e_sectors = 18, - e_vertexCount = e_rings * e_sectors, - e_indexCount = (e_rings - 1) * (e_sectors - 1) * 6, - e_faceCount = e_indexCount / 3 - }; - DrawSolidSphere() { - float32 R = 1.0f / float32(e_rings - 1); - float32 S = 1.0f / float32(e_sectors - 1); + smMesh mesh; + smCreateMesh(mesh, 1); - b3Vec3 vs[e_vertexCount]; - b3Vec3 ns[e_vertexCount]; - - u32 vc = 0; - for (u32 r = 0; r < e_rings; r++) - { - for (u32 s = 0; s < e_sectors; s++) - { - float32 a1 = 2.0f * B3_PI * float32(s) * S; - float32 c1 = cos(a1); - float32 s1 = sin(a1); - - float32 a2 = -0.5f * B3_PI + B3_PI * float32(r) * R; - float32 s2 = sin(a2); - - float32 a3 = B3_PI * float32(r) * R; - float32 s3 = sin(a3); - - float32 x = c1 * s3; - float32 y = s2; - float32 z = s1 * s3; - - b3Vec3 v(x, y, z); - v.Normalize(); - - vs[vc] = v; - ns[vc] = v; - ++vc; - } - } - - u32 is[e_indexCount]; - - u32 ic = 0; - for (u32 r = 0; r < e_rings - 1; r++) - { - for (u32 s = 0; s < e_sectors - 1; s++) - { - u32 i1 = r * e_sectors + s; - u32 i2 = (r + 1) * e_sectors + s; - u32 i3 = (r + 1) * e_sectors + (s + 1); - u32 i4 = r * e_sectors + (s + 1); - - is[ic++] = i1; - is[ic++] = i2; - is[ic++] = i3; - - is[ic++] = i1; - is[ic++] = i3; - is[ic++] = i4; - } - } + m_vertexCount = mesh.vertices.Count(); + m_indexCount = mesh.triangleIndices.Count(); glGenBuffers(3, m_vboIds); glGenBuffers(1, &m_iboId); glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]); - glBufferData(GL_ARRAY_BUFFER, vc * sizeof(b3Vec3), vs, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.vertices.Begin(), GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]); - glBufferData(GL_ARRAY_BUFFER, vc * sizeof(b3Vec3), ns, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.vertices.Begin(), GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_iboId); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, ic * sizeof(u32), is, GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(u32), mesh.triangleIndices.Begin(), GL_STATIC_DRAW); AssertGL(); @@ -805,6 +700,8 @@ struct DrawSolidSphere GLuint m_vboIds[2]; GLuint m_iboId; + u32 m_vertexCount; + u32 m_indexCount; }; struct DrawSolidCylinder @@ -1033,7 +930,7 @@ struct DrawSolid glEnableVertexAttribArray(m_normalAttribute); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_sphere.m_iboId); - glDrawElements(GL_TRIANGLES, m_sphere.e_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0)); + glDrawElements(GL_TRIANGLES, m_sphere.m_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); diff --git a/examples/testbed/framework/sphere_mesh.cpp b/examples/testbed/framework/sphere_mesh.cpp new file mode 100644 index 0000000..59118e3 --- /dev/null +++ b/examples/testbed/framework/sphere_mesh.cpp @@ -0,0 +1,149 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include + +// References: +// https://github.com/caosdoar/spheres +// https://schneide.blog/2016/07/15/generating-an-icosphere-in-c/ + +struct smEdge +{ + smEdge() { } + + smEdge(u32 _v1, u32 _v2) + { + v1 = _v1; + v2 = _v2; + } + + u32 v1, v2; +}; + +struct smEdgeVertexPair +{ + smEdgeVertexPair() { } + + smEdgeVertexPair(const smEdge& _edge, u32 _vertex) + { + edge = _edge; + vertex = _vertex; + } + + smEdge edge; + u32 vertex; +}; + +struct smEdgeVertexMap +{ + smEdgeVertexMap() { } + + smEdgeVertexPair* Find(const smEdge& edge) + { + for (u32 i = 0; i < pairs.Count(); ++i) + { + smEdgeVertexPair* pair = pairs.Get(i); + + if ((pair->edge.v1 == edge.v1 && pair->edge.v2 == edge.v2) || + (pair->edge.v1 == edge.v2 && pair->edge.v2 == edge.v1) ) + { + return pair; + } + } + return nullptr; + } + + b3StackArray pairs; +}; + +// +static inline u32 smSubdivideEdge(u32 i1, u32 i2, + const b3Vec3& v1, const b3Vec3& v2, + smEdgeVertexMap& edgeVertexMap, + smMesh& output) +{ + smEdge edge(i1, i2); + + smEdgeVertexPair* pair = edgeVertexMap.Find(edge); + + if (pair) + { + return pair->vertex; + } + + smEdge newEdge(i1, i2); + u32 newVertex = output.vertices.Count(); + + b3Vec3 v = 0.5f * (v1 + v2); + float32 len = v.Normalize(); + + output.AddVertex(v.x, v.y, v.z); + + smEdgeVertexPair newPair(newEdge, newVertex); + + edgeVertexMap.pairs.PushBack(newPair); + + return newVertex; +} + +// +static inline void smSubdivideMesh(smMesh& output, const smMesh& input) +{ + output.vertices = input.vertices; + + smEdgeVertexMap edgeVertexMap; + + for (u32 i = 0; i < input.triangleIndices.Count() / 3; ++i) + { + u32 vi1 = input.triangleIndices[3 * i + 0]; + u32 vi2 = input.triangleIndices[3 * i + 1]; + u32 vi3 = input.triangleIndices[3 * i + 2]; + + b3Vec3 v1 = input.vertices[vi1]; + b3Vec3 v2 = input.vertices[vi2]; + b3Vec3 v3 = input.vertices[vi3]; + + u32 vi4 = smSubdivideEdge(vi1, vi2, v1, v2, edgeVertexMap, output); + u32 vi5 = smSubdivideEdge(vi2, vi3, v2, v3, edgeVertexMap, output); + u32 vi6 = smSubdivideEdge(vi3, vi1, v3, v1, edgeVertexMap, output); + + output.AddTriangle(vi1, vi4, vi6); + output.AddTriangle(vi4, vi2, vi5); + output.AddTriangle(vi5, vi3, vi6); + output.AddTriangle(vi4, vi5, vi6); + } +} + +// +void smCreateMesh(smMesh& output, u32 subdivisions) +{ + B3_ASSERT(output.vertices.Count() == 0); + B3_ASSERT(output.triangleIndices.Count() == 0); + + smMesh input; + input.SetAsIcosahedron(); + + for (u32 i = 0; i < subdivisions; ++i) + { + smMesh subOutput; + smSubdivideMesh(subOutput, input); + input = subOutput; + } + + output = input; +} diff --git a/examples/testbed/framework/sphere_mesh.h b/examples/testbed/framework/sphere_mesh.h new file mode 100644 index 0000000..0e0b2d9 --- /dev/null +++ b/examples/testbed/framework/sphere_mesh.h @@ -0,0 +1,113 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SPHERE_MESH_H +#define SPHERE_MESH_H + +#include + +// +struct smMesh +{ + smMesh() { } + + smMesh& operator=(const smMesh& other) + { + Copy(other); + return *this; + } + + void Copy(const smMesh& other) + { + vertices = other.vertices; + triangleIndices = other.triangleIndices; + } + + void AddVertex(float32 x, float32 y, float32 z) + { + vertices.PushBack(b3Vec3(x, y, z)); + } + + void AddTriangle(u32 v1, u32 v2, u32 v3) + { + triangleIndices.PushBack(v1); + triangleIndices.PushBack(v2); + triangleIndices.PushBack(v3); + } + + void SetAsIcosahedron() + { + assert(vertices.Count() == 0); + + float32 t = 0.5f * (1.0f + b3Sqrt(5.0f)); + + AddVertex(-1.0f, t, 0.0f); + AddVertex(1.0f, t, 0.0f); + AddVertex(-1.0f, -t, 0.0f); + AddVertex(1.0f, -t, 0.0f); + + AddVertex(0.0f, -1.0f, t); + AddVertex(0.0f, 1.0f, t); + AddVertex(0.0f, -1.0f, -t); + AddVertex(0.0f, 1.0f, -t); + + AddVertex(t, 0.0f, -1.0f); + AddVertex(t, 0.0f, 1.0f); + AddVertex(-t, 0.0f, -1.0f); + AddVertex(-t, 0.0f, 1.0f); + + for (u32 i = 0; i < vertices.Count(); ++i) + { + vertices[i].Normalize(); + } + + assert(triangleIndices.Count() == 0); + + AddTriangle(0, 11, 5); + AddTriangle(0, 5, 1); + AddTriangle(0, 1, 7); + AddTriangle(0, 7, 10); + AddTriangle(0, 10, 11); + + AddTriangle(1, 5, 9); + AddTriangle(5, 11, 4); + AddTriangle(11, 10, 2); + AddTriangle(10, 7, 6); + AddTriangle(7, 1, 8); + + AddTriangle(3, 9, 4); + AddTriangle(3, 4, 2); + AddTriangle(3, 2, 6); + AddTriangle(3, 6, 8); + AddTriangle(3, 8, 9); + + AddTriangle(4, 9, 5); + AddTriangle(2, 4, 11); + AddTriangle(6, 2, 10); + AddTriangle(8, 6, 7); + AddTriangle(9, 8, 1); + } + + b3StackArray vertices; + b3StackArray triangleIndices; +}; + +// Generate an icosphere given the number of subdivisions. +void smCreateMesh(smMesh& output, u32 subdivisions); + +#endif SPHERE_MESH_H \ No newline at end of file diff --git a/examples/testbed/framework/test_entries.cpp b/examples/testbed/framework/test_entries.cpp index 8526371..d0aaca9 100644 --- a/examples/testbed/framework/test_entries.cpp +++ b/examples/testbed/framework/test_entries.cpp @@ -62,13 +62,11 @@ #include #include #include -#include #include #include #include -#include +#include #include -#include TestEntry g_tests[] = { @@ -116,12 +114,10 @@ TestEntry g_tests[] = { "Multiple Pendulum", &MultiplePendulum::Create }, { "Table Cloth", &TableCloth::Create }, { "Pinned Cloth", &PinnedCloth::Create }, - { "Shirt", &Shirt::Create }, { "Particle Types", &ParticleTypes::Create }, { "Tension Mapping", &TensionMapping::Create }, { "Self-Collision", &SelfCollision::Create }, - { "Mass-Spring System", &MassSpring::Create }, - { "Single Pendulum", &SinglePendulum::Create }, + { "Soft Body", &SoftBody::Create }, { "Rope", &Rope::Create }, { NULL, NULL } }; diff --git a/examples/testbed/tests/mass_spring.h b/examples/testbed/tests/mass_spring.h deleted file mode 100644 index dac0e58..0000000 --- a/examples/testbed/tests/mass_spring.h +++ /dev/null @@ -1,158 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef MASS_SPRING_H -#define MASS_SPRING_H - -class MassSpring : public Test -{ -public: - MassSpring() - { - m_x.Set(0.0f, 5.0f, 0.0f); - - m_v.SetZero(); - - m_k = 100.0f; - - m_iterations = 0; - } - - void Solve(float32 h) - { - // ODE - // f(Y) = dY / dt = [v] - // [-k * x] - - // 1. Apply Implicit Euler - - // Y(t + h) = Y(t) + h * f( Y(t + h) ) - // G( Y(t + h) ) = Y(t + h) - Y(t) - h * f( Y(t + h) ) = 0 - - // 2. Solve G = 0 - - // Newton-Raphson Iteration - // - // Y(t + h) = - // Y(t + h)_0 - G( Y(t + h)_0 ) / G'( Y(t + h)_0 ) = - // Y(t + h)_0 - G'( Y(t + h)_0 )^-1 * ( Y(t + h)_0 - Y(t) - h * f( Y(t + h)_0 ) - - // G'( Y ) = I - h * del_f / del_Y - - // del_f / del_Y = [del_f1 / del_x del_f1 / del_v] = [0 I] - // [del_f2 / del_x del_f2 / del_v] [-k * I 0] - - // G'( Y ) = [I 0] - [0 h * I] = [I -h * I] - // [0 I] [-h * k * I 0] [h * k * I I] - - // Compute Jacobian - b3Mat33 I = b3Mat33_identity; - - b3Mat33 A, B, C, D; - - A = I; - B = -h * I; - C = h * m_k * I; - D = I; - - // Invert - // Block matrix inversion - b3Mat33 invD = b3Inverse(D); - b3Mat33 B_invD = B * invD; - - b3Mat33 invJ_A = b3Inverse(A - B_invD * C); - b3Mat33 invJ_B = -invJ_A * B_invD; - b3Mat33 invJ_C = -invD * C * invJ_A; - b3Mat33 invJ_D = invD + invD * C * invJ_A * B_invD; - - // Initial guess - b3Vec3 f1 = m_v; - b3Vec3 f2 = -m_k * m_x; - - b3Vec3 Y1 = m_x + h * f1; - b3Vec3 Y2 = m_v + h * f2; - - const float32 kTol = 0.05f; - - const u32 kMaxIterations = 20; - - float32 eps0 = 0.0f; - - float32 eps1 = B3_MAX_FLOAT; - - m_iterations = 0; - - while (m_iterations < kMaxIterations && eps1 > kTol * kTol * eps0) - { - // Evaluate f(Y_n-1) - f1 = Y2; - f2 = -m_k * Y1; - - // Residual vector - b3Vec3 G1 = Y1 - m_x - h * f1; - b3Vec3 G2 = Y2 - m_v - h * f2; - - eps1 = b3Dot(G1, G1) + b3Dot(G2, G2); - - // Solve Ax = b - b3Vec3 x1 = invJ_A * G1 + invJ_B * G2; - b3Vec3 x2 = invJ_C * G1 + invJ_D * G2; - - Y1 -= x1; - Y2 -= x2; - - ++m_iterations; - } - - // Update state - m_x = Y1; - m_v = Y2; - } - - void Step() - { - float32 h = g_testSettings->inv_hertz; - - Solve(h); - - g_draw->DrawSolidSphere(m_x, 0.25f, b3Color_white); - - g_draw->DrawSegment(b3Vec3_zero, m_x, b3Color_white); - - g_draw->DrawString(b3Color_white, "Iterations = %u", m_iterations); - - float32 E = 0.5f * b3Dot(m_v, m_v); - g_draw->DrawString(b3Color_white, "E = %f", E); - } - - static Test* Create() - { - return new MassSpring(); - } - - // State - b3Vec3 m_x, m_v; - - // Stiffness - float32 m_k; - - // - u32 m_iterations; -}; - -#endif \ No newline at end of file diff --git a/examples/testbed/tests/shirt.h b/examples/testbed/tests/shirt.h deleted file mode 100644 index f081326..0000000 --- a/examples/testbed/tests/shirt.h +++ /dev/null @@ -1,142 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef SHIRT_H -#define SHIRT_H - -class Shirt : public Test -{ -public: - Shirt() - { - // Generate 2D mesh - m_shirtGarmentMesh.Set(&m_shirtGarment, 0.1f); - - // Create 3D mesh - m_shirtClothMesh.Set(&m_shirtGarmentMesh); - - // Create cloth - b3ClothDef def; - def.mesh = &m_shirtClothMesh; - def.density = 0.2f; - def.structural = 1000.0f; - - m_cloth = new b3Cloth(def); - - // Perform fitting - for (u32 i = 0; i < 3; ++i) - { - b3ClothMeshMesh* front = m_shirtClothMesh.meshes + i; - for (u32 j = 0; j < front->vertexCount; ++j) - { - u32 v = front->startVertex + j; - b3Particle* p = m_cloth->GetVertexParticle(v); - - b3Vec3 x = p->GetPosition(); - x.z = -1.0f; - - p->SetPosition(x); - } - } - - for (u32 i = 3; i < 6; ++i) - { - b3ClothMeshMesh* back = m_shirtClothMesh.meshes + i; - for (u32 j = 0; j < back->vertexCount; ++j) - { - u32 v = back->startVertex + j; - b3Particle* p = m_cloth->GetVertexParticle(v); - - b3Vec3 x = p->GetPosition(); - x.z = 1.0f; - - p->SetPosition(x); - } - } - - m_cloth->SetGravity(b3Vec3(0.0f, -9.8f, 0.0f)); - m_cloth->SetWorld(&m_world); - - m_clothDragger = new b3ClothDragger(&m_ray, m_cloth); - } - - ~Shirt() - { - delete m_clothDragger; - delete m_cloth; - } - - void Step() - { - Test::Step(); - - m_cloth->Step(g_testSettings->inv_hertz); - - m_cloth->Draw(); - - extern u32 b3_clothSolverIterations; - g_draw->DrawString(b3Color_white, "Iterations = %d", b3_clothSolverIterations); - - float32 E = m_cloth->GetEnergy(); - g_draw->DrawString(b3Color_white, "E = %f", E); - } - - void MouseMove(const b3Ray3& pw) - { - Test::MouseMove(pw); - - if (m_clothDragger->IsDragging() == true) - { - m_clothDragger->Drag(); - } - } - - void MouseLeftDown(const b3Ray3& pw) - { - Test::MouseLeftDown(pw); - - if (m_clothDragger->IsDragging() == false) - { - m_clothDragger->StartDragging(); - } - } - - void MouseLeftUp(const b3Ray3& pw) - { - Test::MouseLeftUp(pw); - - if (m_clothDragger->IsDragging() == true) - { - m_clothDragger->StopDragging(); - } - } - - static Test* Create() - { - return new Shirt(); - } - - b3ShirtGarment m_shirtGarment; - b3GarmentMesh m_shirtGarmentMesh; - b3GarmentClothMesh m_shirtClothMesh; - - b3Cloth* m_cloth; - b3ClothDragger* m_clothDragger; -}; - -#endif \ No newline at end of file diff --git a/examples/testbed/tests/single_pendulum.h b/examples/testbed/tests/single_pendulum.h deleted file mode 100644 index 3808548..0000000 --- a/examples/testbed/tests/single_pendulum.h +++ /dev/null @@ -1,99 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef PENDULUM_H -#define PENDULUM_H - -class SinglePendulum : public Test -{ -public: - SinglePendulum() - { - m_g = -10.0f; - - m_r = 10.0f; - m_m = 1.0f; - m_I = m_m * m_r * m_r; - - // Initial state - m_theta = -0.5f * B3_PI; - m_omega = 0.0f; - } - - void Step() - { - float32 h = g_testSettings->inv_hertz; - - // Solution (acceleration) - float32 omega_dot = -m_g / m_r * sin(m_theta); - - // Integrate acceleration - m_omega += h * omega_dot; - - // Integrate velocity - m_theta += h * m_omega; - - // Convert from polar coordinates (r, theta) to Cartesian coordinates (x, y) - b3Vec3 c; - c.x = m_r * sin(m_theta); - c.y = m_r * cos(m_theta); - c.z = 0.0f; - g_draw->DrawSolidSphere(c, 1.0f, b3Color_white); - - b3Vec3 pole; - pole.SetZero(); - g_draw->DrawSegment(pole, c, b3Color_white); - - // Kinetic energy - float32 T = 0.5f * m_I * m_omega * m_omega; - - // Potential energy - float32 V = -m_m * m_g * m_r * cos(m_theta); - - // Lagrangian - float32 L = T - V; - - // - g_draw->DrawString(b3Color_white, "T = %f \nV = %f \nL = %f", T, V, L); - } - - static Test* Create() - { - return new SinglePendulum(); - } - - // Gravity - float32 m_g; - - // Mass, inertia - float32 m_m, m_I; - - // Radial coordinate - float32 m_r; - - // The allowable generalized coordinate in polar coordinate frame. - // Only motions satisfying the constraints can be described - // in this frame. Therefore, all solutions satisfy the constraints. - // This is the so called reduced coordinates approach. - float32 m_theta; - - // Velocity - float32 m_omega; -}; - -#endif \ No newline at end of file diff --git a/examples/testbed/tests/soft_body.h b/examples/testbed/tests/soft_body.h new file mode 100644 index 0000000..d12fdc8 --- /dev/null +++ b/examples/testbed/tests/soft_body.h @@ -0,0 +1,278 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SOFT_BODY_H +#define SOFT_BODY_H + +#include + +struct b3SphereClothMesh : public b3ClothMesh +{ + b3StackArray sphereVertices; + b3StackArray sphereTriangles; + b3ClothMeshMesh sphereMesh; + + b3SphereClothMesh() + { + smMesh mesh; + smCreateMesh(mesh, 1); + + sphereVertices.Resize(mesh.vertices.Count()); + for (u32 i = 0; i < mesh.vertices.Count(); ++i) + { + sphereVertices[i] = mesh.vertices[i]; + } + + sphereTriangles.Resize(mesh.triangleIndices.Count() / 3); + for (u32 i = 0; i < mesh.triangleIndices.Count() / 3; ++i) + { + sphereTriangles[i].v1 = mesh.triangleIndices[3 * i + 0]; + sphereTriangles[i].v2 = mesh.triangleIndices[3 * i + 1]; + sphereTriangles[i].v3 = mesh.triangleIndices[3 * i + 2]; + } + + sphereMesh.startTriangle = 0; + sphereMesh.triangleCount = sphereTriangles.Count(); + sphereMesh.startVertex = 0; + sphereMesh.vertexCount = sphereVertices.Count(); + + vertexCount = sphereVertices.Count(); + vertices = sphereVertices.Begin(); + triangleCount = sphereTriangles.Count(); + triangles = sphereTriangles.Begin(); + meshCount = 1; + meshes = &sphereMesh; + sewingLineCount = 0; + sewingLines = nullptr; + } +}; + +class SoftBody : public Test +{ +public: + SoftBody() + { + // Scale and translate the cloth mesh upwards + for (u32 i = 0; i < m_mesh.vertexCount; ++i) + { + m_mesh.vertices[i] *= 2.0f; + m_mesh.vertices[i].y += 10.0f; + } + + // Create cloth + b3ClothDef def; + def.mesh = &m_mesh; + def.density = 0.2f; + def.structural = 10000.0f; + def.bending = 0.0f; + def.damping = 0.0f; + + m_cloth = new b3Cloth(def); + + for (b3Particle* p = m_cloth->GetParticleList().m_head; p; p = p->GetNext()) + { + p->SetRadius(0.05f); + p->SetFriction(0.5f); + } + + b3Vec3 gravity(0.0f, -9.8f, 0.0f); + + m_cloth->SetGravity(gravity); + + // Attach a world to the cloth + m_cloth->SetWorld(&m_world); + + // Create static shapes + { + b3BodyDef bd; + bd.type = e_staticBody; + + b3Body* b = m_world.CreateBody(bd); + + static b3BoxHull boxHull(10.0f, 1.0f, 10.0f); + + b3HullShape boxShape; + boxShape.m_hull = &boxHull; + + b3ShapeDef sd; + sd.shape = &boxShape; + sd.friction = 0.5f; + + b->CreateShape(sd); + } + + m_clothDragger = new b3ClothDragger(&m_ray, m_cloth); + } + + ~SoftBody() + { + delete m_clothDragger; + delete m_cloth; + } + + // Compute the volume of soft body + float32 ComputeVolume() const + { + const b3ClothMesh* mesh = m_cloth->GetMesh(); + + b3StackArray positions; + positions.Resize(mesh->vertexCount); + + for (u32 i = 0; i < mesh->vertexCount; ++i) + { + b3Particle* p = m_cloth->GetVertexParticle(i); + positions[i] = p->GetPosition(); + } + + b3AABB3 aabb; + aabb.Compute(positions.Begin(), positions.Count()); + + return aabb.Volume(); + } + + // Apply pressure forces + // Explanation available in the paper + // "Pressure Model of Soft Body Simulation" + void ApplyPressureForces() + { + const b3ClothMesh* mesh = m_cloth->GetMesh(); + + // Volume in m^3 + float32 V = ComputeVolume(); + + // Inverse volume + float32 invV = V > 0.0f ? 1.0f / V : 0.0f; + + // Apply pressure forces on particles + for (u32 i = 0; i < m_mesh.triangleCount; ++i) + { + u32 i1 = m_mesh.triangles[i].v1; + u32 i2 = m_mesh.triangles[i].v2; + u32 i3 = m_mesh.triangles[i].v3; + + b3Particle* p1 = m_cloth->GetVertexParticle(i1); + b3Particle* p2 = m_cloth->GetVertexParticle(i2); + b3Particle* p3 = m_cloth->GetVertexParticle(i3); + + b3Vec3 v1 = p1->GetPosition(); + b3Vec3 v2 = p2->GetPosition(); + b3Vec3 v3 = p3->GetPosition(); + + b3Vec3 n = b3Cross(v2 - v1, v3 - v1); + + // Triangle area + float32 A = n.Normalize(); + A *= 0.5f; + + // Ideal Gas Approximation + + // Number of gas moles + const float32 k_n = 1.0f; + + // Ideal Gas Constant [J / K mol] + const float32 k_R = 8.31f; + + // Gas temperature in Kelvin + const float32 k_T = 100.0f; + + // Pressure in Pascals + float32 P = invV * k_n * k_R * k_T; + + // Pressure vector + b3Vec3 Pn = P * n; + + // Pressure force + b3Vec3 FP = Pn * A; + + // Distribute + p1->ApplyForce(FP); + p2->ApplyForce(FP); + p3->ApplyForce(FP); + } + } + + void Step() + { + Test::Step(); + + ApplyPressureForces(); + + m_cloth->Step(g_testSettings->inv_hertz); + + m_cloth->Draw(); + + if (m_clothDragger->IsDragging()) + { + b3Vec3 pA = m_clothDragger->GetPointA(); + b3Vec3 pB = m_clothDragger->GetPointB(); + + g_draw->DrawPoint(pA, 2.0f, b3Color_green); + + g_draw->DrawPoint(pB, 2.0f, b3Color_green); + + g_draw->DrawSegment(pA, pB, b3Color_white); + } + + extern u32 b3_clothSolverIterations; + g_draw->DrawString(b3Color_white, "Iterations = %d", b3_clothSolverIterations); + + float32 E = m_cloth->GetEnergy(); + g_draw->DrawString(b3Color_white, "E = %f", E); + } + + void MouseMove(const b3Ray3& pw) + { + Test::MouseMove(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->Drag(); + } + } + + void MouseLeftDown(const b3Ray3& pw) + { + Test::MouseLeftDown(pw); + + if (m_clothDragger->IsDragging() == false) + { + m_clothDragger->StartDragging(); + } + } + + void MouseLeftUp(const b3Ray3& pw) + { + Test::MouseLeftUp(pw); + + if (m_clothDragger->IsDragging() == true) + { + m_clothDragger->StopDragging(); + } + } + + static Test* Create() + { + return new SoftBody(); + } + + b3SphereClothMesh m_mesh; + b3Cloth* m_cloth; + b3ClothDragger* m_clothDragger; +}; + +#endif \ No newline at end of file diff --git a/premake5.lua b/premake5.lua index f0ef7e9..707cbcc 100644 --- a/premake5.lua +++ b/premake5.lua @@ -273,6 +273,10 @@ workspace(solution_name) examples_src_dir .. "/testbed/framework/test.h", + examples_src_dir .. "/testbed/framework/sphere_mesh.h", + + examples_src_dir .. "/testbed/framework/sphere_mesh.cpp", + examples_src_dir .. "/testbed/framework/body_dragger.h", examples_src_dir .. "/testbed/framework/cloth_dragger.h", From 077d1eead5ee74db340ed8c3910b850ec736eb99 Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 19 Apr 2019 08:31:08 -0300 Subject: [PATCH 064/198] Use assert instead of B3_ASSERT. --- examples/testbed/framework/sphere_mesh.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/testbed/framework/sphere_mesh.cpp b/examples/testbed/framework/sphere_mesh.cpp index 59118e3..c259666 100644 --- a/examples/testbed/framework/sphere_mesh.cpp +++ b/examples/testbed/framework/sphere_mesh.cpp @@ -132,8 +132,8 @@ static inline void smSubdivideMesh(smMesh& output, const smMesh& input) // void smCreateMesh(smMesh& output, u32 subdivisions) { - B3_ASSERT(output.vertices.Count() == 0); - B3_ASSERT(output.triangleIndices.Count() == 0); + assert(output.vertices.Count() == 0); + assert(output.triangleIndices.Count() == 0); smMesh input; input.SetAsIcosahedron(); From 14f6b279d283eac1b5e9b8115ae5f99fccecb640 Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 19 Apr 2019 08:31:35 -0300 Subject: [PATCH 065/198] Remove SPHERE_MESH_H after #endif. --- examples/testbed/framework/sphere_mesh.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/testbed/framework/sphere_mesh.h b/examples/testbed/framework/sphere_mesh.h index 0e0b2d9..a75d49e 100644 --- a/examples/testbed/framework/sphere_mesh.h +++ b/examples/testbed/framework/sphere_mesh.h @@ -110,4 +110,4 @@ struct smMesh // Generate an icosphere given the number of subdivisions. void smCreateMesh(smMesh& output, u32 subdivisions); -#endif SPHERE_MESH_H \ No newline at end of file +#endif \ No newline at end of file From 3adebe68c29bc62a23250a47a44fa2da455b060f Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 19 Apr 2019 09:58:02 -0300 Subject: [PATCH 066/198] Removed a condition which isn't reached during the sphere mesh generation. --- examples/testbed/framework/sphere_mesh.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/testbed/framework/sphere_mesh.cpp b/examples/testbed/framework/sphere_mesh.cpp index c259666..1d69720 100644 --- a/examples/testbed/framework/sphere_mesh.cpp +++ b/examples/testbed/framework/sphere_mesh.cpp @@ -59,8 +59,7 @@ struct smEdgeVertexMap { smEdgeVertexPair* pair = pairs.Get(i); - if ((pair->edge.v1 == edge.v1 && pair->edge.v2 == edge.v2) || - (pair->edge.v1 == edge.v2 && pair->edge.v2 == edge.v1) ) + if (pair->edge.v1 == edge.v2 && pair->edge.v2 == edge.v1) { return pair; } From 9c16391700349a9f357a29be1fed9dd687f04297 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 20 Apr 2019 11:52:17 -0300 Subject: [PATCH 067/198] Optimize sphere mesh --- examples/testbed/framework/draw_gl2.h | 18 +- examples/testbed/framework/draw_gl4.h | 18 +- examples/testbed/framework/sphere_mesh.cpp | 254 ++++++++++++++------- examples/testbed/framework/sphere_mesh.h | 93 ++------ examples/testbed/tests/soft_body.h | 14 +- 5 files changed, 216 insertions(+), 181 deletions(-) diff --git a/examples/testbed/framework/draw_gl2.h b/examples/testbed/framework/draw_gl2.h index 20ed762..91f4457 100644 --- a/examples/testbed/framework/draw_gl2.h +++ b/examples/testbed/framework/draw_gl2.h @@ -542,17 +542,17 @@ struct DrawWireSphere smMesh mesh; smCreateMesh(mesh, 1); - m_vertexCount = mesh.vertices.Count(); - m_indexCount = mesh.triangleIndices.Count(); + m_vertexCount = mesh.vertexCount; + m_indexCount = mesh.indexCount; glGenBuffers(1, &m_vboId); glGenBuffers(1, &m_iboId); glBindBuffer(GL_ARRAY_BUFFER, m_vboId); - glBufferData(GL_ARRAY_BUFFER, mesh.vertices.Count() * sizeof(b3Vec3), mesh.vertices.Begin(), GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_iboId); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.triangleIndices.Count() * sizeof(u32), mesh.triangleIndices.Begin(), GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(u32), mesh.indices, GL_STATIC_DRAW); AssertGL(); @@ -656,20 +656,20 @@ struct DrawSolidSphere smMesh mesh; smCreateMesh(mesh, 1); - m_vertexCount = mesh.vertices.Count(); - m_indexCount = mesh.triangleIndices.Count(); + m_vertexCount = mesh.vertexCount; + m_indexCount = mesh.indexCount; glGenBuffers(3, m_vboIds); glGenBuffers(1, &m_iboId); glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]); - glBufferData(GL_ARRAY_BUFFER, mesh.vertices.Count() * sizeof(b3Vec3), mesh.vertices.Begin(), GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.vertices, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]); - glBufferData(GL_ARRAY_BUFFER, mesh.vertices.Count() * sizeof(b3Vec3), mesh.vertices.Begin(), GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_iboId); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.triangleIndices.Count() * sizeof(u32), mesh.triangleIndices.Begin(), GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(u32), mesh.indices, GL_STATIC_DRAW); AssertGL(); diff --git a/examples/testbed/framework/draw_gl4.h b/examples/testbed/framework/draw_gl4.h index 0838b3c..4156a3d 100644 --- a/examples/testbed/framework/draw_gl4.h +++ b/examples/testbed/framework/draw_gl4.h @@ -558,17 +558,17 @@ struct DrawWireSphere smMesh mesh; smCreateMesh(mesh, 1); - m_vertexCount = mesh.vertices.Count(); - m_indexCount = mesh.triangleIndices.Count(); + m_vertexCount = mesh.vertexCount; + m_indexCount = mesh.indexCount; glGenBuffers(1, &m_vboId); glGenBuffers(1, &m_iboId); glBindBuffer(GL_ARRAY_BUFFER, m_vboId); - glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.vertices.Begin(), GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_iboId); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(u32), mesh.triangleIndices.Begin(), GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(u32), mesh.indices, GL_STATIC_DRAW); AssertGL(); @@ -671,20 +671,20 @@ struct DrawSolidSphere smMesh mesh; smCreateMesh(mesh, 1); - m_vertexCount = mesh.vertices.Count(); - m_indexCount = mesh.triangleIndices.Count(); + m_vertexCount = mesh.vertexCount; + m_indexCount = mesh.indexCount; glGenBuffers(3, m_vboIds); glGenBuffers(1, &m_iboId); glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]); - glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.vertices.Begin(), GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.vertices, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]); - glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.vertices.Begin(), GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_iboId); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(u32), mesh.triangleIndices.Begin(), GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(u32), mesh.indices, GL_STATIC_DRAW); AssertGL(); diff --git a/examples/testbed/framework/sphere_mesh.cpp b/examples/testbed/framework/sphere_mesh.cpp index 1d69720..84c6c8d 100644 --- a/examples/testbed/framework/sphere_mesh.cpp +++ b/examples/testbed/framework/sphere_mesh.cpp @@ -18,131 +18,229 @@ #include -// References: -// https://github.com/caosdoar/spheres -// https://schneide.blog/2016/07/15/generating-an-icosphere-in-c/ +static inline void smAddVertex(smMesh& mesh, float32 x, float32 y, float32 z) +{ + mesh.vertices[mesh.vertexCount++].Set(x, y, z); +} + +static inline void smAddTriangle(smMesh& mesh, u32 v1, u32 v2, u32 v3) +{ + mesh.indices[mesh.indexCount++] = v1; + mesh.indices[mesh.indexCount++] = v2; + mesh.indices[mesh.indexCount++] = v3; +} + +static inline void smSetAsIcosahedron(smMesh& mesh) +{ + assert(mesh.vertexCount == 0); + + float32 t = 0.5f * (1.0f + b3Sqrt(5.0f)); + + smAddVertex(mesh, -1.0f, t, 0.0f); + smAddVertex(mesh, 1.0f, t, 0.0f); + smAddVertex(mesh, -1.0f, -t, 0.0f); + smAddVertex(mesh, 1.0f, -t, 0.0f); + + smAddVertex(mesh, 0.0f, -1.0f, t); + smAddVertex(mesh, 0.0f, 1.0f, t); + smAddVertex(mesh, 0.0f, -1.0f, -t); + smAddVertex(mesh, 0.0f, 1.0f, -t); + + smAddVertex(mesh, t, 0.0f, -1.0f); + smAddVertex(mesh, t, 0.0f, 1.0f); + smAddVertex(mesh, -t, 0.0f, -1.0f); + smAddVertex(mesh, -t, 0.0f, 1.0f); + + for (u32 i = 0; i < mesh.vertexCount; ++i) + { + mesh.vertices[i].Normalize(); + } + + assert(mesh.indexCount == 0); + + smAddTriangle(mesh, 0, 11, 5); + smAddTriangle(mesh, 0, 5, 1); + smAddTriangle(mesh, 0, 1, 7); + smAddTriangle(mesh, 0, 7, 10); + smAddTriangle(mesh, 0, 10, 11); + + smAddTriangle(mesh, 1, 5, 9); + smAddTriangle(mesh, 5, 11, 4); + smAddTriangle(mesh, 11, 10, 2); + smAddTriangle(mesh, 10, 7, 6); + smAddTriangle(mesh, 7, 1, 8); + + smAddTriangle(mesh, 3, 9, 4); + smAddTriangle(mesh, 3, 4, 2); + smAddTriangle(mesh, 3, 2, 6); + smAddTriangle(mesh, 3, 6, 8); + smAddTriangle(mesh, 3, 8, 9); + + smAddTriangle(mesh, 4, 9, 5); + smAddTriangle(mesh, 2, 4, 11); + smAddTriangle(mesh, 6, 2, 10); + smAddTriangle(mesh, 8, 6, 7); + smAddTriangle(mesh, 9, 8, 1); +} struct smEdge { - smEdge() { } - - smEdge(u32 _v1, u32 _v2) - { - v1 = _v1; - v2 = _v2; - } - u32 v1, v2; }; struct smEdgeVertexPair { - smEdgeVertexPair() { } - - smEdgeVertexPair(const smEdge& _edge, u32 _vertex) - { - edge = _edge; - vertex = _vertex; - } - smEdge edge; u32 vertex; }; struct smEdgeVertexMap { - smEdgeVertexMap() { } - - smEdgeVertexPair* Find(const smEdge& edge) - { - for (u32 i = 0; i < pairs.Count(); ++i) - { - smEdgeVertexPair* pair = pairs.Get(i); - - if (pair->edge.v1 == edge.v2 && pair->edge.v2 == edge.v1) - { - return pair; - } - } - return nullptr; - } - - b3StackArray pairs; + u32 pairCount; + smEdgeVertexPair* pairs; }; -// -static inline u32 smSubdivideEdge(u32 i1, u32 i2, - const b3Vec3& v1, const b3Vec3& v2, - smEdgeVertexMap& edgeVertexMap, - smMesh& output) +static inline void smAddPair(smEdgeVertexMap& map, const smEdgeVertexPair& pair) { - smEdge edge(i1, i2); + map.pairs[map.pairCount++] = pair; +} - smEdgeVertexPair* pair = edgeVertexMap.Find(edge); +static inline smEdgeVertexPair* smFindOpposite(smEdgeVertexMap& map, const smEdge& edge) +{ + for (u32 i = 0; i < map.pairCount; ++i) + { + smEdgeVertexPair* pair = map.pairs + i; + + if (pair->edge.v1 == edge.v2 && pair->edge.v2 == edge.v1) + { + return pair; + } + } + return nullptr; +} + +static inline u32 smSubdivideEdge(smMesh& in_out, smEdgeVertexMap& map, + u32 i1, u32 i2) +{ + smEdge edge; + edge.v1 = i1; + edge.v2 = i2; + + smEdgeVertexPair* pair = smFindOpposite(map, edge); if (pair) { return pair->vertex; } - smEdge newEdge(i1, i2); - u32 newVertex = output.vertices.Count(); + smEdge newEdge; + newEdge.v1 = i1; + newEdge.v2 = i2; + + u32 newVertex = in_out.vertexCount; + + b3Vec3 v1 = in_out.vertices[i1]; + b3Vec3 v2 = in_out.vertices[i1]; b3Vec3 v = 0.5f * (v1 + v2); float32 len = v.Normalize(); - output.AddVertex(v.x, v.y, v.z); + smAddVertex(in_out, v.x, v.y, v.z); - smEdgeVertexPair newPair(newEdge, newVertex); + smEdgeVertexPair newPair; + newPair.edge = newEdge; + newPair.vertex = newVertex; - edgeVertexMap.pairs.PushBack(newPair); + smAddPair(map, newPair); return newVertex; } -// -static inline void smSubdivideMesh(smMesh& output, const smMesh& input) +static void smSubdivideMesh(smMesh& in_out, smEdgeVertexMap& map) { - output.vertices = input.vertices; + map.pairCount = 0; - smEdgeVertexMap edgeVertexMap; + u32 inputIndexCount = in_out.indexCount; - for (u32 i = 0; i < input.triangleIndices.Count() / 3; ++i) + for (u32 i = 0; i < inputIndexCount / 3; ++i) { - u32 vi1 = input.triangleIndices[3 * i + 0]; - u32 vi2 = input.triangleIndices[3 * i + 1]; - u32 vi3 = input.triangleIndices[3 * i + 2]; + u32 vi1 = in_out.indices[3 * i + 0]; + u32 vi2 = in_out.indices[3 * i + 1]; + u32 vi3 = in_out.indices[3 * i + 2]; + + u32 vi4 = smSubdivideEdge(in_out, map, vi1, vi2); + u32 vi5 = smSubdivideEdge(in_out, map, vi2, vi3); + u32 vi6 = smSubdivideEdge(in_out, map, vi3, vi1); - b3Vec3 v1 = input.vertices[vi1]; - b3Vec3 v2 = input.vertices[vi2]; - b3Vec3 v3 = input.vertices[vi3]; - - u32 vi4 = smSubdivideEdge(vi1, vi2, v1, v2, edgeVertexMap, output); - u32 vi5 = smSubdivideEdge(vi2, vi3, v2, v3, edgeVertexMap, output); - u32 vi6 = smSubdivideEdge(vi3, vi1, v3, v1, edgeVertexMap, output); - - output.AddTriangle(vi1, vi4, vi6); - output.AddTriangle(vi4, vi2, vi5); - output.AddTriangle(vi5, vi3, vi6); - output.AddTriangle(vi4, vi5, vi6); + smAddTriangle(in_out, vi1, vi4, vi6); + smAddTriangle(in_out, vi4, vi2, vi5); + smAddTriangle(in_out, vi5, vi3, vi6); + smAddTriangle(in_out, vi4, vi5, vi6); } } -// +// Compute the maximum number of vertices and +// the number of triangle vertex indices in the intermediate mesh. +// Also compute the maximum number of edge-vertex pairs per subdivision step +static inline void smCount(u32& vertexCount, u32& indexCount, u32& edgeVertexPairCount, u32 subdivisions) +{ + vertexCount = 12; + indexCount = 3 * 20; + for (u32 i = 0; i < subdivisions; ++i) + { + vertexCount += 3 * (indexCount / 3); + indexCount += 4 * 3 * (indexCount / 3); + } + edgeVertexPairCount = 3 * (indexCount / 3); +} + void smCreateMesh(smMesh& output, u32 subdivisions) { - assert(output.vertices.Count() == 0); - assert(output.triangleIndices.Count() == 0); + assert(output.vertexCount == 0); + assert(output.indexCount == 0); - smMesh input; - input.SetAsIcosahedron(); + u32 vertexCount, indexCount, edgeVertexPairCount; + smCount(vertexCount, indexCount, edgeVertexPairCount, subdivisions); + + u32 byteCount = 0; + byteCount += vertexCount * sizeof(b3Vec3); + byteCount += indexCount * sizeof(u32); + byteCount += edgeVertexPairCount * sizeof(smEdgeVertexPair); + + u8* bytes = (u8*)malloc(byteCount); + + smMesh out; + out.vertexCount = 0; + out.vertices = (b3Vec3*)bytes; + out.indexCount = 0; + out.indices = (u32*) ((u8*)(out.vertices) + (vertexCount * sizeof(b3Vec3))); + + smEdgeVertexMap map; + map.pairCount = 0; + map.pairs = (smEdgeVertexPair*) ((u8*)(out.indices) + (indexCount * sizeof(u32))); + + smSetAsIcosahedron(out); for (u32 i = 0; i < subdivisions; ++i) { - smMesh subOutput; - smSubdivideMesh(subOutput, input); - input = subOutput; + smSubdivideMesh(out, map); } - output = input; -} + assert(map.pairCount < edgeVertexPairCount); + + assert(out.vertexCount < vertexCount); + assert(out.indexCount == indexCount); + + output.vertexCount = out.vertexCount; + output.vertices = (b3Vec3*)malloc(out.vertexCount * sizeof(b3Vec3)); + memcpy(output.vertices, out.vertices, out.vertexCount * sizeof(b3Vec3)); + + output.indexCount = out.indexCount; + output.indices = (u32*)malloc(out.indexCount * sizeof(u32)); + memcpy(output.indices, out.indices, out.indexCount * sizeof(u32)); + + free(bytes); + + out.vertices = nullptr; + out.indices = nullptr; +} \ No newline at end of file diff --git a/examples/testbed/framework/sphere_mesh.h b/examples/testbed/framework/sphere_mesh.h index a75d49e..f4c007b 100644 --- a/examples/testbed/framework/sphere_mesh.h +++ b/examples/testbed/framework/sphere_mesh.h @@ -19,95 +19,32 @@ #ifndef SPHERE_MESH_H #define SPHERE_MESH_H -#include +#include -// +// This structure represents a triangle mesh. struct smMesh { - smMesh() { } - - smMesh& operator=(const smMesh& other) + smMesh() { - Copy(other); - return *this; + vertexCount = 0; + vertices = nullptr; + indexCount = 0; + indices = nullptr; } - void Copy(const smMesh& other) + ~smMesh() { - vertices = other.vertices; - triangleIndices = other.triangleIndices; + free(vertices); + free(indices); } - void AddVertex(float32 x, float32 y, float32 z) - { - vertices.PushBack(b3Vec3(x, y, z)); - } - - void AddTriangle(u32 v1, u32 v2, u32 v3) - { - triangleIndices.PushBack(v1); - triangleIndices.PushBack(v2); - triangleIndices.PushBack(v3); - } - - void SetAsIcosahedron() - { - assert(vertices.Count() == 0); - - float32 t = 0.5f * (1.0f + b3Sqrt(5.0f)); - - AddVertex(-1.0f, t, 0.0f); - AddVertex(1.0f, t, 0.0f); - AddVertex(-1.0f, -t, 0.0f); - AddVertex(1.0f, -t, 0.0f); - - AddVertex(0.0f, -1.0f, t); - AddVertex(0.0f, 1.0f, t); - AddVertex(0.0f, -1.0f, -t); - AddVertex(0.0f, 1.0f, -t); - - AddVertex(t, 0.0f, -1.0f); - AddVertex(t, 0.0f, 1.0f); - AddVertex(-t, 0.0f, -1.0f); - AddVertex(-t, 0.0f, 1.0f); - - for (u32 i = 0; i < vertices.Count(); ++i) - { - vertices[i].Normalize(); - } - - assert(triangleIndices.Count() == 0); - - AddTriangle(0, 11, 5); - AddTriangle(0, 5, 1); - AddTriangle(0, 1, 7); - AddTriangle(0, 7, 10); - AddTriangle(0, 10, 11); - - AddTriangle(1, 5, 9); - AddTriangle(5, 11, 4); - AddTriangle(11, 10, 2); - AddTriangle(10, 7, 6); - AddTriangle(7, 1, 8); - - AddTriangle(3, 9, 4); - AddTriangle(3, 4, 2); - AddTriangle(3, 2, 6); - AddTriangle(3, 6, 8); - AddTriangle(3, 8, 9); - - AddTriangle(4, 9, 5); - AddTriangle(2, 4, 11); - AddTriangle(6, 2, 10); - AddTriangle(8, 6, 7); - AddTriangle(9, 8, 1); - } - - b3StackArray vertices; - b3StackArray triangleIndices; + u32 vertexCount; // number of unique vertices + b3Vec3* vertices; // list of unique vertices + u32 indexCount; // number of triangle vertex indices + u32* indices; // list of triangle vertex index }; -// Generate an icosphere given the number of subdivisions. +// Create a unit icosphere given the number of subdivisions. void smCreateMesh(smMesh& output, u32 subdivisions); #endif \ No newline at end of file diff --git a/examples/testbed/tests/soft_body.h b/examples/testbed/tests/soft_body.h index d12fdc8..198162d 100644 --- a/examples/testbed/tests/soft_body.h +++ b/examples/testbed/tests/soft_body.h @@ -32,18 +32,18 @@ struct b3SphereClothMesh : public b3ClothMesh smMesh mesh; smCreateMesh(mesh, 1); - sphereVertices.Resize(mesh.vertices.Count()); - for (u32 i = 0; i < mesh.vertices.Count(); ++i) + sphereVertices.Resize(mesh.vertexCount); + for (u32 i = 0; i < mesh.vertexCount; ++i) { sphereVertices[i] = mesh.vertices[i]; } - sphereTriangles.Resize(mesh.triangleIndices.Count() / 3); - for (u32 i = 0; i < mesh.triangleIndices.Count() / 3; ++i) + sphereTriangles.Resize(mesh.indexCount / 3); + for (u32 i = 0; i < mesh.indexCount / 3; ++i) { - sphereTriangles[i].v1 = mesh.triangleIndices[3 * i + 0]; - sphereTriangles[i].v2 = mesh.triangleIndices[3 * i + 1]; - sphereTriangles[i].v3 = mesh.triangleIndices[3 * i + 2]; + sphereTriangles[i].v1 = mesh.indices[3 * i + 0]; + sphereTriangles[i].v2 = mesh.indices[3 * i + 1]; + sphereTriangles[i].v3 = mesh.indices[3 * i + 2]; } sphereMesh.startTriangle = 0; From 23fa2bd162f111b6b00aaab3a8a37eb9e2395bf6 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 20 Apr 2019 11:54:33 -0300 Subject: [PATCH 068/198] Bugfix --- examples/testbed/framework/sphere_mesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/testbed/framework/sphere_mesh.cpp b/examples/testbed/framework/sphere_mesh.cpp index 84c6c8d..c986a11 100644 --- a/examples/testbed/framework/sphere_mesh.cpp +++ b/examples/testbed/framework/sphere_mesh.cpp @@ -140,7 +140,7 @@ static inline u32 smSubdivideEdge(smMesh& in_out, smEdgeVertexMap& map, u32 newVertex = in_out.vertexCount; b3Vec3 v1 = in_out.vertices[i1]; - b3Vec3 v2 = in_out.vertices[i1]; + b3Vec3 v2 = in_out.vertices[i2]; b3Vec3 v = 0.5f * (v1 + v2); float32 len = v.Normalize(); From 34c5d6f15793c1f43bd297c4c7a060ae46773f47 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 20 Apr 2019 13:27:28 -0300 Subject: [PATCH 069/198] Remove some duplicated code. --- examples/testbed/framework/sphere_mesh.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/examples/testbed/framework/sphere_mesh.cpp b/examples/testbed/framework/sphere_mesh.cpp index c986a11..ee44f98 100644 --- a/examples/testbed/framework/sphere_mesh.cpp +++ b/examples/testbed/framework/sphere_mesh.cpp @@ -105,13 +105,13 @@ static inline void smAddPair(smEdgeVertexMap& map, const smEdgeVertexPair& pair) map.pairs[map.pairCount++] = pair; } -static inline smEdgeVertexPair* smFindOpposite(smEdgeVertexMap& map, const smEdge& edge) +static inline smEdgeVertexPair* smFind(smEdgeVertexMap& map, u32 v1, u32 v2) { for (u32 i = 0; i < map.pairCount; ++i) { smEdgeVertexPair* pair = map.pairs + i; - if (pair->edge.v1 == edge.v2 && pair->edge.v2 == edge.v1) + if (pair->edge.v1 == v1 && pair->edge.v2 == v2) { return pair; } @@ -122,11 +122,7 @@ static inline smEdgeVertexPair* smFindOpposite(smEdgeVertexMap& map, const smEdg static inline u32 smSubdivideEdge(smMesh& in_out, smEdgeVertexMap& map, u32 i1, u32 i2) { - smEdge edge; - edge.v1 = i1; - edge.v2 = i2; - - smEdgeVertexPair* pair = smFindOpposite(map, edge); + smEdgeVertexPair* pair = smFind(map, i2, i1); if (pair) { @@ -136,14 +132,13 @@ static inline u32 smSubdivideEdge(smMesh& in_out, smEdgeVertexMap& map, smEdge newEdge; newEdge.v1 = i1; newEdge.v2 = i2; - + u32 newVertex = in_out.vertexCount; b3Vec3 v1 = in_out.vertices[i1]; b3Vec3 v2 = in_out.vertices[i2]; - b3Vec3 v = 0.5f * (v1 + v2); - float32 len = v.Normalize(); + v.Normalize(); smAddVertex(in_out, v.x, v.y, v.z); From 323ba37b73431bde70e30b012021e400422a3d9c Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 20 Apr 2019 13:45:18 -0300 Subject: [PATCH 070/198] Use a octosphere instead of a icosahedron --- examples/testbed/framework/draw_gl2.h | 4 +- examples/testbed/framework/draw_gl4.h | 4 +- examples/testbed/framework/sphere_mesh.cpp | 67 ++++++---------------- examples/testbed/tests/soft_body.h | 2 +- 4 files changed, 24 insertions(+), 53 deletions(-) diff --git a/examples/testbed/framework/draw_gl2.h b/examples/testbed/framework/draw_gl2.h index 91f4457..c3197f8 100644 --- a/examples/testbed/framework/draw_gl2.h +++ b/examples/testbed/framework/draw_gl2.h @@ -540,7 +540,7 @@ struct DrawWireSphere DrawWireSphere() { smMesh mesh; - smCreateMesh(mesh, 1); + smCreateMesh(mesh, 2); m_vertexCount = mesh.vertexCount; m_indexCount = mesh.indexCount; @@ -654,7 +654,7 @@ struct DrawSolidSphere DrawSolidSphere() { smMesh mesh; - smCreateMesh(mesh, 1); + smCreateMesh(mesh, 2); m_vertexCount = mesh.vertexCount; m_indexCount = mesh.indexCount; diff --git a/examples/testbed/framework/draw_gl4.h b/examples/testbed/framework/draw_gl4.h index 4156a3d..ff1b46f 100644 --- a/examples/testbed/framework/draw_gl4.h +++ b/examples/testbed/framework/draw_gl4.h @@ -556,7 +556,7 @@ struct DrawWireSphere DrawWireSphere() { smMesh mesh; - smCreateMesh(mesh, 1); + smCreateMesh(mesh, 2); m_vertexCount = mesh.vertexCount; m_indexCount = mesh.indexCount; @@ -669,7 +669,7 @@ struct DrawSolidSphere DrawSolidSphere() { smMesh mesh; - smCreateMesh(mesh, 1); + smCreateMesh(mesh, 2); m_vertexCount = mesh.vertexCount; m_indexCount = mesh.indexCount; diff --git a/examples/testbed/framework/sphere_mesh.cpp b/examples/testbed/framework/sphere_mesh.cpp index ee44f98..d1bd184 100644 --- a/examples/testbed/framework/sphere_mesh.cpp +++ b/examples/testbed/framework/sphere_mesh.cpp @@ -30,57 +30,28 @@ static inline void smAddTriangle(smMesh& mesh, u32 v1, u32 v2, u32 v3) mesh.indices[mesh.indexCount++] = v3; } -static inline void smSetAsIcosahedron(smMesh& mesh) +static inline void smSetAsOctahedron(smMesh& mesh) { assert(mesh.vertexCount == 0); - float32 t = 0.5f * (1.0f + b3Sqrt(5.0f)); - - smAddVertex(mesh, -1.0f, t, 0.0f); - smAddVertex(mesh, 1.0f, t, 0.0f); - smAddVertex(mesh, -1.0f, -t, 0.0f); - smAddVertex(mesh, 1.0f, -t, 0.0f); - - smAddVertex(mesh, 0.0f, -1.0f, t); - smAddVertex(mesh, 0.0f, 1.0f, t); - smAddVertex(mesh, 0.0f, -1.0f, -t); - smAddVertex(mesh, 0.0f, 1.0f, -t); - - smAddVertex(mesh, t, 0.0f, -1.0f); - smAddVertex(mesh, t, 0.0f, 1.0f); - smAddVertex(mesh, -t, 0.0f, -1.0f); - smAddVertex(mesh, -t, 0.0f, 1.0f); - - for (u32 i = 0; i < mesh.vertexCount; ++i) - { - mesh.vertices[i].Normalize(); - } + smAddVertex(mesh, 0.0f, -1.0f, 0.0f); + smAddVertex(mesh, 0.0f, 0.0f, 1.0f); + smAddVertex(mesh, -1.0f, 0.0f, 0.0f); + smAddVertex(mesh, 0.0f, 0.0f, -1.0f); + smAddVertex(mesh, 1.0f, 0.0f, 0.0f); + smAddVertex(mesh, 0.0f, 1.0f, 0.0f); assert(mesh.indexCount == 0); - smAddTriangle(mesh, 0, 11, 5); - smAddTriangle(mesh, 0, 5, 1); - smAddTriangle(mesh, 0, 1, 7); - smAddTriangle(mesh, 0, 7, 10); - smAddTriangle(mesh, 0, 10, 11); - - smAddTriangle(mesh, 1, 5, 9); - smAddTriangle(mesh, 5, 11, 4); - smAddTriangle(mesh, 11, 10, 2); - smAddTriangle(mesh, 10, 7, 6); - smAddTriangle(mesh, 7, 1, 8); - - smAddTriangle(mesh, 3, 9, 4); - smAddTriangle(mesh, 3, 4, 2); - smAddTriangle(mesh, 3, 2, 6); - smAddTriangle(mesh, 3, 6, 8); - smAddTriangle(mesh, 3, 8, 9); - - smAddTriangle(mesh, 4, 9, 5); - smAddTriangle(mesh, 2, 4, 11); - smAddTriangle(mesh, 6, 2, 10); - smAddTriangle(mesh, 8, 6, 7); - smAddTriangle(mesh, 9, 8, 1); + smAddTriangle(mesh, 0, 1, 2); + smAddTriangle(mesh, 0, 2, 3); + smAddTriangle(mesh, 0, 3, 4); + smAddTriangle(mesh, 0, 4, 1); + + smAddTriangle(mesh, 5, 2, 1); + smAddTriangle(mesh, 5, 3, 2); + smAddTriangle(mesh, 5, 4, 3); + smAddTriangle(mesh, 5, 1, 4); } struct smEdge @@ -179,8 +150,8 @@ static void smSubdivideMesh(smMesh& in_out, smEdgeVertexMap& map) // Also compute the maximum number of edge-vertex pairs per subdivision step static inline void smCount(u32& vertexCount, u32& indexCount, u32& edgeVertexPairCount, u32 subdivisions) { - vertexCount = 12; - indexCount = 3 * 20; + vertexCount = 6; + indexCount = 3 * 8; for (u32 i = 0; i < subdivisions; ++i) { vertexCount += 3 * (indexCount / 3); @@ -214,7 +185,7 @@ void smCreateMesh(smMesh& output, u32 subdivisions) map.pairCount = 0; map.pairs = (smEdgeVertexPair*) ((u8*)(out.indices) + (indexCount * sizeof(u32))); - smSetAsIcosahedron(out); + smSetAsOctahedron(out); for (u32 i = 0; i < subdivisions; ++i) { diff --git a/examples/testbed/tests/soft_body.h b/examples/testbed/tests/soft_body.h index 198162d..569e3fb 100644 --- a/examples/testbed/tests/soft_body.h +++ b/examples/testbed/tests/soft_body.h @@ -30,7 +30,7 @@ struct b3SphereClothMesh : public b3ClothMesh b3SphereClothMesh() { smMesh mesh; - smCreateMesh(mesh, 1); + smCreateMesh(mesh, 2); sphereVertices.Resize(mesh.vertexCount); for (u32 i = 0; i < mesh.vertexCount; ++i) From 1e85ea0a36fbe747bc5cddae1cc52b4fa7eef50d Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 20 Apr 2019 13:51:03 -0300 Subject: [PATCH 071/198] Handle case where subdivision in zero --- examples/testbed/framework/sphere_mesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/testbed/framework/sphere_mesh.cpp b/examples/testbed/framework/sphere_mesh.cpp index d1bd184..647558d 100644 --- a/examples/testbed/framework/sphere_mesh.cpp +++ b/examples/testbed/framework/sphere_mesh.cpp @@ -194,7 +194,7 @@ void smCreateMesh(smMesh& output, u32 subdivisions) assert(map.pairCount < edgeVertexPairCount); - assert(out.vertexCount < vertexCount); + assert(out.vertexCount <= vertexCount); assert(out.indexCount == indexCount); output.vertexCount = out.vertexCount; From 77ad799d942c8c54291d3eae433f1afcc18ddcd7 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 20 Apr 2019 13:57:12 -0300 Subject: [PATCH 072/198] Add comment --- examples/testbed/framework/sphere_mesh.h | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/testbed/framework/sphere_mesh.h b/examples/testbed/framework/sphere_mesh.h index f4c007b..7f26b56 100644 --- a/examples/testbed/framework/sphere_mesh.h +++ b/examples/testbed/framework/sphere_mesh.h @@ -45,6 +45,7 @@ struct smMesh }; // Create a unit icosphere given the number of subdivisions. +// If the number of subdivisions to perform is zero then the output mesh is an octahedron. void smCreateMesh(smMesh& output, u32 subdivisions); #endif \ No newline at end of file From 490a2963dfaa6bfdd854e697ea0277f18eb51b2c Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 21 Apr 2019 16:06:51 -0300 Subject: [PATCH 073/198] Mesh generation to everybody Add cylinder mesh generation Mesh generation can be used by everyone Mesh generation also gives vertex normals for convenience --- examples/testbed/framework/draw_gl2.h | 94 ++----------- examples/testbed/framework/draw_gl4.h | 92 ++----------- examples/testbed/tests/soft_body.h | 48 +++---- include/bounce/meshgen/cylinder_mesh.h | 54 ++++++++ .../bounce/meshgen}/sphere_mesh.h | 11 +- premake5.lua | 4 - src/bounce/collision/shapes/qhull.cpp | 85 +++--------- src/bounce/meshgen/cylinder_mesh.cpp | 129 ++++++++++++++++++ .../bounce/meshgen}/sphere_mesh.cpp | 30 ++-- 9 files changed, 276 insertions(+), 271 deletions(-) create mode 100644 include/bounce/meshgen/cylinder_mesh.h rename {examples/testbed/framework => include/bounce/meshgen}/sphere_mesh.h (90%) create mode 100644 src/bounce/meshgen/cylinder_mesh.cpp rename {examples/testbed/framework => src/bounce/meshgen}/sphere_mesh.cpp (87%) diff --git a/examples/testbed/framework/draw_gl2.h b/examples/testbed/framework/draw_gl2.h index c3197f8..f2166c7 100644 --- a/examples/testbed/framework/draw_gl2.h +++ b/examples/testbed/framework/draw_gl2.h @@ -28,7 +28,8 @@ #include #include -#include +#include +#include #define BUFFER_OFFSET(i) ((char*)NULL + (i)) @@ -666,7 +667,7 @@ struct DrawSolidSphere glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.vertices, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]); - glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.vertices, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.normals, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_iboId); glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(u32), mesh.indices, GL_STATIC_DRAW); @@ -691,96 +692,25 @@ struct DrawSolidSphere struct DrawSolidCylinder { - enum - { - e_segments = 64, - e_vertexCount = e_segments * 6, - }; - DrawSolidCylinder() { - b3Vec3 vs[e_vertexCount]; - b3Vec3 ns[e_vertexCount]; + cymMesh mesh; + cymCreateMesh(mesh, 20); - u32 vc = 0; - for (u32 i = 0; i < e_segments; ++i) - { - float32 t0 = 2.0f * B3_PI * float32(i) / float32(e_segments); - float32 t1 = 2.0f * B3_PI * float32(i + 1) / float32(e_segments); - - float32 c0 = cos(t0); - float32 s0 = sin(t0); - - float32 c1 = cos(t1); - float32 s1 = sin(t1); - - b3Vec3 v1; - v1.x = s0; - v1.y = -0.5f; - v1.z = c0; - - b3Vec3 v2; - v2.x = s1; - v2.y = -0.5f; - v2.z = c1; - - b3Vec3 v3; - v3.x = s1; - v3.y = 0.5f; - v3.z = c1; - - b3Vec3 v4; - v4.x = s0; - v4.y = 0.5f; - v4.z = c0; - - b3Vec3 n = b3Cross(v2 - v1, v3 - v1); - n.Normalize(); - - vs[vc] = v1; - ns[vc] = n; - ++vc; - - vs[vc] = v2; - ns[vc] = n; - ++vc; - - vs[vc] = v3; - ns[vc] = n; - ++vc; - - vs[vc] = v1; - ns[vc] = n; - ++vc; - - vs[vc] = v3; - ns[vc] = n; - ++vc; - - vs[vc] = v4; - ns[vc] = n; - ++vc; - } - - u32 is[e_vertexCount]; - - u32 ic = vc; - for (u32 i = 0; i < vc; ++i) - { - is[i] = i; - } + m_vertexCount = mesh.vertexCount; + m_indexCount = mesh.indexCount; glGenBuffers(2, m_vboIds); glGenBuffers(1, &m_iboId); glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]); - glBufferData(GL_ARRAY_BUFFER, vc * sizeof(b3Vec3), vs, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.vertices, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]); - glBufferData(GL_ARRAY_BUFFER, vc * sizeof(b3Vec3), ns, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.normals, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_iboId); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, ic * sizeof(u32), is, GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(u32), mesh.indices, GL_STATIC_DRAW); AssertGL(); @@ -796,6 +726,8 @@ struct DrawSolidCylinder GLuint m_vboIds[2]; GLuint m_iboId; + u32 m_vertexCount; + u32 m_indexCount; }; struct DrawSolid @@ -875,7 +807,7 @@ struct DrawSolid glEnableVertexAttribArray(m_normalAttribute); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_cylinder.m_iboId); - glDrawElements(GL_TRIANGLES, m_cylinder.e_vertexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0)); + glDrawElements(GL_TRIANGLES, m_cylinder.m_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0)); glDisableVertexAttribArray(m_normalAttribute); diff --git a/examples/testbed/framework/draw_gl4.h b/examples/testbed/framework/draw_gl4.h index ff1b46f..d276500 100644 --- a/examples/testbed/framework/draw_gl4.h +++ b/examples/testbed/framework/draw_gl4.h @@ -28,7 +28,8 @@ #include #include -#include +#include +#include #define BUFFER_OFFSET(i) ((char*)NULL + (i)) @@ -706,96 +707,25 @@ struct DrawSolidSphere struct DrawSolidCylinder { - enum - { - e_segments = 64, - e_vertexCount = e_segments * 6, - }; - DrawSolidCylinder() { - b3Vec3 vs[e_vertexCount]; - b3Vec3 ns[e_vertexCount]; + cymMesh mesh; + cymCreateMesh(mesh, 20); - u32 vc = 0; - for (u32 i = 0; i < e_segments; ++i) - { - float32 t0 = 2.0f * B3_PI * float32(i) / float32(e_segments); - float32 t1 = 2.0f * B3_PI * float32(i + 1) / float32(e_segments); - - float32 c0 = cos(t0); - float32 s0 = sin(t0); - - float32 c1 = cos(t1); - float32 s1 = sin(t1); - - b3Vec3 v1; - v1.x = s0; - v1.y = -0.5f; - v1.z = c0; - - b3Vec3 v2; - v2.x = s1; - v2.y = -0.5f; - v2.z = c1; - - b3Vec3 v3; - v3.x = s1; - v3.y = 0.5f; - v3.z = c1; - - b3Vec3 v4; - v4.x = s0; - v4.y = 0.5f; - v4.z = c0; - - b3Vec3 n = b3Cross(v2 - v1, v3 - v1); - n.Normalize(); - - vs[vc] = v1; - ns[vc] = n; - ++vc; - - vs[vc] = v2; - ns[vc] = n; - ++vc; - - vs[vc] = v3; - ns[vc] = n; - ++vc; - - vs[vc] = v1; - ns[vc] = n; - ++vc; - - vs[vc] = v3; - ns[vc] = n; - ++vc; - - vs[vc] = v4; - ns[vc] = n; - ++vc; - } - - u32 is[e_vertexCount]; - - u32 ic = vc; - for (u32 i = 0; i < vc; ++i) - { - is[i] = i; - } + m_vertexCount = mesh.vertexCount; + m_indexCount = mesh.indexCount; glGenBuffers(2, m_vboIds); glGenBuffers(1, &m_iboId); glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]); - glBufferData(GL_ARRAY_BUFFER, vc * sizeof(b3Vec3), vs, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.vertices, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]); - glBufferData(GL_ARRAY_BUFFER, vc * sizeof(b3Vec3), ns, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(b3Vec3), mesh.normals, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_iboId); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, ic * sizeof(u32), is, GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(u32), mesh.indices, GL_STATIC_DRAW); AssertGL(); @@ -811,6 +741,8 @@ struct DrawSolidCylinder GLuint m_vboIds[2]; GLuint m_iboId; + u32 m_vertexCount; + u32 m_indexCount; }; struct DrawSolid @@ -891,7 +823,7 @@ struct DrawSolid glEnableVertexAttribArray(m_normalAttribute); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_cylinder.m_iboId); - glDrawElements(GL_TRIANGLES, m_cylinder.e_vertexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0)); + glDrawElements(GL_TRIANGLES, m_cylinder.m_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); diff --git a/examples/testbed/tests/soft_body.h b/examples/testbed/tests/soft_body.h index 569e3fb..bc750db 100644 --- a/examples/testbed/tests/soft_body.h +++ b/examples/testbed/tests/soft_body.h @@ -19,44 +19,46 @@ #ifndef SOFT_BODY_H #define SOFT_BODY_H -#include +#include -struct b3SphereClothMesh : public b3ClothMesh +struct b3QClothMesh : public b3ClothMesh { - b3StackArray sphereVertices; - b3StackArray sphereTriangles; - b3ClothMeshMesh sphereMesh; + b3ClothMeshMesh qMesh; - b3SphereClothMesh() + ~b3QClothMesh() + { + free(vertices); + free(triangles); + } + + b3QClothMesh() { smMesh mesh; smCreateMesh(mesh, 2); - sphereVertices.Resize(mesh.vertexCount); + vertexCount = mesh.vertexCount; + vertices = (b3Vec3*) malloc(vertexCount * sizeof(b3Vec3)); for (u32 i = 0; i < mesh.vertexCount; ++i) { - sphereVertices[i] = mesh.vertices[i]; + vertices[i] = mesh.vertices[i]; } - sphereTriangles.Resize(mesh.indexCount / 3); - for (u32 i = 0; i < mesh.indexCount / 3; ++i) + triangleCount = mesh.indexCount / 3; + triangles = (b3ClothMeshTriangle*) malloc(triangleCount * sizeof(b3ClothMeshTriangle)); + for (u32 i = 0; i < triangleCount; ++i) { - sphereTriangles[i].v1 = mesh.indices[3 * i + 0]; - sphereTriangles[i].v2 = mesh.indices[3 * i + 1]; - sphereTriangles[i].v3 = mesh.indices[3 * i + 2]; + triangles[i].v1 = mesh.indices[3 * i + 0]; + triangles[i].v2 = mesh.indices[3 * i + 1]; + triangles[i].v3 = mesh.indices[3 * i + 2]; } - sphereMesh.startTriangle = 0; - sphereMesh.triangleCount = sphereTriangles.Count(); - sphereMesh.startVertex = 0; - sphereMesh.vertexCount = sphereVertices.Count(); + qMesh.startTriangle = 0; + qMesh.triangleCount = triangleCount; + qMesh.startVertex = 0; + qMesh.vertexCount = vertexCount; - vertexCount = sphereVertices.Count(); - vertices = sphereVertices.Begin(); - triangleCount = sphereTriangles.Count(); - triangles = sphereTriangles.Begin(); meshCount = 1; - meshes = &sphereMesh; + meshes = &qMesh; sewingLineCount = 0; sewingLines = nullptr; } @@ -270,7 +272,7 @@ public: return new SoftBody(); } - b3SphereClothMesh m_mesh; + b3QClothMesh m_mesh; b3Cloth* m_cloth; b3ClothDragger* m_clothDragger; }; diff --git a/include/bounce/meshgen/cylinder_mesh.h b/include/bounce/meshgen/cylinder_mesh.h new file mode 100644 index 0000000..f1d0653 --- /dev/null +++ b/include/bounce/meshgen/cylinder_mesh.h @@ -0,0 +1,54 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef CYM_MESH_H +#define CYM_MESH_H + +#include + +// This structure represents a triangle mesh. +struct cymMesh +{ + cymMesh() + { + vertexCount = 0; + vertices = nullptr; + normals = nullptr; + indexCount = 0; + indices = nullptr; + } + + ~cymMesh() + { + b3Free(vertices); + b3Free(normals); + b3Free(indices); + } + + u32 vertexCount; // number of unique vertices + b3Vec3* vertices; // list of unique vertices + b3Vec3* normals; // list of vertex normals + u32 indexCount; // number of triangle vertex indices + u32* indices; // list of triangle vertex index +}; + +// Create a unit cylinder along the y-axis given the number of segments. +// The number of segments must be greater than 2. +void cymCreateMesh(cymMesh& output, u32 segments); + +#endif \ No newline at end of file diff --git a/examples/testbed/framework/sphere_mesh.h b/include/bounce/meshgen/sphere_mesh.h similarity index 90% rename from examples/testbed/framework/sphere_mesh.h rename to include/bounce/meshgen/sphere_mesh.h index 7f26b56..fbcbc9d 100644 --- a/examples/testbed/framework/sphere_mesh.h +++ b/include/bounce/meshgen/sphere_mesh.h @@ -16,8 +16,8 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef SPHERE_MESH_H -#define SPHERE_MESH_H +#ifndef SM_MESH_H +#define SM_MESH_H #include @@ -28,18 +28,21 @@ struct smMesh { vertexCount = 0; vertices = nullptr; + normals = nullptr; indexCount = 0; indices = nullptr; } ~smMesh() { - free(vertices); - free(indices); + b3Free(vertices); + b3Free(normals); + b3Free(indices); } u32 vertexCount; // number of unique vertices b3Vec3* vertices; // list of unique vertices + b3Vec3* normals; // list of vertex normals u32 indexCount; // number of triangle vertex indices u32* indices; // list of triangle vertex index }; diff --git a/premake5.lua b/premake5.lua index 707cbcc..f0ef7e9 100644 --- a/premake5.lua +++ b/premake5.lua @@ -273,10 +273,6 @@ workspace(solution_name) examples_src_dir .. "/testbed/framework/test.h", - examples_src_dir .. "/testbed/framework/sphere_mesh.h", - - examples_src_dir .. "/testbed/framework/sphere_mesh.cpp", - examples_src_dir .. "/testbed/framework/body_dragger.h", examples_src_dir .. "/testbed/framework/cloth_dragger.h", diff --git a/src/bounce/collision/shapes/qhull.cpp b/src/bounce/collision/shapes/qhull.cpp index 06ff300..b4e1541 100644 --- a/src/bounce/collision/shapes/qhull.cpp +++ b/src/bounce/collision/shapes/qhull.cpp @@ -19,6 +19,9 @@ #include #include +#include +#include + template struct b3UniqueStackArray { @@ -348,35 +351,17 @@ void b3QHull::Set(u32 vtxStride, const void* vtxBase, u32 vtxCount, bool simplif void b3QHull::SetAsSphere(float32 radius) { - enum + B3_ASSERT(radius > 0.0f); + + smMesh mesh; + smCreateMesh(mesh, 2); + + for (u32 i = 0; i < mesh.vertexCount; ++i) { - e_rings = 8, - e_sectors = 8, - e_vertexCount = e_rings * e_sectors - }; - - float32 R = 1.0f / float32(e_rings - 1); - float32 S = 1.0f / float32(e_sectors - 1); - - b3Vec3 vs[e_vertexCount]; - - u32 vc = 0; - for (u32 r = 0; r < e_rings; r++) - { - for (u32 s = 0; s < e_sectors; s++) - { - float32 y = sin(-0.5f * B3_PI + B3_PI * r * R); - float32 x = cos(2.0f * B3_PI * s * S) * sin(B3_PI * r * R); - float32 z = sin(2.0f * B3_PI * s * S) * sin(B3_PI * r * R); - - vs[vc].Set(x, y, z); - vs[vc] *= radius; - ++vc; - } + mesh.vertices[i] *= radius; } - // Set - Set(sizeof(b3Vec3), vs, e_vertexCount, false); + Set(sizeof(b3Vec3), mesh.vertices, mesh.vertexCount, false); } void b3QHull::SetAsCylinder(float32 radius, float32 ey) @@ -384,51 +369,19 @@ void b3QHull::SetAsCylinder(float32 radius, float32 ey) B3_ASSERT(radius > 0.0f); B3_ASSERT(ey > 0.0f); - const u32 kEdgeCount = 20; - const u32 kVertexCount = 4 * kEdgeCount; - b3Vec3 vs[kVertexCount]; + float32 height = 2.0f * ey; - u32 count = 0; - - float32 kAngleInc = 2.0f * B3_PI / float32(kEdgeCount); - b3Quat q = b3QuatRotationY(kAngleInc); + cymMesh mesh; + cymCreateMesh(mesh, 20); + for (u32 i = 0; i < mesh.vertexCount; ++i) { - b3Vec3 center(0.0f, -ey, 0.0f); - b3Vec3 n1(1.0f, 0.0f, 0.0f); - b3Vec3 v1 = center + radius * n1; - for (u32 i = 0; i < kEdgeCount; ++i) - { - b3Vec3 n2 = b3Mul(q, n1); - b3Vec3 v2 = center + radius * n2; - - vs[count++] = v1; - vs[count++] = v2; - - n1 = n2; - v1 = v2; - } + mesh.vertices[i].x *= radius; + mesh.vertices[i].y *= height; + mesh.vertices[i].z *= radius; } - { - b3Vec3 center(0.0f, ey, 0.0f); - b3Vec3 n1(1.0f, 0.0f, 0.0f); - b3Vec3 v1 = center + radius * n1; - for (u32 i = 0; i < kEdgeCount; ++i) - { - b3Vec3 n2 = b3Mul(q, n1); - b3Vec3 v2 = center + radius * n2; - - vs[count++] = v1; - vs[count++] = v2; - - n1 = n2; - v1 = v2; - } - } - - // Set - Set(sizeof(b3Vec3), vs, count, false); + Set(sizeof(b3Vec3), mesh.vertices, mesh.vertexCount, false); } void b3QHull::SetAsCone(float32 radius, float32 ey) diff --git a/src/bounce/meshgen/cylinder_mesh.cpp b/src/bounce/meshgen/cylinder_mesh.cpp new file mode 100644 index 0000000..9d8f99d --- /dev/null +++ b/src/bounce/meshgen/cylinder_mesh.cpp @@ -0,0 +1,129 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include + +void cymCreateMesh(cymMesh& output, u32 segments) +{ + B3_ASSERT(segments > 2); + + u32 vertexCount = 2 * segments; + b3Vec3* vertices = (b3Vec3*)b3Alloc(vertexCount * sizeof(b3Vec3)); + b3Vec3* normals = (b3Vec3*)b3Alloc(vertexCount * sizeof(b3Vec3)); + u32 indexCount = 3 * (2 * segments) + 2 * 3 * (segments - 2); + u32* indices = (u32*)b3Alloc(indexCount * sizeof(u32)); + + float32 angle = 2.0f * B3_PI / float32(segments); + b3Quat q(b3Vec3_y, angle); + + // Lower + b3Vec3 v(1.0f, -0.5f, 0.0f); + for (u32 i = 0; i < segments; ++i) + { + vertices[i] = v; + v = b3Mul(q, v); + } + + // Upper + v.Set(1.0f, 0.5f, 0.0f); + for (u32 i = 0; i < segments; ++i) + { + vertices[segments + i] = v; + v = b3Mul(q, v); + } + + u32 idx = 0; + + // Side triangles + for (u32 i = 0; i < segments; ++i) + { + u32 i1 = i; + u32 i2 = i1 + 1 < segments ? i1 + 1 : 0; + + u32 i3 = segments + i; + u32 i4 = i3 + 1 < 2 * segments ? i3 + 1 : segments; + + indices[idx++] = i1; + indices[idx++] = i2; + indices[idx++] = i4; + + indices[idx++] = i4; + indices[idx++] = i3; + indices[idx++] = i1; + } + + // Side normals + for (u32 i = 0; i < vertexCount; ++i) + { + normals[i].SetZero(); + } + + for (u32 i = 0; i < idx / 3; ++i) + { + u32 i1 = indices[3 * i + 0]; + u32 i2 = indices[3 * i + 1]; + u32 i3 = indices[3 * i + 2]; + + b3Vec3 v1 = vertices[i1]; + b3Vec3 v2 = vertices[i2]; + b3Vec3 v3 = vertices[i3]; + + b3Vec3 n = b3Cross(v2 - v1, v3 - v1); + n.Normalize(); + + normals[i1] += n; + normals[i2] += n; + normals[i3] += n; + } + + for (u32 i = 0; i < vertexCount; ++i) + { + normals[i].Normalize(); + } + + // Lower. Reverse loop to ensure CCW + u32 i1 = segments - 1; + for (u32 i2 = i1 - 1; i2 > 0; --i2) + { + u32 i3 = i2 - 1; + + indices[idx++] = i1; + indices[idx++] = i2; + indices[idx++] = i3; + } + + // Upper + i1 = segments; + for (u32 i2 = i1 + 1; i2 < 2 * segments - 1; ++i2) + { + u32 i3 = i2 + 1; + + indices[idx++] = i1; + indices[idx++] = i2; + indices[idx++] = i3; + } + + B3_ASSERT(idx == indexCount); + + output.vertexCount = vertexCount; + output.vertices = vertices; + output.normals = normals; + output.indexCount = indexCount; + output.indices = indices; +} \ No newline at end of file diff --git a/examples/testbed/framework/sphere_mesh.cpp b/src/bounce/meshgen/sphere_mesh.cpp similarity index 87% rename from examples/testbed/framework/sphere_mesh.cpp rename to src/bounce/meshgen/sphere_mesh.cpp index 647558d..5c24f02 100644 --- a/examples/testbed/framework/sphere_mesh.cpp +++ b/src/bounce/meshgen/sphere_mesh.cpp @@ -16,7 +16,7 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include +#include static inline void smAddVertex(smMesh& mesh, float32 x, float32 y, float32 z) { @@ -32,7 +32,7 @@ static inline void smAddTriangle(smMesh& mesh, u32 v1, u32 v2, u32 v3) static inline void smSetAsOctahedron(smMesh& mesh) { - assert(mesh.vertexCount == 0); + B3_ASSERT(mesh.vertexCount == 0); smAddVertex(mesh, 0.0f, -1.0f, 0.0f); smAddVertex(mesh, 0.0f, 0.0f, 1.0f); @@ -41,7 +41,7 @@ static inline void smSetAsOctahedron(smMesh& mesh) smAddVertex(mesh, 1.0f, 0.0f, 0.0f); smAddVertex(mesh, 0.0f, 1.0f, 0.0f); - assert(mesh.indexCount == 0); + B3_ASSERT(mesh.indexCount == 0); smAddTriangle(mesh, 0, 1, 2); smAddTriangle(mesh, 0, 2, 3); @@ -162,8 +162,8 @@ static inline void smCount(u32& vertexCount, u32& indexCount, u32& edgeVertexPai void smCreateMesh(smMesh& output, u32 subdivisions) { - assert(output.vertexCount == 0); - assert(output.indexCount == 0); + B3_ASSERT(output.vertexCount == 0); + B3_ASSERT(output.indexCount == 0); u32 vertexCount, indexCount, edgeVertexPairCount; smCount(vertexCount, indexCount, edgeVertexPairCount, subdivisions); @@ -173,11 +173,12 @@ void smCreateMesh(smMesh& output, u32 subdivisions) byteCount += indexCount * sizeof(u32); byteCount += edgeVertexPairCount * sizeof(smEdgeVertexPair); - u8* bytes = (u8*)malloc(byteCount); + u8* bytes = (u8*)b3Alloc(byteCount); smMesh out; out.vertexCount = 0; out.vertices = (b3Vec3*)bytes; + out.normals = nullptr; out.indexCount = 0; out.indices = (u32*) ((u8*)(out.vertices) + (vertexCount * sizeof(b3Vec3))); @@ -192,21 +193,24 @@ void smCreateMesh(smMesh& output, u32 subdivisions) smSubdivideMesh(out, map); } - assert(map.pairCount < edgeVertexPairCount); - - assert(out.vertexCount <= vertexCount); - assert(out.indexCount == indexCount); + B3_ASSERT(out.vertexCount <= vertexCount); + B3_ASSERT(out.indexCount == indexCount); + B3_ASSERT(map.pairCount < edgeVertexPairCount); output.vertexCount = out.vertexCount; - output.vertices = (b3Vec3*)malloc(out.vertexCount * sizeof(b3Vec3)); + output.vertices = (b3Vec3*)b3Alloc(out.vertexCount * sizeof(b3Vec3)); memcpy(output.vertices, out.vertices, out.vertexCount * sizeof(b3Vec3)); + + output.normals = (b3Vec3*)b3Alloc(out.vertexCount * sizeof(b3Vec3)); + memcpy(output.normals, output.vertices, output.vertexCount * sizeof(b3Vec3)); output.indexCount = out.indexCount; - output.indices = (u32*)malloc(out.indexCount * sizeof(u32)); + output.indices = (u32*)b3Alloc(out.indexCount * sizeof(u32)); memcpy(output.indices, out.indices, out.indexCount * sizeof(u32)); - free(bytes); + b3Free(bytes); out.vertices = nullptr; + out.normals = nullptr; out.indices = nullptr; } \ No newline at end of file From c6a3e4717674479c6b67eb08d0ef644da1fca2d9 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 21 Apr 2019 16:32:40 -0300 Subject: [PATCH 074/198] Consistency --- examples/testbed/tests/soft_body.h | 53 ++++++++++++++++-------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/examples/testbed/tests/soft_body.h b/examples/testbed/tests/soft_body.h index bc750db..e6d7897 100644 --- a/examples/testbed/tests/soft_body.h +++ b/examples/testbed/tests/soft_body.h @@ -23,44 +23,48 @@ struct b3QClothMesh : public b3ClothMesh { - b3ClothMeshMesh qMesh; - - ~b3QClothMesh() - { - free(vertices); - free(triangles); - } + b3StackArray clothVertices; + b3StackArray clothTriangles; + b3ClothMeshMesh clothMesh; b3QClothMesh() + { + vertices = clothVertices.Begin(); + vertexCount = 0; + triangles = clothTriangles.Begin(); + triangleCount = 0; + meshCount = 1; + meshes = &clothMesh; + sewingLineCount = 0; + sewingLines = nullptr; + } + + void SetAsSphere(float32 radius = 1.0f) { smMesh mesh; smCreateMesh(mesh, 2); - vertexCount = mesh.vertexCount; - vertices = (b3Vec3*) malloc(vertexCount * sizeof(b3Vec3)); + clothVertices.Resize(mesh.vertexCount); for (u32 i = 0; i < mesh.vertexCount; ++i) { - vertices[i] = mesh.vertices[i]; + clothVertices[i] = radius * mesh.vertices[i]; } - - triangleCount = mesh.indexCount / 3; - triangles = (b3ClothMeshTriangle*) malloc(triangleCount * sizeof(b3ClothMeshTriangle)); - for (u32 i = 0; i < triangleCount; ++i) + + clothTriangles.Resize(mesh.indexCount / 3); + for (u32 i = 0; i < mesh.indexCount / 3; ++i) { triangles[i].v1 = mesh.indices[3 * i + 0]; triangles[i].v2 = mesh.indices[3 * i + 1]; triangles[i].v3 = mesh.indices[3 * i + 2]; } - qMesh.startTriangle = 0; - qMesh.triangleCount = triangleCount; - qMesh.startVertex = 0; - qMesh.vertexCount = vertexCount; + clothMesh.startTriangle = 0; + clothMesh.triangleCount = clothTriangles.Count(); + clothMesh.startVertex = 0; + clothMesh.vertexCount = clothVertices.Count(); - meshCount = 1; - meshes = &qMesh; - sewingLineCount = 0; - sewingLines = nullptr; + vertexCount = clothVertices.Count(); + triangleCount = clothTriangles.Count(); } }; @@ -69,10 +73,11 @@ class SoftBody : public Test public: SoftBody() { - // Scale and translate the cloth mesh upwards + m_mesh.SetAsSphere(2.0f); + + // Translate the cloth mesh upwards for (u32 i = 0; i < m_mesh.vertexCount; ++i) { - m_mesh.vertices[i] *= 2.0f; m_mesh.vertices[i].y += 10.0f; } From d2d8ade61106d75f6fb43982d94539431c23bd06 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 22 Apr 2019 13:07:00 -0300 Subject: [PATCH 075/198] Rollback --- src/bounce/meshgen/sphere_mesh.cpp | 153 +++++++++++++++-------------- 1 file changed, 81 insertions(+), 72 deletions(-) diff --git a/src/bounce/meshgen/sphere_mesh.cpp b/src/bounce/meshgen/sphere_mesh.cpp index 5c24f02..91fa3dc 100644 --- a/src/bounce/meshgen/sphere_mesh.cpp +++ b/src/bounce/meshgen/sphere_mesh.cpp @@ -90,7 +90,8 @@ static inline smEdgeVertexPair* smFind(smEdgeVertexMap& map, u32 v1, u32 v2) return nullptr; } -static inline u32 smSubdivideEdge(smMesh& in_out, smEdgeVertexMap& map, +static inline u32 smSubdivideEdge(smMesh& out, smEdgeVertexMap& map, + const smMesh& in, u32 i1, u32 i2) { smEdgeVertexPair* pair = smFind(map, i2, i1); @@ -104,14 +105,14 @@ static inline u32 smSubdivideEdge(smMesh& in_out, smEdgeVertexMap& map, newEdge.v1 = i1; newEdge.v2 = i2; - u32 newVertex = in_out.vertexCount; + u32 newVertex = out.vertexCount; - b3Vec3 v1 = in_out.vertices[i1]; - b3Vec3 v2 = in_out.vertices[i2]; + b3Vec3 v1 = in.vertices[i1]; + b3Vec3 v2 = in.vertices[i2]; b3Vec3 v = 0.5f * (v1 + v2); v.Normalize(); - smAddVertex(in_out, v.x, v.y, v.z); + smAddVertex(out, v.x, v.y, v.z); smEdgeVertexPair newPair; newPair.edge = newEdge; @@ -122,95 +123,103 @@ static inline u32 smSubdivideEdge(smMesh& in_out, smEdgeVertexMap& map, return newVertex; } -static void smSubdivideMesh(smMesh& in_out, smEdgeVertexMap& map) +static void smSubdivideMesh(smMesh& out, const smMesh& in, smEdgeVertexMap& map) { - map.pairCount = 0; + B3_ASSERT(out.vertexCount == 0); + B3_ASSERT(out.indexCount == 0); + B3_ASSERT(map.pairCount == 0); - u32 inputIndexCount = in_out.indexCount; + out.vertexCount = in.vertexCount; + memcpy(out.vertices, in.vertices, in.vertexCount * sizeof(b3Vec3)); - for (u32 i = 0; i < inputIndexCount / 3; ++i) + for (u32 i = 0; i < in.indexCount / 3; ++i) { - u32 vi1 = in_out.indices[3 * i + 0]; - u32 vi2 = in_out.indices[3 * i + 1]; - u32 vi3 = in_out.indices[3 * i + 2]; + u32 vi1 = in.indices[3 * i + 0]; + u32 vi2 = in.indices[3 * i + 1]; + u32 vi3 = in.indices[3 * i + 2]; - u32 vi4 = smSubdivideEdge(in_out, map, vi1, vi2); - u32 vi5 = smSubdivideEdge(in_out, map, vi2, vi3); - u32 vi6 = smSubdivideEdge(in_out, map, vi3, vi1); + u32 vi4 = smSubdivideEdge(out, map, in, vi1, vi2); + u32 vi5 = smSubdivideEdge(out, map, in, vi2, vi3); + u32 vi6 = smSubdivideEdge(out, map, in, vi3, vi1); - smAddTriangle(in_out, vi1, vi4, vi6); - smAddTriangle(in_out, vi4, vi2, vi5); - smAddTriangle(in_out, vi5, vi3, vi6); - smAddTriangle(in_out, vi4, vi5, vi6); + smAddTriangle(out, vi1, vi4, vi6); + smAddTriangle(out, vi4, vi2, vi5); + smAddTriangle(out, vi5, vi3, vi6); + smAddTriangle(out, vi4, vi5, vi6); } } -// Compute the maximum number of vertices and -// the number of triangle vertex indices in the intermediate mesh. -// Also compute the maximum number of edge-vertex pairs per subdivision step -static inline void smCount(u32& vertexCount, u32& indexCount, u32& edgeVertexPairCount, u32 subdivisions) -{ - vertexCount = 6; - indexCount = 3 * 8; - for (u32 i = 0; i < subdivisions; ++i) - { - vertexCount += 3 * (indexCount / 3); - indexCount += 4 * 3 * (indexCount / 3); - } - edgeVertexPairCount = 3 * (indexCount / 3); -} - void smCreateMesh(smMesh& output, u32 subdivisions) { B3_ASSERT(output.vertexCount == 0); B3_ASSERT(output.indexCount == 0); - u32 vertexCount, indexCount, edgeVertexPairCount; - smCount(vertexCount, indexCount, edgeVertexPairCount, subdivisions); + smMesh in; + in.vertexCount = 0; + in.vertices = (b3Vec3*)b3Alloc(6 * sizeof(b3Vec3)); + in.normals = nullptr; + in.indexCount = 0; + in.indices = (u32*)b3Alloc(3 * 8 * sizeof(u32)); - u32 byteCount = 0; - byteCount += vertexCount * sizeof(b3Vec3); - byteCount += indexCount * sizeof(u32); - byteCount += edgeVertexPairCount * sizeof(smEdgeVertexPair); - - u8* bytes = (u8*)b3Alloc(byteCount); - - smMesh out; - out.vertexCount = 0; - out.vertices = (b3Vec3*)bytes; - out.normals = nullptr; - out.indexCount = 0; - out.indices = (u32*) ((u8*)(out.vertices) + (vertexCount * sizeof(b3Vec3))); - - smEdgeVertexMap map; - map.pairCount = 0; - map.pairs = (smEdgeVertexPair*) ((u8*)(out.indices) + (indexCount * sizeof(u32))); - - smSetAsOctahedron(out); + smSetAsOctahedron(in); for (u32 i = 0; i < subdivisions; ++i) { - smSubdivideMesh(out, map); + u32 inTriangleCount = in.indexCount / 3; + u32 outVertexCapacity = in.vertexCount + 3 * inTriangleCount; + + u32 outTriangleCapacity = 4 * inTriangleCount; + u32 outIndexCapacity = 3 * outTriangleCapacity; + + u32 edgeVertexPairCapacity = 3 * inTriangleCount; + + smMesh out; + out.vertexCount = 0; + out.vertices = (b3Vec3*)b3Alloc(outVertexCapacity * sizeof(b3Vec3)); + + out.indexCount = 0; + out.indices = (u32*)b3Alloc(outIndexCapacity * sizeof(u32)); + + smEdgeVertexMap map; + map.pairCount = 0; + map.pairs = (smEdgeVertexPair*)b3Alloc(edgeVertexPairCapacity * sizeof(smEdgeVertexPair)); + + smSubdivideMesh(out, in, map); + + b3Free(map.pairs); + b3Free(in.vertices); + b3Free(in.indices); + + // in = out + in.vertexCount = out.vertexCount; + in.vertices = (b3Vec3*)b3Alloc(out.vertexCount * sizeof(b3Vec3)); + memcpy(in.vertices, out.vertices, out.vertexCount * sizeof(b3Vec3)); + + in.indexCount = out.indexCount; + in.indices = (u32*)b3Alloc(out.indexCount * sizeof(u32)); + memcpy(in.indices, out.indices, out.indexCount * sizeof(u32)); + + b3Free(out.vertices); + out.vertices = nullptr; + out.normals = nullptr; + b3Free(out.indices); + out.indices = nullptr; } - B3_ASSERT(out.vertexCount <= vertexCount); - B3_ASSERT(out.indexCount == indexCount); - B3_ASSERT(map.pairCount < edgeVertexPairCount); - - output.vertexCount = out.vertexCount; - output.vertices = (b3Vec3*)b3Alloc(out.vertexCount * sizeof(b3Vec3)); - memcpy(output.vertices, out.vertices, out.vertexCount * sizeof(b3Vec3)); + output.vertexCount = in.vertexCount; + output.vertices = (b3Vec3*)b3Alloc(in.vertexCount * sizeof(b3Vec3)); + memcpy(output.vertices, in.vertices, in.vertexCount * sizeof(b3Vec3)); - output.normals = (b3Vec3*)b3Alloc(out.vertexCount * sizeof(b3Vec3)); + output.normals = (b3Vec3*)b3Alloc(in.vertexCount * sizeof(b3Vec3)); memcpy(output.normals, output.vertices, output.vertexCount * sizeof(b3Vec3)); - output.indexCount = out.indexCount; - output.indices = (u32*)b3Alloc(out.indexCount * sizeof(u32)); - memcpy(output.indices, out.indices, out.indexCount * sizeof(u32)); + output.indexCount = in.indexCount; + output.indices = (u32*)b3Alloc(in.indexCount * sizeof(u32)); + memcpy(output.indices, in.indices, in.indexCount * sizeof(u32)); - b3Free(bytes); - - out.vertices = nullptr; - out.normals = nullptr; - out.indices = nullptr; + b3Free(in.vertices); + b3Free(in.indices); + in.vertices = nullptr; + in.normals = nullptr; + in.indices = nullptr; } \ No newline at end of file From ecb4df45f428506455775fc69e1e0c71f445b918 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 22 Apr 2019 14:33:59 -0300 Subject: [PATCH 076/198] Array pointers can change after reallocation --- examples/testbed/tests/soft_body.h | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/examples/testbed/tests/soft_body.h b/examples/testbed/tests/soft_body.h index e6d7897..562c894 100644 --- a/examples/testbed/tests/soft_body.h +++ b/examples/testbed/tests/soft_body.h @@ -29,12 +29,12 @@ struct b3QClothMesh : public b3ClothMesh b3QClothMesh() { - vertices = clothVertices.Begin(); + vertices = nullptr; vertexCount = 0; - triangles = clothTriangles.Begin(); + triangles = nullptr; triangleCount = 0; - meshCount = 1; - meshes = &clothMesh; + meshCount = 0; + meshes = nullptr; sewingLineCount = 0; sewingLines = nullptr; } @@ -53,9 +53,9 @@ struct b3QClothMesh : public b3ClothMesh clothTriangles.Resize(mesh.indexCount / 3); for (u32 i = 0; i < mesh.indexCount / 3; ++i) { - triangles[i].v1 = mesh.indices[3 * i + 0]; - triangles[i].v2 = mesh.indices[3 * i + 1]; - triangles[i].v3 = mesh.indices[3 * i + 2]; + clothTriangles[i].v1 = mesh.indices[3 * i + 0]; + clothTriangles[i].v2 = mesh.indices[3 * i + 1]; + clothTriangles[i].v3 = mesh.indices[3 * i + 2]; } clothMesh.startTriangle = 0; @@ -63,8 +63,12 @@ struct b3QClothMesh : public b3ClothMesh clothMesh.startVertex = 0; clothMesh.vertexCount = clothVertices.Count(); + vertices = clothVertices.Begin(); vertexCount = clothVertices.Count(); + triangles = clothTriangles.Begin(); triangleCount = clothTriangles.Count(); + meshCount = 1; + meshes = &clothMesh; } }; From a5a49df8c1946b3c94bd68146c333ca44fabbee8 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 22 Apr 2019 14:38:51 -0300 Subject: [PATCH 077/198] Begin pointer can change after reallocation --- include/bounce/collision/shapes/qhull.h | 8 ++++---- src/bounce/collision/shapes/qhull.cpp | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/include/bounce/collision/shapes/qhull.h b/include/bounce/collision/shapes/qhull.h index 42f3c66..25836de 100644 --- a/include/bounce/collision/shapes/qhull.h +++ b/include/bounce/collision/shapes/qhull.h @@ -32,13 +32,13 @@ struct b3QHull : public b3Hull b3QHull() { - vertices = hullVertices.Begin(); + vertices = nullptr; vertexCount = 0; - edges = hullEdges.Begin(); + edges = nullptr; edgeCount = 0; - faces = hullFaces.Begin(); + faces = nullptr; faceCount = 0; - planes = hullPlanes.Begin(); + planes = nullptr; centroid.SetZero(); } diff --git a/src/bounce/collision/shapes/qhull.cpp b/src/bounce/collision/shapes/qhull.cpp index b4e1541..1c5b259 100644 --- a/src/bounce/collision/shapes/qhull.cpp +++ b/src/bounce/collision/shapes/qhull.cpp @@ -338,8 +338,12 @@ void b3QHull::Set(u32 vtxStride, const void* vtxBase, u32 vtxCount, bool simplif ++iface; } + vertices = hullVertices.Begin(); vertexCount = hullVertices.Count(); + edges = hullEdges.Begin(); edgeCount = hullEdges.Count(); + faces = hullFaces.Begin(); + planes = hullPlanes.Begin(); faceCount = hullFaces.Count(); // Validate From 5c90059689799fe2df499779620e2928ecf7a6cd Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 22 Apr 2019 17:11:42 -0300 Subject: [PATCH 078/198] Optimization --- src/bounce/meshgen/sphere_mesh.cpp | 69 +++++++++++++++++++----------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/src/bounce/meshgen/sphere_mesh.cpp b/src/bounce/meshgen/sphere_mesh.cpp index 91fa3dc..70f5bae 100644 --- a/src/bounce/meshgen/sphere_mesh.cpp +++ b/src/bounce/meshgen/sphere_mesh.cpp @@ -149,60 +149,81 @@ static void smSubdivideMesh(smMesh& out, const smMesh& in, smEdgeVertexMap& map) } } +static inline void smCount(u32& vertexCapacity, u32& indexCount, u32& edgeVertexPairCapacity, u32 subdivisions) +{ + u32 inVertexCapacity = 6; + u32 inTriangleCount = 8; + for (u32 i = 0; i < subdivisions; ++i) + { + u32 outVertexCapacity = inVertexCapacity + 3 * inTriangleCount; + u32 outTriangleCount = 4 * inTriangleCount; + + inVertexCapacity = outVertexCapacity; + inTriangleCount = outTriangleCount; + } + vertexCapacity = inVertexCapacity; + indexCount = 3 * inTriangleCount; + edgeVertexPairCapacity = 3 * inTriangleCount; +} + void smCreateMesh(smMesh& output, u32 subdivisions) { B3_ASSERT(output.vertexCount == 0); B3_ASSERT(output.indexCount == 0); + u32 vertexCapacity, indexCount, edgeVertexPairCapacity; + smCount(vertexCapacity, indexCount, edgeVertexPairCapacity, subdivisions); + + // We use two vertex and index buffers. + // One is used for input mesh and the other for suboutput mesh. + // Each buffer has size equal the maximum vertex or index count. + u32 byteCount = 0; + byteCount += 2 * vertexCapacity * sizeof(b3Vec3); + byteCount += 2 * indexCount * sizeof(u32); + byteCount += edgeVertexPairCapacity * sizeof(smEdgeVertexPair); + + u8* bytes = (u8*)b3Alloc(byteCount); + + b3Vec3* inVertex = (b3Vec3*)bytes; + b3Vec3* outVertex = (b3Vec3*) ((u8*)(inVertex) + vertexCapacity * sizeof(b3Vec3)); + u32* inIndex = (u32*)((u8*)(outVertex) + vertexCapacity * sizeof(b3Vec3)); + u32* outIndex = (u32*)((u8*)(inIndex) + indexCount * sizeof(u32)); + smEdgeVertexPair* pairs = (smEdgeVertexPair*)((u8*)(outIndex) + indexCount * sizeof(u32)); + smMesh in; in.vertexCount = 0; - in.vertices = (b3Vec3*)b3Alloc(6 * sizeof(b3Vec3)); + in.vertices = inVertex; in.normals = nullptr; in.indexCount = 0; - in.indices = (u32*)b3Alloc(3 * 8 * sizeof(u32)); + in.indices = inIndex; smSetAsOctahedron(in); for (u32 i = 0; i < subdivisions; ++i) { - u32 inTriangleCount = in.indexCount / 3; - u32 outVertexCapacity = in.vertexCount + 3 * inTriangleCount; - - u32 outTriangleCapacity = 4 * inTriangleCount; - u32 outIndexCapacity = 3 * outTriangleCapacity; - - u32 edgeVertexPairCapacity = 3 * inTriangleCount; - smMesh out; out.vertexCount = 0; - out.vertices = (b3Vec3*)b3Alloc(outVertexCapacity * sizeof(b3Vec3)); - + out.vertices = outVertex; out.indexCount = 0; - out.indices = (u32*)b3Alloc(outIndexCapacity * sizeof(u32)); + out.indices = outIndex; smEdgeVertexMap map; map.pairCount = 0; - map.pairs = (smEdgeVertexPair*)b3Alloc(edgeVertexPairCapacity * sizeof(smEdgeVertexPair)); + map.pairs = pairs; smSubdivideMesh(out, in, map); - b3Free(map.pairs); - b3Free(in.vertices); - b3Free(in.indices); - // in = out in.vertexCount = out.vertexCount; - in.vertices = (b3Vec3*)b3Alloc(out.vertexCount * sizeof(b3Vec3)); + in.vertices = inVertex; memcpy(in.vertices, out.vertices, out.vertexCount * sizeof(b3Vec3)); in.indexCount = out.indexCount; - in.indices = (u32*)b3Alloc(out.indexCount * sizeof(u32)); + in.indices = inIndex; memcpy(in.indices, out.indices, out.indexCount * sizeof(u32)); - b3Free(out.vertices); out.vertices = nullptr; out.normals = nullptr; - b3Free(out.indices); out.indices = nullptr; } @@ -217,8 +238,8 @@ void smCreateMesh(smMesh& output, u32 subdivisions) output.indices = (u32*)b3Alloc(in.indexCount * sizeof(u32)); memcpy(output.indices, in.indices, in.indexCount * sizeof(u32)); - b3Free(in.vertices); - b3Free(in.indices); + b3Free(bytes); + in.vertices = nullptr; in.normals = nullptr; in.indices = nullptr; From d02a1f512e152cd5aa9f05b8477bb8388e78e3ce Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 23 Apr 2019 04:28:36 -0300 Subject: [PATCH 079/198] Removed maximum iteration count as the unilateral root solver might take a large number of iterations to converge. --- src/bounce/collision/gjk/gjk.cpp | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/bounce/collision/gjk/gjk.cpp b/src/bounce/collision/gjk/gjk.cpp index a6a9a85..4a397cc 100644 --- a/src/bounce/collision/gjk/gjk.cpp +++ b/src/bounce/collision/gjk/gjk.cpp @@ -862,8 +862,8 @@ bool b3GJKShapeCast(b3GJKShapeCastOutput* output, float32 r2 = proxy2.radius; float32 radius = r1 + r2; - float32 d_max = b3Length(translation2); - B3_ASSERT(d_max > 0.0f); + float32 bound = b3Length(translation2); + B3_ASSERT(bound > 0.0f); float32 t = 0.0f; @@ -894,14 +894,14 @@ bool b3GJKShapeCast(b3GJKShapeCastOutput* output, return true; } - const u32 kMaxIters = 20; - u32 iter = 0; for (;;) { + ++iter; + B3_ASSERT(d >= radius); - float32 dt = (d - radius) / d_max; + float32 dt = (d - radius) / bound; t += dt; if (t >= 1.0f) @@ -932,14 +932,6 @@ bool b3GJKShapeCast(b3GJKShapeCastOutput* output, { break; } - - ++iter; - - if (iter == kMaxIters) - { - output->iterations = iter; - return false; - } } if (d > 0.0f) From 25b7ce3f40d2f40e3ef4a66085498f9d40073cc7 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 23 Apr 2019 05:45:47 -0300 Subject: [PATCH 080/198] Output iterations on initial overlap and decrease tolerance --- src/bounce/collision/gjk/gjk.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/bounce/collision/gjk/gjk.cpp b/src/bounce/collision/gjk/gjk.cpp index 4a397cc..ed3a024 100644 --- a/src/bounce/collision/gjk/gjk.cpp +++ b/src/bounce/collision/gjk/gjk.cpp @@ -866,21 +866,20 @@ bool b3GJKShapeCast(b3GJKShapeCastOutput* output, B3_ASSERT(bound > 0.0f); float32 t = 0.0f; - - const float32 tolerance = 0.5f * B3_LINEAR_SLOP; - b3SimplexCache cache; cache.count = 0; - b3GJKOutput gjkOut = b3GJK(xf1, proxy1, xf2, proxy2, false, &cache); float32 d = gjkOut.distance; if (d == 0.0f) { // Overlap + output->iterations = 0; return false; } + const float32 tolerance = 0.25f * B3_LINEAR_SLOP; + b3Vec3 n = gjkOut.point2 - gjkOut.point1; n /= d; @@ -900,7 +899,6 @@ bool b3GJKShapeCast(b3GJKShapeCastOutput* output, ++iter; B3_ASSERT(d >= radius); - float32 dt = (d - radius) / bound; t += dt; From 262cd505231c6e7ed020a1dba50d7f5e06ace67a Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 23 Apr 2019 10:32:27 -0300 Subject: [PATCH 081/198] Output all memory counters --- src/bounce/meshgen/sphere_mesh.cpp | 44 ++++++++++++++++++------------ 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/bounce/meshgen/sphere_mesh.cpp b/src/bounce/meshgen/sphere_mesh.cpp index 70f5bae..d145b1b 100644 --- a/src/bounce/meshgen/sphere_mesh.cpp +++ b/src/bounce/meshgen/sphere_mesh.cpp @@ -149,20 +149,29 @@ static void smSubdivideMesh(smMesh& out, const smMesh& in, smEdgeVertexMap& map) } } -static inline void smCount(u32& vertexCapacity, u32& indexCount, u32& edgeVertexPairCapacity, u32 subdivisions) +static inline void smCount(u32& inVertexCapacity, u32& inIndexCount, + u32& outVertexCapacity, u32& outIndexCount, + u32& edgeVertexPairCapacity, + u32 subdivisions) { - u32 inVertexCapacity = 6; + inVertexCapacity = 6; u32 inTriangleCount = 8; + + outVertexCapacity = 0; + u32 outTriangleCount = 0; + for (u32 i = 0; i < subdivisions; ++i) { - u32 outVertexCapacity = inVertexCapacity + 3 * inTriangleCount; - u32 outTriangleCount = 4 * inTriangleCount; + outVertexCapacity = inVertexCapacity + 3 * inTriangleCount; + outTriangleCount = 4 * inTriangleCount; inVertexCapacity = outVertexCapacity; inTriangleCount = outTriangleCount; } - vertexCapacity = inVertexCapacity; - indexCount = 3 * inTriangleCount; + + inIndexCount = 3 * inTriangleCount; + outIndexCount = 3 * outTriangleCount; + edgeVertexPairCapacity = 3 * inTriangleCount; } @@ -171,24 +180,25 @@ void smCreateMesh(smMesh& output, u32 subdivisions) B3_ASSERT(output.vertexCount == 0); B3_ASSERT(output.indexCount == 0); - u32 vertexCapacity, indexCount, edgeVertexPairCapacity; - smCount(vertexCapacity, indexCount, edgeVertexPairCapacity, subdivisions); + u32 inVertexCapacity, inIndexCount; + u32 outVertexCapacity, outIndexCount; + u32 edgeVertexPairCapacity; + smCount(inVertexCapacity, inIndexCount, outVertexCapacity, outIndexCount, edgeVertexPairCapacity, subdivisions); - // We use two vertex and index buffers. - // One is used for input mesh and the other for suboutput mesh. - // Each buffer has size equal the maximum vertex or index count. u32 byteCount = 0; - byteCount += 2 * vertexCapacity * sizeof(b3Vec3); - byteCount += 2 * indexCount * sizeof(u32); + byteCount += inVertexCapacity * sizeof(b3Vec3); + byteCount += inIndexCount * sizeof(u32); + byteCount += outVertexCapacity * sizeof(b3Vec3); + byteCount += outIndexCount * sizeof(u32); byteCount += edgeVertexPairCapacity * sizeof(smEdgeVertexPair); u8* bytes = (u8*)b3Alloc(byteCount); b3Vec3* inVertex = (b3Vec3*)bytes; - b3Vec3* outVertex = (b3Vec3*) ((u8*)(inVertex) + vertexCapacity * sizeof(b3Vec3)); - u32* inIndex = (u32*)((u8*)(outVertex) + vertexCapacity * sizeof(b3Vec3)); - u32* outIndex = (u32*)((u8*)(inIndex) + indexCount * sizeof(u32)); - smEdgeVertexPair* pairs = (smEdgeVertexPair*)((u8*)(outIndex) + indexCount * sizeof(u32)); + u32* inIndex = (u32*)((u8*)(inVertex) + inVertexCapacity * sizeof(b3Vec3)); + b3Vec3* outVertex = (b3Vec3*) ((u8*)(inIndex) + inIndexCount * sizeof(u32)); + u32* outIndex = (u32*)((u8*)(outVertex) + outVertexCapacity * sizeof(b3Vec3)); + smEdgeVertexPair* pairs = (smEdgeVertexPair*)((u8*)(outIndex) + outIndexCount * sizeof(u32)); smMesh in; in.vertexCount = 0; From 7d4bb261455b05122a009397a717cfa3eb2c7a63 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 23 Apr 2019 10:43:54 -0300 Subject: [PATCH 082/198] Don't allocate edge-vertex pairs if subdivision is zero --- src/bounce/meshgen/sphere_mesh.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bounce/meshgen/sphere_mesh.cpp b/src/bounce/meshgen/sphere_mesh.cpp index d145b1b..8b6f882 100644 --- a/src/bounce/meshgen/sphere_mesh.cpp +++ b/src/bounce/meshgen/sphere_mesh.cpp @@ -160,19 +160,21 @@ static inline void smCount(u32& inVertexCapacity, u32& inIndexCount, outVertexCapacity = 0; u32 outTriangleCount = 0; + edgeVertexPairCapacity = 0; + for (u32 i = 0; i < subdivisions; ++i) { outVertexCapacity = inVertexCapacity + 3 * inTriangleCount; outTriangleCount = 4 * inTriangleCount; + edgeVertexPairCapacity = 3 * inTriangleCount; + inVertexCapacity = outVertexCapacity; inTriangleCount = outTriangleCount; } inIndexCount = 3 * inTriangleCount; outIndexCount = 3 * outTriangleCount; - - edgeVertexPairCapacity = 3 * inTriangleCount; } void smCreateMesh(smMesh& output, u32 subdivisions) From c71b6edc72f210e273c94dc6ca92f0a6bf518a0f Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 30 Apr 2019 10:10:15 -0300 Subject: [PATCH 083/198] Put quaternion constraint stuff inside namespace --- src/bounce/dynamics/joints/weld_joint.cpp | 30 +++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/bounce/dynamics/joints/weld_joint.cpp b/src/bounce/dynamics/joints/weld_joint.cpp index ccd21ae..e9bd161 100644 --- a/src/bounce/dynamics/joints/weld_joint.cpp +++ b/src/bounce/dynamics/joints/weld_joint.cpp @@ -41,7 +41,7 @@ J1 = P * J1 * P^T J2 = P * J2 * P^T */ -static b3Mat44 iQ_mat(const b3Quat& q) +static B3_FORCE_INLINE b3Mat44 b3Mat44_Quat(const b3Quat& q) { b3Mat44 Q; Q.x = b3Vec4(q.w, q.x, q.y, q.z); @@ -51,7 +51,7 @@ static b3Mat44 iQ_mat(const b3Quat& q) return Q; } -static b3Mat44 iP_mat(const b3Quat& q) +static B3_FORCE_INLINE b3Mat44 b3Mat44_Projection(const b3Quat& q) { b3Mat44 P; P.x = b3Vec4(q.w, q.x, q.y, q.z); @@ -61,7 +61,7 @@ static b3Mat44 iP_mat(const b3Quat& q) return P; } -static b3Mat34 P_mat() +static B3_FORCE_INLINE b3Mat34 b3Mat34_Projection() { b3Mat34 P; P.x = b3Vec3(0.0f, 0.0f, 0.0f); @@ -71,7 +71,7 @@ static b3Mat34 P_mat() return P; } -static b3Mat34 P_lock_mat() +static B3_FORCE_INLINE b3Mat34 b3Mat34_Weld_Projection() { b3Mat34 P; P.x = b3Vec3(0.0f, 0.0f, 0.0f); @@ -81,14 +81,14 @@ static b3Mat34 P_lock_mat() return P; } -static b3Vec4 q_to_v(const b3Quat& q) +static B3_FORCE_INLINE b3Vec4 b3Vec4_Quat(const b3Quat& q) { return b3Vec4(q.w, q.x, q.y, q.z); } -static const b3Mat34 P = P_mat(); -static const b3Mat43 PT = b3Transpose(P); -static const b3Mat34 P_lock = P_lock_mat(); +static const b3Mat34 b3Mat34_P = b3Mat34_Projection(); +static const b3Mat43 b3Mat43_PT = b3Transpose(b3Mat34_P); +static const b3Mat34 b3Mat34_Weld_P = b3Mat34_Weld_Projection(); void b3WeldJointDef::Initialize(b3Body* bA, b3Body* bB, const b3Vec3& anchor) { @@ -147,8 +147,8 @@ void b3WeldJoint::InitializeConstraints(const b3SolverData* data) { b3Quat dq = b3Conjugate(m_referenceRotation) * b3Conjugate(qA) * qB; - m_J1 = -0.5f * P_lock * iQ_mat(b3Conjugate(qA)) * iP_mat(qB) * PT; - m_J2 = 0.5f * P_lock * iQ_mat(b3Conjugate(qA)) * iP_mat(qB) * PT; + m_J1 = -0.5f * b3Mat34_Weld_P * b3Mat44_Quat(b3Conjugate(qA)) * b3Mat44_Projection(qB) * b3Mat43_PT; + m_J2 = 0.5f * b3Mat34_Weld_P * b3Mat44_Quat(b3Conjugate(qA)) * b3Mat44_Projection(qB) * b3Mat43_PT; m_J1T = b3Transpose(m_J1); m_J2T = b3Transpose(m_J2); @@ -266,16 +266,16 @@ bool b3WeldJoint::SolvePositionConstraints(const b3SolverData* data) float32 angularError = 0.0f; - { + { b3Quat dq = b3Conjugate(m_referenceRotation) * b3Conjugate(qA) * qB; - b3Vec4 dq_v = q_to_v(dq); + b3Vec4 dq_v = b3Vec4_Quat(dq); - b3Vec3 C = P * dq_v; + b3Vec3 C = b3Mat34_P * dq_v; angularError += b3Length(C); - b3Mat33 J1 = -0.5f * P_lock * iQ_mat(b3Conjugate(qA)) * iP_mat(qB) * PT; - b3Mat33 J2 = 0.5f * P_lock * iQ_mat(b3Conjugate(qA)) * iP_mat(qB) * PT; + b3Mat33 J1 = -0.5f * b3Mat34_Weld_P * b3Mat44_Quat(b3Conjugate(qA)) * b3Mat44_Projection(qB) * b3Mat43_PT; + b3Mat33 J2 = 0.5f * b3Mat34_Weld_P * b3Mat44_Quat(b3Conjugate(qA)) * b3Mat44_Projection(qB) * b3Mat43_PT; b3Mat33 J1T = b3Transpose(J1); b3Mat33 J2T = b3Transpose(J2); From c28069680ef3e687c4f7d1bea4cb30dba0d48d3e Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 30 Apr 2019 10:40:49 -0300 Subject: [PATCH 084/198] Put quaternion constraint stuff inside namespace --- src/bounce/dynamics/joints/revolute_joint.cpp | 60 ++++++++++--------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/src/bounce/dynamics/joints/revolute_joint.cpp b/src/bounce/dynamics/joints/revolute_joint.cpp index 550c47e..f596dbe 100644 --- a/src/bounce/dynamics/joints/revolute_joint.cpp +++ b/src/bounce/dynamics/joints/revolute_joint.cpp @@ -112,7 +112,7 @@ C' = P_lim * (P_hinge * q') - target_speed */ -static B3_FORCE_INLINE b3Mat44 iQ_mat(const b3Quat& q) +static B3_FORCE_INLINE b3Mat44 b3Mat44_Quat(const b3Quat& q) { b3Mat44 Q; Q.x = b3Vec4(q.w, q.x, q.y, q.z); @@ -122,7 +122,7 @@ static B3_FORCE_INLINE b3Mat44 iQ_mat(const b3Quat& q) return Q; } -static B3_FORCE_INLINE b3Mat44 iP_mat(const b3Quat& q) +static B3_FORCE_INLINE b3Mat44 b3Mat44_Projection(const b3Quat& q) { b3Mat44 P; P.x = b3Vec4(q.w, q.x, q.y, q.z); @@ -132,7 +132,7 @@ static B3_FORCE_INLINE b3Mat44 iP_mat(const b3Quat& q) return P; } -static B3_FORCE_INLINE b3Mat34 P_mat() +static B3_FORCE_INLINE b3Mat34 b3Mat34_Projection() { b3Mat34 P; P.x = b3Vec3(0.0f, 0.0f, 0.0f); @@ -142,7 +142,7 @@ static B3_FORCE_INLINE b3Mat34 P_mat() return P; } -static B3_FORCE_INLINE b3Mat24 P_hinge_mat() +static B3_FORCE_INLINE b3Mat24 b3Mat24_Hinge_Projection() { b3Mat24 P; P.x = b3Vec2(0.0f, 0.0f); @@ -152,21 +152,19 @@ static B3_FORCE_INLINE b3Mat24 P_hinge_mat() return P; } -// 1x4 -static B3_FORCE_INLINE b3Vec4 P_hinge_limit_mat(const b3Quat& q) +static B3_FORCE_INLINE b3Vec4 b3Mat14_Hinge_Limit_Projection(const b3Quat& q) { return b3Vec4(-q.z, 0.0f, 0.0f, q.w); } -// 4x1 -static B3_FORCE_INLINE b3Vec4 q_to_v(const b3Quat& q) +static B3_FORCE_INLINE b3Vec4 b3Vec4_Quat(const b3Quat& q) { return b3Vec4(q.w, q.x, q.y, q.z); } -static const b3Mat34 P = P_mat(); -static const b3Mat43 PT = b3Transpose(P); -static const b3Mat24 P_hinge = P_hinge_mat(); +static const b3Mat34 b3Mat34_P = b3Mat34_Projection(); +static const b3Mat43 b3Mat43_PT = b3Transpose(b3Mat34_P); +static const b3Mat24 b3Mat24_P_Hinge = b3Mat24_Hinge_Projection(); void b3RevoluteJointDef::Initialize(b3Body* bA, b3Body* bB, const b3Vec3& axis, const b3Vec3& anchor, @@ -263,13 +261,14 @@ void b3RevoluteJoint::InitializeConstraints(const b3SolverData* data) // Add motor constraint. if (m_enableMotor || m_enableLimit) { - b3Vec4 P_hinge_limit = P_hinge_limit_mat(q); + b3Mat43 PT = b3Mat43_PT; + b3Vec4 P_limit = b3Mat14_Hinge_Limit_Projection(q); - b3Mat44 G1 = -0.5f * iQ_mat(b3Conjugate(fA)) * iP_mat(fB); - b3Mat44 G2 = 0.5f * iQ_mat(b3Conjugate(fA)) * iP_mat(fB); + b3Mat44 G1 = -0.5f * b3Mat44_Quat(b3Conjugate(fA)) * b3Mat44_Projection(fB); + b3Mat44 G2 = 0.5f * b3Mat44_Quat(b3Conjugate(fA)) * b3Mat44_Projection(fB); - b3Vec3 J1 = P_hinge_limit * G1 * PT; - b3Vec3 J2 = P_hinge_limit * G2 * PT; + b3Vec3 J1 = P_limit * G1 * PT; + b3Vec3 J2 = P_limit * G2 * PT; b3Vec3 J1T = J1; b3Vec3 J2T = J2; @@ -338,8 +337,11 @@ void b3RevoluteJoint::InitializeConstraints(const b3SolverData* data) // Add hinge constraints. { - b3Mat44 G1 = -0.5f * iQ_mat(b3Conjugate(fA)) * iP_mat(fB); - b3Mat44 G2 = 0.5f * iQ_mat(b3Conjugate(fA)) * iP_mat(fB); + b3Mat43 PT = b3Mat43_PT; + b3Mat24 P_hinge = b3Mat24_P_Hinge; + + b3Mat44 G1 = -0.5f * b3Mat44_Quat(b3Conjugate(fA)) * b3Mat44_Projection(fB); + b3Mat44 G2 = 0.5f * b3Mat44_Quat(b3Conjugate(fA)) * b3Mat44_Projection(fB); b3Mat23 J1 = P_hinge * G1 * PT; b3Mat23 J2 = P_hinge * G2 * PT; @@ -518,13 +520,14 @@ bool b3RevoluteJoint::SolvePositionConstraints(const b3SolverData* data) b3Quat fB = qB * m_localRotationB; b3Quat q = b3Conjugate(m_referenceRotation) * b3Conjugate(fA) * fB; - b3Vec4 P_hinge_limit = P_hinge_limit_mat(q); + b3Mat43 PT = b3Mat43_PT; + b3Vec4 P_limit = b3Mat14_Hinge_Limit_Projection(q); + + b3Mat44 G1 = -0.5f * b3Mat44_Quat(b3Conjugate(fA)) * b3Mat44_Projection(fB); + b3Mat44 G2 = 0.5f * b3Mat44_Quat(b3Conjugate(fA)) * b3Mat44_Projection(fB); - b3Mat44 G1 = -0.5f * iQ_mat(b3Conjugate(fA)) * iP_mat(fB); - b3Mat44 G2 = 0.5f * iQ_mat(b3Conjugate(fA)) * iP_mat(fB); - - b3Vec3 J1 = P_hinge_limit * G1 * PT; - b3Vec3 J2 = P_hinge_limit * G2 * PT; + b3Vec3 J1 = P_limit * G1 * PT; + b3Vec3 J2 = P_limit * G2 * PT; b3Vec3 J1T = J1; b3Vec3 J2T = J2; @@ -617,13 +620,16 @@ bool b3RevoluteJoint::SolvePositionConstraints(const b3SolverData* data) b3Quat fB = qB * m_localRotationB; b3Quat q = b3Conjugate(m_referenceRotation) * b3Conjugate(fA) * fB; - b3Vec2 C = P_hinge * q_to_v(q); + b3Mat43 PT = b3Mat43_PT; + b3Mat24 P_hinge = b3Mat24_P_Hinge; + + b3Vec2 C = P_hinge * b3Vec4_Quat(q); angularError += b3Length(C); // Compute effective mass - b3Mat44 G1 = -0.5f * iQ_mat(b3Conjugate(fA)) * iP_mat(fB); - b3Mat44 G2 = 0.5f * iQ_mat(b3Conjugate(fA)) * iP_mat(fB); + b3Mat44 G1 = -0.5f * b3Mat44_Quat(b3Conjugate(fA)) * b3Mat44_Projection(fB); + b3Mat44 G2 = 0.5f * b3Mat44_Quat(b3Conjugate(fA)) * b3Mat44_Projection(fB); b3Mat23 J1 = P_hinge * G1 * PT; b3Mat23 J2 = P_hinge * G2 * PT; From 26092d3755e5ed843fc2badc03d186c95cf70c34 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 30 Apr 2019 10:41:30 -0300 Subject: [PATCH 085/198] Remove unused --- include/bounce/cloth/cloth.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index 3c1a5b4..d4ff2a8 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -133,20 +133,12 @@ public: // Return the kinetic (or dynamic) energy in this system. float32 GetEnergy() const; - // Get the next cloth in the world cloth list. - const b3Cloth* GetNext() const; - - // Get the next cloth in the world cloth list. - b3Cloth* GetNext(); - // Perform a time step. void Step(float32 dt); // Debug draw the cloth using the associated cloth mesh. void Draw() const; private: - friend class b3List2; - // Compute mass of each particle. void ComputeMass(); From f567ac47386a9a1610b82e2ec3b9f4f3a48d383b Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 2 May 2019 19:35:39 -0300 Subject: [PATCH 086/198] Bugfix --- src/bounce/cloth/cloth_solver.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index 1fc7718..ac8f1d7 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -293,8 +293,9 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) for (u32 i = 0; i < m_bodyContactCount; ++i) { b3BodyContact* c = m_bodyContacts[i]; + b3Particle* p1 = c->p1; - b3Vec3 cf = f[i]; + b3Vec3 cf = f[p1->m_solverId]; c->fn0 = c->fn; c->fn = b3Dot(cf, c->n); From b5edb9b1c7d4358c6550f8e0c61610e56be3e5a5 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 4 May 2019 15:56:47 -0300 Subject: [PATCH 087/198] Update soft_body.h --- examples/testbed/tests/soft_body.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/testbed/tests/soft_body.h b/examples/testbed/tests/soft_body.h index 562c894..aae10ce 100644 --- a/examples/testbed/tests/soft_body.h +++ b/examples/testbed/tests/soft_body.h @@ -210,10 +210,14 @@ public: // Pressure force b3Vec3 FP = Pn * A; + const float32 inv3 = 1.0f / 3.0f; + + b3Vec3 f = inv3 * FP; + // Distribute - p1->ApplyForce(FP); - p2->ApplyForce(FP); - p3->ApplyForce(FP); + p1->ApplyForce(f); + p2->ApplyForce(f); + p3->ApplyForce(f); } } From b448acfec6f9ccc2dcef696b7781313e9f54c158 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 4 May 2019 19:17:40 -0300 Subject: [PATCH 088/198] Add a view for sparse symmetric matrix to exploit cache coherency. This way we can increase maximum iteration count still with good performance. --- include/bounce/cloth/cloth_solver.h | 3 +- include/bounce/cloth/sparse_sym_mat33.h | 13 -- include/bounce/cloth/sparse_sym_mat33_view.h | 157 +++++++++++++++++++ src/bounce/cloth/cloth_solver.cpp | 12 +- 4 files changed, 167 insertions(+), 18 deletions(-) create mode 100644 include/bounce/cloth/sparse_sym_mat33_view.h diff --git a/include/bounce/cloth/cloth_solver.h b/include/bounce/cloth/cloth_solver.h index 52b6e66..2df48a2 100644 --- a/include/bounce/cloth/cloth_solver.h +++ b/include/bounce/cloth/cloth_solver.h @@ -31,6 +31,7 @@ class b3Force; struct b3DenseVec3; struct b3DiagMat33; struct b3SparseSymMat33; +struct b3SparseSymMat33View; class b3BodyContact; class b3ParticleContact; @@ -86,7 +87,7 @@ private: void ApplyConstraints(); // Solve Ax = b. - void Solve(b3DenseVec3& x, u32& iterations, const b3SparseSymMat33& A, const b3DenseVec3& b, const b3DiagMat33& S, const b3DenseVec3& z, const b3DenseVec3& y) const; + void Solve(b3DenseVec3& x, u32& iterations, const b3SparseSymMat33View& A, const b3DenseVec3& b, const b3DiagMat33& S, const b3DenseVec3& z, const b3DenseVec3& y) const; b3StackAllocator* m_allocator; diff --git a/include/bounce/cloth/sparse_sym_mat33.h b/include/bounce/cloth/sparse_sym_mat33.h index daf2664..a708f42 100644 --- a/include/bounce/cloth/sparse_sym_mat33.h +++ b/include/bounce/cloth/sparse_sym_mat33.h @@ -119,9 +119,6 @@ struct b3SparseSymMat33 // void operator-=(const b3SparseSymMat33& m); - // - void Diagonal(b3DiagMat33& out) const; - u32 rowCount; b3RowValueList* rows; }; @@ -345,16 +342,6 @@ inline void b3SparseSymMat33::operator-=(const b3SparseSymMat33& m) } } -inline void b3SparseSymMat33::Diagonal(b3DiagMat33& out) const -{ - B3_ASSERT(rowCount == out.n); - - for (u32 i = 0; i < rowCount; ++i) - { - out[i] = (*this)(i, i); - } -} - inline void b3Add(b3SparseSymMat33& out, const b3SparseSymMat33& a, const b3SparseSymMat33& b) { out = a; diff --git a/include/bounce/cloth/sparse_sym_mat33_view.h b/include/bounce/cloth/sparse_sym_mat33_view.h new file mode 100644 index 0000000..cc5fc20 --- /dev/null +++ b/include/bounce/cloth/sparse_sym_mat33_view.h @@ -0,0 +1,157 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SPARSE_SYM_MAT_33_VIEW_H +#define B3_SPARSE_SYM_MAT_33_VIEW_H + +#include + +struct b3RowValueArray +{ + u32 count; + b3RowValue* values; +}; + +// A read-only sparse symmetric matrix. +struct b3SparseSymMat33View +{ + // + b3SparseSymMat33View(const b3SparseSymMat33& _m); + + // + ~b3SparseSymMat33View(); + + // + const b3Mat33& operator()(u32 i, u32 j) const; + + // + void Diagonal(b3DiagMat33& out) const; + + u32 rowCount; + b3RowValueArray* rows; +}; + +inline b3SparseSymMat33View::b3SparseSymMat33View(const b3SparseSymMat33& _m) +{ + rowCount = _m.rowCount; + rows = (b3RowValueArray*)b3Alloc(rowCount * sizeof(b3RowValueArray)); + for (u32 i = 0; i < _m.rowCount; ++i) + { + b3RowValueList* rowList = _m.rows + i; + b3RowValueArray* rowArray = rows + i; + + rowArray->count = rowList->count; + rowArray->values = (b3RowValue*)b3Alloc(rowArray->count * sizeof(b3RowValue)); + + u32 valueIndex = 0; + for (b3RowValue* v = rowList->head; v; v = v->next) + { + rowArray->values[valueIndex].column = v->column; + rowArray->values[valueIndex].value = v->value; + ++valueIndex; + } + } +} + +inline b3SparseSymMat33View::~b3SparseSymMat33View() +{ + for (u32 i = 0; i < rowCount; ++i) + { + b3RowValueArray* rowArray = rows + i; + b3Free(rowArray->values); + } + b3Free(rows); +} + +inline const b3Mat33& b3SparseSymMat33View::operator()(u32 i, u32 j) const +{ + B3_ASSERT(i < rowCount); + B3_ASSERT(j < rowCount); + + if (i > j) + { + b3Swap(i, j); + } + + b3RowValueArray* vs = rows + i; + + for (u32 c = 0; c < vs->count; ++c) + { + b3RowValue* rv = vs->values + c; + + u32 column = rv->column; + + if (column < j) + { + break; + } + + if (column == j) + { + return rv->value; + } + } + + return b3Mat33_zero; +} + +inline void b3SparseSymMat33View::Diagonal(b3DiagMat33& out) const +{ + B3_ASSERT(rowCount == out.n); + + for (u32 i = 0; i < rowCount; ++i) + { + out[i] = (*this)(i, i); + } +} + +inline void b3Mul(b3DenseVec3& out, const b3SparseSymMat33View& A, const b3DenseVec3& v) +{ + B3_ASSERT(A.rowCount == out.n); + + out.SetZero(); + + for (u32 i = 0; i < A.rowCount; ++i) + { + b3RowValueArray* rowArray = A.rows + i; + + for (u32 c = 0; c < rowArray->count; ++c) + { + b3RowValue* rv = rowArray->values + c; + + u32 j = rv->column; + b3Mat33 a = rv->value; + + out[i] += a * v[j]; + + if (i != j) + { + out[j] += a * v[i]; + } + } + } +} + +inline b3DenseVec3 operator*(const b3SparseSymMat33View& A, const b3DenseVec3& v) +{ + b3DenseVec3 result(v.n); + b3Mul(result, A, v); + return result; +} + +#endif diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index ac8f1d7..29269f3 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -277,6 +278,9 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) // A b3SparseSymMat33 A = M - h * dfdv - h * h * dfdx; + + // View for A + b3SparseSymMat33View viewA(A); // b b3DenseVec3 b = h * (sf + h * (dfdx * sv) + dfdx * sy); @@ -284,7 +288,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) // x b3DenseVec3 x(m_particleCount); u32 iterations = 0; - Solve(x, iterations, A, b, S, z, sx0); + Solve(x, iterations, viewA, b, S, z, sx0); b3_clothSolverIterations = iterations; #if 0 // Copy constraint forces to the contacts @@ -395,7 +399,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) } void b3ClothSolver::Solve(b3DenseVec3& x, u32& iterations, - const b3SparseSymMat33& A, const b3DenseVec3& b, const b3DiagMat33& S, const b3DenseVec3& z, const b3DenseVec3& y) const + const b3SparseSymMat33View& A, const b3DenseVec3& b, const b3DiagMat33& S, const b3DenseVec3& z, const b3DenseVec3& y) const { B3_PROFILE("Cloth Solve Ax = b"); @@ -446,11 +450,11 @@ void b3ClothSolver::Solve(b3DenseVec3& x, u32& iterations, float32 delta_new = b3Dot(r, p); // Set the tolerance. - const float32 tolerance = 10.0f * B3_EPSILON; + const float32 tolerance = 2.0f * B3_EPSILON; // Maximum number of iterations. // Stop at this iteration if diverged. - const u32 max_iterations = 20; + const u32 max_iterations = 50; u32 iteration = 0; From bb2f11f9481b86cd1d735d334d12955fa74a4c2d Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 4 May 2019 19:25:49 -0300 Subject: [PATCH 089/198] Use clean separate row value structure that doesn't contain list pointers in the view --- include/bounce/cloth/sparse_sym_mat33_view.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/include/bounce/cloth/sparse_sym_mat33_view.h b/include/bounce/cloth/sparse_sym_mat33_view.h index cc5fc20..848242b 100644 --- a/include/bounce/cloth/sparse_sym_mat33_view.h +++ b/include/bounce/cloth/sparse_sym_mat33_view.h @@ -21,10 +21,16 @@ #include +struct b3ArrayRowValue +{ + u32 column; + b3Mat33 value; +}; + struct b3RowValueArray { u32 count; - b3RowValue* values; + b3ArrayRowValue* values; }; // A read-only sparse symmetric matrix. @@ -56,7 +62,7 @@ inline b3SparseSymMat33View::b3SparseSymMat33View(const b3SparseSymMat33& _m) b3RowValueArray* rowArray = rows + i; rowArray->count = rowList->count; - rowArray->values = (b3RowValue*)b3Alloc(rowArray->count * sizeof(b3RowValue)); + rowArray->values = (b3ArrayRowValue*)b3Alloc(rowArray->count * sizeof(b3ArrayRowValue)); u32 valueIndex = 0; for (b3RowValue* v = rowList->head; v; v = v->next) @@ -92,7 +98,7 @@ inline const b3Mat33& b3SparseSymMat33View::operator()(u32 i, u32 j) const for (u32 c = 0; c < vs->count; ++c) { - b3RowValue* rv = vs->values + c; + b3ArrayRowValue* rv = vs->values + c; u32 column = rv->column; @@ -132,7 +138,7 @@ inline void b3Mul(b3DenseVec3& out, const b3SparseSymMat33View& A, const b3Dense for (u32 c = 0; c < rowArray->count; ++c) { - b3RowValue* rv = rowArray->values + c; + b3ArrayRowValue* rv = rowArray->values + c; u32 j = rv->column; b3Mat33 a = rv->value; From 389a45bf9e9dcf60e747178a728d12e62deef46e Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 10 May 2019 10:50:28 -0300 Subject: [PATCH 090/198] Add negate operator --- include/bounce/cloth/dense_vec3.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/bounce/cloth/dense_vec3.h b/include/bounce/cloth/dense_vec3.h index 78a5b52..ffc4e42 100644 --- a/include/bounce/cloth/dense_vec3.h +++ b/include/bounce/cloth/dense_vec3.h @@ -171,4 +171,11 @@ inline b3DenseVec3 operator*(float32 a, const b3DenseVec3& b) return result; } +inline b3DenseVec3 operator-(const b3DenseVec3& v) +{ + b3DenseVec3 result(v.n); + b3Negate(result, v); + return result; +} + #endif \ No newline at end of file From 8d0295f5b78cfb458c5036f976be664ea5f8fc43 Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 10 May 2019 10:51:05 -0300 Subject: [PATCH 091/198] Simplify sparsity structure --- include/bounce/cloth/sparse_sym_mat33.h | 93 +++----------------- include/bounce/cloth/sparse_sym_mat33_view.h | 10 +-- 2 files changed, 14 insertions(+), 89 deletions(-) diff --git a/include/bounce/cloth/sparse_sym_mat33.h b/include/bounce/cloth/sparse_sym_mat33.h index a708f42..9d36f35 100644 --- a/include/bounce/cloth/sparse_sym_mat33.h +++ b/include/bounce/cloth/sparse_sym_mat33.h @@ -28,8 +28,6 @@ struct b3RowValue { u32 column; b3Mat33 value; - - b3RowValue* prev; b3RowValue* next; }; @@ -46,40 +44,15 @@ struct b3RowValueList void PushFront(b3RowValue* link) { - link->prev = NULL; link->next = head; - if (head) - { - head->prev = link; - } head = link; ++count; } - void PushAfter(b3RowValue* prev, b3RowValue* link) - { - link->prev = prev; - - if (prev->next == NULL) - { - link->next = NULL; - } - else - { - link->next = prev->next; - prev->next->prev = link; - } - - prev->next = link; - - ++count; - } - b3RowValue* head; u32 count; }; - // A sparse symmetric matrix. // Each row is a list of non-zero elements in the row. // The total matrix capacity is bounded by @@ -225,24 +198,15 @@ inline const b3Mat33& b3SparseSymMat33::operator()(u32 i, u32 j) const B3_ASSERT(i < rowCount); B3_ASSERT(j < rowCount); - // Ensure i, and j is on the upper triangle if (i > j) { b3Swap(i, j); } b3RowValueList* vs = rows + i; - for (b3RowValue* v = vs->head; v; v = v->next) { - u32 column = v->column; - - if (column < j) - { - break; - } - - if (column == j) + if (v->column == j) { return v->value; } @@ -256,56 +220,27 @@ inline b3Mat33& b3SparseSymMat33::operator()(u32 i, u32 j) B3_ASSERT(i < rowCount); B3_ASSERT(j < rowCount); - // Ensure i, and j is on the upper triangle if (i > j) { b3Swap(i, j); } b3RowValueList* vs = rows + i; - for (b3RowValue* v = vs->head; v; v = v->next) { - u32 column = v->column; - - if (column < j) - { - break; - } - - if (column == j) + if (v->column == j) { return v->value; } } - b3RowValue* v1 = (b3RowValue*)b3Alloc(sizeof(b3RowValue)); - v1->column = j; - v1->value.SetZero(); + b3RowValue* v = (b3RowValue*)b3Alloc(sizeof(b3RowValue)); + v->column = j; + v->value.SetZero(); - b3RowValue* v0 = nullptr; + vs->PushFront(v); - for (b3RowValue* v = vs->head; v; v = v->next) - { - u32 column = v->column; - - if (column > j) - { - v0 = v; - break; - } - } - - if (v0 == nullptr) - { - vs->PushFront(v1); - } - else - { - vs->PushAfter(v0, v1); - } - - return v1->value; + return v->value; } inline void b3SparseSymMat33::operator+=(const b3SparseSymMat33& m) @@ -389,16 +324,14 @@ inline void b3Mul(b3SparseSymMat33& out, float32 s, const b3SparseSymMat33& B) return; } - for (u32 i = 0; i < B.rowCount; ++i) + out = B; + + for (u32 i = 0; i < out.rowCount; ++i) { - b3RowValueList* vs = B.rows + i; - - for (b3RowValue* vB = vs->head; vB; vB = vB->next) + b3RowValueList* vs = out.rows + i; + for (b3RowValue* v = vs->head; v; v = v->next) { - u32 j = vB->column; - b3Mat33 b = vB->value; - - out(i, j) = s * b; + v->value = s * v->value; } } } diff --git a/include/bounce/cloth/sparse_sym_mat33_view.h b/include/bounce/cloth/sparse_sym_mat33_view.h index 848242b..48a3858 100644 --- a/include/bounce/cloth/sparse_sym_mat33_view.h +++ b/include/bounce/cloth/sparse_sym_mat33_view.h @@ -99,15 +99,7 @@ inline const b3Mat33& b3SparseSymMat33View::operator()(u32 i, u32 j) const for (u32 c = 0; c < vs->count; ++c) { b3ArrayRowValue* rv = vs->values + c; - - u32 column = rv->column; - - if (column < j) - { - break; - } - - if (column == j) + if (rv->column == j) { return rv->value; } From 3e5ff2257a9c36d20c162fadb11b7ffb58faf0c1 Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 10 May 2019 10:51:50 -0300 Subject: [PATCH 092/198] Add element write operator --- include/bounce/common/math/mat33.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/bounce/common/math/mat33.h b/include/bounce/common/math/mat33.h index 598210c..897fdce 100644 --- a/include/bounce/common/math/mat33.h +++ b/include/bounce/common/math/mat33.h @@ -48,6 +48,12 @@ struct b3Mat33 return (&x.x)[i + 3 * j]; } + // Write an indexed element from this matrix. + float32& operator()(u32 i, u32 j) + { + return (&x.x)[i + 3 * j]; + } + // Add a matrix to this matrix. void operator+=(const b3Mat33& B) { From 31cac5aacd4b46a49f2d0a404695b5fe0a1da620 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 13 May 2019 16:23:05 -0300 Subject: [PATCH 093/198] Write once ray cast on triangle function --- include/bounce/collision/collision.h | 4 + src/bounce/cloth/cloth.cpp | 84 +-------------------- src/bounce/collision/collision.cpp | 90 ++++++++++++++++++++++- src/bounce/dynamics/shapes/mesh_shape.cpp | 71 +++--------------- 4 files changed, 105 insertions(+), 144 deletions(-) diff --git a/include/bounce/collision/collision.h b/include/bounce/collision/collision.h index 9eda6de..4201dc3 100644 --- a/include/bounce/collision/collision.h +++ b/include/bounce/collision/collision.h @@ -37,4 +37,8 @@ struct b3RayCastOutput b3Vec3 normal; // surface normal of intersection }; +// Perform a ray-cast on a triangle. +bool b3RayCast(b3RayCastOutput* output, const b3RayCastInput* input, + const b3Vec3& v1, const b3Vec3& v2, const b3Vec3& v3); + #endif \ No newline at end of file diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 2866748..90cd13b 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -396,92 +396,19 @@ bool b3Cloth::RayCast(b3RayCastOutput* output, const b3RayCastInput* input, u32 b3Vec3 v2 = m_vertexParticles[triangle->v2]->m_position; b3Vec3 v3 = m_vertexParticles[triangle->v3]->m_position; - b3Vec3 p1 = input->p1; - b3Vec3 p2 = input->p2; - float32 maxFraction = input->maxFraction; - b3Vec3 d = p2 - p1; - B3_ASSERT(b3LengthSquared(d) > B3_EPSILON * B3_EPSILON); - - b3Vec3 n = b3Cross(v2 - v1, v3 - v1); - float32 len = b3Length(n); - if (len <= B3_EPSILON) - { - return false; - } - - n /= len; - - float32 numerator = b3Dot(n, v1 - p1); - float32 denominator = b3Dot(n, d); - - if (denominator == 0.0f) - { - return false; - } - - float32 fraction = numerator / denominator; - - // Is the intersection not on the segment? - if (fraction < 0.0f || maxFraction < fraction) - { - return false; - } - - b3Vec3 Q = p1 + fraction * d; - - b3Vec3 A = v1; - b3Vec3 B = v2; - b3Vec3 C = v3; - - b3Vec3 AB = B - A; - b3Vec3 AC = C - A; - - b3Vec3 QA = A - Q; - b3Vec3 QB = B - Q; - b3Vec3 QC = C - Q; - - b3Vec3 QB_x_QC = b3Cross(QB, QC); - b3Vec3 QC_x_QA = b3Cross(QC, QA); - b3Vec3 QA_x_QB = b3Cross(QA, QB); - - b3Vec3 AB_x_AC = b3Cross(AB, AC); - - // Barycentric coordinates for Q - float32 u = b3Dot(QB_x_QC, AB_x_AC); - float32 v = b3Dot(QC_x_QA, AB_x_AC); - float32 w = b3Dot(QA_x_QB, AB_x_AC); - - // Is the intersection on the triangle? - if (u >= 0.0f && v >= 0.0f && w >= 0.0f) - { - output->fraction = fraction; - - // Does the ray start from below or above the triangle? - if (numerator > 0.0f) - { - output->normal = -n; - } - else - { - output->normal = n; - } - - return true; - } - - return false; + return b3RayCast(output, input, v1, v2, v3); } void b3Cloth::UpdateBodyContacts() { + B3_PROFILE("Cloth Update Body Contacts"); + // Is there a world attached to this cloth? if (m_world == nullptr) { return; } - B3_PROFILE("Cloth Update Body Contacts"); - // Create contacts for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) { @@ -690,11 +617,6 @@ void b3Cloth::UpdateContacts() { // Update body contacts UpdateBodyContacts(); - -#if 0 - // Update particle contacts - UpdateParticleContacts(); -#endif } void b3Cloth::Step(float32 dt) diff --git a/src/bounce/collision/collision.cpp b/src/bounce/collision/collision.cpp index 30fbc81..9246b4f 100644 --- a/src/bounce/collision/collision.cpp +++ b/src/bounce/collision/collision.cpp @@ -16,6 +16,8 @@ * 3. This notice may not be removed or altered from any source distribution. */ +#include + #include #include #include @@ -24,4 +26,90 @@ const b3Sphere b3Sphere_identity(b3Vec3_zero, 1.0f); const b3Capsule b3Capsule_identity(b3Vec3(0.0f, -0.5f, 0.0f), b3Vec3(0.0f, 0.5f, 0.0f), 1.0f); -const b3BoxHull b3BoxHull_identity(1.0f, 1.0f, 1.0f); \ No newline at end of file +const b3BoxHull b3BoxHull_identity(1.0f, 1.0f, 1.0f); + +bool b3RayCast(b3RayCastOutput* output, + const b3RayCastInput* input, + const b3Vec3& v1, const b3Vec3& v2, const b3Vec3& v3) +{ + b3Vec3 p1 = input->p1; + b3Vec3 p2 = input->p2; + float32 maxFraction = input->maxFraction; + + b3Vec3 d = p2 - p1; + + if (b3LengthSquared(d) < B3_EPSILON * B3_EPSILON) + { + return false; + } + + b3Vec3 n = b3Cross(v2 - v1, v3 - v1); + float32 len = b3Length(n); + + if (len == 0.0f) + { + return false; + } + + n /= len; + + float32 num = b3Dot(n, v1 - p1); + float32 den = b3Dot(n, d); + + if (den == 0.0f) + { + return false; + } + + float32 fraction = num / den; + + // Is the intersection not on the segment? + if (fraction < 0.0f || maxFraction < fraction) + { + return false; + } + + b3Vec3 Q = p1 + fraction * d; + + b3Vec3 A = v1; + b3Vec3 B = v2; + b3Vec3 C = v3; + + b3Vec3 AB = B - A; + b3Vec3 AC = C - A; + + b3Vec3 QA = A - Q; + b3Vec3 QB = B - Q; + b3Vec3 QC = C - Q; + + b3Vec3 QB_x_QC = b3Cross(QB, QC); + b3Vec3 QC_x_QA = b3Cross(QC, QA); + b3Vec3 QA_x_QB = b3Cross(QA, QB); + + b3Vec3 AB_x_AC = b3Cross(AB, AC); + + // Barycentric coordinates for Q + float32 u = b3Dot(QB_x_QC, AB_x_AC); + float32 v = b3Dot(QC_x_QA, AB_x_AC); + float32 w = b3Dot(QA_x_QB, AB_x_AC); + + // Is the intersection on the triangle? + if (u >= 0.0f && v >= 0.0f && w >= 0.0f) + { + output->fraction = fraction; + + // Does the ray start from below or above the triangle? + if (num > 0.0f) + { + output->normal = -n; + } + else + { + output->normal = n; + } + + return true; + } + + return false; +} diff --git a/src/bounce/dynamics/shapes/mesh_shape.cpp b/src/bounce/dynamics/shapes/mesh_shape.cpp index 4331b1d..99af62f 100644 --- a/src/bounce/dynamics/shapes/mesh_shape.cpp +++ b/src/bounce/dynamics/shapes/mesh_shape.cpp @@ -112,6 +112,7 @@ bool b3MeshShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, { B3_ASSERT(index < m_mesh->triangleCount); b3Triangle* triangle = m_mesh->triangles + index; + b3Vec3 v1 = m_mesh->vertices[triangle->v1]; b3Vec3 v2 = m_mesh->vertices[triangle->v2]; b3Vec3 v3 = m_mesh->vertices[triangle->v3]; @@ -119,71 +120,17 @@ bool b3MeshShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, // Put the ray into the mesh's frame of reference. b3Vec3 p1 = b3MulT(xf, input.p1); b3Vec3 p2 = b3MulT(xf, input.p2); - b3Vec3 d = p2 - p1; - - b3Vec3 n = b3Cross(v2 - v1, v3 - v1); - n.Normalize(); - float32 numerator = b3Dot(n, v1 - p1); - float32 denominator = b3Dot(n, d); + b3RayCastInput subInput; + subInput.p1 = p1; + subInput.p2 = p2; + subInput.maxFraction = input.maxFraction; - if (denominator == 0.0f) + b3RayCastOutput subOutput; + if (b3RayCast(&subOutput, &subInput, v1, v2, v3)) { - return false; - } - - float32 t = numerator / denominator; - - // Is the intersection not on the segment? - if (t < 0.0f || input.maxFraction < t) - { - return false; - } - - b3Vec3 q = p1 + t * d; - - // Barycentric coordinates for q - b3Vec3 Q = q; - b3Vec3 A = v1; - b3Vec3 B = v2; - b3Vec3 C = v3; - - b3Vec3 AB = B - A; - b3Vec3 AC = C - A; - - b3Vec3 QA = A - Q; - b3Vec3 QB = B - Q; - b3Vec3 QC = C - Q; - - b3Vec3 QB_x_QC = b3Cross(QB, QC); - b3Vec3 QC_x_QA = b3Cross(QC, QA); - b3Vec3 QA_x_QB = b3Cross(QA, QB); - - b3Vec3 AB_x_AC = b3Cross(AB, AC); - - float32 u = b3Dot(QB_x_QC, AB_x_AC); - float32 v = b3Dot(QC_x_QA, AB_x_AC); - float32 w = b3Dot(QA_x_QB, AB_x_AC); - - // This tolerance helps intersections lying on - // shared edges to not be missed. - const float32 kTol = -0.005f; - - // Is the intersection on the triangle? - if (u > kTol && v > kTol && w > kTol) - { - output->fraction = t; - - // Does the ray start from below or above the triangle? - if (numerator > 0.0f) - { - output->normal = -b3Mul(xf.rotation, n); - } - else - { - output->normal = b3Mul(xf.rotation, n); - } - + output->fraction = subOutput.fraction; + output->normal = xf.rotation * subOutput.normal; return true; } From 6f50645601a82b6301feef34acd62014fc0d01fe Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 13 May 2019 16:23:46 -0300 Subject: [PATCH 094/198] Use a local grid mesh --- examples/testbed/tests/mesh_contact_test.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/testbed/tests/mesh_contact_test.h b/examples/testbed/tests/mesh_contact_test.h index 6589eb4..e68dd41 100644 --- a/examples/testbed/tests/mesh_contact_test.h +++ b/examples/testbed/tests/mesh_contact_test.h @@ -24,6 +24,8 @@ class MeshContactTest : public Test public: MeshContactTest() { + m_gridMesh.BuildTree(); + // Transform grid into a terrain for (u32 i = 0; i < m_terrainMesh.vertexCount; ++i) { @@ -37,7 +39,7 @@ public: m_ground = m_world.CreateBody(bd); b3MeshShape ms; - ms.m_mesh = &m_groundMesh; + ms.m_mesh = &m_gridMesh; b3ShapeDef sd; sd.shape = &ms; @@ -138,7 +140,7 @@ public: if (key == GLFW_KEY_G) { b3MeshShape ms; - ms.m_mesh = &m_groundMesh; + ms.m_mesh = &m_gridMesh; b3ShapeDef sd; sd.shape = &ms; @@ -176,7 +178,8 @@ public: } b3GridMesh<25, 25> m_terrainMesh; - + b3GridMesh<25, 25> m_gridMesh; + b3Body* m_ground; b3Body* m_body; }; From f1c4cf46797855d0d20d2f957dba274ccf48af0b Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 13 May 2019 16:24:09 -0300 Subject: [PATCH 095/198] Destroy contact if type changed --- src/bounce/cloth/particle.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bounce/cloth/particle.cpp b/src/bounce/cloth/particle.cpp index 1b3fa6f..aa0bc67 100644 --- a/src/bounce/cloth/particle.cpp +++ b/src/bounce/cloth/particle.cpp @@ -75,4 +75,6 @@ void b3Particle::SetType(b3ParticleType type) m_velocity.SetZero(); m_translation.SetZero(); } + + m_bodyContact.active = false; } \ No newline at end of file From 637199b5fdc48b2e6ee233bb72d7e087f1ff06e4 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 13 May 2019 19:03:23 -0300 Subject: [PATCH 096/198] New feature: soft bodies! --- .../testbed/framework/softbody_dragger.cpp | 128 +++ examples/testbed/framework/softbody_dragger.h | 61 ++ examples/testbed/framework/test_entries.cpp | 6 +- examples/testbed/tests/pinned_softbody.h | 135 +++ examples/testbed/tests/smash_softbody.h | 172 +++ examples/testbed/tests/soft_body.h | 293 ------ examples/testbed/tests/softbody.h | 155 +++ include/bounce/bounce.h | 4 + include/bounce/common/geometry.h | 26 + include/bounce/dynamics/body.h | 11 +- include/bounce/softbody/softbody.h | 193 ++++ .../bounce/softbody/softbody_contact_solver.h | 129 +++ include/bounce/softbody/softbody_mesh.h | 45 + include/bounce/softbody/softbody_node.h | 259 +++++ include/bounce/softbody/softbody_solver.h | 56 + include/bounce/softbody/sparse_mat33.h | 322 ++++++ premake5.lua | 4 +- src/bounce/softbody/softbody.cpp | 992 ++++++++++++++++++ .../softbody/softbody_contact_solver.cpp | 429 ++++++++ src/bounce/softbody/softbody_mesh.cpp | 69 ++ src/bounce/softbody/softbody_solver.cpp | 397 +++++++ 21 files changed, 3588 insertions(+), 298 deletions(-) create mode 100644 examples/testbed/framework/softbody_dragger.cpp create mode 100644 examples/testbed/framework/softbody_dragger.h create mode 100644 examples/testbed/tests/pinned_softbody.h create mode 100644 examples/testbed/tests/smash_softbody.h delete mode 100644 examples/testbed/tests/soft_body.h create mode 100644 examples/testbed/tests/softbody.h create mode 100644 include/bounce/softbody/softbody.h create mode 100644 include/bounce/softbody/softbody_contact_solver.h create mode 100644 include/bounce/softbody/softbody_mesh.h create mode 100644 include/bounce/softbody/softbody_node.h create mode 100644 include/bounce/softbody/softbody_solver.h create mode 100644 include/bounce/softbody/sparse_mat33.h create mode 100644 src/bounce/softbody/softbody.cpp create mode 100644 src/bounce/softbody/softbody_contact_solver.cpp create mode 100644 src/bounce/softbody/softbody_mesh.cpp create mode 100644 src/bounce/softbody/softbody_solver.cpp diff --git a/examples/testbed/framework/softbody_dragger.cpp b/examples/testbed/framework/softbody_dragger.cpp new file mode 100644 index 0000000..1667c2e --- /dev/null +++ b/examples/testbed/framework/softbody_dragger.cpp @@ -0,0 +1,128 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include + +b3SoftBodyDragger::b3SoftBodyDragger(b3Ray3* ray, b3SoftBody* body) +{ + m_ray = ray; + m_body = body; + m_tetrahedron = nullptr; +} + +b3SoftBodyDragger::~b3SoftBodyDragger() +{ + +} + +bool b3SoftBodyDragger::StartDragging() +{ + B3_ASSERT(IsDragging() == false); + + b3SoftBodyRayCastSingleOutput rayOut; + if (m_body->RayCastSingle(&rayOut, m_ray->A(), m_ray->B()) == false) + { + return false; + } + + m_mesh = m_body->GetMesh(); + m_tetrahedron = m_mesh->tetrahedrons + rayOut.tetrahedron; + m_v1 = m_tetrahedron->v1; + m_v2 = m_tetrahedron->v2; + m_v3 = m_tetrahedron->v3; + m_v4 = m_tetrahedron->v4; + m_x = rayOut.fraction; + + b3SoftBodyNode* n1 = m_body->GetVertexNode(m_v1); + b3SoftBodyNode* n2 = m_body->GetVertexNode(m_v2); + b3SoftBodyNode* n3 = m_body->GetVertexNode(m_v3); + b3SoftBodyNode* n4 = m_body->GetVertexNode(m_v4); + + b3Vec3 v1 = n1->GetPosition(); + b3Vec3 v2 = n2->GetPosition(); + b3Vec3 v3 = n3->GetPosition(); + b3Vec3 v4 = n4->GetPosition(); + + b3Vec3 B = GetPointB(); + + float32 wABCD[5]; + b3BarycentricCoordinates(wABCD, v1, v2, v3, v4, B); + + if (wABCD[4] > B3_EPSILON) + { + m_tu = wABCD[0] / wABCD[4]; + m_tv = wABCD[1] / wABCD[4]; + m_tw = wABCD[2] / wABCD[4]; + m_tx = wABCD[3] / wABCD[4]; + } + else + { + m_tu = m_tv = m_tw = m_tx = 0.0f; + } + + return true; +} + +void b3SoftBodyDragger::Drag() +{ + B3_ASSERT(IsDragging() == true); + + b3Vec3 A = GetPointA(); + b3Vec3 B = GetPointB(); + + b3Vec3 dx = B - A; + + const float32 k = 100.0f; + + b3Vec3 f = k * dx; + + b3Vec3 f1 = m_tu * f; + b3Vec3 f2 = m_tv * f; + b3Vec3 f3 = m_tw * f; + b3Vec3 f4 = m_tx * f; + + m_body->GetVertexNode(m_v1)->ApplyForce(f1); + m_body->GetVertexNode(m_v2)->ApplyForce(f2); + m_body->GetVertexNode(m_v3)->ApplyForce(f3); + m_body->GetVertexNode(m_v4)->ApplyForce(f4); +} + +void b3SoftBodyDragger::StopDragging() +{ + B3_ASSERT(IsDragging() == true); + + m_tetrahedron = nullptr; +} + +b3Vec3 b3SoftBodyDragger::GetPointA() const +{ + B3_ASSERT(IsDragging() == true); + + b3Vec3 A = m_body->GetVertexNode(m_v1)->GetPosition(); + b3Vec3 B = m_body->GetVertexNode(m_v2)->GetPosition(); + b3Vec3 C = m_body->GetVertexNode(m_v3)->GetPosition(); + b3Vec3 D = m_body->GetVertexNode(m_v4)->GetPosition(); + + return m_tu * A + m_tv * B + m_tw * C + m_tx * D; +} + +b3Vec3 b3SoftBodyDragger::GetPointB() const +{ + B3_ASSERT(IsDragging() == true); + return (1.0f - m_x) * m_ray->A() + m_x * m_ray->B(); +} \ No newline at end of file diff --git a/examples/testbed/framework/softbody_dragger.h b/examples/testbed/framework/softbody_dragger.h new file mode 100644 index 0000000..e8a6a92 --- /dev/null +++ b/examples/testbed/framework/softbody_dragger.h @@ -0,0 +1,61 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SOFTBODY_DRAGGER_H +#define B3_SOFTBODY_DRAGGER_H + +#include +#include +#include +#include + +// A soft body triangle dragger. +class b3SoftBodyDragger +{ +public: + b3SoftBodyDragger(b3Ray3* ray, b3SoftBody* body); + ~b3SoftBodyDragger(); + + bool IsDragging() const; + + bool StartDragging(); + + void Drag(); + + void StopDragging(); + + b3Vec3 GetPointA() const; + + b3Vec3 GetPointB() const; +private: + b3Ray3* m_ray; + float32 m_x; + + b3SoftBody* m_body; + const b3SoftBodyMesh* m_mesh; + const b3SoftBodyMeshTetrahedron* m_tetrahedron; + u32 m_v1, m_v2, m_v3, m_v4; + float32 m_tu, m_tv, m_tw, m_tx; +}; + +inline bool b3SoftBodyDragger::IsDragging() const +{ + return m_tetrahedron != nullptr; +} + +#endif \ No newline at end of file diff --git a/examples/testbed/framework/test_entries.cpp b/examples/testbed/framework/test_entries.cpp index d0aaca9..5f4d116 100644 --- a/examples/testbed/framework/test_entries.cpp +++ b/examples/testbed/framework/test_entries.cpp @@ -65,8 +65,10 @@ #include #include #include -#include #include +#include +#include +#include TestEntry g_tests[] = { @@ -118,6 +120,8 @@ TestEntry g_tests[] = { "Tension Mapping", &TensionMapping::Create }, { "Self-Collision", &SelfCollision::Create }, { "Soft Body", &SoftBody::Create }, + { "Pinned Soft Body", &PinnedSoftBody::Create }, + { "Smash Soft Body", &SmashSoftBody::Create }, { "Rope", &Rope::Create }, { NULL, NULL } }; diff --git a/examples/testbed/tests/pinned_softbody.h b/examples/testbed/tests/pinned_softbody.h new file mode 100644 index 0000000..023b490 --- /dev/null +++ b/examples/testbed/tests/pinned_softbody.h @@ -0,0 +1,135 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef PINNED_SOFTBODY_H +#define PINNED_SOFTBODY_H + +#include + +class PinnedSoftBody : public Test +{ +public: + PinnedSoftBody() + { + m_mesh.SetAsSphere(5.0f, 0); + + // Create soft body + b3SoftBodyDef def; + def.mesh = &m_mesh; + def.density = 0.2f; + def.E = 100.0f; + def.nu = 0.33f; + + m_body = new b3SoftBody(def); + + u32 pinIndex = ~0; + float32 pinDot = -B3_MAX_FLOAT; + for (u32 i = 0; i < m_mesh.vertexCount; ++i) + { + float32 dot = b3Dot(m_mesh.vertices[i], b3Vec3_y); + if (dot > pinDot) + { + pinDot = dot; + pinIndex = i; + } + } + + b3SoftBodyNode* pinNode = m_body->GetVertexNode(pinIndex); + pinNode->SetType(e_staticSoftBodyNode); + + b3Vec3 gravity(0.0f, -9.8f, 0.0f); + m_body->SetGravity(gravity); + + m_bodyDragger = new b3SoftBodyDragger(&m_ray, m_body); + } + + ~PinnedSoftBody() + { + delete m_bodyDragger; + delete m_body; + } + + void Step() + { + Test::Step(); + + if (m_bodyDragger->IsDragging()) + { + m_bodyDragger->Drag(); + } + + m_body->Step(g_testSettings->inv_hertz, g_testSettings->velocityIterations, g_testSettings->positionIterations); + + m_body->Draw(); + + if (m_bodyDragger->IsDragging()) + { + b3Vec3 pA = m_bodyDragger->GetPointA(); + b3Vec3 pB = m_bodyDragger->GetPointB(); + + g_draw->DrawPoint(pA, 2.0f, b3Color_green); + + g_draw->DrawPoint(pB, 2.0f, b3Color_green); + + g_draw->DrawSegment(pA, pB, b3Color_white); + } + + extern u32 b3_softBodySolverIterations; + g_draw->DrawString(b3Color_white, "Iterations = %d", b3_softBodySolverIterations); + + float32 E = m_body->GetEnergy(); + g_draw->DrawString(b3Color_white, "E = %f", E); + } + + void MouseMove(const b3Ray3& pw) + { + Test::MouseMove(pw); + } + + void MouseLeftDown(const b3Ray3& pw) + { + Test::MouseLeftDown(pw); + + if (m_bodyDragger->IsDragging() == false) + { + m_bodyDragger->StartDragging(); + } + } + + void MouseLeftUp(const b3Ray3& pw) + { + Test::MouseLeftUp(pw); + + if (m_bodyDragger->IsDragging() == true) + { + m_bodyDragger->StopDragging(); + } + } + + static Test* Create() + { + return new PinnedSoftBody(); + } + + b3QSoftBodyMesh m_mesh; + + b3SoftBody* m_body; + b3SoftBodyDragger* m_bodyDragger; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/smash_softbody.h b/examples/testbed/tests/smash_softbody.h new file mode 100644 index 0000000..1b54d7c --- /dev/null +++ b/examples/testbed/tests/smash_softbody.h @@ -0,0 +1,172 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SMASH_SOFTBODY_H +#define SMASH_SOFTBODY_H + +#include + +class SmashSoftBody : public Test +{ +public: + SmashSoftBody() + { + m_mesh.SetAsSphere(2.0f, 1); + + for (u32 i = 0; i < m_mesh.vertexCount; ++i) + { + m_mesh.vertices[i].y += 3.0f; + } + + // Create soft body + b3SoftBodyDef def; + def.mesh = &m_mesh; + def.density = 0.2f; + def.E = 100.0f; + def.nu = 0.33f; + + m_body = new b3SoftBody(def); + + b3Vec3 gravity(0.0f, -9.8f, 0.0f); + m_body->SetGravity(gravity); + m_body->SetWorld(&m_world); + + for (u32 i = 0; i < m_mesh.vertexCount; ++i) + { + b3SoftBodyNode* n = m_body->GetVertexNode(i); + + n->SetRadius(0.05f); + n->SetFriction(0.2f); + } + + // Create ground + { + b3BodyDef bd; + bd.type = e_staticBody; + + b3Body* b = m_world.CreateBody(bd); + + b3HullShape groundShape; + groundShape.m_hull = &m_groundHull; + + b3ShapeDef sd; + sd.shape = &groundShape; + sd.friction = 0.3f; + + b->CreateShape(sd); + } + + // Create body + { + b3BodyDef bd; + bd.type = e_dynamicBody; + bd.position.y = 10.0f; + + b3Body* b = m_world.CreateBody(bd); + + static b3BoxHull boxHull(5.0f, 1.0f, 5.0f); + + b3HullShape boxShape; + boxShape.m_hull = &boxHull; + + b3ShapeDef sd; + sd.shape = &boxShape; + sd.density = 0.1f; + sd.friction = 0.3f; + + b->CreateShape(sd); + } + + m_bodyDragger = new b3SoftBodyDragger(&m_ray, m_body); + } + + ~SmashSoftBody() + { + delete m_bodyDragger; + delete m_body; + } + + void Step() + { + Test::Step(); + + if (m_bodyDragger->IsDragging()) + { + m_bodyDragger->Drag(); + } + + m_body->Step(g_testSettings->inv_hertz, g_testSettings->velocityIterations, g_testSettings->positionIterations); + + m_body->Draw(); + + if (m_bodyDragger->IsDragging()) + { + b3Vec3 pA = m_bodyDragger->GetPointA(); + b3Vec3 pB = m_bodyDragger->GetPointB(); + + g_draw->DrawPoint(pA, 2.0f, b3Color_green); + + g_draw->DrawPoint(pB, 2.0f, b3Color_green); + + g_draw->DrawSegment(pA, pB, b3Color_white); + } + + extern u32 b3_softBodySolverIterations; + g_draw->DrawString(b3Color_white, "Iterations = %d", b3_softBodySolverIterations); + + float32 E = m_body->GetEnergy(); + g_draw->DrawString(b3Color_white, "E = %f", E); + } + + void MouseMove(const b3Ray3& pw) + { + Test::MouseMove(pw); + } + + void MouseLeftDown(const b3Ray3& pw) + { + Test::MouseLeftDown(pw); + + if (m_bodyDragger->IsDragging() == false) + { + m_bodyDragger->StartDragging(); + } + } + + void MouseLeftUp(const b3Ray3& pw) + { + Test::MouseLeftUp(pw); + + if (m_bodyDragger->IsDragging() == true) + { + m_bodyDragger->StopDragging(); + } + } + + static Test* Create() + { + return new SmashSoftBody(); + } + + b3QSoftBodyMesh m_mesh; + + b3SoftBody* m_body; + b3SoftBodyDragger* m_bodyDragger; +}; + +#endif \ No newline at end of file diff --git a/examples/testbed/tests/soft_body.h b/examples/testbed/tests/soft_body.h deleted file mode 100644 index aae10ce..0000000 --- a/examples/testbed/tests/soft_body.h +++ /dev/null @@ -1,293 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef SOFT_BODY_H -#define SOFT_BODY_H - -#include - -struct b3QClothMesh : public b3ClothMesh -{ - b3StackArray clothVertices; - b3StackArray clothTriangles; - b3ClothMeshMesh clothMesh; - - b3QClothMesh() - { - vertices = nullptr; - vertexCount = 0; - triangles = nullptr; - triangleCount = 0; - meshCount = 0; - meshes = nullptr; - sewingLineCount = 0; - sewingLines = nullptr; - } - - void SetAsSphere(float32 radius = 1.0f) - { - smMesh mesh; - smCreateMesh(mesh, 2); - - clothVertices.Resize(mesh.vertexCount); - for (u32 i = 0; i < mesh.vertexCount; ++i) - { - clothVertices[i] = radius * mesh.vertices[i]; - } - - clothTriangles.Resize(mesh.indexCount / 3); - for (u32 i = 0; i < mesh.indexCount / 3; ++i) - { - clothTriangles[i].v1 = mesh.indices[3 * i + 0]; - clothTriangles[i].v2 = mesh.indices[3 * i + 1]; - clothTriangles[i].v3 = mesh.indices[3 * i + 2]; - } - - clothMesh.startTriangle = 0; - clothMesh.triangleCount = clothTriangles.Count(); - clothMesh.startVertex = 0; - clothMesh.vertexCount = clothVertices.Count(); - - vertices = clothVertices.Begin(); - vertexCount = clothVertices.Count(); - triangles = clothTriangles.Begin(); - triangleCount = clothTriangles.Count(); - meshCount = 1; - meshes = &clothMesh; - } -}; - -class SoftBody : public Test -{ -public: - SoftBody() - { - m_mesh.SetAsSphere(2.0f); - - // Translate the cloth mesh upwards - for (u32 i = 0; i < m_mesh.vertexCount; ++i) - { - m_mesh.vertices[i].y += 10.0f; - } - - // Create cloth - b3ClothDef def; - def.mesh = &m_mesh; - def.density = 0.2f; - def.structural = 10000.0f; - def.bending = 0.0f; - def.damping = 0.0f; - - m_cloth = new b3Cloth(def); - - for (b3Particle* p = m_cloth->GetParticleList().m_head; p; p = p->GetNext()) - { - p->SetRadius(0.05f); - p->SetFriction(0.5f); - } - - b3Vec3 gravity(0.0f, -9.8f, 0.0f); - - m_cloth->SetGravity(gravity); - - // Attach a world to the cloth - m_cloth->SetWorld(&m_world); - - // Create static shapes - { - b3BodyDef bd; - bd.type = e_staticBody; - - b3Body* b = m_world.CreateBody(bd); - - static b3BoxHull boxHull(10.0f, 1.0f, 10.0f); - - b3HullShape boxShape; - boxShape.m_hull = &boxHull; - - b3ShapeDef sd; - sd.shape = &boxShape; - sd.friction = 0.5f; - - b->CreateShape(sd); - } - - m_clothDragger = new b3ClothDragger(&m_ray, m_cloth); - } - - ~SoftBody() - { - delete m_clothDragger; - delete m_cloth; - } - - // Compute the volume of soft body - float32 ComputeVolume() const - { - const b3ClothMesh* mesh = m_cloth->GetMesh(); - - b3StackArray positions; - positions.Resize(mesh->vertexCount); - - for (u32 i = 0; i < mesh->vertexCount; ++i) - { - b3Particle* p = m_cloth->GetVertexParticle(i); - positions[i] = p->GetPosition(); - } - - b3AABB3 aabb; - aabb.Compute(positions.Begin(), positions.Count()); - - return aabb.Volume(); - } - - // Apply pressure forces - // Explanation available in the paper - // "Pressure Model of Soft Body Simulation" - void ApplyPressureForces() - { - const b3ClothMesh* mesh = m_cloth->GetMesh(); - - // Volume in m^3 - float32 V = ComputeVolume(); - - // Inverse volume - float32 invV = V > 0.0f ? 1.0f / V : 0.0f; - - // Apply pressure forces on particles - for (u32 i = 0; i < m_mesh.triangleCount; ++i) - { - u32 i1 = m_mesh.triangles[i].v1; - u32 i2 = m_mesh.triangles[i].v2; - u32 i3 = m_mesh.triangles[i].v3; - - b3Particle* p1 = m_cloth->GetVertexParticle(i1); - b3Particle* p2 = m_cloth->GetVertexParticle(i2); - b3Particle* p3 = m_cloth->GetVertexParticle(i3); - - b3Vec3 v1 = p1->GetPosition(); - b3Vec3 v2 = p2->GetPosition(); - b3Vec3 v3 = p3->GetPosition(); - - b3Vec3 n = b3Cross(v2 - v1, v3 - v1); - - // Triangle area - float32 A = n.Normalize(); - A *= 0.5f; - - // Ideal Gas Approximation - - // Number of gas moles - const float32 k_n = 1.0f; - - // Ideal Gas Constant [J / K mol] - const float32 k_R = 8.31f; - - // Gas temperature in Kelvin - const float32 k_T = 100.0f; - - // Pressure in Pascals - float32 P = invV * k_n * k_R * k_T; - - // Pressure vector - b3Vec3 Pn = P * n; - - // Pressure force - b3Vec3 FP = Pn * A; - - const float32 inv3 = 1.0f / 3.0f; - - b3Vec3 f = inv3 * FP; - - // Distribute - p1->ApplyForce(f); - p2->ApplyForce(f); - p3->ApplyForce(f); - } - } - - void Step() - { - Test::Step(); - - ApplyPressureForces(); - - m_cloth->Step(g_testSettings->inv_hertz); - - m_cloth->Draw(); - - if (m_clothDragger->IsDragging()) - { - b3Vec3 pA = m_clothDragger->GetPointA(); - b3Vec3 pB = m_clothDragger->GetPointB(); - - g_draw->DrawPoint(pA, 2.0f, b3Color_green); - - g_draw->DrawPoint(pB, 2.0f, b3Color_green); - - g_draw->DrawSegment(pA, pB, b3Color_white); - } - - extern u32 b3_clothSolverIterations; - g_draw->DrawString(b3Color_white, "Iterations = %d", b3_clothSolverIterations); - - float32 E = m_cloth->GetEnergy(); - g_draw->DrawString(b3Color_white, "E = %f", E); - } - - void MouseMove(const b3Ray3& pw) - { - Test::MouseMove(pw); - - if (m_clothDragger->IsDragging() == true) - { - m_clothDragger->Drag(); - } - } - - void MouseLeftDown(const b3Ray3& pw) - { - Test::MouseLeftDown(pw); - - if (m_clothDragger->IsDragging() == false) - { - m_clothDragger->StartDragging(); - } - } - - void MouseLeftUp(const b3Ray3& pw) - { - Test::MouseLeftUp(pw); - - if (m_clothDragger->IsDragging() == true) - { - m_clothDragger->StopDragging(); - } - } - - static Test* Create() - { - return new SoftBody(); - } - - b3QClothMesh m_mesh; - b3Cloth* m_cloth; - b3ClothDragger* m_clothDragger; -}; - -#endif \ No newline at end of file diff --git a/examples/testbed/tests/softbody.h b/examples/testbed/tests/softbody.h new file mode 100644 index 0000000..a7af14a --- /dev/null +++ b/examples/testbed/tests/softbody.h @@ -0,0 +1,155 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SOFTBODY_H +#define SOFTBODY_H + +#include + +class SoftBody : public Test +{ +public: + SoftBody() + { + m_mesh.SetAsSphere(5.0f, 1); + + for (u32 i = 0; i < m_mesh.vertexCount; ++i) + { + m_mesh.vertices[i].y += 10.0f; + } + + // Create soft body + b3SoftBodyDef def; + def.mesh = &m_mesh; + def.density = 0.2f; + def.E = 100.0f; + def.nu = 0.33f; + + m_body = new b3SoftBody(def); + + b3Vec3 gravity(0.0f, -9.8f, 0.0f); + m_body->SetGravity(gravity); + m_body->SetWorld(&m_world); + + for (u32 i = 0; i < m_mesh.vertexCount; ++i) + { + b3SoftBodyNode* n = m_body->GetVertexNode(i); + + n->SetRadius(0.05f); + n->SetFriction(0.2f); + } + + // Create ground + { + b3BodyDef bd; + bd.type = e_staticBody; + + b3Body* b = m_world.CreateBody(bd); + + m_groundHull.SetAsCylinder(20.0f, 2.0f); + + b3HullShape groundShape; + groundShape.m_hull = &m_groundHull; + + b3ShapeDef sd; + sd.shape = &groundShape; + sd.friction = 0.3f; + + b->CreateShape(sd); + } + + m_bodyDragger = new b3SoftBodyDragger(&m_ray, m_body); + } + + ~SoftBody() + { + delete m_bodyDragger; + delete m_body; + } + + void Step() + { + Test::Step(); + + if (m_bodyDragger->IsDragging()) + { + m_bodyDragger->Drag(); + } + + m_body->Step(g_testSettings->inv_hertz, g_testSettings->velocityIterations, g_testSettings->positionIterations); + + m_body->Draw(); + + if (m_bodyDragger->IsDragging()) + { + b3Vec3 pA = m_bodyDragger->GetPointA(); + b3Vec3 pB = m_bodyDragger->GetPointB(); + + g_draw->DrawPoint(pA, 2.0f, b3Color_green); + + g_draw->DrawPoint(pB, 2.0f, b3Color_green); + + g_draw->DrawSegment(pA, pB, b3Color_white); + } + + extern u32 b3_softBodySolverIterations; + g_draw->DrawString(b3Color_white, "Iterations = %d", b3_softBodySolverIterations); + + float32 E = m_body->GetEnergy(); + g_draw->DrawString(b3Color_white, "E = %f", E); + } + + void MouseMove(const b3Ray3& pw) + { + Test::MouseMove(pw); + } + + void MouseLeftDown(const b3Ray3& pw) + { + Test::MouseLeftDown(pw); + + if (m_bodyDragger->IsDragging() == false) + { + m_bodyDragger->StartDragging(); + } + } + + void MouseLeftUp(const b3Ray3& pw) + { + Test::MouseLeftUp(pw); + + if (m_bodyDragger->IsDragging() == true) + { + m_bodyDragger->StopDragging(); + } + } + + static Test* Create() + { + return new SoftBody(); + } + + b3QSoftBodyMesh m_mesh; + + b3SoftBody* m_body; + b3SoftBodyDragger* m_bodyDragger; + + b3QHull m_groundHull; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/bounce.h b/include/bounce/bounce.h index b44bacc..393e011 100644 --- a/include/bounce/bounce.h +++ b/include/bounce/bounce.h @@ -72,4 +72,8 @@ #include #include +#include +#include +#include + #endif \ No newline at end of file diff --git a/include/bounce/common/geometry.h b/include/bounce/common/geometry.h index 212a17f..50adc14 100644 --- a/include/bounce/common/geometry.h +++ b/include/bounce/common/geometry.h @@ -144,6 +144,32 @@ inline void b3BarycentricCoordinates(float32 out[4], out[3] = out[0] + out[1] + out[2]; } +// Convert a point Q from Cartesian coordinates to Barycentric coordinates (u, v, w, x) +// with respect to a tetrahedron ABCD. +// The last output value is the (positive) divisor. +inline void b3BarycentricCoordinates(float32 out[5], + const b3Vec3& A, const b3Vec3& B, const b3Vec3& C, const b3Vec3& D, + const b3Vec3& Q) +{ + b3Vec3 AB = B - A; + b3Vec3 AC = C - A; + b3Vec3 AD = D - A; + + b3Vec3 QA = A - Q; + b3Vec3 QB = B - Q; + b3Vec3 QC = C - Q; + b3Vec3 QD = D - Q; + + float32 divisor = b3Det(AB, AC, AD); + float32 sign = b3Sign(divisor); + + out[0] = sign * b3Det(QB, QC, QD); + out[1] = sign * b3Det(QA, QD, QC); + out[2] = sign * b3Det(QA, QB, QD); + out[3] = sign * b3Det(QA, QC, QB); + out[4] = sign * divisor; +} + // Project a point onto a segment AB. inline b3Vec3 b3ClosestPointOnSegment(const b3Vec3& P, const b3Vec3& A, const b3Vec3& B) { diff --git a/include/bounce/dynamics/body.h b/include/bounce/dynamics/body.h index 24ba1c8..b945ee8 100644 --- a/include/bounce/dynamics/body.h +++ b/include/bounce/dynamics/body.h @@ -293,9 +293,7 @@ private: friend class b3MeshContact; friend class b3ContactManager; friend class b3ContactSolver; - friend class b3ClothSolver; - friend class b3ClothContactSolver; - + friend class b3Joint; friend class b3JointManager; friend class b3JointSolver; @@ -308,6 +306,13 @@ private: friend class b3List2; + friend class b3ClothSolver; + friend class b3ClothContactSolver; + + friend class b3SoftBody; + friend class b3SoftBodySolver; + friend class b3SoftBodyContactSolver; + enum b3BodyFlags { e_awakeFlag = 0x0001, diff --git a/include/bounce/softbody/softbody.h b/include/bounce/softbody/softbody.h new file mode 100644 index 0000000..9ea65e9 --- /dev/null +++ b/include/bounce/softbody/softbody.h @@ -0,0 +1,193 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SOFT_BODY_H +#define B3_SOFT_BODY_H + +#include +#include + +class b3World; + +struct b3SoftBodyMesh; + +class b3SoftBodyNode; + +struct b3RayCastInput; +struct b3RayCastOutput; + +struct b3SoftBodyRayCastSingleOutput +{ + u32 tetrahedron; + u32 v1, v2, v3; + float32 fraction; + b3Vec3 normal; +}; + +// Soft body tetrahedron element +struct b3SoftBodyElement +{ + float32 invP[16]; + b3Mat33 K[16]; + b3Quat q; +}; + +// Soft body tetrahedron triangle +struct b3SoftBodyTriangle +{ + u32 v1, v2, v3; + u32 tetrahedron; +}; + +// Soft body definition +// This requires defining a soft body mesh which is typically bound to a render mesh +struct b3SoftBodyDef +{ + b3SoftBodyDef() + { + mesh = nullptr; + density = 0.1f; + E = 100.0f; + nu = 0.3f; + } + + // Soft body mesh + const b3SoftBodyMesh* mesh; + + // Density in kg/m^3 + float32 density; + + // Material Young's modulus in [0, inf] + // Units are 1e3N/m^2 + float32 E; + + // Material Poisson ratio in [0, 0.5] + // This is a dimensionless value + float32 nu; +}; + +// A soft body represents a deformable volume as a collection of nodes. +class b3SoftBody +{ +public: + b3SoftBody(const b3SoftBodyDef& def); + ~b3SoftBody(); + + // Perform a ray cast with the soft body. + bool RayCastSingle(b3SoftBodyRayCastSingleOutput* output, const b3Vec3& p1, const b3Vec3& p2) const; + + // Set the acceleration of gravity. + void SetGravity(const b3Vec3& gravity); + + // Get the acceleration of gravity. + b3Vec3 GetGravity() const; + + // Attach a world to this cloth. + // The cloth will be able to respond to collisions with the bodies in the attached world. + void SetWorld(b3World* world); + + // Get the world attached to this cloth. + const b3World* GetWorld() const; + b3World* GetWorld(); + + // Return the soft body mesh proxy. + const b3SoftBodyMesh* GetMesh() const; + + // Return the node associated with the given vertex. + b3SoftBodyNode* GetVertexNode(u32 i); + + // Return the kinetic (or dynamic) energy in this system. + float32 GetEnergy() const; + + // Perform a time step. + void Step(float32 dt, u32 velocityIterations, u32 positionIterations); + + // Debug draw the body using the associated mesh. + void Draw() const; +private: + // Compute mass of each node. + void ComputeMass(); + + // Update contacts. + void UpdateContacts(); + + // Solve + void Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations); + + // Stack allocator + b3StackAllocator m_stackAllocator; + + // Gravity acceleration + b3Vec3 m_gravity; + + // Proxy mesh + const b3SoftBodyMesh* m_mesh; + + // Soft body density + float32 m_density; + + // Material Young's modulus + float32 m_E; + + // Material poisson ratio + float32 m_nu; + + // Soft body nodes + b3SoftBodyNode* m_nodes; + + // Soft body elements + b3SoftBodyElement* m_elements; + + // Soft body triangles + b3SoftBodyTriangle* m_triangles; + + // Attached world + b3World* m_world; +}; + +inline void b3SoftBody::SetGravity(const b3Vec3& gravity) +{ + m_gravity = gravity; +} + +inline b3Vec3 b3SoftBody::GetGravity() const +{ + return m_gravity; +} + +inline void b3SoftBody::SetWorld(b3World* world) +{ + m_world = world; +} + +inline const b3World* b3SoftBody::GetWorld() const +{ + return m_world; +} + +inline b3World* b3SoftBody::GetWorld() +{ + return m_world; +} + +inline const b3SoftBodyMesh* b3SoftBody::GetMesh() const +{ + return m_mesh; +} + +#endif \ No newline at end of file diff --git a/include/bounce/softbody/softbody_contact_solver.h b/include/bounce/softbody/softbody_contact_solver.h new file mode 100644 index 0000000..ff8350b --- /dev/null +++ b/include/bounce/softbody/softbody_contact_solver.h @@ -0,0 +1,129 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SOFT_BODY_CONTACT_SOLVER_H +#define B3_SOFT_BODY_CONTACT_SOLVER_H + +#include +#include + +class b3StackAllocator; + +class b3SoftBodyNode; +class b3Body; + +class b3NodeBodyContact; + +struct b3DenseVec3; + +struct b3SoftBodySolverBodyContactVelocityConstraint +{ + u32 indexA; + float32 invMassA; + b3Mat33 invIA; + + b3Body* bodyB; + float32 invMassB; + b3Mat33 invIB; + + float32 friction; + + b3Vec3 point; + b3Vec3 rA; + b3Vec3 rB; + + b3Vec3 normal; + float32 normalMass; + float32 normalImpulse; + float32 velocityBias; + + b3Vec3 tangent1; + b3Vec3 tangent2; + b3Mat22 tangentMass; + b3Vec2 tangentImpulse; +}; + +struct b3SoftBodySolverBodyContactPositionConstraint +{ + u32 indexA; + float32 invMassA; + b3Mat33 invIA; + float32 radiusA; + b3Vec3 localCenterA; + + b3Body* bodyB; + float32 invMassB; + b3Mat33 invIB; + float32 radiusB; + b3Vec3 localCenterB; + + b3Vec3 rA; + b3Vec3 rB; + + b3Vec3 normalA; + b3Vec3 localPointA; + b3Vec3 localPointB; +}; + +struct b3SoftBodyContactSolverDef +{ + b3StackAllocator* allocator; + + b3DenseVec3* positions; + b3DenseVec3* velocities; + + u32 bodyContactCapacity; +}; + +inline float32 b3MixFriction(float32 u1, float32 u2) +{ + return b3Sqrt(u1 * u2); +} + +class b3SoftBodyContactSolver +{ +public: + b3SoftBodyContactSolver(const b3SoftBodyContactSolverDef& def); + ~b3SoftBodyContactSolver(); + + void Add(b3NodeBodyContact* c); + + void InitializeBodyContactConstraints(); + + void WarmStart(); + + void SolveBodyContactVelocityConstraints(); + + void StoreImpulses(); + + bool SolveBodyContactPositionConstraints(); +protected: + b3StackAllocator* m_allocator; + + b3DenseVec3* m_positions; + b3DenseVec3* m_velocities; + + u32 m_bodyContactCapacity; + u32 m_bodyContactCount; + b3NodeBodyContact** m_bodyContacts; + + b3SoftBodySolverBodyContactVelocityConstraint* m_bodyVelocityConstraints; + b3SoftBodySolverBodyContactPositionConstraint* m_bodyPositionConstraints; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/softbody/softbody_mesh.h b/include/bounce/softbody/softbody_mesh.h new file mode 100644 index 0000000..e7f392a --- /dev/null +++ b/include/bounce/softbody/softbody_mesh.h @@ -0,0 +1,45 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SOFT_BODY_MESH_H +#define B3_SOFT_BODY_MESH_H + +#include + +struct b3SoftBodyMeshTetrahedron +{ + u32 v1, v2, v3, v4; +}; + +struct b3SoftBodyMesh +{ + u32 vertexCount; + b3Vec3* vertices; + u32 tetrahedronCount; + b3SoftBodyMeshTetrahedron* tetrahedrons; +}; + +struct b3QSoftBodyMesh : public b3SoftBodyMesh +{ + b3QSoftBodyMesh(); + ~b3QSoftBodyMesh(); + + void SetAsSphere(float32 radius, u32 subdivisions); +}; + +#endif \ No newline at end of file diff --git a/include/bounce/softbody/softbody_node.h b/include/bounce/softbody/softbody_node.h new file mode 100644 index 0000000..5ddfba1 --- /dev/null +++ b/include/bounce/softbody/softbody_node.h @@ -0,0 +1,259 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SOFT_BODY_NODE_H +#define B3_SOFT_BODY_NODE_H + +#include +#include +#include + +class b3Shape; +class b3SoftBody; +class b3SoftBodyNode; + +// A contact between a node and a body +class b3NodeBodyContact +{ +public: + b3NodeBodyContact() { } + ~b3NodeBodyContact() { } + + b3SoftBodyNode* n1; + b3Shape* s2; + + // Contact constraint + b3Vec3 normal1; + b3Vec3 localPoint1; + b3Vec3 localPoint2; + float32 normalImpulse; + + // Friction constraint + b3Vec3 t1, t2; + b3Vec2 tangentImpulse; + + bool active; +}; + +struct b3NodeBodyContactWorldPoint +{ + void Initialize(const b3NodeBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB) + { + b3Vec3 nA = c->normal1; + + b3Vec3 cA = xfA * c->localPoint1; + b3Vec3 cB = xfB * c->localPoint2; + + b3Vec3 pA = cA + rA * nA; + b3Vec3 pB = cB - rB * nA; + + point = 0.5f * (pA + pB); + normal = nA; + separation = b3Dot(cB - cA, nA) - rA - rB; + } + + b3Vec3 point; + b3Vec3 normal; + float32 separation; +}; + +// Static node: Can be moved manually. +// Dynamic node: Non-zero velocity determined by force, can be moved by the solver. +enum b3SoftBodyNodeType +{ + e_staticSoftBodyNode, + e_dynamicSoftBodyNode +}; + +// A soft body node. +class b3SoftBodyNode +{ +public: + // Set the node type. + void SetType(b3SoftBodyNodeType type); + + // Get the node type. + b3SoftBodyNodeType GetType() const; + + // Get the vertex index. + u32 GetVertex() const; + + // Set the particle position. + // If the particle is dynamic changing the position directly might lead + // to physically incorrect simulation behaviour. + void SetPosition(const b3Vec3& position); + + // Get the particle position. + const b3Vec3& GetPosition() const; + + // Set the particle velocity. + void SetVelocity(const b3Vec3& velocity); + + // Get the particle velocity. + const b3Vec3& GetVelocity() const; + + // Get the particle mass. + float32 GetMass() const; + + // Set the particle radius. + void SetRadius(float32 radius); + + // Get the particle radius. + float32 GetRadius() const; + + // Set the particle coefficient of friction. + void SetFriction(float32 friction); + + // Get the particle coefficient of friction. + float32 GetFriction() const; + + // Apply a force. + void ApplyForce(const b3Vec3& force); +private: + friend class b3SoftBody; + friend class b3SoftBodySolver; + friend class b3SoftBodyContactSolver; + + b3SoftBodyNode() { } + + ~b3SoftBodyNode() { } + + // Type + b3SoftBodyNodeType m_type; + + // Position + b3Vec3 m_position; + + // Velocity + b3Vec3 m_velocity; + + // Applied external force + b3Vec3 m_force; + + // Mass + float32 m_mass; + + // Inverse mass + float32 m_invMass; + + // Radius + float32 m_radius; + + // Coefficient of friction + float32 m_friction; + + // User data. + void* m_userData; + + // Soft body mesh vertex index. + u32 m_vertex; + + // Node and body contact + b3NodeBodyContact m_bodyContact; + + // Soft body + b3SoftBody* m_body; +}; + +inline void b3SoftBodyNode::SetType(b3SoftBodyNodeType type) +{ + if (m_type == type) + { + return; + } + + m_type = type; + m_force.SetZero(); + + if (type == e_staticSoftBodyNode) + { + m_velocity.SetZero(); + } + + m_bodyContact.active = false; +} + +inline b3SoftBodyNodeType b3SoftBodyNode::GetType() const +{ + return m_type; +} + +inline u32 b3SoftBodyNode::GetVertex() const +{ + return m_vertex; +} + +inline void b3SoftBodyNode::SetPosition(const b3Vec3& position) +{ + m_position = position; +} + +inline const b3Vec3& b3SoftBodyNode::GetPosition() const +{ + return m_position; +} + +inline void b3SoftBodyNode::SetVelocity(const b3Vec3& velocity) +{ + if (m_type == e_staticSoftBodyNode) + { + return; + } + m_velocity = velocity; +} + +inline const b3Vec3& b3SoftBodyNode::GetVelocity() const +{ + return m_velocity; +} + +inline float32 b3SoftBodyNode::GetMass() const +{ + return m_mass; +} + +inline void b3SoftBodyNode::SetRadius(float32 radius) +{ + m_radius = radius; +} + +inline float32 b3SoftBodyNode::GetRadius() const +{ + return m_radius; +} + +inline void b3SoftBodyNode::SetFriction(float32 friction) +{ + m_friction = friction; +} + +inline float32 b3SoftBodyNode::GetFriction() const +{ + return m_friction; +} + +inline void b3SoftBodyNode::ApplyForce(const b3Vec3& force) +{ + if (m_type != e_dynamicSoftBodyNode) + { + return; + } + m_force += force; +} + +#endif \ No newline at end of file diff --git a/include/bounce/softbody/softbody_solver.h b/include/bounce/softbody/softbody_solver.h new file mode 100644 index 0000000..30661da --- /dev/null +++ b/include/bounce/softbody/softbody_solver.h @@ -0,0 +1,56 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SOFT_BODY_SOLVER_H +#define B3_SOFT_BODY_SOLVER_H + +#include +#include + +class b3StackAllocator; + +class b3SoftBodyMesh; + +class b3SoftBodyNode; +struct b3SoftBodyElement; + +class b3NodeBodyContact; + +struct b3SoftBodySolverDef +{ + b3StackAllocator* stack; + const b3SoftBodyMesh* mesh; + b3SoftBodyNode* nodes; + b3SoftBodyElement* elements; +}; + +class b3SoftBodySolver +{ +public: + b3SoftBodySolver(const b3SoftBodySolverDef& def); + ~b3SoftBodySolver(); + + void Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations); +private: + b3StackAllocator* m_allocator; + const b3SoftBodyMesh* m_mesh; + b3SoftBodyNode* m_nodes; + b3SoftBodyElement* m_elements; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/softbody/sparse_mat33.h b/include/bounce/softbody/sparse_mat33.h new file mode 100644 index 0000000..a746849 --- /dev/null +++ b/include/bounce/softbody/sparse_mat33.h @@ -0,0 +1,322 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SPARSE_MAT_33_H +#define B3_SPARSE_MAT_33_H + +#include +#include +#include +#include + +// A sparse matrix. +// Each row is a list of non-zero elements in the row. +struct b3SparseMat33 +{ + // + b3SparseMat33(); + + // + b3SparseMat33(u32 m); + + // + b3SparseMat33(const b3SparseMat33& _m); + + // + ~b3SparseMat33(); + + // + b3SparseMat33& operator=(const b3SparseMat33& _m); + + // + void Copy(const b3SparseMat33& _m); + + // + void Destroy(); + + // + b3Mat33& operator()(u32 i, u32 j); + + // + const b3Mat33& operator()(u32 i, u32 j) const; + + // + void operator+=(const b3SparseMat33& m); + + // + void operator-=(const b3SparseMat33& m); + + u32 rowCount; + b3RowValueList* rows; +}; + +inline b3SparseMat33::b3SparseMat33() +{ + rowCount = 0; + rows = nullptr; +} + +inline b3SparseMat33::b3SparseMat33(u32 m) +{ + rowCount = m; + rows = (b3RowValueList*)b3Alloc(rowCount * sizeof(b3RowValueList)); + for (u32 i = 0; i < rowCount; ++i) + { + new (rows + i)b3RowValueList(); + } +} + +inline b3SparseMat33::b3SparseMat33(const b3SparseMat33& m) +{ + rowCount = m.rowCount; + rows = (b3RowValueList*)b3Alloc(rowCount * sizeof(b3RowValueList)); + for (u32 i = 0; i < rowCount; ++i) + { + new (rows + i)b3RowValueList(); + } + + Copy(m); +} + +inline b3SparseMat33::~b3SparseMat33() +{ + Destroy(); +} + +inline void b3SparseMat33::Destroy() +{ + for (u32 i = 0; i < rowCount; ++i) + { + b3RowValueList* vs = rows + i; + + b3RowValue* v = vs->head; + while (v) + { + b3RowValue* v0 = v->next; + b3Free(v); + v = v0; + } + + vs->~b3RowValueList(); + } + + b3Free(rows); +} + +inline b3SparseMat33& b3SparseMat33::operator=(const b3SparseMat33& _m) +{ + if (_m.rows == rows) + { + return *this; + } + + Destroy(); + + rowCount = _m.rowCount; + rows = (b3RowValueList*)b3Alloc(rowCount * sizeof(b3RowValueList)); + for (u32 i = 0; i < rowCount; ++i) + { + new (rows + i)b3RowValueList(); + } + + Copy(_m); + + return *this; +} + +inline void b3SparseMat33::Copy(const b3SparseMat33& _m) +{ + B3_ASSERT(rowCount == _m.rowCount); + + for (u32 i = 0; i < rowCount; ++i) + { + b3RowValueList* vs1 = _m.rows + i; + b3RowValueList* vs2 = rows + i; + + B3_ASSERT(vs2->count == 0); + + for (b3RowValue* v1 = vs1->head; v1; v1 = v1->next) + { + b3RowValue* v2 = (b3RowValue*)b3Alloc(sizeof(b3RowValue)); + + v2->column = v1->column; + v2->value = v1->value; + + vs2->PushFront(v2); + } + } +} + +inline const b3Mat33& b3SparseMat33::operator()(u32 i, u32 j) const +{ + B3_ASSERT(i < rowCount); + B3_ASSERT(j < rowCount); + + b3RowValueList* vs = rows + i; + + for (b3RowValue* v = vs->head; v; v = v->next) + { + if (v->column == j) + { + return v->value; + } + } + + return b3Mat33_zero; +} + +inline b3Mat33& b3SparseMat33::operator()(u32 i, u32 j) +{ + B3_ASSERT(i < rowCount); + B3_ASSERT(j < rowCount); + + b3RowValueList* vs = rows + i; + + for (b3RowValue* v = vs->head; v; v = v->next) + { + if (v->column == j) + { + return v->value; + } + } + + b3RowValue* v = (b3RowValue*)b3Alloc(sizeof(b3RowValue)); + v->column = j; + v->value.SetZero(); + + vs->PushFront(v); + + return v->value; +} + +inline void b3SparseMat33::operator+=(const b3SparseMat33& m) +{ + B3_ASSERT(rowCount == m.rowCount); + + for (u32 i = 0; i < m.rowCount; ++i) + { + b3RowValueList* mvs = m.rows + i; + + for (b3RowValue* v = mvs->head; v; v = v->next) + { + u32 j = v->column; + + (*this)(i, j) += v->value; + } + } +} + +inline void b3SparseMat33::operator-=(const b3SparseMat33& m) +{ + B3_ASSERT(rowCount == m.rowCount); + + for (u32 i = 0; i < m.rowCount; ++i) + { + b3RowValueList* mvs = m.rows + i; + + for (b3RowValue* v = mvs->head; v; v = v->next) + { + u32 j = v->column; + + (*this)(i, j) -= v->value; + } + } +} + +inline void b3Add(b3SparseMat33& out, const b3SparseMat33& a, const b3SparseMat33& b) +{ + out = a; + out += b; +} + +inline void b3Sub(b3SparseMat33& out, const b3SparseMat33& a, const b3SparseMat33& b) +{ + out = a; + out -= b; +} + +inline void b3Mul(b3DenseVec3& out, const b3SparseMat33& A, const b3DenseVec3& v) +{ + B3_ASSERT(A.rowCount == out.n); + + out.SetZero(); + + for (u32 i = 0; i < A.rowCount; ++i) + { + b3RowValueList* vs = A.rows + i; + + for (b3RowValue* vA = vs->head; vA; vA = vA->next) + { + u32 j = vA->column; + b3Mat33 a = vA->value; + + out[i] += a * v[j]; + } + } +} + +inline void b3Mul(b3SparseMat33& out, float32 s, const b3SparseMat33& B) +{ + B3_ASSERT(out.rowCount == B.rowCount); + + if (s == 0.0f) + { + return; + } + + out = B; + + for (u32 i = 0; i < out.rowCount; ++i) + { + b3RowValueList* vs = out.rows + i; + for (b3RowValue* v = vs->head; v; v = v->next) + { + v->value = s * v->value; + } + } +} + +inline b3SparseMat33 operator+(const b3SparseMat33& A, const b3SparseMat33& B) +{ + b3SparseMat33 result(A.rowCount); + b3Add(result, A, B); + return result; +} + +inline b3SparseMat33 operator-(const b3SparseMat33& A, const b3SparseMat33& B) +{ + b3SparseMat33 result(A.rowCount); + b3Sub(result, A, B); + return result; +} + +inline b3SparseMat33 operator*(float32 A, const b3SparseMat33& B) +{ + b3SparseMat33 result(B.rowCount); + b3Mul(result, A, B); + return result; +} + +inline b3DenseVec3 operator*(const b3SparseMat33& A, const b3DenseVec3& v) +{ + b3DenseVec3 result(v.n); + b3Mul(result, A, v); + return result; +} + +#endif diff --git a/premake5.lua b/premake5.lua index f0ef7e9..cc2c029 100644 --- a/premake5.lua +++ b/premake5.lua @@ -275,9 +275,11 @@ workspace(solution_name) examples_src_dir .. "/testbed/framework/body_dragger.h", examples_src_dir .. "/testbed/framework/cloth_dragger.h", + examples_src_dir .. "/testbed/framework/softbody_dragger.h", examples_src_dir .. "/testbed/framework/body_dragger.cpp", examples_src_dir .. "/testbed/framework/cloth_dragger.cpp", + examples_src_dir .. "/testbed/framework/softbody_dragger.cpp", examples_inc_dir .. "/testbed/tests/**.h", @@ -370,7 +372,7 @@ end -- clean newaction { - trigger = "clean", + trigger = "clean", description = "Clean solution", execute = function () os.rmdir( "doc" ) diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp new file mode 100644 index 0000000..17b02d2 --- /dev/null +++ b/src/bounce/softbody/softbody.cpp @@ -0,0 +1,992 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include + +static B3_FORCE_INLINE void b3Mul(float32* C, float32* A, u32 AM, u32 AN, float32* B, u32 BM, u32 BN) +{ + B3_ASSERT(AN == BM); + + for (u32 i = 0; i < AM; ++i) + { + for (u32 j = 0; j < BN; ++j) + { + C[i + AM * j] = 0.0f; + + for (u32 k = 0; k < AN; ++k) + { + C[i + AM * j] += A[i + AM * k] * B[k + BM * j]; + } + } + } +} + +static B3_FORCE_INLINE void b3Transpose(float32* B, float32* A, u32 AM, u32 AN) +{ + for (u32 i = 0; i < AM; ++i) + { + for (u32 j = 0; j < AN; ++j) + { + B[j + AN * i] = A[i + AM * j]; + } + } +} + +static B3_FORCE_INLINE void b3Inverse4(float32 out[16], float32 m[16]) +{ + float32 inv[16]; + + inv[0] = m[5] * m[10] * m[15] - + m[5] * m[11] * m[14] - + m[9] * m[6] * m[15] + + m[9] * m[7] * m[14] + + m[13] * m[6] * m[11] - + m[13] * m[7] * m[10]; + + inv[4] = -m[4] * m[10] * m[15] + + m[4] * m[11] * m[14] + + m[8] * m[6] * m[15] - + m[8] * m[7] * m[14] - + m[12] * m[6] * m[11] + + m[12] * m[7] * m[10]; + + inv[8] = m[4] * m[9] * m[15] - + m[4] * m[11] * m[13] - + m[8] * m[5] * m[15] + + m[8] * m[7] * m[13] + + m[12] * m[5] * m[11] - + m[12] * m[7] * m[9]; + + inv[12] = -m[4] * m[9] * m[14] + + m[4] * m[10] * m[13] + + m[8] * m[5] * m[14] - + m[8] * m[6] * m[13] - + m[12] * m[5] * m[10] + + m[12] * m[6] * m[9]; + + inv[1] = -m[1] * m[10] * m[15] + + m[1] * m[11] * m[14] + + m[9] * m[2] * m[15] - + m[9] * m[3] * m[14] - + m[13] * m[2] * m[11] + + m[13] * m[3] * m[10]; + + inv[5] = m[0] * m[10] * m[15] - + m[0] * m[11] * m[14] - + m[8] * m[2] * m[15] + + m[8] * m[3] * m[14] + + m[12] * m[2] * m[11] - + m[12] * m[3] * m[10]; + + inv[9] = -m[0] * m[9] * m[15] + + m[0] * m[11] * m[13] + + m[8] * m[1] * m[15] - + m[8] * m[3] * m[13] - + m[12] * m[1] * m[11] + + m[12] * m[3] * m[9]; + + inv[13] = m[0] * m[9] * m[14] - + m[0] * m[10] * m[13] - + m[8] * m[1] * m[14] + + m[8] * m[2] * m[13] + + m[12] * m[1] * m[10] - + m[12] * m[2] * m[9]; + + inv[2] = m[1] * m[6] * m[15] - + m[1] * m[7] * m[14] - + m[5] * m[2] * m[15] + + m[5] * m[3] * m[14] + + m[13] * m[2] * m[7] - + m[13] * m[3] * m[6]; + + inv[6] = -m[0] * m[6] * m[15] + + m[0] * m[7] * m[14] + + m[4] * m[2] * m[15] - + m[4] * m[3] * m[14] - + m[12] * m[2] * m[7] + + m[12] * m[3] * m[6]; + + inv[10] = m[0] * m[5] * m[15] - + m[0] * m[7] * m[13] - + m[4] * m[1] * m[15] + + m[4] * m[3] * m[13] + + m[12] * m[1] * m[7] - + m[12] * m[3] * m[5]; + + inv[14] = -m[0] * m[5] * m[14] + + m[0] * m[6] * m[13] + + m[4] * m[1] * m[14] - + m[4] * m[2] * m[13] - + m[12] * m[1] * m[6] + + m[12] * m[2] * m[5]; + + inv[3] = -m[1] * m[6] * m[11] + + m[1] * m[7] * m[10] + + m[5] * m[2] * m[11] - + m[5] * m[3] * m[10] - + m[9] * m[2] * m[7] + + m[9] * m[3] * m[6]; + + inv[7] = m[0] * m[6] * m[11] - + m[0] * m[7] * m[10] - + m[4] * m[2] * m[11] + + m[4] * m[3] * m[10] + + m[8] * m[2] * m[7] - + m[8] * m[3] * m[6]; + + inv[11] = -m[0] * m[5] * m[11] + + m[0] * m[7] * m[9] + + m[4] * m[1] * m[11] - + m[4] * m[3] * m[9] - + m[8] * m[1] * m[7] + + m[8] * m[3] * m[5]; + + inv[15] = m[0] * m[5] * m[10] - + m[0] * m[6] * m[9] - + m[4] * m[1] * m[10] + + m[4] * m[2] * m[9] + + m[8] * m[1] * m[6] - + m[8] * m[2] * m[5]; + + float32 det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12]; + + if (det != 0.0f) + { + det = 1.0f / det; + } + + for (u32 i = 0; i < 16; ++i) + { + out[i] = det * inv[i]; + } +} + +static B3_FORCE_INLINE void b3Lame(float32& lambda, float32& mu, float32 E, float32 nu) +{ + lambda = (nu * E) / ((1 + nu) * (1 - 2 * nu)); + mu = E / (2 * (1 + nu)); +} + +static B3_FORCE_INLINE void b3CreateE(float32 out[36], float32 lambda, float32 mu) +{ + float32 E[36] = + { + lambda + 2 * mu, lambda, lambda, 0, 0, 0, + lambda, lambda + 2 * mu, lambda, 0, 0, 0, + lambda, lambda, lambda + 2 * mu, 0, 0, 0, + 0, 0, 0, mu, 0, 0, + 0, 0, 0, 0, mu, 0, + 0, 0, 0, 0, 0, mu + }; + + for (u32 i = 0; i < 36; ++i) + { + out[i] = E[i]; + } +} + +static B3_FORCE_INLINE void b3CreateB(float32 out[72], float32 invP[16]) +{ + float32 a11 = invP[0]; + float32 a21 = invP[1]; + float32 a31 = invP[2]; + float32 a41 = invP[3]; + + float32 a12 = invP[4]; + float32 a22 = invP[5]; + float32 a32 = invP[6]; + float32 a42 = invP[7]; + + float32 a13 = invP[8]; + float32 a23 = invP[9]; + float32 a33 = invP[10]; + float32 a43 = invP[11]; + + //float32 a14 = invP[12]; + //float32 a24 = invP[13]; + //float32 a34 = invP[14]; + //float32 a44 = invP[15]; + + // 6 x 12 + // a11 0 0 a21 0 0 a31 0 0 a41 0 0 + // 0 a12 0 0 a22 0 0 a32 0 0 a42 0 + // 0 0 a13 0 0 a23 0 0 a33 0 0 a43 + // a12 a11 0 a22 a21 0 a32 a31 0 a42 a41 0 + // 0 a13 a12 0 a23 a22 0 a33 a32 0 a43 a42 + // a13 0 a11 a23 0 a21 a33 0 a31 a43 0 a41 + float32 B[72] = + { + a11, 0, 0, a12, 0, a13, + 0, a12, 0, a11, a13, 0, + 0, 0, a13, 0, a12, a11, + a21, 0, 0, a22, 0, a23, + 0, a22, 0, a21, a23, 0, + 0, 0, a23, 0, a22, a21, + a31, 0, 0, a32, 0, a33, + 0, a32, 0, a31, a33, 0, + 0, 0, a33, 0, a32, a31, + a41, 0, 0, a42, 0, a43, + 0, a42, 0, a41, a43, 0, + 0, 0, a43, 0, a42, a41 + }; + + for (u32 i = 0; i < 72; ++i) + { + out[i] = B[i]; + } +} + +static B3_FORCE_INLINE void b3SetK(b3Mat33 K[16], float32 Ke[144]) +{ + b3Mat33& k11 = K[0 + 4 * 0]; + b3Mat33& k12 = K[0 + 4 * 1]; + b3Mat33& k13 = K[0 + 4 * 2]; + b3Mat33& k14 = K[0 + 4 * 3]; + + b3Mat33& k21 = K[1 + 4 * 0]; + b3Mat33& k22 = K[1 + 4 * 1]; + b3Mat33& k23 = K[1 + 4 * 2]; + b3Mat33& k24 = K[1 + 4 * 3]; + + b3Mat33& k31 = K[2 + 4 * 0]; + b3Mat33& k32 = K[2 + 4 * 1]; + b3Mat33& k33 = K[2 + 4 * 2]; + b3Mat33& k34 = K[2 + 4 * 3]; + + b3Mat33& k41 = K[3 + 4 * 0]; + b3Mat33& k42 = K[3 + 4 * 1]; + b3Mat33& k43 = K[3 + 4 * 2]; + b3Mat33& k44 = K[3 + 4 * 3]; + + // k11 + // a11 a12 a13 + // a21 a22 a23 + // a31 a32 a33 + k11.x.x = Ke[0 + 12 * 0]; + k11.x.y = Ke[1 + 12 * 0]; + k11.x.z = Ke[2 + 12 * 0]; + + k11.y.x = Ke[0 + 12 * 1]; + k11.y.y = Ke[1 + 12 * 1]; + k11.y.z = Ke[2 + 12 * 1]; + + k11.z.x = Ke[0 + 12 * 2]; + k11.z.y = Ke[1 + 12 * 2]; + k11.z.z = Ke[2 + 12 * 2]; + + // k12 + // a14 a15 a16 + // a24 a25 a26 + // a34 a35 a36 + k12.x.x = Ke[0 + 12 * 3]; + k12.x.y = Ke[1 + 12 * 3]; + k12.x.z = Ke[2 + 12 * 3]; + + k12.y.x = Ke[0 + 12 * 4]; + k12.y.y = Ke[1 + 12 * 4]; + k12.y.z = Ke[2 + 12 * 4]; + + k12.z.x = Ke[0 + 12 * 5]; + k12.z.y = Ke[1 + 12 * 5]; + k12.z.z = Ke[2 + 12 * 5]; + + // k13 + // a17 a18 a19 + // a27 a28 a29 + // a37 a38 a39 + k13.x.x = Ke[0 + 12 * 6]; + k13.x.y = Ke[1 + 12 * 6]; + k13.x.z = Ke[2 + 12 * 6]; + + k13.y.x = Ke[0 + 12 * 7]; + k13.y.y = Ke[1 + 12 * 7]; + k13.y.z = Ke[2 + 12 * 7]; + + k13.z.x = Ke[0 + 12 * 8]; + k13.z.y = Ke[1 + 12 * 8]; + k13.z.z = Ke[2 + 12 * 8]; + + // k14 + // a1_10 a1_11 a1_12 + // a2_10 a2_11 a2_12 + // a3_10 a3_11 a3_12 + k14.x.x = Ke[0 + 12 * 9]; + k14.x.y = Ke[1 + 12 * 9]; + k14.x.z = Ke[2 + 12 * 9]; + + k14.y.x = Ke[0 + 12 * 10]; + k14.y.y = Ke[1 + 12 * 10]; + k14.y.z = Ke[2 + 12 * 10]; + + k14.z.x = Ke[0 + 12 * 11]; + k14.z.y = Ke[1 + 12 * 11]; + k14.z.z = Ke[2 + 12 * 11]; + + // k21 + // a41 a42 a43 + // a51 a52 a53 + // a61 a62 a63 + k21.x.x = Ke[3 + 12 * 0]; + k21.x.y = Ke[4 + 12 * 0]; + k21.x.z = Ke[5 + 12 * 0]; + + k21.y.x = Ke[3 + 12 * 1]; + k21.y.y = Ke[4 + 12 * 1]; + k21.y.z = Ke[5 + 12 * 1]; + + k21.z.x = Ke[3 + 12 * 2]; + k21.z.y = Ke[4 + 12 * 2]; + k21.z.z = Ke[5 + 12 * 2]; + + // k22 + // a44 a45 a46 + // a54 a55 a56 + // a64 a65 a66 + k22.x.x = Ke[3 + 12 * 3]; + k22.x.y = Ke[4 + 12 * 3]; + k22.x.z = Ke[5 + 12 * 3]; + + k22.y.x = Ke[3 + 12 * 4]; + k22.y.y = Ke[4 + 12 * 4]; + k22.y.z = Ke[5 + 12 * 4]; + + k22.z.x = Ke[3 + 12 * 5]; + k22.z.y = Ke[4 + 12 * 5]; + k22.z.z = Ke[5 + 12 * 5]; + + // k23 + // a47 a48 a49 + // a57 a58 a59 + // a67 a68 a69 + k23.x.x = Ke[3 + 12 * 6]; + k23.x.y = Ke[4 + 12 * 6]; + k23.x.z = Ke[5 + 12 * 6]; + + k23.y.x = Ke[3 + 12 * 7]; + k23.y.y = Ke[4 + 12 * 7]; + k23.y.z = Ke[5 + 12 * 7]; + + k23.z.x = Ke[3 + 12 * 8]; + k23.z.y = Ke[4 + 12 * 8]; + k23.z.z = Ke[5 + 12 * 8]; + + // k24 + // a4_10 a4_11 a4_12 + // a5_10 a5_11 a5_12 + // a6_10 a6_11 a6_12 + k24.x.x = Ke[3 + 12 * 9]; + k24.x.y = Ke[4 + 12 * 9]; + k24.x.z = Ke[5 + 12 * 9]; + + k24.y.x = Ke[3 + 12 * 10]; + k24.y.y = Ke[4 + 12 * 10]; + k24.y.z = Ke[5 + 12 * 10]; + + k24.z.x = Ke[3 + 12 * 11]; + k24.z.y = Ke[4 + 12 * 11]; + k24.z.z = Ke[5 + 12 * 11]; + + // k31 + // a71 a72 a73 + // a81 a82 a83 + // a91 a92 a93 + k31.x.x = Ke[6 + 12 * 0]; + k31.x.y = Ke[7 + 12 * 0]; + k31.x.z = Ke[8 + 12 * 0]; + + k31.y.x = Ke[6 + 12 * 1]; + k31.y.y = Ke[7 + 12 * 1]; + k31.y.z = Ke[8 + 12 * 1]; + + k31.z.x = Ke[6 + 12 * 2]; + k31.z.y = Ke[7 + 12 * 2]; + k31.z.z = Ke[8 + 12 * 2]; + + // k32 + // a74 a75 a76 + // a84 a85 a86 + // a94 a95 a96 + k32.x.x = Ke[6 + 12 * 3]; + k32.x.y = Ke[7 + 12 * 3]; + k32.x.z = Ke[8 + 12 * 3]; + + k32.y.x = Ke[6 + 12 * 4]; + k32.y.y = Ke[7 + 12 * 4]; + k32.y.z = Ke[8 + 12 * 4]; + + k32.z.x = Ke[6 + 12 * 5]; + k32.z.y = Ke[7 + 12 * 5]; + k32.z.z = Ke[8 + 12 * 5]; + + // k33 + // a77 a78 a79 + // a87 a88 a89 + // a97 a98 a99 + k33.x.x = Ke[6 + 12 * 6]; + k33.x.y = Ke[7 + 12 * 6]; + k33.x.z = Ke[8 + 12 * 6]; + + k33.y.x = Ke[6 + 12 * 7]; + k33.y.y = Ke[7 + 12 * 7]; + k33.y.z = Ke[8 + 12 * 7]; + + k33.z.x = Ke[6 + 12 * 8]; + k33.z.y = Ke[7 + 12 * 8]; + k33.z.z = Ke[8 + 12 * 8]; + + // k34 + // a7_10 a7_11 a7_12 + // a8_10 a8_11 a8_12 + // a9_10 a9_11 a9_12 + k34.x.x = Ke[6 + 12 * 9]; + k34.x.y = Ke[7 + 12 * 9]; + k34.x.z = Ke[8 + 12 * 9]; + + k34.y.x = Ke[6 + 12 * 10]; + k34.y.y = Ke[7 + 12 * 10]; + k34.y.z = Ke[8 + 12 * 10]; + + k34.z.x = Ke[6 + 12 * 11]; + k34.z.y = Ke[7 + 12 * 11]; + k34.z.z = Ke[8 + 12 * 11]; + + // k41 + // a10_1 a10_2 a10_3 + // a11_1 a11_2 a11_3 + // a12_1 a12_2 a12_3 + k41.x.x = Ke[9 + 12 * 0]; + k41.x.y = Ke[10 + 12 * 0]; + k41.x.z = Ke[11 + 12 * 0]; + + k41.y.x = Ke[9 + 12 * 1]; + k41.y.y = Ke[10 + 12 * 1]; + k41.y.z = Ke[11 + 12 * 1]; + + k41.z.x = Ke[9 + 12 * 2]; + k41.z.y = Ke[10 + 12 * 2]; + k41.z.z = Ke[11 + 12 * 2]; + + // k42 + // a10_4 a10_5 a10_6 + // a11_4 a11_5 a11_6 + // a12_4 a12_5 a12_6 + k42.x.x = Ke[9 + 12 * 3]; + k42.x.y = Ke[10 + 12 * 3]; + k42.x.z = Ke[11 + 12 * 3]; + + k42.y.x = Ke[9 + 12 * 4]; + k42.y.y = Ke[10 + 12 * 4]; + k42.y.z = Ke[11 + 12 * 4]; + + k42.z.x = Ke[9 + 12 * 5]; + k42.z.y = Ke[10 + 12 * 5]; + k42.z.z = Ke[11 + 12 * 5]; + + // k43 + // a10_7 a10_8 a10_9 + // a11_7 a11_8 a11_9 + // a12_7 a12_8 a12_9 + k43.x.x = Ke[9 + 12 * 6]; + k43.x.y = Ke[10 + 12 * 6]; + k43.x.z = Ke[11 + 12 * 6]; + + k43.y.x = Ke[9 + 12 * 7]; + k43.y.y = Ke[10 + 12 * 7]; + k43.y.z = Ke[11 + 12 * 7]; + + k43.z.x = Ke[9 + 12 * 8]; + k43.z.y = Ke[10 + 12 * 8]; + k43.z.z = Ke[11 + 12 * 8]; + + // k44 + // a10_10 a10_11 a10_12 + // a11_10 a11_11 a11_12 + // a12_10 a12_11 a12_12 + k44.x.x = Ke[9 + 12 * 9]; + k44.x.y = Ke[10 + 12 * 9]; + k44.x.z = Ke[11 + 12 * 9]; + + k44.y.x = Ke[9 + 12 * 10]; + k44.y.y = Ke[10 + 12 * 10]; + k44.y.z = Ke[11 + 12 * 10]; + + k44.z.x = Ke[9 + 12 * 11]; + k44.z.y = Ke[10 + 12 * 11]; + k44.z.z = Ke[11 + 12 * 11]; +} + +b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) +{ + B3_ASSERT(def.mesh); + B3_ASSERT(def.density > 0.0f); + + m_mesh = def.mesh; + m_density = def.density; + m_E = def.E; + m_nu = def.nu; + m_gravity.SetZero(); + m_world = nullptr; + + const b3SoftBodyMesh* m = m_mesh; + + m_nodes = (b3SoftBodyNode*)b3Alloc(m->vertexCount * sizeof(b3SoftBodyNode)); + + // Initialize nodes + for (u32 i = 0; i < m->vertexCount; ++i) + { + b3SoftBodyNode* n = m_nodes + i; + + n->m_body = this; + n->m_type = e_dynamicSoftBodyNode; + n->m_position = m->vertices[i]; + n->m_velocity.SetZero(); + n->m_force.SetZero(); + n->m_mass = 0.0f; + n->m_invMass = 0.0f; + n->m_radius = 0.0f; + n->m_friction = 0.0f; + n->m_userData = nullptr; + n->m_vertex = i; + n->m_bodyContact.active = false; + } + + // Compute mass + ComputeMass(); + + // Initialize elements + m_elements = (b3SoftBodyElement*)b3Alloc(m->tetrahedronCount * sizeof(b3SoftBodyElement)); + for (u32 ei = 0; ei < m->tetrahedronCount; ++ei) + { + b3SoftBodyMeshTetrahedron* mt = m->tetrahedrons + ei; + b3SoftBodyElement* e = m_elements + ei; + + u32 v1 = mt->v1; + u32 v2 = mt->v2; + u32 v3 = mt->v3; + u32 v4 = mt->v4; + + b3Vec3 p1 = m->vertices[v1]; + b3Vec3 p2 = m->vertices[v2]; + b3Vec3 p3 = m->vertices[v3]; + b3Vec3 p4 = m->vertices[v4]; + + float32 lambda, mu; + b3Lame(lambda, mu, m_E, m_nu); + + // 6 x 6 + float32 E[36]; + b3CreateE(E, lambda, mu); + + // 4 x 4 + float32 P[16] = + { + p1.x, p1.y, p1.z, 1.0f, + p2.x, p2.y, p2.z, 1.0f, + p3.x, p3.y, p3.z, 1.0f, + p4.x, p4.y, p4.z, 1.0f + }; + + b3Inverse4(e->invP, P); + + // 6 x 12 + float32 B[72]; + b3CreateB(B, e->invP); + + // 6 x 12 + float32 EB[72]; + b3Mul(EB, E, 6, 6, B, 6, 12); + + // 12 x 6 + float32 BT[72]; + b3Transpose(BT, B, 6, 12); + + float32 V = b3Volume(p1, p2, p3, p4); + + // 12 x 12 + float32 Ke[144]; + b3Mul(Ke, BT, 12, 6, EB, 6, 12); + for (u32 i = 0; i < 144; ++i) + { + Ke[i] *= V; + } + + b3SetK(e->K, Ke); + } + + // Initialize triangles + m_triangles = (b3SoftBodyTriangle*)b3Alloc(4 * m_mesh->tetrahedronCount * sizeof(b3SoftBodyTriangle)); + for (u32 i = 0; i < m_mesh->tetrahedronCount; ++i) + { + b3SoftBodyMeshTetrahedron* mt = m_mesh->tetrahedrons + i; + + u32 v1 = mt->v1; + u32 v2 = mt->v2; + u32 v3 = mt->v3; + u32 v4 = mt->v4; + + b3SoftBodyTriangle* t1 = m_triangles + 4 * i + 0; + b3SoftBodyTriangle* t2 = m_triangles + 4 * i + 1; + b3SoftBodyTriangle* t3 = m_triangles + 4 * i + 2; + b3SoftBodyTriangle* t4 = m_triangles + 4 * i + 3; + + t1->v1 = v1; + t1->v2 = v2; + t1->v3 = v3; + t1->tetrahedron = i; + + t2->v1 = v1; + t2->v2 = v3; + t2->v3 = v4; + t2->tetrahedron = i; + + t3->v1 = v1; + t3->v2 = v4; + t3->v3 = v2; + t3->tetrahedron = i; + + t4->v1 = v2; + t4->v2 = v4; + t4->v3 = v3; + t4->tetrahedron = i; + } +} + +b3SoftBody::~b3SoftBody() +{ + b3Free(m_nodes); + b3Free(m_elements); + b3Free(m_triangles); +} + +bool b3SoftBody::RayCastSingle(b3SoftBodyRayCastSingleOutput* output, const b3Vec3& p1, const b3Vec3& p2) const +{ + b3RayCastInput input; + input.p1 = p1; + input.p2 = p2; + input.maxFraction = 1.0f; + + u32 triangle = ~0; + u32 tetrahedron = ~0; + + b3RayCastOutput output0; + output0.fraction = B3_MAX_FLOAT; + + for (u32 i = 0; i < 4 * m_mesh->tetrahedronCount; ++i) + { + b3SoftBodyTriangle* t = m_triangles + i; + + b3Vec3 v1 = m_nodes[t->v1].m_position; + b3Vec3 v2 = m_nodes[t->v2].m_position; + b3Vec3 v3 = m_nodes[t->v3].m_position; + + b3RayCastOutput subOutput; + if (b3RayCast(&subOutput, &input, v1, v2, v3)) + { + if (subOutput.fraction < output0.fraction) + { + triangle = i; + tetrahedron = t->tetrahedron; + output0.fraction = subOutput.fraction; + output0.normal = subOutput.normal; + } + } + } + + if (tetrahedron != ~0) + { + output->tetrahedron = tetrahedron; + output->v1 = m_triangles[triangle].v1; + output->v2 = m_triangles[triangle].v2; + output->v3 = m_triangles[triangle].v3; + output->fraction = output0.fraction; + output->normal = output0.normal; + + return true; + } + + return false; +} + +b3SoftBodyNode* b3SoftBody::GetVertexNode(u32 i) +{ + B3_ASSERT(i < m_mesh->vertexCount); + return m_nodes + i; +} + +float32 b3SoftBody::GetEnergy() const +{ + float32 E = 0.0f; + for (u32 i = 0; i < m_mesh->vertexCount; ++i) + { + b3SoftBodyNode* n = m_nodes + i; + + E += n->m_mass * b3Dot(n->m_velocity, n->m_velocity); + } + return 0.5f * E; +} + +void b3SoftBody::ComputeMass() +{ + for (u32 i = 0; i < m_mesh->vertexCount; ++i) + { + b3SoftBodyNode* n = m_nodes + i; + n->m_mass = 0.0f; + n->m_invMass = 0.0f; + } + + const float32 inv4 = 1.0f / 4.0f; + const float32 rho = m_density; + + for (u32 i = 0; i < m_mesh->tetrahedronCount; ++i) + { + b3SoftBodyMeshTetrahedron* tetrahedron = m_mesh->tetrahedrons + i; + + b3Vec3 v1 = m_mesh->vertices[tetrahedron->v1]; + b3Vec3 v2 = m_mesh->vertices[tetrahedron->v2]; + b3Vec3 v3 = m_mesh->vertices[tetrahedron->v3]; + b3Vec3 v4 = m_mesh->vertices[tetrahedron->v4]; + + float32 volume = b3Volume(v1, v2, v3, v4); + B3_ASSERT(volume > 0.0f); + + float32 mass = rho * volume; + + b3SoftBodyNode* n1 = m_nodes + tetrahedron->v1; + b3SoftBodyNode* n2 = m_nodes + tetrahedron->v2; + b3SoftBodyNode* n3 = m_nodes + tetrahedron->v3; + b3SoftBodyNode* n4 = m_nodes + tetrahedron->v4; + + n1->m_mass += inv4 * mass; + n2->m_mass += inv4 * mass; + n3->m_mass += inv4 * mass; + n4->m_mass += inv4 * mass; + } + + // Invert + for (u32 i = 0; i < m_mesh->vertexCount; ++i) + { + b3SoftBodyNode* n = m_nodes + i; + B3_ASSERT(n->m_mass > 0.0f); + n->m_invMass = 1.0f / n->m_mass; + } +} + +void b3SoftBody::UpdateContacts() +{ + B3_PROFILE("Soft Body Update Contacts"); + + // Is there a world attached to this cloth? + if (m_world == nullptr) + { + return; + } + + // Create contacts + for (u32 i = 0; i < m_mesh->vertexCount; ++i) + { + b3SoftBodyNode* n = m_nodes + i; + + b3Sphere s1; + s1.vertex = n->m_position; + s1.radius = n->m_radius; + + // Find the deepest penetration + b3Shape* bestShape = nullptr; + float32 bestSeparation = 0.0f; + b3Vec3 bestPoint(0.0f, 0.0f, 0.0f); + b3Vec3 bestNormal(0.0f, 0.0f, 0.0f); + + for (b3Body* body = m_world->GetBodyList().m_head; body; body = body->GetNext()) + { + if (n->m_type != e_dynamicSoftBodyNode) + { + continue; + } + + if (body->GetType() != e_staticBody) + { + //continue; + } + + b3Transform xf = body->GetTransform(); + for (b3Shape* shape = body->GetShapeList().m_head; shape; shape = shape->GetNext()) + { + b3TestSphereOutput output; + if (shape->TestSphere(&output, s1, xf)) + { + if (output.separation < bestSeparation) + { + bestShape = shape; + bestSeparation = output.separation; + bestPoint = output.point; + bestNormal = output.normal; + } + } + } + } + + if (bestShape == nullptr) + { + n->m_bodyContact.active = false; + continue; + } + + b3Shape* shape = bestShape; + b3Body* body = shape->GetBody(); + float32 separation = bestSeparation; + b3Vec3 point = bestPoint; + b3Vec3 normal = -bestNormal; + + b3NodeBodyContact* c = &n->m_bodyContact; + + b3NodeBodyContact c0 = *c; + + c->active = true; + c->n1 = n; + c->s2 = shape; + c->normal1 = normal; + c->localPoint1.SetZero(); + c->localPoint2 = body->GetLocalPoint(point); + c->t1 = b3Perp(normal); + c->t2 = b3Cross(c->t1, normal); + c->normalImpulse = 0.0f; + c->tangentImpulse.SetZero(); + + if (c0.active == true) + { + c->normalImpulse = c0.normalImpulse; + c->tangentImpulse = c0.tangentImpulse; + } + } +} + +void b3SoftBody::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations) +{ + B3_PROFILE("Soft Body Solve"); + + b3SoftBodySolverDef def; + def.stack = &m_stackAllocator; + def.mesh = m_mesh; + def.nodes = m_nodes; + def.elements = m_elements; + + b3SoftBodySolver solver(def); + + solver.Solve(dt, gravity, velocityIterations, positionIterations); +} + +void b3SoftBody::Step(float32 dt, u32 velocityIterations, u32 positionIterations) +{ + B3_PROFILE("Soft Body Step"); + + // Update contacts + UpdateContacts(); + + // Integrate state, solve constraints. + if (dt > 0.0f) + { + Solve(dt, m_gravity, velocityIterations, positionIterations); + } + + // Clear forces + for (u32 i = 0; i < m_mesh->vertexCount; ++i) + { + m_nodes[i].m_force.SetZero(); + } +} + +void b3SoftBody::Draw() const +{ + const b3SoftBodyMesh* m = m_mesh; + + for (u32 i = 0; i < m->vertexCount; ++i) + { + b3SoftBodyNode* n = m_nodes + i; + + b3Vec3 v = n->m_position; + + if (n->m_type == e_staticSoftBodyNode) + { + b3Draw_draw->DrawPoint(v, 4.0f, b3Color_white); + } + + if (n->m_type == e_dynamicSoftBodyNode) + { + b3Draw_draw->DrawPoint(v, 4.0f, b3Color_green); + } + } + + for (u32 i = 0; i < m->tetrahedronCount; ++i) + { + b3SoftBodyMeshTetrahedron* t = m->tetrahedrons + i; + + b3Vec3 v1 = m_nodes[t->v1].m_position; + b3Vec3 v2 = m_nodes[t->v2].m_position; + b3Vec3 v3 = m_nodes[t->v3].m_position; + b3Vec3 v4 = m_nodes[t->v4].m_position; + + b3Vec3 c = (v1 + v2 + v3 + v4) / 4.0f; + + float32 s = 0.9f; + + v1 = s * (v1 - c) + c; + v2 = s * (v2 - c) + c; + v3 = s * (v3 - c) + c; + v4 = s * (v4 - c) + c; + + // v1, v2, v3 + b3Draw_draw->DrawTriangle(v1, v2, v3, b3Color_black); + + b3Vec3 n1 = b3Cross(v2 - v1, v3 - v1); + n1.Normalize(); + b3Draw_draw->DrawSolidTriangle(-n1, v1, v2, v3, b3Color_blue); + + // v1, v3, v4 + b3Draw_draw->DrawTriangle(v1, v3, v4, b3Color_black); + + b3Vec3 n2 = b3Cross(v3 - v1, v4 - v1); + n2.Normalize(); + b3Draw_draw->DrawSolidTriangle(-n2, v1, v3, v4, b3Color_blue); + + // v1, v4, v2 + b3Draw_draw->DrawTriangle(v1, v4, v2, b3Color_black); + + b3Vec3 n3 = b3Cross(v4 - v1, v2 - v1); + n3.Normalize(); + b3Draw_draw->DrawSolidTriangle(-n3, v1, v4, v2, b3Color_blue); + + // v2, v4, v3 + b3Draw_draw->DrawTriangle(v2, v4, v3, b3Color_black); + + b3Vec3 n4 = b3Cross(v4 - v2, v3 - v2); + n4.Normalize(); + b3Draw_draw->DrawSolidTriangle(-n4, v2, v4, v3, b3Color_blue); + } +} \ No newline at end of file diff --git a/src/bounce/softbody/softbody_contact_solver.cpp b/src/bounce/softbody/softbody_contact_solver.cpp new file mode 100644 index 0000000..f7ac4c4 --- /dev/null +++ b/src/bounce/softbody/softbody_contact_solver.cpp @@ -0,0 +1,429 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include +#include + +b3SoftBodyContactSolver::b3SoftBodyContactSolver(const b3SoftBodyContactSolverDef& def) +{ + m_allocator = def.allocator; + + m_positions = def.positions; + m_velocities = def.velocities; + + m_bodyContactCapacity = def.bodyContactCapacity; + m_bodyContactCount = 0; + m_bodyContacts = (b3NodeBodyContact**)m_allocator->Allocate(m_bodyContactCapacity * sizeof(b3NodeBodyContact*)); +} + +b3SoftBodyContactSolver::~b3SoftBodyContactSolver() +{ + m_allocator->Free(m_bodyPositionConstraints); + m_allocator->Free(m_bodyVelocityConstraints); + m_allocator->Free(m_bodyContacts); +} + +void b3SoftBodyContactSolver::Add(b3NodeBodyContact* c) +{ + m_bodyContacts[m_bodyContactCount++] = c; +} + +void b3SoftBodyContactSolver::InitializeBodyContactConstraints() +{ + m_bodyVelocityConstraints = (b3SoftBodySolverBodyContactVelocityConstraint*)m_allocator->Allocate(m_bodyContactCount * sizeof(b3SoftBodySolverBodyContactVelocityConstraint)); + m_bodyPositionConstraints = (b3SoftBodySolverBodyContactPositionConstraint*)m_allocator->Allocate(m_bodyContactCount * sizeof(b3SoftBodySolverBodyContactPositionConstraint)); + + b3DenseVec3& x = *m_positions; + b3DenseVec3& v = *m_velocities; + + for (u32 i = 0; i < m_bodyContactCount; ++i) + { + b3NodeBodyContact* c = m_bodyContacts[i]; + b3SoftBodySolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; + b3SoftBodySolverBodyContactPositionConstraint* pc = m_bodyPositionConstraints + i; + + vc->indexA = c->n1->m_vertex; + vc->bodyB = c->s2->GetBody(); + + vc->invMassA = c->n1->m_type == e_staticSoftBodyNode ? 0.0f : c->n1->m_invMass; + vc->invMassB = vc->bodyB->GetInverseMass(); + + vc->invIA.SetZero(); + vc->invIB = vc->bodyB->GetWorldInverseInertia(); + + vc->friction = b3MixFriction(c->n1->m_friction, c->s2->GetFriction()); + + pc->indexA = c->n1->m_vertex; + pc->bodyB = vc->bodyB; + + pc->invMassA = c->n1->m_type == e_staticSoftBodyNode ? 0.0f : c->n1->m_invMass; + pc->invMassB = vc->bodyB->m_invMass; + + pc->invIA.SetZero(); + pc->invIB = vc->bodyB->m_worldInvI; + + pc->radiusA = c->n1->m_radius; + pc->radiusB = c->s2->m_radius; + + pc->localCenterA.SetZero(); + pc->localCenterB = pc->bodyB->m_sweep.localCenter; + + pc->normalA = c->normal1; + pc->localPointA = c->localPoint1; + pc->localPointB = c->localPoint2; + } + + for (u32 i = 0; i < m_bodyContactCount; ++i) + { + b3NodeBodyContact* c = m_bodyContacts[i]; + b3SoftBodySolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; + b3SoftBodySolverBodyContactPositionConstraint* pc = m_bodyPositionConstraints + i; + + u32 indexA = vc->indexA; + b3Body* bodyB = vc->bodyB; + + float32 mA = vc->invMassA; + float32 mB = vc->invMassB; + + b3Mat33 iA = vc->invIA; + b3Mat33 iB = vc->invIB; + + b3Vec3 xA = x[indexA]; + b3Vec3 xB = bodyB->m_sweep.worldCenter; + + b3Quat qA; qA.SetIdentity(); + b3Quat qB = bodyB->m_sweep.orientation; + + b3Vec3 localCenterA = pc->localCenterA; + b3Vec3 localCenterB = pc->localCenterB; + + b3Transform xfA; + xfA.rotation = b3QuatMat33(qA); + xfA.position = xA - b3Mul(xfA.rotation, localCenterA); + + b3Transform xfB; + xfB.rotation = b3QuatMat33(qB); + xfB.position = xB - b3Mul(xfB.rotation, localCenterB); + + b3NodeBodyContactWorldPoint wp; + wp.Initialize(c, pc->radiusA, xfA, pc->radiusB, xfB); + + vc->normal = wp.normal; + vc->tangent1 = c->t1; + vc->tangent2 = c->t2; + vc->point = wp.point; + + b3Vec3 point = vc->point; + + b3Vec3 rA = point - xA; + b3Vec3 rB = point - xB; + + vc->rA = rA; + vc->rB = rB; + + vc->normalImpulse = c->normalImpulse; + vc->tangentImpulse = c->tangentImpulse; + + { + b3Vec3 n = vc->normal; + + b3Vec3 rnA = b3Cross(rA, n); + b3Vec3 rnB = b3Cross(rB, n); + + float32 K = mA + mB + b3Dot(iA * rnA, rnA) + b3Dot(iB * rnB, rnB); + + vc->normalMass = K > 0.0f ? 1.0f / K : 0.0f; + vc->velocityBias = 0.0f; + } + + { + b3Vec3 t1 = vc->tangent1; + b3Vec3 t2 = vc->tangent2; + + b3Vec3 rn1A = b3Cross(rA, t1); + b3Vec3 rn1B = b3Cross(rB, t1); + b3Vec3 rn2A = b3Cross(rA, t2); + b3Vec3 rn2B = b3Cross(rB, t2); + + // dot(t1, t2) = 0 + // J1_l1 * M1 * J2_l1 = J1_l2 * M2 * J2_l2 = 0 + float32 k11 = mA + mB + b3Dot(iA * rn1A, rn1A) + b3Dot(iB * rn1B, rn1B); + float32 k12 = b3Dot(iA * rn1A, rn2A) + b3Dot(iB * rn1B, rn2B); + float32 k22 = mA + mB + b3Dot(iA * rn2A, rn2A) + b3Dot(iB * rn2B, rn2B); + + b3Mat22 K; + K.x.Set(k11, k12); + K.y.Set(k12, k22); + + vc->tangentMass = b3Inverse(K); + } + } +} + +void b3SoftBodyContactSolver::WarmStart() +{ + b3DenseVec3& v = *m_velocities; + + for (u32 i = 0; i < m_bodyContactCount; ++i) + { + b3SoftBodySolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; + + u32 indexA = vc->indexA; + b3Body* bodyB = vc->bodyB; + + b3Vec3 vA = v[indexA]; + b3Vec3 vB = bodyB->GetLinearVelocity(); + + b3Vec3 wA; wA.SetZero(); + b3Vec3 wB = bodyB->GetAngularVelocity(); + + float32 mA = vc->invMassA; + float32 mB = vc->invMassB; + + b3Mat33 iA = vc->invIA; + b3Mat33 iB = vc->invIB; + + b3Vec3 P = vc->normalImpulse * vc->normal; + + vA -= mA * P; + wA -= iA * b3Cross(vc->rA, P); + + vB += mB * P; + wB += iB * b3Cross(vc->rB, P); + + b3Vec3 P1 = vc->tangentImpulse.x * vc->tangent1; + b3Vec3 P2 = vc->tangentImpulse.y * vc->tangent2; + + vA -= mA * (P1 + P2); + wA -= iA * b3Cross(vc->rA, P1 + P2); + + vB += mB * (P1 + P2); + wB += iB * b3Cross(vc->rB, P1 + P2); + + v[indexA] = vA; + + bodyB->SetLinearVelocity(vB); + bodyB->SetAngularVelocity(wB); + } +} + +void b3SoftBodyContactSolver::SolveBodyContactVelocityConstraints() +{ + b3DenseVec3& v = *m_velocities; + + for (u32 i = 0; i < m_bodyContactCount; ++i) + { + b3SoftBodySolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; + + u32 indexA = vc->indexA; + b3Body* bodyB = vc->bodyB; + + b3Vec3 vA = v[indexA]; + b3Vec3 vB = bodyB->GetLinearVelocity(); + + b3Vec3 wA; wA.SetZero(); + b3Vec3 wB = bodyB->GetAngularVelocity(); + + float32 mA = vc->invMassA; + float32 mB = vc->invMassB; + + b3Mat33 iA = vc->invIA; + b3Mat33 iB = vc->invIB; + + b3Vec3 normal = vc->normal; + b3Vec3 point = vc->point; + + b3Vec3 rA = vc->rA; + b3Vec3 rB = vc->rB; + + // Solve normal constraint. + { + b3Vec3 dv = vB + b3Cross(wB, rB) - vA - b3Cross(wA, rA); + float32 Cdot = b3Dot(normal, dv); + + float32 impulse = vc->normalMass * (-Cdot + vc->velocityBias); + + float32 oldImpulse = vc->normalImpulse; + vc->normalImpulse = b3Max(vc->normalImpulse + impulse, 0.0f); + impulse = vc->normalImpulse - oldImpulse; + + b3Vec3 P = impulse * normal; + + vA -= mA * P; + wA -= iA * b3Cross(rA, P); + + vB += mB * P; + wB += iB * b3Cross(rB, P); + } + + // Solve tangent constraints. + { + b3Vec3 dv = vB + b3Cross(wB, rB) - vA - b3Cross(wA, rA); + + b3Vec2 Cdot; + Cdot.x = b3Dot(dv, vc->tangent1); + Cdot.y = b3Dot(dv, vc->tangent2); + + b3Vec2 impulse = vc->tangentMass * -Cdot; + b3Vec2 oldImpulse = vc->tangentImpulse; + vc->tangentImpulse += impulse; + + float32 maxImpulse = vc->friction * vc->normalImpulse; + if (b3Dot(vc->tangentImpulse, vc->tangentImpulse) > maxImpulse * maxImpulse) + { + vc->tangentImpulse.Normalize(); + vc->tangentImpulse *= maxImpulse; + } + + impulse = vc->tangentImpulse - oldImpulse; + + b3Vec3 P1 = impulse.x * vc->tangent1; + b3Vec3 P2 = impulse.y * vc->tangent2; + b3Vec3 P = P1 + P2; + + vA -= mA * P; + wA -= iA * b3Cross(rA, P); + + vB += mB * P; + wB += iB * b3Cross(rB, P); + } + + v[indexA] = vA; + + bodyB->SetLinearVelocity(vB); + bodyB->SetAngularVelocity(wB); + } +} + +void b3SoftBodyContactSolver::StoreImpulses() +{ + for (u32 i = 0; i < m_bodyContactCount; ++i) + { + b3NodeBodyContact* c = m_bodyContacts[i]; + b3SoftBodySolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; + + c->normalImpulse = vc->normalImpulse; + c->tangentImpulse = vc->tangentImpulse; + } +} + +struct b3ClothSolverBodyContactSolverPoint +{ + void Initialize(const b3SoftBodySolverBodyContactPositionConstraint* pc, const b3Transform& xfA, const b3Transform& xfB) + { + b3Vec3 cA = xfA * pc->localPointA; + b3Vec3 cB = xfB * pc->localPointB; + + float32 rA = pc->radiusA; + float32 rB = pc->radiusB; + + b3Vec3 nA = pc->normalA; + + b3Vec3 pA = cA + rA * nA; + b3Vec3 pB = cB - rB * nA; + + point = cB; + normal = nA; + separation = b3Dot(cB - cA, nA) - rA - rB; + } + + b3Vec3 normal; + b3Vec3 point; + float32 separation; +}; + +bool b3SoftBodyContactSolver::SolveBodyContactPositionConstraints() +{ + b3DenseVec3& x = *m_positions; + + float32 minSeparation = 0.0f; + + for (u32 i = 0; i < m_bodyContactCount; ++i) + { + b3SoftBodySolverBodyContactPositionConstraint* pc = m_bodyPositionConstraints + i; + + u32 indexA = pc->indexA; + float32 mA = pc->invMassA; + b3Mat33 iA = pc->invIA; + b3Vec3 localCenterA = pc->localCenterA; + + b3Body* bodyB = pc->bodyB; + float32 mB = pc->invMassB; + b3Mat33 iB = pc->invIB; + b3Vec3 localCenterB = pc->localCenterB; + + b3Vec3 cA = x[indexA]; + b3Quat qA; qA.SetIdentity(); + + b3Vec3 cB = bodyB->m_sweep.worldCenter; + b3Quat qB = bodyB->m_sweep.orientation; + + // Solve normal constraint + b3Transform xfA; + xfA.rotation = b3QuatMat33(qA); + xfA.position = cA - b3Mul(xfA.rotation, localCenterA); + + b3Transform xfB; + xfB.rotation = b3QuatMat33(qB); + xfB.position = cB - b3Mul(xfB.rotation, localCenterB); + + b3ClothSolverBodyContactSolverPoint cpcp; + cpcp.Initialize(pc, xfA, xfB); + + b3Vec3 normal = cpcp.normal; + b3Vec3 point = cpcp.point; + float32 separation = cpcp.separation; + + // Update max constraint error. + minSeparation = b3Min(minSeparation, separation); + + // Allow some slop and prevent large corrections. + float32 C = b3Clamp(B3_BAUMGARTE * (separation + B3_LINEAR_SLOP), -B3_MAX_LINEAR_CORRECTION, 0.0f); + + // Compute effective mass. + b3Vec3 rA = point - cA; + b3Vec3 rB = point - cB; + + b3Vec3 rnA = b3Cross(rA, normal); + b3Vec3 rnB = b3Cross(rB, normal); + float32 K = mA + mB + b3Dot(rnA, iA * rnA) + b3Dot(rnB, iB * rnB); + + // Compute normal impulse. + float32 impulse = K > 0.0f ? -C / K : 0.0f; + b3Vec3 P = impulse * normal; + + cA -= mA * P; + qA -= b3Derivative(qA, iA * b3Cross(rA, P)); + qA.Normalize(); + + cB += mB * P; + qB += b3Derivative(qB, iB * b3Cross(rB, P)); + qB.Normalize(); + + x[indexA] = cA; + + bodyB->m_sweep.worldCenter = cB; + bodyB->m_sweep.orientation = qB; + } + + return minSeparation >= -3.0f * B3_LINEAR_SLOP; +} \ No newline at end of file diff --git a/src/bounce/softbody/softbody_mesh.cpp b/src/bounce/softbody/softbody_mesh.cpp new file mode 100644 index 0000000..2dd3135 --- /dev/null +++ b/src/bounce/softbody/softbody_mesh.cpp @@ -0,0 +1,69 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include + +b3QSoftBodyMesh::b3QSoftBodyMesh() +{ + vertexCount = 0; + vertices = nullptr; + tetrahedronCount = 0; + tetrahedrons = nullptr; +} + +b3QSoftBodyMesh::~b3QSoftBodyMesh() +{ + b3Free(vertices); + b3Free(tetrahedrons); +} + +void b3QSoftBodyMesh::SetAsSphere(float32 radius, u32 subdivisions) +{ + smMesh mesh; + smCreateMesh(mesh, subdivisions); + + vertexCount = 1 + mesh.vertexCount; + vertices = (b3Vec3*)b3Alloc(vertexCount * sizeof(b3Vec3)); + vertices[0].SetZero(); + for (u32 i = 0; i < mesh.vertexCount; ++i) + { + vertices[1 + i] = mesh.vertices[i]; + } + + tetrahedronCount = mesh.indexCount / 3; + tetrahedrons = (b3SoftBodyMeshTetrahedron*)b3Alloc(tetrahedronCount * sizeof(b3SoftBodyMeshTetrahedron)); + for (u32 i = 0; i < mesh.indexCount / 3; ++i) + { + u32 v1 = mesh.indices[3 * i + 0]; + u32 v2 = mesh.indices[3 * i + 1]; + u32 v3 = mesh.indices[3 * i + 2]; + + b3SoftBodyMeshTetrahedron* t = tetrahedrons + i; + + t->v1 = 1 + v3; + t->v2 = 1 + v2; + t->v3 = 1 + v1; + t->v4 = 0; + } + + for (u32 i = 0; i < vertexCount; ++i) + { + vertices[i] *= radius; + } +} \ No newline at end of file diff --git a/src/bounce/softbody/softbody_solver.cpp b/src/bounce/softbody/softbody_solver.cpp new file mode 100644 index 0000000..cb6ca56 --- /dev/null +++ b/src/bounce/softbody/softbody_solver.cpp @@ -0,0 +1,397 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +// This work is based on the paper "Interactive Virtual Materials" written by +// Matthias Mueller Fischer +// The paper is available here: +// http://matthias-mueller-fischer.ch/publications/GI2004.pdf + +// In order to support velocity constraints on node velocities +// we solve Ax = b using a Modified Preconditioned Conjugate Gradient (MPCG) algorithm. + +// Number of MPCG iterations, a value that is normally small when small time steps are taken. +u32 b3_softBodySolverIterations = 0; + +// Enables the stiffness warping solver. +// bool b3_enableStiffnessWarping = true; + +b3SoftBodySolver::b3SoftBodySolver(const b3SoftBodySolverDef& def) +{ + m_allocator = def.stack; + m_mesh = def.mesh; + m_nodes = def.nodes; + m_elements = def.elements; +} + +b3SoftBodySolver::~b3SoftBodySolver() +{ + +} + +// https://animation.rwth-aachen.de/media/papers/2016-MIG-StableRotation.pdf +static B3_FORCE_INLINE void b3ExtractRotation(b3Mat33& out, b3Quat& q, const b3Mat33& A, u32 maxIterations = 20) +{ + for (u32 iteration = 0; iteration < maxIterations; ++iteration) + { + b3Mat33 R = b3QuatMat33(q); + + float32 s = b3Abs(b3Dot(R.x, A.x) + b3Dot(R.y, A.y) + b3Dot(R.z, A.z)); + + if (s == 0.0f) + { + break; + } + + float32 inv_s = 1.0f / s + 1.0e-9f; + + b3Vec3 v = b3Cross(R.x, A.x) + b3Cross(R.y, A.y) + b3Cross(R.z, A.z); + + b3Vec3 omega = inv_s * v; + + float32 w = b3Length(omega); + + if (w < 1.0e-9f) + { + break; + } + + b3Quat omega_q(omega / w, w); + + q = omega_q * q; + q.Normalize(); + } + + out = b3QuatMat33(q); +} + +static B3_FORCE_INLINE void b3SolveMPCG(b3DenseVec3& x, + const b3SparseMat33& A, const b3DenseVec3& b, + const b3DenseVec3& x0, const b3DiagMat33& S, u32 maxIterations = 20) +{ + b3DiagMat33 P(A.rowCount); + b3DiagMat33 invP(A.rowCount); + for (u32 i = 0; i < A.rowCount; ++i) + { + b3Mat33 D = A(i, i); + + // Sylvester Criterion to ensure PD-ness + B3_ASSERT(b3Det(D.x, D.y, D.z) > 0.0f); + + B3_ASSERT(D.x.x != 0.0f); + float32 xx = 1.0f / D.x.x; + + B3_ASSERT(D.y.y != 0.0f); + float32 yy = 1.0f / D.y.y; + + B3_ASSERT(D.z.z != 0.0f); + float32 zz = 1.0f / D.z.z; + + P[i] = b3Diagonal(xx, yy, zz); + invP[i] = b3Diagonal(D.x.x, D.y.y, D.z.z); + } + + x = x0; + + float32 delta_0 = b3Dot(S * b, P * (S * b)); + + b3DenseVec3 r = S * (b - A * x); + b3DenseVec3 c = S * (invP * r); + + float32 delta_new = b3Dot(r, c); + + u32 iteration = 0; + for (;;) + { + if (iteration == maxIterations) + { + break; + } + + if (delta_new <= B3_EPSILON * B3_EPSILON * delta_0) + { + break; + } + + b3DenseVec3 q = S * (A * c); + + float32 alpha = delta_new / b3Dot(c, q); + + x = x + alpha * c; + r = r - alpha * q; + + b3DenseVec3 s = invP * r; + + float32 delta_old = delta_new; + + delta_new = b3Dot(r, s); + + float32 beta = delta_new / delta_old; + + c = S * (s + beta * c); + + ++iteration; + } + + b3_softBodySolverIterations = iteration; +} + +static B3_FORCE_INLINE void b3Mul(float32* C, float32* A, u32 AM, u32 AN, float32* B, u32 BM, u32 BN) +{ + B3_ASSERT(AN == BM); + + for (u32 i = 0; i < AM; ++i) + { + for (u32 j = 0; j < BN; ++j) + { + C[i + AM * j] = 0.0f; + + for (u32 k = 0; k < AN; ++k) + { + C[i + AM * j] += A[i + AM * k] * B[k + BM * j]; + } + } + } +} + +void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations) +{ + float32 h = dt; + + b3SparseMat33 M(m_mesh->vertexCount); + b3DenseVec3 x(m_mesh->vertexCount); + b3DenseVec3 p(m_mesh->vertexCount); + b3DenseVec3 v(m_mesh->vertexCount); + b3DenseVec3 fe(m_mesh->vertexCount); + b3DenseVec3 x0(m_mesh->vertexCount); + b3DiagMat33 S(m_mesh->vertexCount); + + for (u32 i = 0; i < m_mesh->vertexCount; ++i) + { + b3SoftBodyNode* n = m_nodes + i; + + M(i, i) = b3Diagonal(n->m_mass); + x[i] = m_mesh->vertices[i]; + p[i] = n->m_position; + v[i] = n->m_velocity; + fe[i] = n->m_force; + x0[i] = n->m_velocity; + + // Apply gravity + if (n->m_type == e_dynamicSoftBodyNode) + { + fe[i] += n->m_mass * gravity; + S[i].SetIdentity(); + } + else + { + S[i].SetZero(); + } + } + + // Element assembly + b3SparseMat33 K(m_mesh->vertexCount); + + b3DenseVec3 f0(m_mesh->vertexCount); + f0.SetZero(); + + for (u32 ei = 0; ei < m_mesh->tetrahedronCount; ++ei) + { + b3SoftBodyMeshTetrahedron* mt = m_mesh->tetrahedrons + ei; + b3SoftBodyElement* e = m_elements + ei; + + b3Mat33* Ke = e->K; + + u32 v1 = mt->v1; + u32 v2 = mt->v2; + u32 v3 = mt->v3; + u32 v4 = mt->v4; + + b3Vec3 p1 = p[v1]; + b3Vec3 p2 = p[v2]; + b3Vec3 p3 = p[v3]; + b3Vec3 p4 = p[v4]; + + float32 P[16] = + { + p1.x, p1.y, p1.z, 1.0f, + p2.x, p2.y, p2.z, 1.0f, + p3.x, p3.y, p3.z, 1.0f, + p4.x, p4.y, p4.z, 1.0f + }; + + float32 P_invP[16]; + b3Mul(P_invP, P, 4, 4, e->invP, 4, 4); + + b3Mat33 A; + for (u32 i = 0; i < 3; ++i) + { + for (u32 j = 0; j < 3; ++j) + { + A(i, j) = P_invP[i + 4 * j]; + } + } + + b3Mat33 R; + b3ExtractRotation(R, e->q, A); + + b3Mat33 RT = b3Transpose(R); + + u32 vs[4] = { v1, v2, v3, v4 }; + + for (u32 i = 0; i < 4; ++i) + { + u32 vi = vs[i]; + + for (u32 j = 0; j < 4; ++j) + { + u32 vj = vs[j]; + + K(vi, vj) += R * Ke[i + 4 * j] * RT; + } + } + + b3Vec3 x1 = x[v1]; + b3Vec3 x2 = x[v2]; + b3Vec3 x3 = x[v3]; + b3Vec3 x4 = x[v4]; + + b3Vec3 xs[4] = { x1, x2, x3, x4 }; + + b3Vec3 f0s[4]; + + for (u32 i = 0; i < 4; ++i) + { + f0s[i].SetZero(); + + for (u32 j = 0; j < 4; ++j) + { + f0s[i] += R * Ke[i + 4 * j] * xs[j]; + } + } + + f0[v1] += f0s[0]; + f0[v2] += f0s[1]; + f0[v3] += f0s[2]; + f0[v4] += f0s[3]; + } + + f0 = -f0; + + b3SparseMat33 A = M + h * h * K; + + b3DenseVec3 b = M * v - h * (K * p + f0 - fe); + + // Solve Ax = b + b3DenseVec3 sx(m_mesh->vertexCount); + b3SolveMPCG(sx, A, b, x0, S); + + // Solve constraints + b3SoftBodyContactSolverDef contactSolverDef; + contactSolverDef.allocator = m_allocator; + contactSolverDef.positions = &p; + contactSolverDef.velocities = &sx; + contactSolverDef.bodyContactCapacity = m_mesh->vertexCount; + + b3SoftBodyContactSolver contactSolver(contactSolverDef); + + for (u32 i = 0; i < m_mesh->vertexCount; ++i) + { + if (m_nodes[i].m_bodyContact.active) + { + contactSolver.Add(&m_nodes[i].m_bodyContact); + } + } + + { + contactSolver.InitializeBodyContactConstraints(); + } + + { + contactSolver.WarmStart(); + } + + // Solve velocity constraints + { + for (u32 i = 0; i < velocityIterations; ++i) + { + contactSolver.SolveBodyContactVelocityConstraints(); + } + } + + { + contactSolver.StoreImpulses(); + } + + // Integrate velocities + p = p + h * sx; + + // Solve position constraints + { + bool positionSolved = false; + for (u32 i = 0; i < positionIterations; ++i) + { + bool bodyContactsSolved = contactSolver.SolveBodyContactPositionConstraints(); + + if (bodyContactsSolved) + { + positionSolved = true; + break; + } + } + } + + // Synchronize bodies + for (u32 i = 0; i < m_mesh->vertexCount; ++i) + { + b3SoftBodyNode* n = m_nodes + i; + + b3NodeBodyContact* c = &n->m_bodyContact; + + if (c->active == false) + { + continue; + } + + b3Body* body = c->s2->GetBody(); + + body->SynchronizeTransform(); + + body->m_worldInvI = b3RotateToFrame(body->m_invI, body->m_xf.rotation); + + body->SynchronizeShapes(); + } + + // Copy state buffers back to the nodes + for (u32 i = 0; i < m_mesh->vertexCount; ++i) + { + b3SoftBodyNode* n = m_nodes + i; + + n->m_position = p[i]; + n->m_velocity = sx[i]; + } +} \ No newline at end of file From a40a872efaea13deab990d89ba98c315ddcb03cc Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 13 May 2019 19:08:40 -0300 Subject: [PATCH 097/198] Update readme.md --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index cd2eedb..44d8d4d 100644 --- a/readme.md +++ b/readme.md @@ -105,6 +105,7 @@ Below are the external dependencies for testbed. If you don't care about testbed * Rigid bodies * Cloth +* Soft body * Contact, friction, restitution * Mouse, spring, sphere, cone, revolute joint types * Quaternion constraints From 33a661cb32b16a533be3763ebc777ef6285f3beb Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 13 May 2019 19:12:46 -0300 Subject: [PATCH 098/198] Update comments --- include/bounce/softbody/softbody_node.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/bounce/softbody/softbody_node.h b/include/bounce/softbody/softbody_node.h index 5ddfba1..81f7e28 100644 --- a/include/bounce/softbody/softbody_node.h +++ b/include/bounce/softbody/softbody_node.h @@ -93,33 +93,33 @@ public: // Get the vertex index. u32 GetVertex() const; - // Set the particle position. - // If the particle is dynamic changing the position directly might lead + // Set the node position. + // If the node is dynamic changing the position directly might lead // to physically incorrect simulation behaviour. void SetPosition(const b3Vec3& position); - // Get the particle position. + // Get the node position. const b3Vec3& GetPosition() const; - // Set the particle velocity. + // Set the node velocity. void SetVelocity(const b3Vec3& velocity); - // Get the particle velocity. + // Get the node velocity. const b3Vec3& GetVelocity() const; - // Get the particle mass. + // Get the node mass. float32 GetMass() const; - // Set the particle radius. + // Set the node radius. void SetRadius(float32 radius); - // Get the particle radius. + // Get the node radius. float32 GetRadius() const; - // Set the particle coefficient of friction. + // Set the node coefficient of friction. void SetFriction(float32 friction); - // Get the particle coefficient of friction. + // Get the node coefficient of friction. float32 GetFriction() const; // Apply a force. From 4980e3c7f0d47f7a1eb742b549179633fcf3e9c8 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 14 May 2019 09:37:17 -0300 Subject: [PATCH 099/198] Update comments --- include/bounce/softbody/softbody.h | 8 ++++---- src/bounce/softbody/softbody.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/bounce/softbody/softbody.h b/include/bounce/softbody/softbody.h index 9ea65e9..e837ed9 100644 --- a/include/bounce/softbody/softbody.h +++ b/include/bounce/softbody/softbody.h @@ -81,7 +81,7 @@ struct b3SoftBodyDef float32 nu; }; -// A soft body represents a deformable volume as a collection of nodes. +// A soft body represents a deformable volume as a collection of nodes and elements. class b3SoftBody { public: @@ -97,11 +97,11 @@ public: // Get the acceleration of gravity. b3Vec3 GetGravity() const; - // Attach a world to this cloth. - // The cloth will be able to respond to collisions with the bodies in the attached world. + // Attach a world to this soft body. + // The soft body will be able to respond to collisions with the bodies in the attached world. void SetWorld(b3World* world); - // Get the world attached to this cloth. + // Get the world attached to this soft body. const b3World* GetWorld() const; b3World* GetWorld(); diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index 17b02d2..e98bff7 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -802,7 +802,7 @@ void b3SoftBody::UpdateContacts() { B3_PROFILE("Soft Body Update Contacts"); - // Is there a world attached to this cloth? + // Is there a world attached to this soft body? if (m_world == nullptr) { return; From c972052f3bfb9deb22e65736a8d9d434c758946e Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 14 May 2019 09:37:25 -0300 Subject: [PATCH 100/198] Update softbody_contact_solver.cpp --- src/bounce/softbody/softbody_contact_solver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bounce/softbody/softbody_contact_solver.cpp b/src/bounce/softbody/softbody_contact_solver.cpp index f7ac4c4..c079a66 100644 --- a/src/bounce/softbody/softbody_contact_solver.cpp +++ b/src/bounce/softbody/softbody_contact_solver.cpp @@ -326,7 +326,7 @@ void b3SoftBodyContactSolver::StoreImpulses() } } -struct b3ClothSolverBodyContactSolverPoint +struct b3SoftBodySolverBodyContactSolverPoint { void Initialize(const b3SoftBodySolverBodyContactPositionConstraint* pc, const b3Transform& xfA, const b3Transform& xfB) { @@ -386,7 +386,7 @@ bool b3SoftBodyContactSolver::SolveBodyContactPositionConstraints() xfB.rotation = b3QuatMat33(qB); xfB.position = cB - b3Mul(xfB.rotation, localCenterB); - b3ClothSolverBodyContactSolverPoint cpcp; + b3SoftBodySolverBodyContactSolverPoint cpcp; cpcp.Initialize(pc, xfA, xfB); b3Vec3 normal = cpcp.normal; From e1b5e615e3db5cdb7a5bdcd76bd12357f70a8423 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 14 May 2019 13:47:04 -0300 Subject: [PATCH 101/198] Generate tetrahedral cylinder. Also added some assertion code. --- include/bounce/softbody/softbody_mesh.h | 2 ++ src/bounce/softbody/softbody_mesh.cpp | 44 +++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/include/bounce/softbody/softbody_mesh.h b/include/bounce/softbody/softbody_mesh.h index e7f392a..4ffc686 100644 --- a/include/bounce/softbody/softbody_mesh.h +++ b/include/bounce/softbody/softbody_mesh.h @@ -40,6 +40,8 @@ struct b3QSoftBodyMesh : public b3SoftBodyMesh ~b3QSoftBodyMesh(); void SetAsSphere(float32 radius, u32 subdivisions); + + void SetAsCylinder(float32 radius, float32 ey, u32 segments); }; #endif \ No newline at end of file diff --git a/src/bounce/softbody/softbody_mesh.cpp b/src/bounce/softbody/softbody_mesh.cpp index 2dd3135..995e12a 100644 --- a/src/bounce/softbody/softbody_mesh.cpp +++ b/src/bounce/softbody/softbody_mesh.cpp @@ -18,6 +18,7 @@ #include #include +#include b3QSoftBodyMesh::b3QSoftBodyMesh() { @@ -38,6 +39,7 @@ void b3QSoftBodyMesh::SetAsSphere(float32 radius, u32 subdivisions) smMesh mesh; smCreateMesh(mesh, subdivisions); + B3_ASSERT(vertexCount == 0); vertexCount = 1 + mesh.vertexCount; vertices = (b3Vec3*)b3Alloc(vertexCount * sizeof(b3Vec3)); vertices[0].SetZero(); @@ -46,6 +48,7 @@ void b3QSoftBodyMesh::SetAsSphere(float32 radius, u32 subdivisions) vertices[1 + i] = mesh.vertices[i]; } + B3_ASSERT(tetrahedronCount == 0); tetrahedronCount = mesh.indexCount / 3; tetrahedrons = (b3SoftBodyMeshTetrahedron*)b3Alloc(tetrahedronCount * sizeof(b3SoftBodyMeshTetrahedron)); for (u32 i = 0; i < mesh.indexCount / 3; ++i) @@ -66,4 +69,45 @@ void b3QSoftBodyMesh::SetAsSphere(float32 radius, u32 subdivisions) { vertices[i] *= radius; } +} + +void b3QSoftBodyMesh::SetAsCylinder(float32 radius, float32 ey, u32 segments) +{ + cymMesh mesh; + cymCreateMesh(mesh, segments); + + B3_ASSERT(vertexCount == 0); + vertexCount = 1 + mesh.vertexCount; + vertices = (b3Vec3*)b3Alloc(vertexCount * sizeof(b3Vec3)); + vertices[0].SetZero(); + for (u32 i = 0; i < mesh.vertexCount; ++i) + { + vertices[1 + i] = mesh.vertices[i]; + } + + B3_ASSERT(tetrahedronCount == 0); + tetrahedronCount = mesh.indexCount / 3; + tetrahedrons = (b3SoftBodyMeshTetrahedron*)b3Alloc(tetrahedronCount * sizeof(b3SoftBodyMeshTetrahedron)); + for (u32 i = 0; i < mesh.indexCount / 3; ++i) + { + u32 v1 = mesh.indices[3 * i + 0]; + u32 v2 = mesh.indices[3 * i + 1]; + u32 v3 = mesh.indices[3 * i + 2]; + + b3SoftBodyMeshTetrahedron* t = tetrahedrons + i; + + t->v1 = 1 + v3; + t->v2 = 1 + v2; + t->v3 = 1 + v1; + t->v4 = 0; + } + + float32 height = 2.0f * ey; + + for (u32 i = 0; i < vertexCount; ++i) + { + vertices[i].x *= radius; + vertices[i].y *= height; + vertices[i].z *= radius; + } } \ No newline at end of file From e28fd2e07f10bddebf4ba31fc2b75c6bbe7d5d5c Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 21 May 2019 20:36:46 -0300 Subject: [PATCH 102/198] Simplify soft body initialization and solver Thanks Open Tissue!! --- include/bounce/softbody/softbody.h | 2 +- src/bounce/softbody/softbody.cpp | 577 ++++-------------------- src/bounce/softbody/softbody_solver.cpp | 22 +- 3 files changed, 91 insertions(+), 510 deletions(-) diff --git a/include/bounce/softbody/softbody.h b/include/bounce/softbody/softbody.h index e837ed9..a9b6d0d 100644 --- a/include/bounce/softbody/softbody.h +++ b/include/bounce/softbody/softbody.h @@ -42,8 +42,8 @@ struct b3SoftBodyRayCastSingleOutput // Soft body tetrahedron element struct b3SoftBodyElement { - float32 invP[16]; b3Mat33 K[16]; + b3Mat33 invE; b3Quat q; }; diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index e98bff7..d3395d1 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -59,144 +59,14 @@ static B3_FORCE_INLINE void b3Transpose(float32* B, float32* A, u32 AM, u32 AN) } } -static B3_FORCE_INLINE void b3Inverse4(float32 out[16], float32 m[16]) +// Compute the elasticity matrix given Young modulus and Poisson's ratio +// This is a 6 x 6 matrix +static B3_FORCE_INLINE void b3ComputeD(float32 out[36], float32 E, float32 nu) { - float32 inv[16]; + float32 lambda = (nu * E) / ((1 + nu) * (1 - 2 * nu)); + float32 mu = E / (2 * (1 + nu)); - inv[0] = m[5] * m[10] * m[15] - - m[5] * m[11] * m[14] - - m[9] * m[6] * m[15] + - m[9] * m[7] * m[14] + - m[13] * m[6] * m[11] - - m[13] * m[7] * m[10]; - - inv[4] = -m[4] * m[10] * m[15] + - m[4] * m[11] * m[14] + - m[8] * m[6] * m[15] - - m[8] * m[7] * m[14] - - m[12] * m[6] * m[11] + - m[12] * m[7] * m[10]; - - inv[8] = m[4] * m[9] * m[15] - - m[4] * m[11] * m[13] - - m[8] * m[5] * m[15] + - m[8] * m[7] * m[13] + - m[12] * m[5] * m[11] - - m[12] * m[7] * m[9]; - - inv[12] = -m[4] * m[9] * m[14] + - m[4] * m[10] * m[13] + - m[8] * m[5] * m[14] - - m[8] * m[6] * m[13] - - m[12] * m[5] * m[10] + - m[12] * m[6] * m[9]; - - inv[1] = -m[1] * m[10] * m[15] + - m[1] * m[11] * m[14] + - m[9] * m[2] * m[15] - - m[9] * m[3] * m[14] - - m[13] * m[2] * m[11] + - m[13] * m[3] * m[10]; - - inv[5] = m[0] * m[10] * m[15] - - m[0] * m[11] * m[14] - - m[8] * m[2] * m[15] + - m[8] * m[3] * m[14] + - m[12] * m[2] * m[11] - - m[12] * m[3] * m[10]; - - inv[9] = -m[0] * m[9] * m[15] + - m[0] * m[11] * m[13] + - m[8] * m[1] * m[15] - - m[8] * m[3] * m[13] - - m[12] * m[1] * m[11] + - m[12] * m[3] * m[9]; - - inv[13] = m[0] * m[9] * m[14] - - m[0] * m[10] * m[13] - - m[8] * m[1] * m[14] + - m[8] * m[2] * m[13] + - m[12] * m[1] * m[10] - - m[12] * m[2] * m[9]; - - inv[2] = m[1] * m[6] * m[15] - - m[1] * m[7] * m[14] - - m[5] * m[2] * m[15] + - m[5] * m[3] * m[14] + - m[13] * m[2] * m[7] - - m[13] * m[3] * m[6]; - - inv[6] = -m[0] * m[6] * m[15] + - m[0] * m[7] * m[14] + - m[4] * m[2] * m[15] - - m[4] * m[3] * m[14] - - m[12] * m[2] * m[7] + - m[12] * m[3] * m[6]; - - inv[10] = m[0] * m[5] * m[15] - - m[0] * m[7] * m[13] - - m[4] * m[1] * m[15] + - m[4] * m[3] * m[13] + - m[12] * m[1] * m[7] - - m[12] * m[3] * m[5]; - - inv[14] = -m[0] * m[5] * m[14] + - m[0] * m[6] * m[13] + - m[4] * m[1] * m[14] - - m[4] * m[2] * m[13] - - m[12] * m[1] * m[6] + - m[12] * m[2] * m[5]; - - inv[3] = -m[1] * m[6] * m[11] + - m[1] * m[7] * m[10] + - m[5] * m[2] * m[11] - - m[5] * m[3] * m[10] - - m[9] * m[2] * m[7] + - m[9] * m[3] * m[6]; - - inv[7] = m[0] * m[6] * m[11] - - m[0] * m[7] * m[10] - - m[4] * m[2] * m[11] + - m[4] * m[3] * m[10] + - m[8] * m[2] * m[7] - - m[8] * m[3] * m[6]; - - inv[11] = -m[0] * m[5] * m[11] + - m[0] * m[7] * m[9] + - m[4] * m[1] * m[11] - - m[4] * m[3] * m[9] - - m[8] * m[1] * m[7] + - m[8] * m[3] * m[5]; - - inv[15] = m[0] * m[5] * m[10] - - m[0] * m[6] * m[9] - - m[4] * m[1] * m[10] + - m[4] * m[2] * m[9] + - m[8] * m[1] * m[6] - - m[8] * m[2] * m[5]; - - float32 det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12]; - - if (det != 0.0f) - { - det = 1.0f / det; - } - - for (u32 i = 0; i < 16; ++i) - { - out[i] = det * inv[i]; - } -} - -static B3_FORCE_INLINE void b3Lame(float32& lambda, float32& mu, float32 E, float32 nu) -{ - lambda = (nu * E) / ((1 + nu) * (1 - 2 * nu)); - mu = E / (2 * (1 + nu)); -} - -static B3_FORCE_INLINE void b3CreateE(float32 out[36], float32 lambda, float32 mu) -{ - float32 E[36] = + float32 D[36] = { lambda + 2 * mu, lambda, lambda, 0, 0, 0, lambda, lambda + 2 * mu, lambda, 0, 0, 0, @@ -208,340 +78,58 @@ static B3_FORCE_INLINE void b3CreateE(float32 out[36], float32 lambda, float32 m for (u32 i = 0; i < 36; ++i) { - out[i] = E[i]; + out[i] = D[i]; } } -static B3_FORCE_INLINE void b3CreateB(float32 out[72], float32 invP[16]) +// Compute derivatives of shape functions N +static B3_FORCE_INLINE void b3ComputeBVec(b3Vec3 out[4], + const b3Vec3& e10, + const b3Vec3& e20, + const b3Vec3& e30, + float32 V) { - float32 a11 = invP[0]; - float32 a21 = invP[1]; - float32 a31 = invP[2]; - float32 a41 = invP[3]; + float32 inv6V = 1.0f / (6.0f * V); - float32 a12 = invP[4]; - float32 a22 = invP[5]; - float32 a32 = invP[6]; - float32 a42 = invP[7]; + b3Vec3* B = out; - float32 a13 = invP[8]; - float32 a23 = invP[9]; - float32 a33 = invP[10]; - float32 a43 = invP[11]; + B[1][0] = (e20[2] * e30[1] - e20[1] * e30[2]) * inv6V; + B[2][0] = (e10[1] * e30[2] - e10[2] * e30[1]) * inv6V; + B[3][0] = (e10[2] * e20[1] - e10[1] * e20[2]) * inv6V; + B[0][0] = -B[1][0] - B[2][0] - B[3][0]; - //float32 a14 = invP[12]; - //float32 a24 = invP[13]; - //float32 a34 = invP[14]; - //float32 a44 = invP[15]; + B[1][1] = (e20[0] * e30[2] - e20[2] * e30[0]) * inv6V; + B[2][1] = (e10[2] * e30[0] - e10[0] * e30[2]) * inv6V; + B[3][1] = (e10[0] * e20[2] - e10[2] * e20[0]) * inv6V; + B[0][1] = -B[1][1] - B[2][1] - B[3][1]; - // 6 x 12 - // a11 0 0 a21 0 0 a31 0 0 a41 0 0 - // 0 a12 0 0 a22 0 0 a32 0 0 a42 0 - // 0 0 a13 0 0 a23 0 0 a33 0 0 a43 - // a12 a11 0 a22 a21 0 a32 a31 0 a42 a41 0 - // 0 a13 a12 0 a23 a22 0 a33 a32 0 a43 a42 - // a13 0 a11 a23 0 a21 a33 0 a31 a43 0 a41 - float32 B[72] = + B[1][2] = (e20[1] * e30[0] - e20[0] * e30[1]) * inv6V; + B[2][2] = (e10[0] * e30[1] - e10[1] * e30[0]) * inv6V; + B[3][2] = (e10[1] * e20[0] - e10[0] * e20[1]) * inv6V; + B[0][2] = -B[1][2] - B[2][2] - B[3][2]; +} + +// Convert a B vector to its corresponding matrix form. +// This is a 6 x 3 matrix. +static B3_FORCE_INLINE void b3ComputeBMat(float32 out[18], const b3Vec3& v) +{ + float32 bi = v.x; + float32 ci = v.y; + float32 di = v.z; + + float32 B[18] = { - a11, 0, 0, a12, 0, a13, - 0, a12, 0, a11, a13, 0, - 0, 0, a13, 0, a12, a11, - a21, 0, 0, a22, 0, a23, - 0, a22, 0, a21, a23, 0, - 0, 0, a23, 0, a22, a21, - a31, 0, 0, a32, 0, a33, - 0, a32, 0, a31, a33, 0, - 0, 0, a33, 0, a32, a31, - a41, 0, 0, a42, 0, a43, - 0, a42, 0, a41, a43, 0, - 0, 0, a43, 0, a42, a41 + bi, 0, 0, ci, di, 0, + 0, ci, 0, bi, 0, di, + 0, 0, di, 0, bi, ci }; - for (u32 i = 0; i < 72; ++i) + for (u32 i = 0; i < 18; ++i) { out[i] = B[i]; } } -static B3_FORCE_INLINE void b3SetK(b3Mat33 K[16], float32 Ke[144]) -{ - b3Mat33& k11 = K[0 + 4 * 0]; - b3Mat33& k12 = K[0 + 4 * 1]; - b3Mat33& k13 = K[0 + 4 * 2]; - b3Mat33& k14 = K[0 + 4 * 3]; - - b3Mat33& k21 = K[1 + 4 * 0]; - b3Mat33& k22 = K[1 + 4 * 1]; - b3Mat33& k23 = K[1 + 4 * 2]; - b3Mat33& k24 = K[1 + 4 * 3]; - - b3Mat33& k31 = K[2 + 4 * 0]; - b3Mat33& k32 = K[2 + 4 * 1]; - b3Mat33& k33 = K[2 + 4 * 2]; - b3Mat33& k34 = K[2 + 4 * 3]; - - b3Mat33& k41 = K[3 + 4 * 0]; - b3Mat33& k42 = K[3 + 4 * 1]; - b3Mat33& k43 = K[3 + 4 * 2]; - b3Mat33& k44 = K[3 + 4 * 3]; - - // k11 - // a11 a12 a13 - // a21 a22 a23 - // a31 a32 a33 - k11.x.x = Ke[0 + 12 * 0]; - k11.x.y = Ke[1 + 12 * 0]; - k11.x.z = Ke[2 + 12 * 0]; - - k11.y.x = Ke[0 + 12 * 1]; - k11.y.y = Ke[1 + 12 * 1]; - k11.y.z = Ke[2 + 12 * 1]; - - k11.z.x = Ke[0 + 12 * 2]; - k11.z.y = Ke[1 + 12 * 2]; - k11.z.z = Ke[2 + 12 * 2]; - - // k12 - // a14 a15 a16 - // a24 a25 a26 - // a34 a35 a36 - k12.x.x = Ke[0 + 12 * 3]; - k12.x.y = Ke[1 + 12 * 3]; - k12.x.z = Ke[2 + 12 * 3]; - - k12.y.x = Ke[0 + 12 * 4]; - k12.y.y = Ke[1 + 12 * 4]; - k12.y.z = Ke[2 + 12 * 4]; - - k12.z.x = Ke[0 + 12 * 5]; - k12.z.y = Ke[1 + 12 * 5]; - k12.z.z = Ke[2 + 12 * 5]; - - // k13 - // a17 a18 a19 - // a27 a28 a29 - // a37 a38 a39 - k13.x.x = Ke[0 + 12 * 6]; - k13.x.y = Ke[1 + 12 * 6]; - k13.x.z = Ke[2 + 12 * 6]; - - k13.y.x = Ke[0 + 12 * 7]; - k13.y.y = Ke[1 + 12 * 7]; - k13.y.z = Ke[2 + 12 * 7]; - - k13.z.x = Ke[0 + 12 * 8]; - k13.z.y = Ke[1 + 12 * 8]; - k13.z.z = Ke[2 + 12 * 8]; - - // k14 - // a1_10 a1_11 a1_12 - // a2_10 a2_11 a2_12 - // a3_10 a3_11 a3_12 - k14.x.x = Ke[0 + 12 * 9]; - k14.x.y = Ke[1 + 12 * 9]; - k14.x.z = Ke[2 + 12 * 9]; - - k14.y.x = Ke[0 + 12 * 10]; - k14.y.y = Ke[1 + 12 * 10]; - k14.y.z = Ke[2 + 12 * 10]; - - k14.z.x = Ke[0 + 12 * 11]; - k14.z.y = Ke[1 + 12 * 11]; - k14.z.z = Ke[2 + 12 * 11]; - - // k21 - // a41 a42 a43 - // a51 a52 a53 - // a61 a62 a63 - k21.x.x = Ke[3 + 12 * 0]; - k21.x.y = Ke[4 + 12 * 0]; - k21.x.z = Ke[5 + 12 * 0]; - - k21.y.x = Ke[3 + 12 * 1]; - k21.y.y = Ke[4 + 12 * 1]; - k21.y.z = Ke[5 + 12 * 1]; - - k21.z.x = Ke[3 + 12 * 2]; - k21.z.y = Ke[4 + 12 * 2]; - k21.z.z = Ke[5 + 12 * 2]; - - // k22 - // a44 a45 a46 - // a54 a55 a56 - // a64 a65 a66 - k22.x.x = Ke[3 + 12 * 3]; - k22.x.y = Ke[4 + 12 * 3]; - k22.x.z = Ke[5 + 12 * 3]; - - k22.y.x = Ke[3 + 12 * 4]; - k22.y.y = Ke[4 + 12 * 4]; - k22.y.z = Ke[5 + 12 * 4]; - - k22.z.x = Ke[3 + 12 * 5]; - k22.z.y = Ke[4 + 12 * 5]; - k22.z.z = Ke[5 + 12 * 5]; - - // k23 - // a47 a48 a49 - // a57 a58 a59 - // a67 a68 a69 - k23.x.x = Ke[3 + 12 * 6]; - k23.x.y = Ke[4 + 12 * 6]; - k23.x.z = Ke[5 + 12 * 6]; - - k23.y.x = Ke[3 + 12 * 7]; - k23.y.y = Ke[4 + 12 * 7]; - k23.y.z = Ke[5 + 12 * 7]; - - k23.z.x = Ke[3 + 12 * 8]; - k23.z.y = Ke[4 + 12 * 8]; - k23.z.z = Ke[5 + 12 * 8]; - - // k24 - // a4_10 a4_11 a4_12 - // a5_10 a5_11 a5_12 - // a6_10 a6_11 a6_12 - k24.x.x = Ke[3 + 12 * 9]; - k24.x.y = Ke[4 + 12 * 9]; - k24.x.z = Ke[5 + 12 * 9]; - - k24.y.x = Ke[3 + 12 * 10]; - k24.y.y = Ke[4 + 12 * 10]; - k24.y.z = Ke[5 + 12 * 10]; - - k24.z.x = Ke[3 + 12 * 11]; - k24.z.y = Ke[4 + 12 * 11]; - k24.z.z = Ke[5 + 12 * 11]; - - // k31 - // a71 a72 a73 - // a81 a82 a83 - // a91 a92 a93 - k31.x.x = Ke[6 + 12 * 0]; - k31.x.y = Ke[7 + 12 * 0]; - k31.x.z = Ke[8 + 12 * 0]; - - k31.y.x = Ke[6 + 12 * 1]; - k31.y.y = Ke[7 + 12 * 1]; - k31.y.z = Ke[8 + 12 * 1]; - - k31.z.x = Ke[6 + 12 * 2]; - k31.z.y = Ke[7 + 12 * 2]; - k31.z.z = Ke[8 + 12 * 2]; - - // k32 - // a74 a75 a76 - // a84 a85 a86 - // a94 a95 a96 - k32.x.x = Ke[6 + 12 * 3]; - k32.x.y = Ke[7 + 12 * 3]; - k32.x.z = Ke[8 + 12 * 3]; - - k32.y.x = Ke[6 + 12 * 4]; - k32.y.y = Ke[7 + 12 * 4]; - k32.y.z = Ke[8 + 12 * 4]; - - k32.z.x = Ke[6 + 12 * 5]; - k32.z.y = Ke[7 + 12 * 5]; - k32.z.z = Ke[8 + 12 * 5]; - - // k33 - // a77 a78 a79 - // a87 a88 a89 - // a97 a98 a99 - k33.x.x = Ke[6 + 12 * 6]; - k33.x.y = Ke[7 + 12 * 6]; - k33.x.z = Ke[8 + 12 * 6]; - - k33.y.x = Ke[6 + 12 * 7]; - k33.y.y = Ke[7 + 12 * 7]; - k33.y.z = Ke[8 + 12 * 7]; - - k33.z.x = Ke[6 + 12 * 8]; - k33.z.y = Ke[7 + 12 * 8]; - k33.z.z = Ke[8 + 12 * 8]; - - // k34 - // a7_10 a7_11 a7_12 - // a8_10 a8_11 a8_12 - // a9_10 a9_11 a9_12 - k34.x.x = Ke[6 + 12 * 9]; - k34.x.y = Ke[7 + 12 * 9]; - k34.x.z = Ke[8 + 12 * 9]; - - k34.y.x = Ke[6 + 12 * 10]; - k34.y.y = Ke[7 + 12 * 10]; - k34.y.z = Ke[8 + 12 * 10]; - - k34.z.x = Ke[6 + 12 * 11]; - k34.z.y = Ke[7 + 12 * 11]; - k34.z.z = Ke[8 + 12 * 11]; - - // k41 - // a10_1 a10_2 a10_3 - // a11_1 a11_2 a11_3 - // a12_1 a12_2 a12_3 - k41.x.x = Ke[9 + 12 * 0]; - k41.x.y = Ke[10 + 12 * 0]; - k41.x.z = Ke[11 + 12 * 0]; - - k41.y.x = Ke[9 + 12 * 1]; - k41.y.y = Ke[10 + 12 * 1]; - k41.y.z = Ke[11 + 12 * 1]; - - k41.z.x = Ke[9 + 12 * 2]; - k41.z.y = Ke[10 + 12 * 2]; - k41.z.z = Ke[11 + 12 * 2]; - - // k42 - // a10_4 a10_5 a10_6 - // a11_4 a11_5 a11_6 - // a12_4 a12_5 a12_6 - k42.x.x = Ke[9 + 12 * 3]; - k42.x.y = Ke[10 + 12 * 3]; - k42.x.z = Ke[11 + 12 * 3]; - - k42.y.x = Ke[9 + 12 * 4]; - k42.y.y = Ke[10 + 12 * 4]; - k42.y.z = Ke[11 + 12 * 4]; - - k42.z.x = Ke[9 + 12 * 5]; - k42.z.y = Ke[10 + 12 * 5]; - k42.z.z = Ke[11 + 12 * 5]; - - // k43 - // a10_7 a10_8 a10_9 - // a11_7 a11_8 a11_9 - // a12_7 a12_8 a12_9 - k43.x.x = Ke[9 + 12 * 6]; - k43.x.y = Ke[10 + 12 * 6]; - k43.x.z = Ke[11 + 12 * 6]; - - k43.y.x = Ke[9 + 12 * 7]; - k43.y.y = Ke[10 + 12 * 7]; - k43.y.z = Ke[11 + 12 * 7]; - - k43.z.x = Ke[9 + 12 * 8]; - k43.z.y = Ke[10 + 12 * 8]; - k43.z.z = Ke[11 + 12 * 8]; - - // k44 - // a10_10 a10_11 a10_12 - // a11_10 a11_11 a11_12 - // a12_10 a12_11 a12_12 - k44.x.x = Ke[9 + 12 * 9]; - k44.x.y = Ke[10 + 12 * 9]; - k44.x.z = Ke[11 + 12 * 9]; - - k44.y.x = Ke[9 + 12 * 10]; - k44.y.y = Ke[10 + 12 * 10]; - k44.y.z = Ke[11 + 12 * 10]; - - k44.z.x = Ke[9 + 12 * 11]; - k44.z.y = Ke[10 + 12 * 11]; - k44.z.z = Ke[11 + 12 * 11]; -} - b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) { B3_ASSERT(def.mesh); @@ -597,47 +185,52 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) b3Vec3 p3 = m->vertices[v3]; b3Vec3 p4 = m->vertices[v4]; - float32 lambda, mu; - b3Lame(lambda, mu, m_E, m_nu); - - // 6 x 6 - float32 E[36]; - b3CreateE(E, lambda, mu); - - // 4 x 4 - float32 P[16] = - { - p1.x, p1.y, p1.z, 1.0f, - p2.x, p2.y, p2.z, 1.0f, - p3.x, p3.y, p3.z, 1.0f, - p4.x, p4.y, p4.z, 1.0f - }; - - b3Inverse4(e->invP, P); - - // 6 x 12 - float32 B[72]; - b3CreateB(B, e->invP); - - // 6 x 12 - float32 EB[72]; - b3Mul(EB, E, 6, 6, B, 6, 12); - - // 12 x 6 - float32 BT[72]; - b3Transpose(BT, B, 6, 12); - float32 V = b3Volume(p1, p2, p3, p4); - // 12 x 12 - float32 Ke[144]; - b3Mul(Ke, BT, 12, 6, EB, 6, 12); - for (u32 i = 0; i < 144; ++i) - { - Ke[i] *= V; - } + B3_ASSERT(V > 0.0f); - b3SetK(e->K, Ke); + b3Vec3 e10 = p2 - p1; + b3Vec3 e20 = p3 - p1; + b3Vec3 e30 = p4 - p1; + + b3Mat33 E(e10, e20, e30); + + e->invE = b3Inverse(E); + + // 6 x 6 + float32 D[36]; + b3ComputeD(D, m_E, m_nu); + + b3Vec3 B[4]; + b3ComputeBVec(B, e10, e20, e30, V); + + for (u32 i = 0; i < 4; ++i) + { + // 6 x 3 + float32 B_i[18]; + b3ComputeBMat(B_i, B[i]); + + // 3 x 6 + float32 B_i_T[18]; + b3Transpose(B_i_T, B_i, 6, 3); + + for (u32 j = 0; j < 4; ++j) + { + // 6 x 3 + float32 B_j[18]; + b3ComputeBMat(B_j, B[j]); + + // 6 x 3 + float32 D_B_j[18]; + b3Mul(D_B_j, D, 6, 6, B_j, 6, 3); + + // 3 x 3 + b3Mat33& Ke = e->K[i + 4 * j]; + b3Mul(&Ke.x.x, B_i_T, 3, 6, D_B_j, 6, 3); + + Ke = V * Ke; + } + } } // Initialize triangles diff --git a/src/bounce/softbody/softbody_solver.cpp b/src/bounce/softbody/softbody_solver.cpp index cb6ca56..188844f 100644 --- a/src/bounce/softbody/softbody_solver.cpp +++ b/src/bounce/softbody/softbody_solver.cpp @@ -236,25 +236,13 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter b3Vec3 p3 = p[v3]; b3Vec3 p4 = p[v4]; - float32 P[16] = - { - p1.x, p1.y, p1.z, 1.0f, - p2.x, p2.y, p2.z, 1.0f, - p3.x, p3.y, p3.z, 1.0f, - p4.x, p4.y, p4.z, 1.0f - }; + b3Vec3 e1 = p2 - p1; + b3Vec3 e2 = p3 - p1; + b3Vec3 e3 = p4 - p1; - float32 P_invP[16]; - b3Mul(P_invP, P, 4, 4, e->invP, 4, 4); + b3Mat33 E(e1, e2, e3); - b3Mat33 A; - for (u32 i = 0; i < 3; ++i) - { - for (u32 j = 0; j < 3; ++j) - { - A(i, j) = P_invP[i + 4 * j]; - } - } + b3Mat33 A = E * e->invE; b3Mat33 R; b3ExtractRotation(R, e->q, A); From 56b5675dcdd48ea15e36b6561d52484166ba6982 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 21 May 2019 20:46:00 -0300 Subject: [PATCH 103/198] Revert "Simplify soft body initialization and solver" This reverts commit e28fd2e07f10bddebf4ba31fc2b75c6bbe7d5d5c. --- include/bounce/softbody/softbody.h | 2 +- src/bounce/softbody/softbody.cpp | 563 ++++++++++++++++++++---- src/bounce/softbody/softbody_solver.cpp | 22 +- 3 files changed, 503 insertions(+), 84 deletions(-) diff --git a/include/bounce/softbody/softbody.h b/include/bounce/softbody/softbody.h index a9b6d0d..e837ed9 100644 --- a/include/bounce/softbody/softbody.h +++ b/include/bounce/softbody/softbody.h @@ -42,8 +42,8 @@ struct b3SoftBodyRayCastSingleOutput // Soft body tetrahedron element struct b3SoftBodyElement { + float32 invP[16]; b3Mat33 K[16]; - b3Mat33 invE; b3Quat q; }; diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index d3395d1..e98bff7 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -59,14 +59,144 @@ static B3_FORCE_INLINE void b3Transpose(float32* B, float32* A, u32 AM, u32 AN) } } -// Compute the elasticity matrix given Young modulus and Poisson's ratio -// This is a 6 x 6 matrix -static B3_FORCE_INLINE void b3ComputeD(float32 out[36], float32 E, float32 nu) +static B3_FORCE_INLINE void b3Inverse4(float32 out[16], float32 m[16]) { - float32 lambda = (nu * E) / ((1 + nu) * (1 - 2 * nu)); - float32 mu = E / (2 * (1 + nu)); + float32 inv[16]; - float32 D[36] = + inv[0] = m[5] * m[10] * m[15] - + m[5] * m[11] * m[14] - + m[9] * m[6] * m[15] + + m[9] * m[7] * m[14] + + m[13] * m[6] * m[11] - + m[13] * m[7] * m[10]; + + inv[4] = -m[4] * m[10] * m[15] + + m[4] * m[11] * m[14] + + m[8] * m[6] * m[15] - + m[8] * m[7] * m[14] - + m[12] * m[6] * m[11] + + m[12] * m[7] * m[10]; + + inv[8] = m[4] * m[9] * m[15] - + m[4] * m[11] * m[13] - + m[8] * m[5] * m[15] + + m[8] * m[7] * m[13] + + m[12] * m[5] * m[11] - + m[12] * m[7] * m[9]; + + inv[12] = -m[4] * m[9] * m[14] + + m[4] * m[10] * m[13] + + m[8] * m[5] * m[14] - + m[8] * m[6] * m[13] - + m[12] * m[5] * m[10] + + m[12] * m[6] * m[9]; + + inv[1] = -m[1] * m[10] * m[15] + + m[1] * m[11] * m[14] + + m[9] * m[2] * m[15] - + m[9] * m[3] * m[14] - + m[13] * m[2] * m[11] + + m[13] * m[3] * m[10]; + + inv[5] = m[0] * m[10] * m[15] - + m[0] * m[11] * m[14] - + m[8] * m[2] * m[15] + + m[8] * m[3] * m[14] + + m[12] * m[2] * m[11] - + m[12] * m[3] * m[10]; + + inv[9] = -m[0] * m[9] * m[15] + + m[0] * m[11] * m[13] + + m[8] * m[1] * m[15] - + m[8] * m[3] * m[13] - + m[12] * m[1] * m[11] + + m[12] * m[3] * m[9]; + + inv[13] = m[0] * m[9] * m[14] - + m[0] * m[10] * m[13] - + m[8] * m[1] * m[14] + + m[8] * m[2] * m[13] + + m[12] * m[1] * m[10] - + m[12] * m[2] * m[9]; + + inv[2] = m[1] * m[6] * m[15] - + m[1] * m[7] * m[14] - + m[5] * m[2] * m[15] + + m[5] * m[3] * m[14] + + m[13] * m[2] * m[7] - + m[13] * m[3] * m[6]; + + inv[6] = -m[0] * m[6] * m[15] + + m[0] * m[7] * m[14] + + m[4] * m[2] * m[15] - + m[4] * m[3] * m[14] - + m[12] * m[2] * m[7] + + m[12] * m[3] * m[6]; + + inv[10] = m[0] * m[5] * m[15] - + m[0] * m[7] * m[13] - + m[4] * m[1] * m[15] + + m[4] * m[3] * m[13] + + m[12] * m[1] * m[7] - + m[12] * m[3] * m[5]; + + inv[14] = -m[0] * m[5] * m[14] + + m[0] * m[6] * m[13] + + m[4] * m[1] * m[14] - + m[4] * m[2] * m[13] - + m[12] * m[1] * m[6] + + m[12] * m[2] * m[5]; + + inv[3] = -m[1] * m[6] * m[11] + + m[1] * m[7] * m[10] + + m[5] * m[2] * m[11] - + m[5] * m[3] * m[10] - + m[9] * m[2] * m[7] + + m[9] * m[3] * m[6]; + + inv[7] = m[0] * m[6] * m[11] - + m[0] * m[7] * m[10] - + m[4] * m[2] * m[11] + + m[4] * m[3] * m[10] + + m[8] * m[2] * m[7] - + m[8] * m[3] * m[6]; + + inv[11] = -m[0] * m[5] * m[11] + + m[0] * m[7] * m[9] + + m[4] * m[1] * m[11] - + m[4] * m[3] * m[9] - + m[8] * m[1] * m[7] + + m[8] * m[3] * m[5]; + + inv[15] = m[0] * m[5] * m[10] - + m[0] * m[6] * m[9] - + m[4] * m[1] * m[10] + + m[4] * m[2] * m[9] + + m[8] * m[1] * m[6] - + m[8] * m[2] * m[5]; + + float32 det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12]; + + if (det != 0.0f) + { + det = 1.0f / det; + } + + for (u32 i = 0; i < 16; ++i) + { + out[i] = det * inv[i]; + } +} + +static B3_FORCE_INLINE void b3Lame(float32& lambda, float32& mu, float32 E, float32 nu) +{ + lambda = (nu * E) / ((1 + nu) * (1 - 2 * nu)); + mu = E / (2 * (1 + nu)); +} + +static B3_FORCE_INLINE void b3CreateE(float32 out[36], float32 lambda, float32 mu) +{ + float32 E[36] = { lambda + 2 * mu, lambda, lambda, 0, 0, 0, lambda, lambda + 2 * mu, lambda, 0, 0, 0, @@ -78,58 +208,340 @@ static B3_FORCE_INLINE void b3ComputeD(float32 out[36], float32 E, float32 nu) for (u32 i = 0; i < 36; ++i) { - out[i] = D[i]; + out[i] = E[i]; } } -// Compute derivatives of shape functions N -static B3_FORCE_INLINE void b3ComputeBVec(b3Vec3 out[4], - const b3Vec3& e10, - const b3Vec3& e20, - const b3Vec3& e30, - float32 V) +static B3_FORCE_INLINE void b3CreateB(float32 out[72], float32 invP[16]) { - float32 inv6V = 1.0f / (6.0f * V); + float32 a11 = invP[0]; + float32 a21 = invP[1]; + float32 a31 = invP[2]; + float32 a41 = invP[3]; - b3Vec3* B = out; + float32 a12 = invP[4]; + float32 a22 = invP[5]; + float32 a32 = invP[6]; + float32 a42 = invP[7]; - B[1][0] = (e20[2] * e30[1] - e20[1] * e30[2]) * inv6V; - B[2][0] = (e10[1] * e30[2] - e10[2] * e30[1]) * inv6V; - B[3][0] = (e10[2] * e20[1] - e10[1] * e20[2]) * inv6V; - B[0][0] = -B[1][0] - B[2][0] - B[3][0]; + float32 a13 = invP[8]; + float32 a23 = invP[9]; + float32 a33 = invP[10]; + float32 a43 = invP[11]; - B[1][1] = (e20[0] * e30[2] - e20[2] * e30[0]) * inv6V; - B[2][1] = (e10[2] * e30[0] - e10[0] * e30[2]) * inv6V; - B[3][1] = (e10[0] * e20[2] - e10[2] * e20[0]) * inv6V; - B[0][1] = -B[1][1] - B[2][1] - B[3][1]; + //float32 a14 = invP[12]; + //float32 a24 = invP[13]; + //float32 a34 = invP[14]; + //float32 a44 = invP[15]; - B[1][2] = (e20[1] * e30[0] - e20[0] * e30[1]) * inv6V; - B[2][2] = (e10[0] * e30[1] - e10[1] * e30[0]) * inv6V; - B[3][2] = (e10[1] * e20[0] - e10[0] * e20[1]) * inv6V; - B[0][2] = -B[1][2] - B[2][2] - B[3][2]; -} - -// Convert a B vector to its corresponding matrix form. -// This is a 6 x 3 matrix. -static B3_FORCE_INLINE void b3ComputeBMat(float32 out[18], const b3Vec3& v) -{ - float32 bi = v.x; - float32 ci = v.y; - float32 di = v.z; - - float32 B[18] = + // 6 x 12 + // a11 0 0 a21 0 0 a31 0 0 a41 0 0 + // 0 a12 0 0 a22 0 0 a32 0 0 a42 0 + // 0 0 a13 0 0 a23 0 0 a33 0 0 a43 + // a12 a11 0 a22 a21 0 a32 a31 0 a42 a41 0 + // 0 a13 a12 0 a23 a22 0 a33 a32 0 a43 a42 + // a13 0 a11 a23 0 a21 a33 0 a31 a43 0 a41 + float32 B[72] = { - bi, 0, 0, ci, di, 0, - 0, ci, 0, bi, 0, di, - 0, 0, di, 0, bi, ci + a11, 0, 0, a12, 0, a13, + 0, a12, 0, a11, a13, 0, + 0, 0, a13, 0, a12, a11, + a21, 0, 0, a22, 0, a23, + 0, a22, 0, a21, a23, 0, + 0, 0, a23, 0, a22, a21, + a31, 0, 0, a32, 0, a33, + 0, a32, 0, a31, a33, 0, + 0, 0, a33, 0, a32, a31, + a41, 0, 0, a42, 0, a43, + 0, a42, 0, a41, a43, 0, + 0, 0, a43, 0, a42, a41 }; - for (u32 i = 0; i < 18; ++i) + for (u32 i = 0; i < 72; ++i) { out[i] = B[i]; } } +static B3_FORCE_INLINE void b3SetK(b3Mat33 K[16], float32 Ke[144]) +{ + b3Mat33& k11 = K[0 + 4 * 0]; + b3Mat33& k12 = K[0 + 4 * 1]; + b3Mat33& k13 = K[0 + 4 * 2]; + b3Mat33& k14 = K[0 + 4 * 3]; + + b3Mat33& k21 = K[1 + 4 * 0]; + b3Mat33& k22 = K[1 + 4 * 1]; + b3Mat33& k23 = K[1 + 4 * 2]; + b3Mat33& k24 = K[1 + 4 * 3]; + + b3Mat33& k31 = K[2 + 4 * 0]; + b3Mat33& k32 = K[2 + 4 * 1]; + b3Mat33& k33 = K[2 + 4 * 2]; + b3Mat33& k34 = K[2 + 4 * 3]; + + b3Mat33& k41 = K[3 + 4 * 0]; + b3Mat33& k42 = K[3 + 4 * 1]; + b3Mat33& k43 = K[3 + 4 * 2]; + b3Mat33& k44 = K[3 + 4 * 3]; + + // k11 + // a11 a12 a13 + // a21 a22 a23 + // a31 a32 a33 + k11.x.x = Ke[0 + 12 * 0]; + k11.x.y = Ke[1 + 12 * 0]; + k11.x.z = Ke[2 + 12 * 0]; + + k11.y.x = Ke[0 + 12 * 1]; + k11.y.y = Ke[1 + 12 * 1]; + k11.y.z = Ke[2 + 12 * 1]; + + k11.z.x = Ke[0 + 12 * 2]; + k11.z.y = Ke[1 + 12 * 2]; + k11.z.z = Ke[2 + 12 * 2]; + + // k12 + // a14 a15 a16 + // a24 a25 a26 + // a34 a35 a36 + k12.x.x = Ke[0 + 12 * 3]; + k12.x.y = Ke[1 + 12 * 3]; + k12.x.z = Ke[2 + 12 * 3]; + + k12.y.x = Ke[0 + 12 * 4]; + k12.y.y = Ke[1 + 12 * 4]; + k12.y.z = Ke[2 + 12 * 4]; + + k12.z.x = Ke[0 + 12 * 5]; + k12.z.y = Ke[1 + 12 * 5]; + k12.z.z = Ke[2 + 12 * 5]; + + // k13 + // a17 a18 a19 + // a27 a28 a29 + // a37 a38 a39 + k13.x.x = Ke[0 + 12 * 6]; + k13.x.y = Ke[1 + 12 * 6]; + k13.x.z = Ke[2 + 12 * 6]; + + k13.y.x = Ke[0 + 12 * 7]; + k13.y.y = Ke[1 + 12 * 7]; + k13.y.z = Ke[2 + 12 * 7]; + + k13.z.x = Ke[0 + 12 * 8]; + k13.z.y = Ke[1 + 12 * 8]; + k13.z.z = Ke[2 + 12 * 8]; + + // k14 + // a1_10 a1_11 a1_12 + // a2_10 a2_11 a2_12 + // a3_10 a3_11 a3_12 + k14.x.x = Ke[0 + 12 * 9]; + k14.x.y = Ke[1 + 12 * 9]; + k14.x.z = Ke[2 + 12 * 9]; + + k14.y.x = Ke[0 + 12 * 10]; + k14.y.y = Ke[1 + 12 * 10]; + k14.y.z = Ke[2 + 12 * 10]; + + k14.z.x = Ke[0 + 12 * 11]; + k14.z.y = Ke[1 + 12 * 11]; + k14.z.z = Ke[2 + 12 * 11]; + + // k21 + // a41 a42 a43 + // a51 a52 a53 + // a61 a62 a63 + k21.x.x = Ke[3 + 12 * 0]; + k21.x.y = Ke[4 + 12 * 0]; + k21.x.z = Ke[5 + 12 * 0]; + + k21.y.x = Ke[3 + 12 * 1]; + k21.y.y = Ke[4 + 12 * 1]; + k21.y.z = Ke[5 + 12 * 1]; + + k21.z.x = Ke[3 + 12 * 2]; + k21.z.y = Ke[4 + 12 * 2]; + k21.z.z = Ke[5 + 12 * 2]; + + // k22 + // a44 a45 a46 + // a54 a55 a56 + // a64 a65 a66 + k22.x.x = Ke[3 + 12 * 3]; + k22.x.y = Ke[4 + 12 * 3]; + k22.x.z = Ke[5 + 12 * 3]; + + k22.y.x = Ke[3 + 12 * 4]; + k22.y.y = Ke[4 + 12 * 4]; + k22.y.z = Ke[5 + 12 * 4]; + + k22.z.x = Ke[3 + 12 * 5]; + k22.z.y = Ke[4 + 12 * 5]; + k22.z.z = Ke[5 + 12 * 5]; + + // k23 + // a47 a48 a49 + // a57 a58 a59 + // a67 a68 a69 + k23.x.x = Ke[3 + 12 * 6]; + k23.x.y = Ke[4 + 12 * 6]; + k23.x.z = Ke[5 + 12 * 6]; + + k23.y.x = Ke[3 + 12 * 7]; + k23.y.y = Ke[4 + 12 * 7]; + k23.y.z = Ke[5 + 12 * 7]; + + k23.z.x = Ke[3 + 12 * 8]; + k23.z.y = Ke[4 + 12 * 8]; + k23.z.z = Ke[5 + 12 * 8]; + + // k24 + // a4_10 a4_11 a4_12 + // a5_10 a5_11 a5_12 + // a6_10 a6_11 a6_12 + k24.x.x = Ke[3 + 12 * 9]; + k24.x.y = Ke[4 + 12 * 9]; + k24.x.z = Ke[5 + 12 * 9]; + + k24.y.x = Ke[3 + 12 * 10]; + k24.y.y = Ke[4 + 12 * 10]; + k24.y.z = Ke[5 + 12 * 10]; + + k24.z.x = Ke[3 + 12 * 11]; + k24.z.y = Ke[4 + 12 * 11]; + k24.z.z = Ke[5 + 12 * 11]; + + // k31 + // a71 a72 a73 + // a81 a82 a83 + // a91 a92 a93 + k31.x.x = Ke[6 + 12 * 0]; + k31.x.y = Ke[7 + 12 * 0]; + k31.x.z = Ke[8 + 12 * 0]; + + k31.y.x = Ke[6 + 12 * 1]; + k31.y.y = Ke[7 + 12 * 1]; + k31.y.z = Ke[8 + 12 * 1]; + + k31.z.x = Ke[6 + 12 * 2]; + k31.z.y = Ke[7 + 12 * 2]; + k31.z.z = Ke[8 + 12 * 2]; + + // k32 + // a74 a75 a76 + // a84 a85 a86 + // a94 a95 a96 + k32.x.x = Ke[6 + 12 * 3]; + k32.x.y = Ke[7 + 12 * 3]; + k32.x.z = Ke[8 + 12 * 3]; + + k32.y.x = Ke[6 + 12 * 4]; + k32.y.y = Ke[7 + 12 * 4]; + k32.y.z = Ke[8 + 12 * 4]; + + k32.z.x = Ke[6 + 12 * 5]; + k32.z.y = Ke[7 + 12 * 5]; + k32.z.z = Ke[8 + 12 * 5]; + + // k33 + // a77 a78 a79 + // a87 a88 a89 + // a97 a98 a99 + k33.x.x = Ke[6 + 12 * 6]; + k33.x.y = Ke[7 + 12 * 6]; + k33.x.z = Ke[8 + 12 * 6]; + + k33.y.x = Ke[6 + 12 * 7]; + k33.y.y = Ke[7 + 12 * 7]; + k33.y.z = Ke[8 + 12 * 7]; + + k33.z.x = Ke[6 + 12 * 8]; + k33.z.y = Ke[7 + 12 * 8]; + k33.z.z = Ke[8 + 12 * 8]; + + // k34 + // a7_10 a7_11 a7_12 + // a8_10 a8_11 a8_12 + // a9_10 a9_11 a9_12 + k34.x.x = Ke[6 + 12 * 9]; + k34.x.y = Ke[7 + 12 * 9]; + k34.x.z = Ke[8 + 12 * 9]; + + k34.y.x = Ke[6 + 12 * 10]; + k34.y.y = Ke[7 + 12 * 10]; + k34.y.z = Ke[8 + 12 * 10]; + + k34.z.x = Ke[6 + 12 * 11]; + k34.z.y = Ke[7 + 12 * 11]; + k34.z.z = Ke[8 + 12 * 11]; + + // k41 + // a10_1 a10_2 a10_3 + // a11_1 a11_2 a11_3 + // a12_1 a12_2 a12_3 + k41.x.x = Ke[9 + 12 * 0]; + k41.x.y = Ke[10 + 12 * 0]; + k41.x.z = Ke[11 + 12 * 0]; + + k41.y.x = Ke[9 + 12 * 1]; + k41.y.y = Ke[10 + 12 * 1]; + k41.y.z = Ke[11 + 12 * 1]; + + k41.z.x = Ke[9 + 12 * 2]; + k41.z.y = Ke[10 + 12 * 2]; + k41.z.z = Ke[11 + 12 * 2]; + + // k42 + // a10_4 a10_5 a10_6 + // a11_4 a11_5 a11_6 + // a12_4 a12_5 a12_6 + k42.x.x = Ke[9 + 12 * 3]; + k42.x.y = Ke[10 + 12 * 3]; + k42.x.z = Ke[11 + 12 * 3]; + + k42.y.x = Ke[9 + 12 * 4]; + k42.y.y = Ke[10 + 12 * 4]; + k42.y.z = Ke[11 + 12 * 4]; + + k42.z.x = Ke[9 + 12 * 5]; + k42.z.y = Ke[10 + 12 * 5]; + k42.z.z = Ke[11 + 12 * 5]; + + // k43 + // a10_7 a10_8 a10_9 + // a11_7 a11_8 a11_9 + // a12_7 a12_8 a12_9 + k43.x.x = Ke[9 + 12 * 6]; + k43.x.y = Ke[10 + 12 * 6]; + k43.x.z = Ke[11 + 12 * 6]; + + k43.y.x = Ke[9 + 12 * 7]; + k43.y.y = Ke[10 + 12 * 7]; + k43.y.z = Ke[11 + 12 * 7]; + + k43.z.x = Ke[9 + 12 * 8]; + k43.z.y = Ke[10 + 12 * 8]; + k43.z.z = Ke[11 + 12 * 8]; + + // k44 + // a10_10 a10_11 a10_12 + // a11_10 a11_11 a11_12 + // a12_10 a12_11 a12_12 + k44.x.x = Ke[9 + 12 * 9]; + k44.x.y = Ke[10 + 12 * 9]; + k44.x.z = Ke[11 + 12 * 9]; + + k44.y.x = Ke[9 + 12 * 10]; + k44.y.y = Ke[10 + 12 * 10]; + k44.y.z = Ke[11 + 12 * 10]; + + k44.z.x = Ke[9 + 12 * 11]; + k44.z.y = Ke[10 + 12 * 11]; + k44.z.z = Ke[11 + 12 * 11]; +} + b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) { B3_ASSERT(def.mesh); @@ -185,52 +597,47 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) b3Vec3 p3 = m->vertices[v3]; b3Vec3 p4 = m->vertices[v4]; - float32 V = b3Volume(p1, p2, p3, p4); - - B3_ASSERT(V > 0.0f); - - b3Vec3 e10 = p2 - p1; - b3Vec3 e20 = p3 - p1; - b3Vec3 e30 = p4 - p1; - - b3Mat33 E(e10, e20, e30); - - e->invE = b3Inverse(E); + float32 lambda, mu; + b3Lame(lambda, mu, m_E, m_nu); // 6 x 6 - float32 D[36]; - b3ComputeD(D, m_E, m_nu); + float32 E[36]; + b3CreateE(E, lambda, mu); - b3Vec3 B[4]; - b3ComputeBVec(B, e10, e20, e30, V); - - for (u32 i = 0; i < 4; ++i) + // 4 x 4 + float32 P[16] = { - // 6 x 3 - float32 B_i[18]; - b3ComputeBMat(B_i, B[i]); + p1.x, p1.y, p1.z, 1.0f, + p2.x, p2.y, p2.z, 1.0f, + p3.x, p3.y, p3.z, 1.0f, + p4.x, p4.y, p4.z, 1.0f + }; - // 3 x 6 - float32 B_i_T[18]; - b3Transpose(B_i_T, B_i, 6, 3); + b3Inverse4(e->invP, P); - for (u32 j = 0; j < 4; ++j) - { - // 6 x 3 - float32 B_j[18]; - b3ComputeBMat(B_j, B[j]); + // 6 x 12 + float32 B[72]; + b3CreateB(B, e->invP); - // 6 x 3 - float32 D_B_j[18]; - b3Mul(D_B_j, D, 6, 6, B_j, 6, 3); + // 6 x 12 + float32 EB[72]; + b3Mul(EB, E, 6, 6, B, 6, 12); - // 3 x 3 - b3Mat33& Ke = e->K[i + 4 * j]; - b3Mul(&Ke.x.x, B_i_T, 3, 6, D_B_j, 6, 3); + // 12 x 6 + float32 BT[72]; + b3Transpose(BT, B, 6, 12); - Ke = V * Ke; - } + float32 V = b3Volume(p1, p2, p3, p4); + + // 12 x 12 + float32 Ke[144]; + b3Mul(Ke, BT, 12, 6, EB, 6, 12); + for (u32 i = 0; i < 144; ++i) + { + Ke[i] *= V; } + + b3SetK(e->K, Ke); } // Initialize triangles diff --git a/src/bounce/softbody/softbody_solver.cpp b/src/bounce/softbody/softbody_solver.cpp index 188844f..cb6ca56 100644 --- a/src/bounce/softbody/softbody_solver.cpp +++ b/src/bounce/softbody/softbody_solver.cpp @@ -236,13 +236,25 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter b3Vec3 p3 = p[v3]; b3Vec3 p4 = p[v4]; - b3Vec3 e1 = p2 - p1; - b3Vec3 e2 = p3 - p1; - b3Vec3 e3 = p4 - p1; + float32 P[16] = + { + p1.x, p1.y, p1.z, 1.0f, + p2.x, p2.y, p2.z, 1.0f, + p3.x, p3.y, p3.z, 1.0f, + p4.x, p4.y, p4.z, 1.0f + }; - b3Mat33 E(e1, e2, e3); + float32 P_invP[16]; + b3Mul(P_invP, P, 4, 4, e->invP, 4, 4); - b3Mat33 A = E * e->invE; + b3Mat33 A; + for (u32 i = 0; i < 3; ++i) + { + for (u32 j = 0; j < 3; ++j) + { + A(i, j) = P_invP[i + 4 * j]; + } + } b3Mat33 R; b3ExtractRotation(R, e->q, A); From 9a14c1903c95d22835e48dac1f90125034e946f4 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 21 May 2019 20:48:41 -0300 Subject: [PATCH 104/198] Revert "Revert "Simplify soft body initialization and solver"" This reverts commit 56b5675dcdd48ea15e36b6561d52484166ba6982. --- include/bounce/softbody/softbody.h | 2 +- src/bounce/softbody/softbody.cpp | 577 ++++-------------------- src/bounce/softbody/softbody_solver.cpp | 22 +- 3 files changed, 91 insertions(+), 510 deletions(-) diff --git a/include/bounce/softbody/softbody.h b/include/bounce/softbody/softbody.h index e837ed9..a9b6d0d 100644 --- a/include/bounce/softbody/softbody.h +++ b/include/bounce/softbody/softbody.h @@ -42,8 +42,8 @@ struct b3SoftBodyRayCastSingleOutput // Soft body tetrahedron element struct b3SoftBodyElement { - float32 invP[16]; b3Mat33 K[16]; + b3Mat33 invE; b3Quat q; }; diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index e98bff7..d3395d1 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -59,144 +59,14 @@ static B3_FORCE_INLINE void b3Transpose(float32* B, float32* A, u32 AM, u32 AN) } } -static B3_FORCE_INLINE void b3Inverse4(float32 out[16], float32 m[16]) +// Compute the elasticity matrix given Young modulus and Poisson's ratio +// This is a 6 x 6 matrix +static B3_FORCE_INLINE void b3ComputeD(float32 out[36], float32 E, float32 nu) { - float32 inv[16]; + float32 lambda = (nu * E) / ((1 + nu) * (1 - 2 * nu)); + float32 mu = E / (2 * (1 + nu)); - inv[0] = m[5] * m[10] * m[15] - - m[5] * m[11] * m[14] - - m[9] * m[6] * m[15] + - m[9] * m[7] * m[14] + - m[13] * m[6] * m[11] - - m[13] * m[7] * m[10]; - - inv[4] = -m[4] * m[10] * m[15] + - m[4] * m[11] * m[14] + - m[8] * m[6] * m[15] - - m[8] * m[7] * m[14] - - m[12] * m[6] * m[11] + - m[12] * m[7] * m[10]; - - inv[8] = m[4] * m[9] * m[15] - - m[4] * m[11] * m[13] - - m[8] * m[5] * m[15] + - m[8] * m[7] * m[13] + - m[12] * m[5] * m[11] - - m[12] * m[7] * m[9]; - - inv[12] = -m[4] * m[9] * m[14] + - m[4] * m[10] * m[13] + - m[8] * m[5] * m[14] - - m[8] * m[6] * m[13] - - m[12] * m[5] * m[10] + - m[12] * m[6] * m[9]; - - inv[1] = -m[1] * m[10] * m[15] + - m[1] * m[11] * m[14] + - m[9] * m[2] * m[15] - - m[9] * m[3] * m[14] - - m[13] * m[2] * m[11] + - m[13] * m[3] * m[10]; - - inv[5] = m[0] * m[10] * m[15] - - m[0] * m[11] * m[14] - - m[8] * m[2] * m[15] + - m[8] * m[3] * m[14] + - m[12] * m[2] * m[11] - - m[12] * m[3] * m[10]; - - inv[9] = -m[0] * m[9] * m[15] + - m[0] * m[11] * m[13] + - m[8] * m[1] * m[15] - - m[8] * m[3] * m[13] - - m[12] * m[1] * m[11] + - m[12] * m[3] * m[9]; - - inv[13] = m[0] * m[9] * m[14] - - m[0] * m[10] * m[13] - - m[8] * m[1] * m[14] + - m[8] * m[2] * m[13] + - m[12] * m[1] * m[10] - - m[12] * m[2] * m[9]; - - inv[2] = m[1] * m[6] * m[15] - - m[1] * m[7] * m[14] - - m[5] * m[2] * m[15] + - m[5] * m[3] * m[14] + - m[13] * m[2] * m[7] - - m[13] * m[3] * m[6]; - - inv[6] = -m[0] * m[6] * m[15] + - m[0] * m[7] * m[14] + - m[4] * m[2] * m[15] - - m[4] * m[3] * m[14] - - m[12] * m[2] * m[7] + - m[12] * m[3] * m[6]; - - inv[10] = m[0] * m[5] * m[15] - - m[0] * m[7] * m[13] - - m[4] * m[1] * m[15] + - m[4] * m[3] * m[13] + - m[12] * m[1] * m[7] - - m[12] * m[3] * m[5]; - - inv[14] = -m[0] * m[5] * m[14] + - m[0] * m[6] * m[13] + - m[4] * m[1] * m[14] - - m[4] * m[2] * m[13] - - m[12] * m[1] * m[6] + - m[12] * m[2] * m[5]; - - inv[3] = -m[1] * m[6] * m[11] + - m[1] * m[7] * m[10] + - m[5] * m[2] * m[11] - - m[5] * m[3] * m[10] - - m[9] * m[2] * m[7] + - m[9] * m[3] * m[6]; - - inv[7] = m[0] * m[6] * m[11] - - m[0] * m[7] * m[10] - - m[4] * m[2] * m[11] + - m[4] * m[3] * m[10] + - m[8] * m[2] * m[7] - - m[8] * m[3] * m[6]; - - inv[11] = -m[0] * m[5] * m[11] + - m[0] * m[7] * m[9] + - m[4] * m[1] * m[11] - - m[4] * m[3] * m[9] - - m[8] * m[1] * m[7] + - m[8] * m[3] * m[5]; - - inv[15] = m[0] * m[5] * m[10] - - m[0] * m[6] * m[9] - - m[4] * m[1] * m[10] + - m[4] * m[2] * m[9] + - m[8] * m[1] * m[6] - - m[8] * m[2] * m[5]; - - float32 det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12]; - - if (det != 0.0f) - { - det = 1.0f / det; - } - - for (u32 i = 0; i < 16; ++i) - { - out[i] = det * inv[i]; - } -} - -static B3_FORCE_INLINE void b3Lame(float32& lambda, float32& mu, float32 E, float32 nu) -{ - lambda = (nu * E) / ((1 + nu) * (1 - 2 * nu)); - mu = E / (2 * (1 + nu)); -} - -static B3_FORCE_INLINE void b3CreateE(float32 out[36], float32 lambda, float32 mu) -{ - float32 E[36] = + float32 D[36] = { lambda + 2 * mu, lambda, lambda, 0, 0, 0, lambda, lambda + 2 * mu, lambda, 0, 0, 0, @@ -208,340 +78,58 @@ static B3_FORCE_INLINE void b3CreateE(float32 out[36], float32 lambda, float32 m for (u32 i = 0; i < 36; ++i) { - out[i] = E[i]; + out[i] = D[i]; } } -static B3_FORCE_INLINE void b3CreateB(float32 out[72], float32 invP[16]) +// Compute derivatives of shape functions N +static B3_FORCE_INLINE void b3ComputeBVec(b3Vec3 out[4], + const b3Vec3& e10, + const b3Vec3& e20, + const b3Vec3& e30, + float32 V) { - float32 a11 = invP[0]; - float32 a21 = invP[1]; - float32 a31 = invP[2]; - float32 a41 = invP[3]; + float32 inv6V = 1.0f / (6.0f * V); - float32 a12 = invP[4]; - float32 a22 = invP[5]; - float32 a32 = invP[6]; - float32 a42 = invP[7]; + b3Vec3* B = out; - float32 a13 = invP[8]; - float32 a23 = invP[9]; - float32 a33 = invP[10]; - float32 a43 = invP[11]; + B[1][0] = (e20[2] * e30[1] - e20[1] * e30[2]) * inv6V; + B[2][0] = (e10[1] * e30[2] - e10[2] * e30[1]) * inv6V; + B[3][0] = (e10[2] * e20[1] - e10[1] * e20[2]) * inv6V; + B[0][0] = -B[1][0] - B[2][0] - B[3][0]; - //float32 a14 = invP[12]; - //float32 a24 = invP[13]; - //float32 a34 = invP[14]; - //float32 a44 = invP[15]; + B[1][1] = (e20[0] * e30[2] - e20[2] * e30[0]) * inv6V; + B[2][1] = (e10[2] * e30[0] - e10[0] * e30[2]) * inv6V; + B[3][1] = (e10[0] * e20[2] - e10[2] * e20[0]) * inv6V; + B[0][1] = -B[1][1] - B[2][1] - B[3][1]; - // 6 x 12 - // a11 0 0 a21 0 0 a31 0 0 a41 0 0 - // 0 a12 0 0 a22 0 0 a32 0 0 a42 0 - // 0 0 a13 0 0 a23 0 0 a33 0 0 a43 - // a12 a11 0 a22 a21 0 a32 a31 0 a42 a41 0 - // 0 a13 a12 0 a23 a22 0 a33 a32 0 a43 a42 - // a13 0 a11 a23 0 a21 a33 0 a31 a43 0 a41 - float32 B[72] = + B[1][2] = (e20[1] * e30[0] - e20[0] * e30[1]) * inv6V; + B[2][2] = (e10[0] * e30[1] - e10[1] * e30[0]) * inv6V; + B[3][2] = (e10[1] * e20[0] - e10[0] * e20[1]) * inv6V; + B[0][2] = -B[1][2] - B[2][2] - B[3][2]; +} + +// Convert a B vector to its corresponding matrix form. +// This is a 6 x 3 matrix. +static B3_FORCE_INLINE void b3ComputeBMat(float32 out[18], const b3Vec3& v) +{ + float32 bi = v.x; + float32 ci = v.y; + float32 di = v.z; + + float32 B[18] = { - a11, 0, 0, a12, 0, a13, - 0, a12, 0, a11, a13, 0, - 0, 0, a13, 0, a12, a11, - a21, 0, 0, a22, 0, a23, - 0, a22, 0, a21, a23, 0, - 0, 0, a23, 0, a22, a21, - a31, 0, 0, a32, 0, a33, - 0, a32, 0, a31, a33, 0, - 0, 0, a33, 0, a32, a31, - a41, 0, 0, a42, 0, a43, - 0, a42, 0, a41, a43, 0, - 0, 0, a43, 0, a42, a41 + bi, 0, 0, ci, di, 0, + 0, ci, 0, bi, 0, di, + 0, 0, di, 0, bi, ci }; - for (u32 i = 0; i < 72; ++i) + for (u32 i = 0; i < 18; ++i) { out[i] = B[i]; } } -static B3_FORCE_INLINE void b3SetK(b3Mat33 K[16], float32 Ke[144]) -{ - b3Mat33& k11 = K[0 + 4 * 0]; - b3Mat33& k12 = K[0 + 4 * 1]; - b3Mat33& k13 = K[0 + 4 * 2]; - b3Mat33& k14 = K[0 + 4 * 3]; - - b3Mat33& k21 = K[1 + 4 * 0]; - b3Mat33& k22 = K[1 + 4 * 1]; - b3Mat33& k23 = K[1 + 4 * 2]; - b3Mat33& k24 = K[1 + 4 * 3]; - - b3Mat33& k31 = K[2 + 4 * 0]; - b3Mat33& k32 = K[2 + 4 * 1]; - b3Mat33& k33 = K[2 + 4 * 2]; - b3Mat33& k34 = K[2 + 4 * 3]; - - b3Mat33& k41 = K[3 + 4 * 0]; - b3Mat33& k42 = K[3 + 4 * 1]; - b3Mat33& k43 = K[3 + 4 * 2]; - b3Mat33& k44 = K[3 + 4 * 3]; - - // k11 - // a11 a12 a13 - // a21 a22 a23 - // a31 a32 a33 - k11.x.x = Ke[0 + 12 * 0]; - k11.x.y = Ke[1 + 12 * 0]; - k11.x.z = Ke[2 + 12 * 0]; - - k11.y.x = Ke[0 + 12 * 1]; - k11.y.y = Ke[1 + 12 * 1]; - k11.y.z = Ke[2 + 12 * 1]; - - k11.z.x = Ke[0 + 12 * 2]; - k11.z.y = Ke[1 + 12 * 2]; - k11.z.z = Ke[2 + 12 * 2]; - - // k12 - // a14 a15 a16 - // a24 a25 a26 - // a34 a35 a36 - k12.x.x = Ke[0 + 12 * 3]; - k12.x.y = Ke[1 + 12 * 3]; - k12.x.z = Ke[2 + 12 * 3]; - - k12.y.x = Ke[0 + 12 * 4]; - k12.y.y = Ke[1 + 12 * 4]; - k12.y.z = Ke[2 + 12 * 4]; - - k12.z.x = Ke[0 + 12 * 5]; - k12.z.y = Ke[1 + 12 * 5]; - k12.z.z = Ke[2 + 12 * 5]; - - // k13 - // a17 a18 a19 - // a27 a28 a29 - // a37 a38 a39 - k13.x.x = Ke[0 + 12 * 6]; - k13.x.y = Ke[1 + 12 * 6]; - k13.x.z = Ke[2 + 12 * 6]; - - k13.y.x = Ke[0 + 12 * 7]; - k13.y.y = Ke[1 + 12 * 7]; - k13.y.z = Ke[2 + 12 * 7]; - - k13.z.x = Ke[0 + 12 * 8]; - k13.z.y = Ke[1 + 12 * 8]; - k13.z.z = Ke[2 + 12 * 8]; - - // k14 - // a1_10 a1_11 a1_12 - // a2_10 a2_11 a2_12 - // a3_10 a3_11 a3_12 - k14.x.x = Ke[0 + 12 * 9]; - k14.x.y = Ke[1 + 12 * 9]; - k14.x.z = Ke[2 + 12 * 9]; - - k14.y.x = Ke[0 + 12 * 10]; - k14.y.y = Ke[1 + 12 * 10]; - k14.y.z = Ke[2 + 12 * 10]; - - k14.z.x = Ke[0 + 12 * 11]; - k14.z.y = Ke[1 + 12 * 11]; - k14.z.z = Ke[2 + 12 * 11]; - - // k21 - // a41 a42 a43 - // a51 a52 a53 - // a61 a62 a63 - k21.x.x = Ke[3 + 12 * 0]; - k21.x.y = Ke[4 + 12 * 0]; - k21.x.z = Ke[5 + 12 * 0]; - - k21.y.x = Ke[3 + 12 * 1]; - k21.y.y = Ke[4 + 12 * 1]; - k21.y.z = Ke[5 + 12 * 1]; - - k21.z.x = Ke[3 + 12 * 2]; - k21.z.y = Ke[4 + 12 * 2]; - k21.z.z = Ke[5 + 12 * 2]; - - // k22 - // a44 a45 a46 - // a54 a55 a56 - // a64 a65 a66 - k22.x.x = Ke[3 + 12 * 3]; - k22.x.y = Ke[4 + 12 * 3]; - k22.x.z = Ke[5 + 12 * 3]; - - k22.y.x = Ke[3 + 12 * 4]; - k22.y.y = Ke[4 + 12 * 4]; - k22.y.z = Ke[5 + 12 * 4]; - - k22.z.x = Ke[3 + 12 * 5]; - k22.z.y = Ke[4 + 12 * 5]; - k22.z.z = Ke[5 + 12 * 5]; - - // k23 - // a47 a48 a49 - // a57 a58 a59 - // a67 a68 a69 - k23.x.x = Ke[3 + 12 * 6]; - k23.x.y = Ke[4 + 12 * 6]; - k23.x.z = Ke[5 + 12 * 6]; - - k23.y.x = Ke[3 + 12 * 7]; - k23.y.y = Ke[4 + 12 * 7]; - k23.y.z = Ke[5 + 12 * 7]; - - k23.z.x = Ke[3 + 12 * 8]; - k23.z.y = Ke[4 + 12 * 8]; - k23.z.z = Ke[5 + 12 * 8]; - - // k24 - // a4_10 a4_11 a4_12 - // a5_10 a5_11 a5_12 - // a6_10 a6_11 a6_12 - k24.x.x = Ke[3 + 12 * 9]; - k24.x.y = Ke[4 + 12 * 9]; - k24.x.z = Ke[5 + 12 * 9]; - - k24.y.x = Ke[3 + 12 * 10]; - k24.y.y = Ke[4 + 12 * 10]; - k24.y.z = Ke[5 + 12 * 10]; - - k24.z.x = Ke[3 + 12 * 11]; - k24.z.y = Ke[4 + 12 * 11]; - k24.z.z = Ke[5 + 12 * 11]; - - // k31 - // a71 a72 a73 - // a81 a82 a83 - // a91 a92 a93 - k31.x.x = Ke[6 + 12 * 0]; - k31.x.y = Ke[7 + 12 * 0]; - k31.x.z = Ke[8 + 12 * 0]; - - k31.y.x = Ke[6 + 12 * 1]; - k31.y.y = Ke[7 + 12 * 1]; - k31.y.z = Ke[8 + 12 * 1]; - - k31.z.x = Ke[6 + 12 * 2]; - k31.z.y = Ke[7 + 12 * 2]; - k31.z.z = Ke[8 + 12 * 2]; - - // k32 - // a74 a75 a76 - // a84 a85 a86 - // a94 a95 a96 - k32.x.x = Ke[6 + 12 * 3]; - k32.x.y = Ke[7 + 12 * 3]; - k32.x.z = Ke[8 + 12 * 3]; - - k32.y.x = Ke[6 + 12 * 4]; - k32.y.y = Ke[7 + 12 * 4]; - k32.y.z = Ke[8 + 12 * 4]; - - k32.z.x = Ke[6 + 12 * 5]; - k32.z.y = Ke[7 + 12 * 5]; - k32.z.z = Ke[8 + 12 * 5]; - - // k33 - // a77 a78 a79 - // a87 a88 a89 - // a97 a98 a99 - k33.x.x = Ke[6 + 12 * 6]; - k33.x.y = Ke[7 + 12 * 6]; - k33.x.z = Ke[8 + 12 * 6]; - - k33.y.x = Ke[6 + 12 * 7]; - k33.y.y = Ke[7 + 12 * 7]; - k33.y.z = Ke[8 + 12 * 7]; - - k33.z.x = Ke[6 + 12 * 8]; - k33.z.y = Ke[7 + 12 * 8]; - k33.z.z = Ke[8 + 12 * 8]; - - // k34 - // a7_10 a7_11 a7_12 - // a8_10 a8_11 a8_12 - // a9_10 a9_11 a9_12 - k34.x.x = Ke[6 + 12 * 9]; - k34.x.y = Ke[7 + 12 * 9]; - k34.x.z = Ke[8 + 12 * 9]; - - k34.y.x = Ke[6 + 12 * 10]; - k34.y.y = Ke[7 + 12 * 10]; - k34.y.z = Ke[8 + 12 * 10]; - - k34.z.x = Ke[6 + 12 * 11]; - k34.z.y = Ke[7 + 12 * 11]; - k34.z.z = Ke[8 + 12 * 11]; - - // k41 - // a10_1 a10_2 a10_3 - // a11_1 a11_2 a11_3 - // a12_1 a12_2 a12_3 - k41.x.x = Ke[9 + 12 * 0]; - k41.x.y = Ke[10 + 12 * 0]; - k41.x.z = Ke[11 + 12 * 0]; - - k41.y.x = Ke[9 + 12 * 1]; - k41.y.y = Ke[10 + 12 * 1]; - k41.y.z = Ke[11 + 12 * 1]; - - k41.z.x = Ke[9 + 12 * 2]; - k41.z.y = Ke[10 + 12 * 2]; - k41.z.z = Ke[11 + 12 * 2]; - - // k42 - // a10_4 a10_5 a10_6 - // a11_4 a11_5 a11_6 - // a12_4 a12_5 a12_6 - k42.x.x = Ke[9 + 12 * 3]; - k42.x.y = Ke[10 + 12 * 3]; - k42.x.z = Ke[11 + 12 * 3]; - - k42.y.x = Ke[9 + 12 * 4]; - k42.y.y = Ke[10 + 12 * 4]; - k42.y.z = Ke[11 + 12 * 4]; - - k42.z.x = Ke[9 + 12 * 5]; - k42.z.y = Ke[10 + 12 * 5]; - k42.z.z = Ke[11 + 12 * 5]; - - // k43 - // a10_7 a10_8 a10_9 - // a11_7 a11_8 a11_9 - // a12_7 a12_8 a12_9 - k43.x.x = Ke[9 + 12 * 6]; - k43.x.y = Ke[10 + 12 * 6]; - k43.x.z = Ke[11 + 12 * 6]; - - k43.y.x = Ke[9 + 12 * 7]; - k43.y.y = Ke[10 + 12 * 7]; - k43.y.z = Ke[11 + 12 * 7]; - - k43.z.x = Ke[9 + 12 * 8]; - k43.z.y = Ke[10 + 12 * 8]; - k43.z.z = Ke[11 + 12 * 8]; - - // k44 - // a10_10 a10_11 a10_12 - // a11_10 a11_11 a11_12 - // a12_10 a12_11 a12_12 - k44.x.x = Ke[9 + 12 * 9]; - k44.x.y = Ke[10 + 12 * 9]; - k44.x.z = Ke[11 + 12 * 9]; - - k44.y.x = Ke[9 + 12 * 10]; - k44.y.y = Ke[10 + 12 * 10]; - k44.y.z = Ke[11 + 12 * 10]; - - k44.z.x = Ke[9 + 12 * 11]; - k44.z.y = Ke[10 + 12 * 11]; - k44.z.z = Ke[11 + 12 * 11]; -} - b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) { B3_ASSERT(def.mesh); @@ -597,47 +185,52 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) b3Vec3 p3 = m->vertices[v3]; b3Vec3 p4 = m->vertices[v4]; - float32 lambda, mu; - b3Lame(lambda, mu, m_E, m_nu); - - // 6 x 6 - float32 E[36]; - b3CreateE(E, lambda, mu); - - // 4 x 4 - float32 P[16] = - { - p1.x, p1.y, p1.z, 1.0f, - p2.x, p2.y, p2.z, 1.0f, - p3.x, p3.y, p3.z, 1.0f, - p4.x, p4.y, p4.z, 1.0f - }; - - b3Inverse4(e->invP, P); - - // 6 x 12 - float32 B[72]; - b3CreateB(B, e->invP); - - // 6 x 12 - float32 EB[72]; - b3Mul(EB, E, 6, 6, B, 6, 12); - - // 12 x 6 - float32 BT[72]; - b3Transpose(BT, B, 6, 12); - float32 V = b3Volume(p1, p2, p3, p4); - // 12 x 12 - float32 Ke[144]; - b3Mul(Ke, BT, 12, 6, EB, 6, 12); - for (u32 i = 0; i < 144; ++i) - { - Ke[i] *= V; - } + B3_ASSERT(V > 0.0f); - b3SetK(e->K, Ke); + b3Vec3 e10 = p2 - p1; + b3Vec3 e20 = p3 - p1; + b3Vec3 e30 = p4 - p1; + + b3Mat33 E(e10, e20, e30); + + e->invE = b3Inverse(E); + + // 6 x 6 + float32 D[36]; + b3ComputeD(D, m_E, m_nu); + + b3Vec3 B[4]; + b3ComputeBVec(B, e10, e20, e30, V); + + for (u32 i = 0; i < 4; ++i) + { + // 6 x 3 + float32 B_i[18]; + b3ComputeBMat(B_i, B[i]); + + // 3 x 6 + float32 B_i_T[18]; + b3Transpose(B_i_T, B_i, 6, 3); + + for (u32 j = 0; j < 4; ++j) + { + // 6 x 3 + float32 B_j[18]; + b3ComputeBMat(B_j, B[j]); + + // 6 x 3 + float32 D_B_j[18]; + b3Mul(D_B_j, D, 6, 6, B_j, 6, 3); + + // 3 x 3 + b3Mat33& Ke = e->K[i + 4 * j]; + b3Mul(&Ke.x.x, B_i_T, 3, 6, D_B_j, 6, 3); + + Ke = V * Ke; + } + } } // Initialize triangles diff --git a/src/bounce/softbody/softbody_solver.cpp b/src/bounce/softbody/softbody_solver.cpp index cb6ca56..188844f 100644 --- a/src/bounce/softbody/softbody_solver.cpp +++ b/src/bounce/softbody/softbody_solver.cpp @@ -236,25 +236,13 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter b3Vec3 p3 = p[v3]; b3Vec3 p4 = p[v4]; - float32 P[16] = - { - p1.x, p1.y, p1.z, 1.0f, - p2.x, p2.y, p2.z, 1.0f, - p3.x, p3.y, p3.z, 1.0f, - p4.x, p4.y, p4.z, 1.0f - }; + b3Vec3 e1 = p2 - p1; + b3Vec3 e2 = p3 - p1; + b3Vec3 e3 = p4 - p1; - float32 P_invP[16]; - b3Mul(P_invP, P, 4, 4, e->invP, 4, 4); + b3Mat33 E(e1, e2, e3); - b3Mat33 A; - for (u32 i = 0; i < 3; ++i) - { - for (u32 j = 0; j < 3; ++j) - { - A(i, j) = P_invP[i + 4 * j]; - } - } + b3Mat33 A = E * e->invE; b3Mat33 R; b3ExtractRotation(R, e->q, A); From e0d2f9f51277df0248f120780d60fec7cf238359 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 22 May 2019 12:04:04 -0300 Subject: [PATCH 105/198] Use full matrices for computing the stiffness matrices. Enable/disable stiffness warping. --- include/bounce/softbody/softbody.h | 4 +- include/bounce/softbody/softbody_solver.h | 7 +- src/bounce/softbody/softbody.cpp | 389 +++++++++++++++++++--- src/bounce/softbody/softbody_solver.cpp | 52 ++- 4 files changed, 380 insertions(+), 72 deletions(-) diff --git a/include/bounce/softbody/softbody.h b/include/bounce/softbody/softbody.h index a9b6d0d..b9f7c2c 100644 --- a/include/bounce/softbody/softbody.h +++ b/include/bounce/softbody/softbody.h @@ -42,7 +42,7 @@ struct b3SoftBodyRayCastSingleOutput // Soft body tetrahedron element struct b3SoftBodyElement { - b3Mat33 K[16]; + b3Mat33 K[16]; b3Mat33 invE; b3Quat q; }; @@ -120,6 +120,8 @@ public: // Debug draw the body using the associated mesh. void Draw() const; private: + friend class b3SoftBodySolver; + // Compute mass of each node. void ComputeMass(); diff --git a/include/bounce/softbody/softbody_solver.h b/include/bounce/softbody/softbody_solver.h index 30661da..3018b38 100644 --- a/include/bounce/softbody/softbody_solver.h +++ b/include/bounce/softbody/softbody_solver.h @@ -24,6 +24,7 @@ class b3StackAllocator; +class b3SoftBody; class b3SoftBodyMesh; class b3SoftBodyNode; @@ -33,10 +34,7 @@ class b3NodeBodyContact; struct b3SoftBodySolverDef { - b3StackAllocator* stack; - const b3SoftBodyMesh* mesh; - b3SoftBodyNode* nodes; - b3SoftBodyElement* elements; + b3SoftBody* body; }; class b3SoftBodySolver @@ -47,6 +45,7 @@ public: void Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations); private: + b3SoftBody* m_body; b3StackAllocator* m_allocator; const b3SoftBodyMesh* m_mesh; b3SoftBodyNode* m_nodes; diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index d3395d1..186fbe3 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -82,16 +82,16 @@ static B3_FORCE_INLINE void b3ComputeD(float32 out[36], float32 E, float32 nu) } } -// Compute derivatives of shape functions N -static B3_FORCE_INLINE void b3ComputeBVec(b3Vec3 out[4], - const b3Vec3& e10, - const b3Vec3& e20, +static B3_FORCE_INLINE void b3ComputeB(float32 out[72], + const b3Vec3& e10, + const b3Vec3& e20, const b3Vec3& e30, float32 V) { + // Compute derivatives of shape functions N float32 inv6V = 1.0f / (6.0f * V); - b3Vec3* B = out; + b3Vec3 B[4]; B[1][0] = (e20[2] * e30[1] - e20[1] * e30[2]) * inv6V; B[2][0] = (e10[1] * e30[2] - e10[2] * e30[1]) * inv6V; @@ -101,35 +101,332 @@ static B3_FORCE_INLINE void b3ComputeBVec(b3Vec3 out[4], B[1][1] = (e20[0] * e30[2] - e20[2] * e30[0]) * inv6V; B[2][1] = (e10[2] * e30[0] - e10[0] * e30[2]) * inv6V; B[3][1] = (e10[0] * e20[2] - e10[2] * e20[0]) * inv6V; - B[0][1] = -B[1][1] - B[2][1] - B[3][1]; + B[0][1] = -B[1][1] - B[2][1] - B[3][1]; B[1][2] = (e20[1] * e30[0] - e20[0] * e30[1]) * inv6V; B[2][2] = (e10[0] * e30[1] - e10[1] * e30[0]) * inv6V; B[3][2] = (e10[1] * e20[0] - e10[0] * e20[1]) * inv6V; - B[0][2] = -B[1][2] - B[2][2] - B[3][2]; -} + B[0][2] = -B[1][2] - B[2][2] - B[3][2]; -// Convert a B vector to its corresponding matrix form. -// This is a 6 x 3 matrix. -static B3_FORCE_INLINE void b3ComputeBMat(float32 out[18], const b3Vec3& v) -{ - float32 bi = v.x; - float32 ci = v.y; - float32 di = v.z; + float32 b1 = B[0].x; + float32 c1 = B[0].y; + float32 d1 = B[0].z; - float32 B[18] = + float32 b2 = B[1].x; + float32 c2 = B[1].y; + float32 d2 = B[1].z; + + float32 b3 = B[2].x; + float32 c3 = B[2].y; + float32 d3 = B[2].z; + + float32 b4 = B[3].x; + float32 c4 = B[3].y; + float32 d4 = B[3].z; + + float32 MB[72] = { - bi, 0, 0, ci, di, 0, - 0, ci, 0, bi, 0, di, - 0, 0, di, 0, bi, ci + b1, 0, 0, c1, d1, 0, + 0, c1, 0, b1, 0, d1, + 0, 0, d1, 0, b1, c1, + + b2, 0, 0, c2, d2, 0, + 0, c2, 0, b2, 0, d2, + 0, 0, d2, 0, b2, c2, + + b3, 0, 0, c3, d3, 0, + 0, c3, 0, b3, 0, d3, + 0, 0, d3, 0, b3, c3, + + b4, 0, 0, c4, d4, 0, + 0, c4, 0, b4, 0, d4, + 0, 0, d4, 0, b4, c4, }; - for (u32 i = 0; i < 18; ++i) + for (u32 i = 0; i < 72; ++i) { - out[i] = B[i]; + out[i] = MB[i]; } } +static B3_FORCE_INLINE void b3SetK(b3Mat33 K[16], float32 Ke[144]) +{ + b3Mat33& k11 = K[0 + 4 * 0]; + b3Mat33& k12 = K[0 + 4 * 1]; + b3Mat33& k13 = K[0 + 4 * 2]; + b3Mat33& k14 = K[0 + 4 * 3]; + + b3Mat33& k21 = K[1 + 4 * 0]; + b3Mat33& k22 = K[1 + 4 * 1]; + b3Mat33& k23 = K[1 + 4 * 2]; + b3Mat33& k24 = K[1 + 4 * 3]; + + b3Mat33& k31 = K[2 + 4 * 0]; + b3Mat33& k32 = K[2 + 4 * 1]; + b3Mat33& k33 = K[2 + 4 * 2]; + b3Mat33& k34 = K[2 + 4 * 3]; + + b3Mat33& k41 = K[3 + 4 * 0]; + b3Mat33& k42 = K[3 + 4 * 1]; + b3Mat33& k43 = K[3 + 4 * 2]; + b3Mat33& k44 = K[3 + 4 * 3]; + + // k11 + // a11 a12 a13 + // a21 a22 a23 + // a31 a32 a33 + k11.x.x = Ke[0 + 12 * 0]; + k11.x.y = Ke[1 + 12 * 0]; + k11.x.z = Ke[2 + 12 * 0]; + + k11.y.x = Ke[0 + 12 * 1]; + k11.y.y = Ke[1 + 12 * 1]; + k11.y.z = Ke[2 + 12 * 1]; + + k11.z.x = Ke[0 + 12 * 2]; + k11.z.y = Ke[1 + 12 * 2]; + k11.z.z = Ke[2 + 12 * 2]; + + // k12 + // a14 a15 a16 + // a24 a25 a26 + // a34 a35 a36 + k12.x.x = Ke[0 + 12 * 3]; + k12.x.y = Ke[1 + 12 * 3]; + k12.x.z = Ke[2 + 12 * 3]; + + k12.y.x = Ke[0 + 12 * 4]; + k12.y.y = Ke[1 + 12 * 4]; + k12.y.z = Ke[2 + 12 * 4]; + + k12.z.x = Ke[0 + 12 * 5]; + k12.z.y = Ke[1 + 12 * 5]; + k12.z.z = Ke[2 + 12 * 5]; + + // k13 + // a17 a18 a19 + // a27 a28 a29 + // a37 a38 a39 + k13.x.x = Ke[0 + 12 * 6]; + k13.x.y = Ke[1 + 12 * 6]; + k13.x.z = Ke[2 + 12 * 6]; + + k13.y.x = Ke[0 + 12 * 7]; + k13.y.y = Ke[1 + 12 * 7]; + k13.y.z = Ke[2 + 12 * 7]; + + k13.z.x = Ke[0 + 12 * 8]; + k13.z.y = Ke[1 + 12 * 8]; + k13.z.z = Ke[2 + 12 * 8]; + + // k14 + // a1_10 a1_11 a1_12 + // a2_10 a2_11 a2_12 + // a3_10 a3_11 a3_12 + k14.x.x = Ke[0 + 12 * 9]; + k14.x.y = Ke[1 + 12 * 9]; + k14.x.z = Ke[2 + 12 * 9]; + + k14.y.x = Ke[0 + 12 * 10]; + k14.y.y = Ke[1 + 12 * 10]; + k14.y.z = Ke[2 + 12 * 10]; + + k14.z.x = Ke[0 + 12 * 11]; + k14.z.y = Ke[1 + 12 * 11]; + k14.z.z = Ke[2 + 12 * 11]; + + // k21 + // a41 a42 a43 + // a51 a52 a53 + // a61 a62 a63 + k21.x.x = Ke[3 + 12 * 0]; + k21.x.y = Ke[4 + 12 * 0]; + k21.x.z = Ke[5 + 12 * 0]; + + k21.y.x = Ke[3 + 12 * 1]; + k21.y.y = Ke[4 + 12 * 1]; + k21.y.z = Ke[5 + 12 * 1]; + + k21.z.x = Ke[3 + 12 * 2]; + k21.z.y = Ke[4 + 12 * 2]; + k21.z.z = Ke[5 + 12 * 2]; + + // k22 + // a44 a45 a46 + // a54 a55 a56 + // a64 a65 a66 + k22.x.x = Ke[3 + 12 * 3]; + k22.x.y = Ke[4 + 12 * 3]; + k22.x.z = Ke[5 + 12 * 3]; + + k22.y.x = Ke[3 + 12 * 4]; + k22.y.y = Ke[4 + 12 * 4]; + k22.y.z = Ke[5 + 12 * 4]; + + k22.z.x = Ke[3 + 12 * 5]; + k22.z.y = Ke[4 + 12 * 5]; + k22.z.z = Ke[5 + 12 * 5]; + + // k23 + // a47 a48 a49 + // a57 a58 a59 + // a67 a68 a69 + k23.x.x = Ke[3 + 12 * 6]; + k23.x.y = Ke[4 + 12 * 6]; + k23.x.z = Ke[5 + 12 * 6]; + + k23.y.x = Ke[3 + 12 * 7]; + k23.y.y = Ke[4 + 12 * 7]; + k23.y.z = Ke[5 + 12 * 7]; + + k23.z.x = Ke[3 + 12 * 8]; + k23.z.y = Ke[4 + 12 * 8]; + k23.z.z = Ke[5 + 12 * 8]; + + // k24 + // a4_10 a4_11 a4_12 + // a5_10 a5_11 a5_12 + // a6_10 a6_11 a6_12 + k24.x.x = Ke[3 + 12 * 9]; + k24.x.y = Ke[4 + 12 * 9]; + k24.x.z = Ke[5 + 12 * 9]; + + k24.y.x = Ke[3 + 12 * 10]; + k24.y.y = Ke[4 + 12 * 10]; + k24.y.z = Ke[5 + 12 * 10]; + + k24.z.x = Ke[3 + 12 * 11]; + k24.z.y = Ke[4 + 12 * 11]; + k24.z.z = Ke[5 + 12 * 11]; + + // k31 + // a71 a72 a73 + // a81 a82 a83 + // a91 a92 a93 + k31.x.x = Ke[6 + 12 * 0]; + k31.x.y = Ke[7 + 12 * 0]; + k31.x.z = Ke[8 + 12 * 0]; + + k31.y.x = Ke[6 + 12 * 1]; + k31.y.y = Ke[7 + 12 * 1]; + k31.y.z = Ke[8 + 12 * 1]; + + k31.z.x = Ke[6 + 12 * 2]; + k31.z.y = Ke[7 + 12 * 2]; + k31.z.z = Ke[8 + 12 * 2]; + + // k32 + // a74 a75 a76 + // a84 a85 a86 + // a94 a95 a96 + k32.x.x = Ke[6 + 12 * 3]; + k32.x.y = Ke[7 + 12 * 3]; + k32.x.z = Ke[8 + 12 * 3]; + + k32.y.x = Ke[6 + 12 * 4]; + k32.y.y = Ke[7 + 12 * 4]; + k32.y.z = Ke[8 + 12 * 4]; + + k32.z.x = Ke[6 + 12 * 5]; + k32.z.y = Ke[7 + 12 * 5]; + k32.z.z = Ke[8 + 12 * 5]; + + // k33 + // a77 a78 a79 + // a87 a88 a89 + // a97 a98 a99 + k33.x.x = Ke[6 + 12 * 6]; + k33.x.y = Ke[7 + 12 * 6]; + k33.x.z = Ke[8 + 12 * 6]; + + k33.y.x = Ke[6 + 12 * 7]; + k33.y.y = Ke[7 + 12 * 7]; + k33.y.z = Ke[8 + 12 * 7]; + + k33.z.x = Ke[6 + 12 * 8]; + k33.z.y = Ke[7 + 12 * 8]; + k33.z.z = Ke[8 + 12 * 8]; + + // k34 + // a7_10 a7_11 a7_12 + // a8_10 a8_11 a8_12 + // a9_10 a9_11 a9_12 + k34.x.x = Ke[6 + 12 * 9]; + k34.x.y = Ke[7 + 12 * 9]; + k34.x.z = Ke[8 + 12 * 9]; + + k34.y.x = Ke[6 + 12 * 10]; + k34.y.y = Ke[7 + 12 * 10]; + k34.y.z = Ke[8 + 12 * 10]; + + k34.z.x = Ke[6 + 12 * 11]; + k34.z.y = Ke[7 + 12 * 11]; + k34.z.z = Ke[8 + 12 * 11]; + + // k41 + // a10_1 a10_2 a10_3 + // a11_1 a11_2 a11_3 + // a12_1 a12_2 a12_3 + k41.x.x = Ke[9 + 12 * 0]; + k41.x.y = Ke[10 + 12 * 0]; + k41.x.z = Ke[11 + 12 * 0]; + + k41.y.x = Ke[9 + 12 * 1]; + k41.y.y = Ke[10 + 12 * 1]; + k41.y.z = Ke[11 + 12 * 1]; + + k41.z.x = Ke[9 + 12 * 2]; + k41.z.y = Ke[10 + 12 * 2]; + k41.z.z = Ke[11 + 12 * 2]; + + // k42 + // a10_4 a10_5 a10_6 + // a11_4 a11_5 a11_6 + // a12_4 a12_5 a12_6 + k42.x.x = Ke[9 + 12 * 3]; + k42.x.y = Ke[10 + 12 * 3]; + k42.x.z = Ke[11 + 12 * 3]; + + k42.y.x = Ke[9 + 12 * 4]; + k42.y.y = Ke[10 + 12 * 4]; + k42.y.z = Ke[11 + 12 * 4]; + + k42.z.x = Ke[9 + 12 * 5]; + k42.z.y = Ke[10 + 12 * 5]; + k42.z.z = Ke[11 + 12 * 5]; + + // k43 + // a10_7 a10_8 a10_9 + // a11_7 a11_8 a11_9 + // a12_7 a12_8 a12_9 + k43.x.x = Ke[9 + 12 * 6]; + k43.x.y = Ke[10 + 12 * 6]; + k43.x.z = Ke[11 + 12 * 6]; + + k43.y.x = Ke[9 + 12 * 7]; + k43.y.y = Ke[10 + 12 * 7]; + k43.y.z = Ke[11 + 12 * 7]; + + k43.z.x = Ke[9 + 12 * 8]; + k43.z.y = Ke[10 + 12 * 8]; + k43.z.z = Ke[11 + 12 * 8]; + + // k44 + // a10_10 a10_11 a10_12 + // a11_10 a11_11 a11_12 + // a12_10 a12_11 a12_12 + k44.x.x = Ke[9 + 12 * 9]; + k44.x.y = Ke[10 + 12 * 9]; + k44.x.z = Ke[11 + 12 * 9]; + + k44.y.x = Ke[9 + 12 * 10]; + k44.y.y = Ke[10 + 12 * 10]; + k44.y.z = Ke[11 + 12 * 10]; + + k44.z.x = Ke[9 + 12 * 11]; + k44.z.y = Ke[10 + 12 * 11]; + k44.z.z = Ke[11 + 12 * 11]; +} b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) { B3_ASSERT(def.mesh); @@ -201,36 +498,27 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) float32 D[36]; b3ComputeD(D, m_E, m_nu); - b3Vec3 B[4]; - b3ComputeBVec(B, e10, e20, e30, V); + // 6 x 12 + float32 B[72]; + b3ComputeB(B, e10, e20, e30, V); - for (u32 i = 0; i < 4; ++i) + // 12 x 6 + float32 BT[72]; + b3Transpose(BT, B, 6, 12); + + // 12 x 6 + float32 BT_D[72]; + b3Mul(BT_D, BT, 12, 6, D, 6, 6); + + // 12 x 12 + float32 BT_D_B[144]; + b3Mul(BT_D_B, BT_D, 12, 6, B, 6, 12); + for (u32 i = 0; i < 144; ++i) { - // 6 x 3 - float32 B_i[18]; - b3ComputeBMat(B_i, B[i]); - - // 3 x 6 - float32 B_i_T[18]; - b3Transpose(B_i_T, B_i, 6, 3); - - for (u32 j = 0; j < 4; ++j) - { - // 6 x 3 - float32 B_j[18]; - b3ComputeBMat(B_j, B[j]); - - // 6 x 3 - float32 D_B_j[18]; - b3Mul(D_B_j, D, 6, 6, B_j, 6, 3); - - // 3 x 3 - b3Mat33& Ke = e->K[i + 4 * j]; - b3Mul(&Ke.x.x, B_i_T, 3, 6, D_B_j, 6, 3); - - Ke = V * Ke; - } + BT_D_B[i] *= V; } + + b3SetK(e->K, BT_D_B); } // Initialize triangles @@ -485,10 +773,7 @@ void b3SoftBody::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations B3_PROFILE("Soft Body Solve"); b3SoftBodySolverDef def; - def.stack = &m_stackAllocator; - def.mesh = m_mesh; - def.nodes = m_nodes; - def.elements = m_elements; + def.body = this; b3SoftBodySolver solver(def); diff --git a/src/bounce/softbody/softbody_solver.cpp b/src/bounce/softbody/softbody_solver.cpp index 188844f..f46653c 100644 --- a/src/bounce/softbody/softbody_solver.cpp +++ b/src/bounce/softbody/softbody_solver.cpp @@ -38,14 +38,15 @@ u32 b3_softBodySolverIterations = 0; // Enables the stiffness warping solver. -// bool b3_enableStiffnessWarping = true; +bool b3_enableStiffnessWarping = true; b3SoftBodySolver::b3SoftBodySolver(const b3SoftBodySolverDef& def) { - m_allocator = def.stack; - m_mesh = def.mesh; - m_nodes = def.nodes; - m_elements = def.elements; + m_body = def.body; + m_allocator = &m_body->m_stackAllocator; + m_mesh = m_body->m_mesh; + m_nodes = m_body->m_nodes; + m_elements = m_body->m_elements; } b3SoftBodySolver::~b3SoftBodySolver() @@ -178,9 +179,20 @@ static B3_FORCE_INLINE void b3Mul(float32* C, float32* A, u32 AM, u32 AN, float3 } } +static B3_FORCE_INLINE float32 b3Length(float32* a, u32 an) +{ + float32 result = 0.0f; + for (u32 i = 0; i < an; ++i) + { + result += a[i] * a[i]; + } + return b3Sqrt(result); +} + void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations) { float32 h = dt; + float32 inv_h = 1.0f / h; b3SparseMat33 M(m_mesh->vertexCount); b3DenseVec3 x(m_mesh->vertexCount); @@ -219,6 +231,9 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter b3DenseVec3 f0(m_mesh->vertexCount); f0.SetZero(); + b3DenseVec3 f_plastic(m_mesh->vertexCount); + f_plastic.SetZero(); + for (u32 ei = 0; ei < m_mesh->tetrahedronCount; ++ei) { b3SoftBodyMeshTetrahedron* mt = m_mesh->tetrahedrons + ei; @@ -236,16 +251,23 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter b3Vec3 p3 = p[v3]; b3Vec3 p4 = p[v4]; - b3Vec3 e1 = p2 - p1; - b3Vec3 e2 = p3 - p1; - b3Vec3 e3 = p4 - p1; - - b3Mat33 E(e1, e2, e3); - - b3Mat33 A = E * e->invE; - b3Mat33 R; - b3ExtractRotation(R, e->q, A); + if (b3_enableStiffnessWarping) + { + b3Vec3 e1 = p2 - p1; + b3Vec3 e2 = p3 - p1; + b3Vec3 e3 = p4 - p1; + + b3Mat33 E(e1, e2, e3); + + b3Mat33 A = E * e->invE; + + b3ExtractRotation(R, e->q, A); + } + else + { + R.SetIdentity(); + } b3Mat33 RT = b3Transpose(R); @@ -292,7 +314,7 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter b3SparseMat33 A = M + h * h * K; - b3DenseVec3 b = M * v - h * (K * p + f0 - fe); + b3DenseVec3 b = M * v - h * (K * p + f0 + f_plastic - fe); // Solve Ax = b b3DenseVec3 sx(m_mesh->vertexCount); From 494fa0baa9259f58ecc12aac4d349e0524f5e2cd Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 22 May 2019 18:16:47 -0300 Subject: [PATCH 106/198] Incorporate plasticity model. Update the tests --- examples/testbed/tests/pinned_softbody.h | 5 +- examples/testbed/tests/smash_softbody.h | 3 ++ include/bounce/softbody/softbody.h | 28 ++++++++++- src/bounce/softbody/softbody.cpp | 18 ++++++- src/bounce/softbody/softbody_solver.cpp | 60 +++++++++++++++++++++++- 5 files changed, 110 insertions(+), 4 deletions(-) diff --git a/examples/testbed/tests/pinned_softbody.h b/examples/testbed/tests/pinned_softbody.h index 023b490..131d2e4 100644 --- a/examples/testbed/tests/pinned_softbody.h +++ b/examples/testbed/tests/pinned_softbody.h @@ -33,7 +33,10 @@ public: def.mesh = &m_mesh; def.density = 0.2f; def.E = 100.0f; - def.nu = 0.33f; + def.nu = 0.33f; + def.c_yield = 0.1f; + def.c_creep = 0.5f; + def.c_max = 1.0f; m_body = new b3SoftBody(def); diff --git a/examples/testbed/tests/smash_softbody.h b/examples/testbed/tests/smash_softbody.h index 1b54d7c..544d5a2 100644 --- a/examples/testbed/tests/smash_softbody.h +++ b/examples/testbed/tests/smash_softbody.h @@ -39,6 +39,9 @@ public: def.density = 0.2f; def.E = 100.0f; def.nu = 0.33f; + def.c_yield = 0.6f; + def.c_creep = 1.0f; + def.c_max = 1.0f; m_body = new b3SoftBody(def); diff --git a/include/bounce/softbody/softbody.h b/include/bounce/softbody/softbody.h index b9f7c2c..2457d3a 100644 --- a/include/bounce/softbody/softbody.h +++ b/include/bounce/softbody/softbody.h @@ -42,9 +42,12 @@ struct b3SoftBodyRayCastSingleOutput // Soft body tetrahedron element struct b3SoftBodyElement { - b3Mat33 K[16]; + b3Mat33 K[16]; // 12 x 12 b3Mat33 invE; b3Quat q; + float32 B[72]; // 6 x 12 + float32 P[72]; // V * BT * E -> 12 x 6 + float32 epsilon_plastic[6]; // 6 x 1 }; // Soft body tetrahedron triangle @@ -64,6 +67,9 @@ struct b3SoftBodyDef density = 0.1f; E = 100.0f; nu = 0.3f; + c_yield = B3_MAX_FLOAT; + c_creep = 0.0f; + c_max = 0.0f; } // Soft body mesh @@ -79,6 +85,17 @@ struct b3SoftBodyDef // Material Poisson ratio in [0, 0.5] // This is a dimensionless value float32 nu; + + // Material yield in [0, inf] + // This is a dimensionless value + float32 c_yield; + + // Material creep rate in [0, 1 / dt] + // Units are inverse seconds + float32 c_creep; + + // Material maximum plastic strain in [0, inf] + float32 c_max; }; // A soft body represents a deformable volume as a collection of nodes and elements. @@ -149,6 +166,15 @@ private: // Material poisson ratio float32 m_nu; + // Material yield + float32 m_c_yield; + + // Material creep rate + float32 m_c_creep; + + // Material maximum plastic strain + float32 m_c_max; + // Soft body nodes b3SoftBodyNode* m_nodes; diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index 186fbe3..140e85b 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -436,6 +436,9 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) m_density = def.density; m_E = def.E; m_nu = def.nu; + m_c_yield = def.c_yield; + m_c_creep = def.c_creep; + m_c_max = def.c_max; m_gravity.SetZero(); m_world = nullptr; @@ -499,7 +502,7 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) b3ComputeD(D, m_E, m_nu); // 6 x 12 - float32 B[72]; + float32* B = e->B; b3ComputeB(B, e10, e20, e30, V); // 12 x 6 @@ -519,6 +522,19 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) } b3SetK(e->K, BT_D_B); + + // 12 x 6 + float32* P = e->P; + b3Mul(P, BT, 12, 6, D, 6, 6); + for (u32 i = 0; i < 72; ++i) + { + P[i] *= V; + } + + for (u32 i = 0; i < 6; ++i) + { + e->epsilon_plastic[i] = 0.0f; + } } // Initialize triangles diff --git a/src/bounce/softbody/softbody_solver.cpp b/src/bounce/softbody/softbody_solver.cpp index f46653c..5ced0ec 100644 --- a/src/bounce/softbody/softbody_solver.cpp +++ b/src/bounce/softbody/softbody_solver.cpp @@ -241,6 +241,10 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter b3Mat33* Ke = e->K; + float32* Be = e->B; + float32* Pe = e->P; + float32* epsilon_plastic = e->epsilon_plastic; + u32 v1 = mt->v1; u32 v2 = mt->v2; u32 v3 = mt->v3; @@ -285,6 +289,7 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter } } + // Elasticity b3Vec3 x1 = x[v1]; b3Vec3 x2 = x[v2]; b3Vec3 x3 = x[v3]; @@ -308,13 +313,66 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter f0[v2] += f0s[1]; f0[v3] += f0s[2]; f0[v4] += f0s[3]; + + // Plasticity + b3Vec3 ps[4] = { p1, p2, p3, p4 }; + + b3Vec3 RT_x_x0[4]; + for (u32 i = 0; i < 4; ++i) + { + RT_x_x0[i] = RT * ps[i] - xs[i]; + } + + // 6 x 1 + float32 epsilon_total[6]; + b3Mul(epsilon_total, Be, 6, 12, &RT_x_x0[0].x, 12, 1); + + // 6 x 1 + float32 epsilon_elastic[6]; + for (u32 i = 0; i < 6; ++i) + { + epsilon_elastic[i] = epsilon_total[i] - epsilon_plastic[i]; + } + + float32 len_epsilon_elastic = b3Length(epsilon_elastic, 6); + if (len_epsilon_elastic > m_body->m_c_yield) + { + float32 amount = h * b3Min(m_body->m_c_creep, inv_h); + for (u32 i = 0; i < 6; ++i) + { + epsilon_plastic[i] += amount * epsilon_elastic[i]; + } + } + + float32 len_epsilon_plastic = b3Length(epsilon_plastic, 6); + if (len_epsilon_plastic > m_body->m_c_max) + { + float32 scale = m_body->m_c_max / len_epsilon_plastic; + for (u32 i = 0; i < 6; ++i) + { + epsilon_plastic[i] *= scale; + } + } + + b3Vec3 fs_plastic[4]; + b3Mul(&fs_plastic[0].x, Pe, 12, 6, epsilon_plastic, 6, 1); + for (u32 i = 0; i < 4; ++i) + { + fs_plastic[i] = R * fs_plastic[i]; + } + + f_plastic[v1] += fs_plastic[0]; + f_plastic[v2] += fs_plastic[1]; + f_plastic[v3] += fs_plastic[2]; + f_plastic[v4] += fs_plastic[3]; } f0 = -f0; b3SparseMat33 A = M + h * h * K; - b3DenseVec3 b = M * v - h * (K * p + f0 + f_plastic - fe); + //b3DenseVec3 b = M * v - h * (K * p + f0 + f_plastic - fe); + b3DenseVec3 b = M * v - h * (K * p + f0 - (f_plastic + fe)); // Solve Ax = b b3DenseVec3 sx(m_mesh->vertexCount); From 5e044795df590317206a47bc68cd71b1ace8241b Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 23 May 2019 11:17:32 -0300 Subject: [PATCH 107/198] Use transpose --- src/bounce/softbody/softbody.cpp | 115 ++++++++++++++++--------------- 1 file changed, 61 insertions(+), 54 deletions(-) diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index 140e85b..6a66a6f 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -239,17 +239,18 @@ static B3_FORCE_INLINE void b3SetK(b3Mat33 K[16], float32 Ke[144]) // a41 a42 a43 // a51 a52 a53 // a61 a62 a63 - k21.x.x = Ke[3 + 12 * 0]; - k21.x.y = Ke[4 + 12 * 0]; - k21.x.z = Ke[5 + 12 * 0]; + //k21.x.x = Ke[3 + 12 * 0]; + //k21.x.y = Ke[4 + 12 * 0]; + //k21.x.z = Ke[5 + 12 * 0]; - k21.y.x = Ke[3 + 12 * 1]; - k21.y.y = Ke[4 + 12 * 1]; - k21.y.z = Ke[5 + 12 * 1]; + //k21.y.x = Ke[3 + 12 * 1]; + //k21.y.y = Ke[4 + 12 * 1]; + //k21.y.z = Ke[5 + 12 * 1]; - k21.z.x = Ke[3 + 12 * 2]; - k21.z.y = Ke[4 + 12 * 2]; - k21.z.z = Ke[5 + 12 * 2]; + //k21.z.x = Ke[3 + 12 * 2]; + //k21.z.y = Ke[4 + 12 * 2]; + //k21.z.z = Ke[5 + 12 * 2]; + k21 = b3Transpose(k12); // k22 // a44 a45 a46 @@ -303,33 +304,35 @@ static B3_FORCE_INLINE void b3SetK(b3Mat33 K[16], float32 Ke[144]) // a71 a72 a73 // a81 a82 a83 // a91 a92 a93 - k31.x.x = Ke[6 + 12 * 0]; - k31.x.y = Ke[7 + 12 * 0]; - k31.x.z = Ke[8 + 12 * 0]; + //k31.x.x = Ke[6 + 12 * 0]; + //k31.x.y = Ke[7 + 12 * 0]; + //k31.x.z = Ke[8 + 12 * 0]; - k31.y.x = Ke[6 + 12 * 1]; - k31.y.y = Ke[7 + 12 * 1]; - k31.y.z = Ke[8 + 12 * 1]; + //k31.y.x = Ke[6 + 12 * 1]; + //k31.y.y = Ke[7 + 12 * 1]; + //k31.y.z = Ke[8 + 12 * 1]; - k31.z.x = Ke[6 + 12 * 2]; - k31.z.y = Ke[7 + 12 * 2]; - k31.z.z = Ke[8 + 12 * 2]; + //k31.z.x = Ke[6 + 12 * 2]; + //k31.z.y = Ke[7 + 12 * 2]; + //k31.z.z = Ke[8 + 12 * 2]; + k31 = b3Transpose(k13); // k32 // a74 a75 a76 // a84 a85 a86 // a94 a95 a96 - k32.x.x = Ke[6 + 12 * 3]; - k32.x.y = Ke[7 + 12 * 3]; - k32.x.z = Ke[8 + 12 * 3]; + //k32.x.x = Ke[6 + 12 * 3]; + //k32.x.y = Ke[7 + 12 * 3]; + //k32.x.z = Ke[8 + 12 * 3]; - k32.y.x = Ke[6 + 12 * 4]; - k32.y.y = Ke[7 + 12 * 4]; - k32.y.z = Ke[8 + 12 * 4]; + //k32.y.x = Ke[6 + 12 * 4]; + //k32.y.y = Ke[7 + 12 * 4]; + //k32.y.z = Ke[8 + 12 * 4]; - k32.z.x = Ke[6 + 12 * 5]; - k32.z.y = Ke[7 + 12 * 5]; - k32.z.z = Ke[8 + 12 * 5]; + //k32.z.x = Ke[6 + 12 * 5]; + //k32.z.y = Ke[7 + 12 * 5]; + //k32.z.z = Ke[8 + 12 * 5]; + k32 = b3Transpose(k23); // k33 // a77 a78 a79 @@ -367,49 +370,52 @@ static B3_FORCE_INLINE void b3SetK(b3Mat33 K[16], float32 Ke[144]) // a10_1 a10_2 a10_3 // a11_1 a11_2 a11_3 // a12_1 a12_2 a12_3 - k41.x.x = Ke[9 + 12 * 0]; - k41.x.y = Ke[10 + 12 * 0]; - k41.x.z = Ke[11 + 12 * 0]; + //k41.x.x = Ke[9 + 12 * 0]; + //k41.x.y = Ke[10 + 12 * 0]; + //k41.x.z = Ke[11 + 12 * 0]; - k41.y.x = Ke[9 + 12 * 1]; - k41.y.y = Ke[10 + 12 * 1]; - k41.y.z = Ke[11 + 12 * 1]; + //k41.y.x = Ke[9 + 12 * 1]; + //k41.y.y = Ke[10 + 12 * 1]; + //k41.y.z = Ke[11 + 12 * 1]; - k41.z.x = Ke[9 + 12 * 2]; - k41.z.y = Ke[10 + 12 * 2]; - k41.z.z = Ke[11 + 12 * 2]; + //k41.z.x = Ke[9 + 12 * 2]; + //k41.z.y = Ke[10 + 12 * 2]; + //k41.z.z = Ke[11 + 12 * 2]; + k41 = b3Transpose(k14); // k42 // a10_4 a10_5 a10_6 // a11_4 a11_5 a11_6 // a12_4 a12_5 a12_6 - k42.x.x = Ke[9 + 12 * 3]; - k42.x.y = Ke[10 + 12 * 3]; - k42.x.z = Ke[11 + 12 * 3]; + //k42.x.x = Ke[9 + 12 * 3]; + //k42.x.y = Ke[10 + 12 * 3]; + //k42.x.z = Ke[11 + 12 * 3]; - k42.y.x = Ke[9 + 12 * 4]; - k42.y.y = Ke[10 + 12 * 4]; - k42.y.z = Ke[11 + 12 * 4]; + //k42.y.x = Ke[9 + 12 * 4]; + //k42.y.y = Ke[10 + 12 * 4]; + //k42.y.z = Ke[11 + 12 * 4]; - k42.z.x = Ke[9 + 12 * 5]; - k42.z.y = Ke[10 + 12 * 5]; - k42.z.z = Ke[11 + 12 * 5]; + //k42.z.x = Ke[9 + 12 * 5]; + //k42.z.y = Ke[10 + 12 * 5]; + //k42.z.z = Ke[11 + 12 * 5]; + k42 = b3Transpose(k24); // k43 // a10_7 a10_8 a10_9 // a11_7 a11_8 a11_9 // a12_7 a12_8 a12_9 - k43.x.x = Ke[9 + 12 * 6]; - k43.x.y = Ke[10 + 12 * 6]; - k43.x.z = Ke[11 + 12 * 6]; + //k43.x.x = Ke[9 + 12 * 6]; + //k43.x.y = Ke[10 + 12 * 6]; + //k43.x.z = Ke[11 + 12 * 6]; - k43.y.x = Ke[9 + 12 * 7]; - k43.y.y = Ke[10 + 12 * 7]; - k43.y.z = Ke[11 + 12 * 7]; + //k43.y.x = Ke[9 + 12 * 7]; + //k43.y.y = Ke[10 + 12 * 7]; + //k43.y.z = Ke[11 + 12 * 7]; - k43.z.x = Ke[9 + 12 * 8]; - k43.z.y = Ke[10 + 12 * 8]; - k43.z.z = Ke[11 + 12 * 8]; + //k43.z.x = Ke[9 + 12 * 8]; + //k43.z.y = Ke[10 + 12 * 8]; + //k43.z.z = Ke[11 + 12 * 8]; + k43 = b3Transpose(k34); // k44 // a10_10 a10_11 a10_12 @@ -427,6 +433,7 @@ static B3_FORCE_INLINE void b3SetK(b3Mat33 K[16], float32 Ke[144]) k44.z.y = Ke[10 + 12 * 11]; k44.z.z = Ke[11 + 12 * 11]; } + b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) { B3_ASSERT(def.mesh); From b6f504b37167c64b91f10bfe56fe833f95abcb36 Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 24 May 2019 13:04:42 -0300 Subject: [PATCH 108/198] Include Rayleigh damping. Use struct instead of class. Update PinnedSoftBody test --- examples/testbed/tests/pinned_softbody.h | 8 ++++- include/bounce/softbody/softbody.h | 3 +- .../bounce/softbody/softbody_contact_solver.h | 4 +-- include/bounce/softbody/softbody_node.h | 29 ++++++++++++++----- include/bounce/softbody/softbody_solver.h | 4 +-- src/bounce/softbody/softbody.cpp | 1 + src/bounce/softbody/softbody_solver.cpp | 10 +++++-- 7 files changed, 44 insertions(+), 15 deletions(-) diff --git a/examples/testbed/tests/pinned_softbody.h b/examples/testbed/tests/pinned_softbody.h index 131d2e4..1b4eefe 100644 --- a/examples/testbed/tests/pinned_softbody.h +++ b/examples/testbed/tests/pinned_softbody.h @@ -32,7 +32,7 @@ public: b3SoftBodyDef def; def.mesh = &m_mesh; def.density = 0.2f; - def.E = 100.0f; + def.E = 1000.0f; def.nu = 0.33f; def.c_yield = 0.1f; def.c_creep = 0.5f; @@ -40,6 +40,12 @@ public: m_body = new b3SoftBody(def); + for (u32 i = 0; i < m_mesh.vertexCount; ++i) + { + b3SoftBodyNode* n = m_body->GetVertexNode(i); + n->SetDamping(0.2f); + } + u32 pinIndex = ~0; float32 pinDot = -B3_MAX_FLOAT; for (u32 i = 0; i < m_mesh.vertexCount; ++i) diff --git a/include/bounce/softbody/softbody.h b/include/bounce/softbody/softbody.h index 2457d3a..9316e95 100644 --- a/include/bounce/softbody/softbody.h +++ b/include/bounce/softbody/softbody.h @@ -26,7 +26,8 @@ class b3World; struct b3SoftBodyMesh; -class b3SoftBodyNode; +struct b3SoftBodyNode; +struct b3SoftBodyElement; struct b3RayCastInput; struct b3RayCastOutput; diff --git a/include/bounce/softbody/softbody_contact_solver.h b/include/bounce/softbody/softbody_contact_solver.h index ff8350b..dea16cf 100644 --- a/include/bounce/softbody/softbody_contact_solver.h +++ b/include/bounce/softbody/softbody_contact_solver.h @@ -24,10 +24,10 @@ class b3StackAllocator; -class b3SoftBodyNode; +struct b3SoftBodyNode; class b3Body; -class b3NodeBodyContact; +struct b3NodeBodyContact; struct b3DenseVec3; diff --git a/include/bounce/softbody/softbody_node.h b/include/bounce/softbody/softbody_node.h index 81f7e28..00ef770 100644 --- a/include/bounce/softbody/softbody_node.h +++ b/include/bounce/softbody/softbody_node.h @@ -25,15 +25,11 @@ class b3Shape; class b3SoftBody; -class b3SoftBodyNode; +struct b3SoftBodyNode; // A contact between a node and a body -class b3NodeBodyContact +struct b3NodeBodyContact { -public: - b3NodeBodyContact() { } - ~b3NodeBodyContact() { } - b3SoftBodyNode* n1; b3Shape* s2; @@ -81,7 +77,7 @@ enum b3SoftBodyNodeType }; // A soft body node. -class b3SoftBodyNode +struct b3SoftBodyNode { public: // Set the node type. @@ -110,6 +106,12 @@ public: // Get the node mass. float32 GetMass() const; + // Set the node damping. + void SetDamping(float32 damping); + + // Get the node damping. + float32 GetDamping() const; + // Set the node radius. void SetRadius(float32 radius); @@ -151,6 +153,9 @@ private: // Inverse mass float32 m_invMass; + // Damping + float32 m_damping; + // Radius float32 m_radius; @@ -227,6 +232,16 @@ inline float32 b3SoftBodyNode::GetMass() const return m_mass; } +inline void b3SoftBodyNode::SetDamping(float32 damping) +{ + m_damping = damping; +} + +inline float32 b3SoftBodyNode::GetDamping() const +{ + return m_damping; +} + inline void b3SoftBodyNode::SetRadius(float32 radius) { m_radius = radius; diff --git a/include/bounce/softbody/softbody_solver.h b/include/bounce/softbody/softbody_solver.h index 3018b38..a38491b 100644 --- a/include/bounce/softbody/softbody_solver.h +++ b/include/bounce/softbody/softbody_solver.h @@ -27,10 +27,10 @@ class b3StackAllocator; class b3SoftBody; class b3SoftBodyMesh; -class b3SoftBodyNode; +struct b3SoftBodyNode; struct b3SoftBodyElement; -class b3NodeBodyContact; +struct b3NodeBodyContact; struct b3SoftBodySolverDef { diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index 6a66a6f..5712357 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -465,6 +465,7 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) n->m_force.SetZero(); n->m_mass = 0.0f; n->m_invMass = 0.0f; + n->m_damping = 0.0f; n->m_radius = 0.0f; n->m_friction = 0.0f; n->m_userData = nullptr; diff --git a/src/bounce/softbody/softbody_solver.cpp b/src/bounce/softbody/softbody_solver.cpp index 5ced0ec..a00f8a5 100644 --- a/src/bounce/softbody/softbody_solver.cpp +++ b/src/bounce/softbody/softbody_solver.cpp @@ -195,6 +195,7 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter float32 inv_h = 1.0f / h; b3SparseMat33 M(m_mesh->vertexCount); + b3SparseMat33 C(m_mesh->vertexCount); b3DenseVec3 x(m_mesh->vertexCount); b3DenseVec3 p(m_mesh->vertexCount); b3DenseVec3 v(m_mesh->vertexCount); @@ -207,6 +208,12 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter b3SoftBodyNode* n = m_nodes + i; M(i, i) = b3Diagonal(n->m_mass); + + // Rayleigh damping + // C = alpha * M + beta * K + // Here the stiffness coefficient beta is zero + C(i, i) = b3Diagonal(n->m_damping * n->m_mass); + x[i] = m_mesh->vertices[i]; p[i] = n->m_position; v[i] = n->m_velocity; @@ -369,9 +376,8 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter f0 = -f0; - b3SparseMat33 A = M + h * h * K; + b3SparseMat33 A = M + h * C + h * h * K; - //b3DenseVec3 b = M * v - h * (K * p + f0 + f_plastic - fe); b3DenseVec3 b = M * v - h * (K * p + f0 - (f_plastic + fe)); // Solve Ax = b From 416157bccfeedfde9b26fa1633420b5a2ee1aa13 Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 24 May 2019 19:30:38 -0300 Subject: [PATCH 109/198] Add block tetrahedral mesh for convenience --- include/bounce/bounce.h | 1 + include/bounce/softbody/block_softbody_mesh.h | 123 ++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 include/bounce/softbody/block_softbody_mesh.h diff --git a/include/bounce/bounce.h b/include/bounce/bounce.h index 393e011..a36b9bb 100644 --- a/include/bounce/bounce.h +++ b/include/bounce/bounce.h @@ -73,6 +73,7 @@ #include #include +#include #include #include diff --git a/include/bounce/softbody/block_softbody_mesh.h b/include/bounce/softbody/block_softbody_mesh.h new file mode 100644 index 0000000..7bf1d4e --- /dev/null +++ b/include/bounce/softbody/block_softbody_mesh.h @@ -0,0 +1,123 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_BLOCK_SOFT_BODY_MESH_H +#define B3_BLOCK_SOFT_BODY_MESH_H + +#include + +template +struct b3BlockSoftBodyMesh : public b3SoftBodyMesh +{ + b3Vec3 blockVertices[(W + 1) * (H + 1) * (D + 1)]; + b3SoftBodyMeshTetrahedron blockTetrahedrons[5 * W * H * D]; + + b3BlockSoftBodyMesh() + { + vertexCount = 0; + for (u32 x = 0; x <= W; ++x) + { + for (u32 y = 0; y <= H; ++y) + { + for (u32 z = 0; z <= D; ++z) + { + blockVertices[vertexCount++].Set(x, y, z); + } + } + } + + B3_ASSERT(vertexCount == (W + 1) * (H + 1) * (D + 1)); + + b3Vec3 translation; + translation.x = -0.5f * float32(W); + translation.y = -0.5f * float32(H); + translation.z = -0.5f * float32(D); + + for (u32 i = 0; i < vertexCount; ++i) + { + blockVertices[i] += translation; + } + + tetrahedronCount = 0; + for (u32 x = 0; x < W; ++x) + { + for (u32 y = 0; y < H; ++y) + { + for (u32 z = 0; z < D; ++z) + { + // 4*-----*7 + // /| /| + // / | / | + // 5*-----*6 | + // | 0*--|--*3 + // | / | / + // |/ |/ + // 1*-----*2 + u32 v0 = (x * (H + 1) + y) * (D + 1) + z; + u32 v1 = v0 + 1; + u32 v3 = ((x + 1) * (H + 1) + y) * (D + 1) + z; + u32 v2 = v3 + 1; + u32 v7 = ((x + 1) * (H + 1) + (y + 1)) * (D + 1) + z; + u32 v6 = v7 + 1; + u32 v4 = (x * (H + 1) + (y + 1)) * (D + 1) + z; + u32 v5 = v4 + 1; + + if ((x + y + z) % 2 == 1) + { + // CCW + //blockTetrahedrons[tetrahedronCount++] = { v1, v2, v6, v3 }; + //blockTetrahedrons[tetrahedronCount++] = { v3, v6, v4, v7 }; + //blockTetrahedrons[tetrahedronCount++] = { v1, v4, v6, v5 }; + //blockTetrahedrons[tetrahedronCount++] = { v1, v3, v4, v0 }; + //blockTetrahedrons[tetrahedronCount++] = { v1, v6, v4, v3 }; + + // CW + blockTetrahedrons[tetrahedronCount++] = { v2, v1, v6, v3 }; + blockTetrahedrons[tetrahedronCount++] = { v6, v3, v4, v7 }; + blockTetrahedrons[tetrahedronCount++] = { v4, v1, v6, v5 }; + blockTetrahedrons[tetrahedronCount++] = { v3, v1, v4, v0 }; + blockTetrahedrons[tetrahedronCount++] = { v6, v1, v4, v3 }; + } + else + { + // CCW + //blockTetrahedrons[tetrahedronCount++] = { v2, v0, v5, v1 }; + //blockTetrahedrons[tetrahedronCount++] = { v2, v7, v0, v3 }; + //blockTetrahedrons[tetrahedronCount++] = { v2, v5, v7, v6 }; + //blockTetrahedrons[tetrahedronCount++] = { v0, v7, v5, v4 }; + //blockTetrahedrons[tetrahedronCount++] = { v2, v0, v7, v5 }; + + // CW + blockTetrahedrons[tetrahedronCount++] = { v0, v2, v5, v1 }; + blockTetrahedrons[tetrahedronCount++] = { v7, v2, v0, v3 }; + blockTetrahedrons[tetrahedronCount++] = { v5, v2, v7, v6 }; + blockTetrahedrons[tetrahedronCount++] = { v7, v0, v5, v4 }; + blockTetrahedrons[tetrahedronCount++] = { v0, v2, v7, v5 }; + } + } + } + } + + B3_ASSERT(tetrahedronCount == 5 * W * H * D); + + vertices = blockVertices; + tetrahedrons = blockTetrahedrons; + } +}; + +#endif \ No newline at end of file From c696cb1a86533b5d9069b65e37dbf35e5e679653 Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 24 May 2019 20:36:33 -0300 Subject: [PATCH 110/198] Update test --- examples/testbed/tests/softbody.h | 41 ++++++++++++++++++------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/examples/testbed/tests/softbody.h b/examples/testbed/tests/softbody.h index a7af14a..fbcad0d 100644 --- a/examples/testbed/tests/softbody.h +++ b/examples/testbed/tests/softbody.h @@ -26,18 +26,11 @@ class SoftBody : public Test public: SoftBody() { - m_mesh.SetAsSphere(5.0f, 1); - - for (u32 i = 0; i < m_mesh.vertexCount; ++i) - { - m_mesh.vertices[i].y += 10.0f; - } - // Create soft body b3SoftBodyDef def; def.mesh = &m_mesh; def.density = 0.2f; - def.E = 100.0f; + def.E = 1000.0f; def.nu = 0.33f; m_body = new b3SoftBody(def); @@ -54,23 +47,37 @@ public: n->SetFriction(0.2f); } - // Create ground + // Create body { b3BodyDef bd; bd.type = e_staticBody; + bd.position.x = -3.5f; b3Body* b = m_world.CreateBody(bd); - m_groundHull.SetAsCylinder(20.0f, 2.0f); + m_wallHull.Set(1.0f, 5.0f, 5.0f); - b3HullShape groundShape; - groundShape.m_hull = &m_groundHull; + b3HullShape wallShape; + wallShape.m_hull = &m_wallHull; b3ShapeDef sd; - sd.shape = &groundShape; - sd.friction = 0.3f; + sd.shape = &wallShape; - b->CreateShape(sd); + b3Shape* wall = b->CreateShape(sd); + } + + b3AABB3 aabb; + aabb.m_lower.Set(-3.0f, -5.0f, -5.0f); + aabb.m_upper.Set(-2.0f, 5.0f, 5.0f); + + for (u32 i = 0; i < m_mesh.vertexCount; ++i) + { + b3SoftBodyNode* n = m_body->GetVertexNode(i); + b3Vec3 p = n->GetPosition(); + if (aabb.Contains(p)) + { + n->SetType(e_staticSoftBodyNode); + } } m_bodyDragger = new b3SoftBodyDragger(&m_ray, m_body); @@ -144,12 +151,12 @@ public: return new SoftBody(); } - b3QSoftBodyMesh m_mesh; + b3BlockSoftBodyMesh<5, 2, 2> m_mesh; b3SoftBody* m_body; b3SoftBodyDragger* m_bodyDragger; - b3QHull m_groundHull; + b3BoxHull m_wallHull; }; #endif \ No newline at end of file From 6d2ac0d714751ceef097e3e165d2cc4bfe285a39 Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 24 May 2019 21:05:02 -0300 Subject: [PATCH 111/198] Remove capital H --- include/bounce/softbody/block_softbody_mesh.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/bounce/softbody/block_softbody_mesh.h b/include/bounce/softbody/block_softbody_mesh.h index 7bf1d4e..618caaf 100644 --- a/include/bounce/softbody/block_softbody_mesh.h +++ b/include/bounce/softbody/block_softbody_mesh.h @@ -19,7 +19,7 @@ #ifndef B3_BLOCK_SOFT_BODY_MESH_H #define B3_BLOCK_SOFT_BODY_MESH_H -#include +#include template struct b3BlockSoftBodyMesh : public b3SoftBodyMesh From df21ca37d746cf0c0c8b2dde6c20e1d0f5268fda Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 27 May 2019 19:16:35 -0300 Subject: [PATCH 112/198] Remove __forceinline --- src/bounce/softbody/softbody_solver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bounce/softbody/softbody_solver.cpp b/src/bounce/softbody/softbody_solver.cpp index a00f8a5..9876124 100644 --- a/src/bounce/softbody/softbody_solver.cpp +++ b/src/bounce/softbody/softbody_solver.cpp @@ -55,7 +55,7 @@ b3SoftBodySolver::~b3SoftBodySolver() } // https://animation.rwth-aachen.de/media/papers/2016-MIG-StableRotation.pdf -static B3_FORCE_INLINE void b3ExtractRotation(b3Mat33& out, b3Quat& q, const b3Mat33& A, u32 maxIterations = 20) +static void b3ExtractRotation(b3Mat33& out, b3Quat& q, const b3Mat33& A, u32 maxIterations = 20) { for (u32 iteration = 0; iteration < maxIterations; ++iteration) { @@ -90,7 +90,7 @@ static B3_FORCE_INLINE void b3ExtractRotation(b3Mat33& out, b3Quat& q, const b3M out = b3QuatMat33(q); } -static B3_FORCE_INLINE void b3SolveMPCG(b3DenseVec3& x, +static void b3SolveMPCG(b3DenseVec3& x, const b3SparseMat33& A, const b3DenseVec3& b, const b3DenseVec3& x0, const b3DiagMat33& S, u32 maxIterations = 20) { From 0e5262629d94b82cfc4a1414c4d282cc79b0bfe6 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 28 May 2019 14:01:27 -0300 Subject: [PATCH 113/198] Support kinematic nodes --- include/bounce/softbody/softbody_node.h | 2 ++ src/bounce/softbody/softbody.cpp | 5 +++++ src/bounce/softbody/softbody_solver.cpp | 10 +++++----- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/include/bounce/softbody/softbody_node.h b/include/bounce/softbody/softbody_node.h index 00ef770..3e9021d 100644 --- a/include/bounce/softbody/softbody_node.h +++ b/include/bounce/softbody/softbody_node.h @@ -69,10 +69,12 @@ struct b3NodeBodyContactWorldPoint }; // Static node: Can be moved manually. +// Kinematic node: Non-zero velocity, can be moved by the solver. // Dynamic node: Non-zero velocity determined by force, can be moved by the solver. enum b3SoftBodyNodeType { e_staticSoftBodyNode, + e_kinematicSoftBodyNode, e_dynamicSoftBodyNode }; diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index 5712357..d326420 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -839,6 +839,11 @@ void b3SoftBody::Draw() const b3Draw_draw->DrawPoint(v, 4.0f, b3Color_white); } + if (n->m_type == e_kinematicSoftBodyNode) + { + b3Draw_draw->DrawPoint(v, 4.0f, b3Color_blue); + } + if (n->m_type == e_dynamicSoftBodyNode) { b3Draw_draw->DrawPoint(v, 4.0f, b3Color_green); diff --git a/src/bounce/softbody/softbody_solver.cpp b/src/bounce/softbody/softbody_solver.cpp index 9876124..973586e 100644 --- a/src/bounce/softbody/softbody_solver.cpp +++ b/src/bounce/softbody/softbody_solver.cpp @@ -92,7 +92,7 @@ static void b3ExtractRotation(b3Mat33& out, b3Quat& q, const b3Mat33& A, u32 max static void b3SolveMPCG(b3DenseVec3& x, const b3SparseMat33& A, const b3DenseVec3& b, - const b3DenseVec3& x0, const b3DiagMat33& S, u32 maxIterations = 20) + const b3DenseVec3& z, const b3DiagMat33& S, u32 maxIterations = 20) { b3DiagMat33 P(A.rowCount); b3DiagMat33 invP(A.rowCount); @@ -116,7 +116,7 @@ static void b3SolveMPCG(b3DenseVec3& x, invP[i] = b3Diagonal(D.x.x, D.y.y, D.z.z); } - x = x0; + x = z; float32 delta_0 = b3Dot(S * b, P * (S * b)); @@ -200,7 +200,7 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter b3DenseVec3 p(m_mesh->vertexCount); b3DenseVec3 v(m_mesh->vertexCount); b3DenseVec3 fe(m_mesh->vertexCount); - b3DenseVec3 x0(m_mesh->vertexCount); + b3DenseVec3 z(m_mesh->vertexCount); b3DiagMat33 S(m_mesh->vertexCount); for (u32 i = 0; i < m_mesh->vertexCount; ++i) @@ -218,7 +218,7 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter p[i] = n->m_position; v[i] = n->m_velocity; fe[i] = n->m_force; - x0[i] = n->m_velocity; + z[i] = n->m_velocity; // Apply gravity if (n->m_type == e_dynamicSoftBodyNode) @@ -382,7 +382,7 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter // Solve Ax = b b3DenseVec3 sx(m_mesh->vertexCount); - b3SolveMPCG(sx, A, b, x0, S); + b3SolveMPCG(sx, A, b, z, S); // Solve constraints b3SoftBodyContactSolverDef contactSolverDef; From a97e5c07cce11ca74ca1edd5b1eeaa4ef07640a2 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 28 May 2019 14:10:45 -0300 Subject: [PATCH 114/198] Rename Softbody to Beam --- examples/testbed/framework/test_entries.cpp | 4 ++-- examples/testbed/tests/{softbody.h => beam.h} | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) rename examples/testbed/tests/{softbody.h => beam.h} (96%) diff --git a/examples/testbed/framework/test_entries.cpp b/examples/testbed/framework/test_entries.cpp index 5f4d116..d44b574 100644 --- a/examples/testbed/framework/test_entries.cpp +++ b/examples/testbed/framework/test_entries.cpp @@ -66,7 +66,7 @@ #include #include #include -#include +#include #include #include @@ -119,7 +119,7 @@ TestEntry g_tests[] = { "Particle Types", &ParticleTypes::Create }, { "Tension Mapping", &TensionMapping::Create }, { "Self-Collision", &SelfCollision::Create }, - { "Soft Body", &SoftBody::Create }, + { "Beam", &Beam::Create }, { "Pinned Soft Body", &PinnedSoftBody::Create }, { "Smash Soft Body", &SmashSoftBody::Create }, { "Rope", &Rope::Create }, diff --git a/examples/testbed/tests/softbody.h b/examples/testbed/tests/beam.h similarity index 96% rename from examples/testbed/tests/softbody.h rename to examples/testbed/tests/beam.h index fbcad0d..6a0a913 100644 --- a/examples/testbed/tests/softbody.h +++ b/examples/testbed/tests/beam.h @@ -16,15 +16,15 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef SOFTBODY_H -#define SOFTBODY_H +#ifndef BEAM_H +#define BEAM_H #include -class SoftBody : public Test +class Beam : public Test { public: - SoftBody() + Beam() { // Create soft body b3SoftBodyDef def; @@ -83,7 +83,7 @@ public: m_bodyDragger = new b3SoftBodyDragger(&m_ray, m_body); } - ~SoftBody() + ~Beam() { delete m_bodyDragger; delete m_body; @@ -148,7 +148,7 @@ public: static Test* Create() { - return new SoftBody(); + return new Beam(); } b3BlockSoftBodyMesh<5, 2, 2> m_mesh; From 2e65edc6a83bba76ba50e785c383671d6e8eee1a Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 31 May 2019 11:58:08 -0300 Subject: [PATCH 115/198] Move files around. Centralize sparsity and pattern structures. Add a view for b3SparseMat33. --- include/bounce/cloth/cloth_solver.h | 4 +- .../bounce/{softbody => cloth}/sparse_mat33.h | 31 +++- include/bounce/cloth/sparse_mat33_view.h | 137 ++++++++++++++++++ include/bounce/cloth/sparse_sym_mat33.h | 36 +---- include/bounce/cloth/sparse_sym_mat33_view.h | 29 +--- src/bounce/cloth/cloth_solver.cpp | 60 +++----- src/bounce/softbody/softbody_solver.cpp | 12 +- 7 files changed, 202 insertions(+), 107 deletions(-) rename include/bounce/{softbody => cloth}/sparse_mat33.h (94%) create mode 100644 include/bounce/cloth/sparse_mat33_view.h diff --git a/include/bounce/cloth/cloth_solver.h b/include/bounce/cloth/cloth_solver.h index 2df48a2..7eb4229 100644 --- a/include/bounce/cloth/cloth_solver.h +++ b/include/bounce/cloth/cloth_solver.h @@ -87,7 +87,9 @@ private: void ApplyConstraints(); // Solve Ax = b. - void Solve(b3DenseVec3& x, u32& iterations, const b3SparseSymMat33View& A, const b3DenseVec3& b, const b3DiagMat33& S, const b3DenseVec3& z, const b3DenseVec3& y) const; + void SolveMPCG(b3DenseVec3& x, + const b3SparseSymMat33View& A, const b3DenseVec3& b, + const b3DiagMat33& S, const b3DenseVec3& z, const b3DenseVec3& y, u32 maxIterations = 30) const; b3StackAllocator* m_allocator; diff --git a/include/bounce/softbody/sparse_mat33.h b/include/bounce/cloth/sparse_mat33.h similarity index 94% rename from include/bounce/softbody/sparse_mat33.h rename to include/bounce/cloth/sparse_mat33.h index a746849..273b7ad 100644 --- a/include/bounce/softbody/sparse_mat33.h +++ b/include/bounce/cloth/sparse_mat33.h @@ -22,7 +22,36 @@ #include #include #include -#include + +// An element in a sparse matrix. +struct b3RowValue +{ + u32 column; + b3Mat33 value; + b3RowValue* next; +}; + +// Singly linked list of row elements. +struct b3RowValueList +{ + b3RowValueList() + { + head = nullptr; + count = 0; + } + + ~b3RowValueList() { } + + void PushFront(b3RowValue* link) + { + link->next = head; + head = link; + ++count; + } + + b3RowValue* head; + u32 count; +}; // A sparse matrix. // Each row is a list of non-zero elements in the row. diff --git a/include/bounce/cloth/sparse_mat33_view.h b/include/bounce/cloth/sparse_mat33_view.h new file mode 100644 index 0000000..9abb275 --- /dev/null +++ b/include/bounce/cloth/sparse_mat33_view.h @@ -0,0 +1,137 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SPARSE_MAT_33_VIEW_H +#define B3_SPARSE_MAT_33_VIEW_H + +#include + +struct b3ArrayRowValue +{ + u32 column; + b3Mat33 value; +}; + +struct b3RowValueArray +{ + u32 count; + b3ArrayRowValue* values; +}; + +// A read-only sparse matrix. +struct b3SparseMat33View +{ + // + b3SparseMat33View(const b3SparseMat33& _m); + + // + ~b3SparseMat33View(); + + // + const b3Mat33& operator()(u32 i, u32 j) const; + + u32 rowCount; + b3RowValueArray* rows; +}; + +inline b3SparseMat33View::b3SparseMat33View(const b3SparseMat33& _m) +{ + rowCount = _m.rowCount; + rows = (b3RowValueArray*)b3Alloc(rowCount * sizeof(b3RowValueArray)); + for (u32 i = 0; i < _m.rowCount; ++i) + { + b3RowValueList* rowList = _m.rows + i; + b3RowValueArray* rowArray = rows + i; + + rowArray->count = rowList->count; + rowArray->values = (b3ArrayRowValue*)b3Alloc(rowArray->count * sizeof(b3ArrayRowValue)); + + u32 valueIndex = 0; + for (b3RowValue* v = rowList->head; v; v = v->next) + { + rowArray->values[valueIndex].column = v->column; + rowArray->values[valueIndex].value = v->value; + ++valueIndex; + } + } +} + +inline b3SparseMat33View::~b3SparseMat33View() +{ + for (u32 i = 0; i < rowCount; ++i) + { + b3RowValueArray* rowArray = rows + i; + b3Free(rowArray->values); + } + b3Free(rows); +} + +inline const b3Mat33& b3SparseMat33View::operator()(u32 i, u32 j) const +{ + B3_ASSERT(i < rowCount); + B3_ASSERT(j < rowCount); + + if (i > j) + { + b3Swap(i, j); + } + + b3RowValueArray* vs = rows + i; + + for (u32 c = 0; c < vs->count; ++c) + { + b3ArrayRowValue* rv = vs->values + c; + if (rv->column == j) + { + return rv->value; + } + } + + return b3Mat33_zero; +} + +inline void b3Mul(b3DenseVec3& out, const b3SparseMat33View& A, const b3DenseVec3& v) +{ + B3_ASSERT(A.rowCount == out.n); + + out.SetZero(); + + for (u32 i = 0; i < A.rowCount; ++i) + { + b3RowValueArray* rowArray = A.rows + i; + + for (u32 c = 0; c < rowArray->count; ++c) + { + b3ArrayRowValue* rv = rowArray->values + c; + + u32 j = rv->column; + b3Mat33 a = rv->value; + + out[i] += a * v[j]; + } + } +} + +inline b3DenseVec3 operator*(const b3SparseMat33View& A, const b3DenseVec3& v) +{ + b3DenseVec3 result(v.n); + b3Mul(result, A, v); + return result; +} + +#endif diff --git a/include/bounce/cloth/sparse_sym_mat33.h b/include/bounce/cloth/sparse_sym_mat33.h index 9d36f35..c8af4a2 100644 --- a/include/bounce/cloth/sparse_sym_mat33.h +++ b/include/bounce/cloth/sparse_sym_mat33.h @@ -19,44 +19,14 @@ #ifndef B3_SPARSE_SYM_MAT_33_H #define B3_SPARSE_SYM_MAT_33_H -#include -#include -#include - -// An element in a sparse symmetric matrix. -struct b3RowValue -{ - u32 column; - b3Mat33 value; - b3RowValue* next; -}; - -// Doubly linked list of row elements. -struct b3RowValueList -{ - b3RowValueList() - { - head = nullptr; - count = 0; - } - - ~b3RowValueList() { } - - void PushFront(b3RowValue* link) - { - link->next = head; - head = link; - ++count; - } - - b3RowValue* head; - u32 count; -}; +#include // A sparse symmetric matrix. // Each row is a list of non-zero elements in the row. // The total matrix capacity is bounded by // M * (M + 1) / 2 +// You must write only the upper triangle elements of the original +// matrix to this matrix because a_ji = a_ij. struct b3SparseSymMat33 { // diff --git a/include/bounce/cloth/sparse_sym_mat33_view.h b/include/bounce/cloth/sparse_sym_mat33_view.h index 48a3858..269d4dc 100644 --- a/include/bounce/cloth/sparse_sym_mat33_view.h +++ b/include/bounce/cloth/sparse_sym_mat33_view.h @@ -19,20 +19,9 @@ #ifndef B3_SPARSE_SYM_MAT_33_VIEW_H #define B3_SPARSE_SYM_MAT_33_VIEW_H +#include #include -struct b3ArrayRowValue -{ - u32 column; - b3Mat33 value; -}; - -struct b3RowValueArray -{ - u32 count; - b3ArrayRowValue* values; -}; - // A read-only sparse symmetric matrix. struct b3SparseSymMat33View { @@ -45,9 +34,6 @@ struct b3SparseSymMat33View // const b3Mat33& operator()(u32 i, u32 j) const; - // - void Diagonal(b3DiagMat33& out) const; - u32 rowCount; b3RowValueArray* rows; }; @@ -99,6 +85,7 @@ inline const b3Mat33& b3SparseSymMat33View::operator()(u32 i, u32 j) const for (u32 c = 0; c < vs->count; ++c) { b3ArrayRowValue* rv = vs->values + c; + if (rv->column == j) { return rv->value; @@ -108,16 +95,6 @@ inline const b3Mat33& b3SparseSymMat33View::operator()(u32 i, u32 j) const return b3Mat33_zero; } -inline void b3SparseSymMat33View::Diagonal(b3DiagMat33& out) const -{ - B3_ASSERT(rowCount == out.n); - - for (u32 i = 0; i < rowCount; ++i) - { - out[i] = (*this)(i, i); - } -} - inline void b3Mul(b3DenseVec3& out, const b3SparseSymMat33View& A, const b3DenseVec3& v) { B3_ASSERT(A.rowCount == out.n); @@ -152,4 +129,4 @@ inline b3DenseVec3 operator*(const b3SparseSymMat33View& A, const b3DenseVec3& v return result; } -#endif +#endif \ No newline at end of file diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index 29269f3..632f04e 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -287,9 +287,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) // x b3DenseVec3 x(m_particleCount); - u32 iterations = 0; - Solve(x, iterations, viewA, b, S, z, sx0); - b3_clothSolverIterations = iterations; + SolveMPCG(x, viewA, b, S, z, sx0); #if 0 // Copy constraint forces to the contacts b3DenseVec3 f = A * x - b; @@ -398,17 +396,21 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) } } -void b3ClothSolver::Solve(b3DenseVec3& x, u32& iterations, - const b3SparseSymMat33View& A, const b3DenseVec3& b, const b3DiagMat33& S, const b3DenseVec3& z, const b3DenseVec3& y) const +void b3ClothSolver::SolveMPCG(b3DenseVec3& x, + const b3SparseSymMat33View& A, const b3DenseVec3& b, + const b3DiagMat33& S, const b3DenseVec3& z, const b3DenseVec3& y, u32 maxIterations) const { B3_PROFILE("Cloth Solve Ax = b"); // P = diag(A) - b3DiagMat33 inv_P(m_particleCount); - A.Diagonal(inv_P); + b3DiagMat33 inv_P(A.rowCount); + for (u32 i = 0; i < A.rowCount; ++i) + { + inv_P[i] = A(i, i); + } // Invert - for (u32 i = 0; i < m_particleCount; ++i) + for (u32 i = 0; i < inv_P.n; ++i) { b3Mat33& D = inv_P[i]; @@ -427,81 +429,53 @@ void b3ClothSolver::Solve(b3DenseVec3& x, u32& iterations, D = b3Diagonal(xx, yy, zz); } - // I b3DiagMat33 I(m_particleCount); I.SetIdentity(); - // x = S * y + (I - S) * z x = (S * y) + (I - S) * z; - // b^ = S * (b - A * (I - S) * z) b3DenseVec3 b_hat = S * (b - A * ((I - S) * z)); - // b_delta = dot(b^, P^-1 * b_^) float32 b_delta = b3Dot(b_hat, inv_P * b_hat); - // r = S * (b - A * x) b3DenseVec3 r = S * (b - A * x); - // p = S * (P^-1 * r) b3DenseVec3 p = S * (inv_P * r); - // delta_new = dot(r, p) float32 delta_new = b3Dot(r, p); - // Set the tolerance. - const float32 tolerance = 2.0f * B3_EPSILON; - - // Maximum number of iterations. - // Stop at this iteration if diverged. - const u32 max_iterations = 50; - u32 iteration = 0; - - // Main iteration loop. for (;;) { - // Divergence check. - if (iteration >= max_iterations) + if (iteration >= maxIterations) + { + break; + } + + if (delta_new <= B3_EPSILON * B3_EPSILON * b_delta) { break; } - // Convergence check. - if (delta_new <= tolerance * tolerance * b_delta) - { - break; - } - - // s = S * (A * p) b3DenseVec3 s = S * (A * p); - // alpha = delta_new / dot(p, s) float32 alpha = delta_new / b3Dot(p, s); - // x = x + alpha * p x = x + alpha * p; - - // r = r - alpha * s r = r - alpha * s; - // h = inv_P * r b3DenseVec3 h = inv_P * r; - // delta_old = delta_new float32 delta_old = delta_new; - // delta_new = dot(r, h) delta_new = b3Dot(r, h); - // beta = delta_new / delta_old float32 beta = delta_new / delta_old; - // p = S * (h + beta * p) p = S * (h + beta * p); ++iteration; } - iterations = iteration; + b3_clothSolverIterations = iteration; } \ No newline at end of file diff --git a/src/bounce/softbody/softbody_solver.cpp b/src/bounce/softbody/softbody_solver.cpp index 973586e..9907e7a 100644 --- a/src/bounce/softbody/softbody_solver.cpp +++ b/src/bounce/softbody/softbody_solver.cpp @@ -20,12 +20,14 @@ #include #include #include -#include #include #include #include +#include +#include + // This work is based on the paper "Interactive Virtual Materials" written by // Matthias Mueller Fischer // The paper is available here: @@ -91,9 +93,11 @@ static void b3ExtractRotation(b3Mat33& out, b3Quat& q, const b3Mat33& A, u32 max } static void b3SolveMPCG(b3DenseVec3& x, - const b3SparseMat33& A, const b3DenseVec3& b, + const b3SparseMat33View& A, const b3DenseVec3& b, const b3DenseVec3& z, const b3DiagMat33& S, u32 maxIterations = 20) { + B3_PROFILE("Soft Body Solve Ax = b"); + b3DiagMat33 P(A.rowCount); b3DiagMat33 invP(A.rowCount); for (u32 i = 0; i < A.rowCount; ++i) @@ -377,12 +381,14 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter f0 = -f0; b3SparseMat33 A = M + h * C + h * h * K; + + b3SparseMat33View viewA(A); b3DenseVec3 b = M * v - h * (K * p + f0 - (f_plastic + fe)); // Solve Ax = b b3DenseVec3 sx(m_mesh->vertexCount); - b3SolveMPCG(sx, A, b, z, S); + b3SolveMPCG(sx, viewA, b, z, S); // Solve constraints b3SoftBodyContactSolverDef contactSolverDef; From f2c7eb64ed1c936863784dbcf093c14cb8a00ec8 Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 31 May 2019 12:29:18 -0300 Subject: [PATCH 116/198] Update sparse_mat33_view.h --- include/bounce/cloth/sparse_mat33_view.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/include/bounce/cloth/sparse_mat33_view.h b/include/bounce/cloth/sparse_mat33_view.h index 9abb275..414808c 100644 --- a/include/bounce/cloth/sparse_mat33_view.h +++ b/include/bounce/cloth/sparse_mat33_view.h @@ -85,12 +85,7 @@ inline const b3Mat33& b3SparseMat33View::operator()(u32 i, u32 j) const { B3_ASSERT(i < rowCount); B3_ASSERT(j < rowCount); - - if (i > j) - { - b3Swap(i, j); - } - + b3RowValueArray* vs = rows + i; for (u32 c = 0; c < vs->count; ++c) From d89e658313c719b4b758f265e6f518b0e1c53652 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 2 Jun 2019 11:03:19 -0300 Subject: [PATCH 117/198] Removed experimental code --- examples/testbed/framework/test_entries.cpp | 6 +- examples/testbed/tests/character_test.h | 624 ------------------ .../{self_collision.h => cloth_capsule.h} | 12 +- examples/testbed/tests/table_cloth.h | 2 +- src/bounce/dynamics/shapes/mesh_shape.cpp | 25 +- 5 files changed, 16 insertions(+), 653 deletions(-) delete mode 100644 examples/testbed/tests/character_test.h rename examples/testbed/tests/{self_collision.h => cloth_capsule.h} (94%) diff --git a/examples/testbed/framework/test_entries.cpp b/examples/testbed/framework/test_entries.cpp index d44b574..b2b6596 100644 --- a/examples/testbed/framework/test_entries.cpp +++ b/examples/testbed/framework/test_entries.cpp @@ -54,7 +54,6 @@ #include #include #include -#include #include #include #include @@ -64,7 +63,7 @@ #include #include #include -#include +#include #include #include #include @@ -107,7 +106,6 @@ TestEntry g_tests[] = { "Box Pyramid Rows", &Pyramids::Create }, { "Ray Cast", &RayCast::Create }, { "Sensor Test", &SensorTest::Create }, - { "Character", &CharacterTest::Create }, { "Body Types", &BodyTypes::Create }, { "Varying Friction", &VaryingFriction::Create }, { "Varying Restitution", &VaryingRestitution::Create }, @@ -118,7 +116,7 @@ TestEntry g_tests[] = { "Pinned Cloth", &PinnedCloth::Create }, { "Particle Types", &ParticleTypes::Create }, { "Tension Mapping", &TensionMapping::Create }, - { "Self-Collision", &SelfCollision::Create }, + { "Cloth and Capsule", &ClothCapsule::Create }, { "Beam", &Beam::Create }, { "Pinned Soft Body", &PinnedSoftBody::Create }, { "Smash Soft Body", &SmashSoftBody::Create }, diff --git a/examples/testbed/tests/character_test.h b/examples/testbed/tests/character_test.h deleted file mode 100644 index 93a8f02..0000000 --- a/examples/testbed/tests/character_test.h +++ /dev/null @@ -1,624 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef CHARACTER_TEST_H -#define CHARACTER_TEST_H - -struct Plane -{ - b3Vec3 n; - b3Vec3 p; -}; - -class CharacterController -{ -public: - CharacterController(b3SphereShape* shape) - { - m_characterShape = shape; - m_characterBody = m_characterShape->GetBody(); - m_world = m_characterBody->GetWorld(); - m_translation.SetZero(); - m_isGrounded = false; - - UpdateGrounded(); - } - - ~CharacterController() - { - - } - - bool IsGrounded() const - { - return m_isGrounded; - } - - void Move(const b3Vec3& translation) - { - m_translation += translation; - } - - void Step() - { - Solve(); - - UpdateGrounded(); - } -private: - b3Sphere GetCharacterSphere() const - { - b3Sphere sphere; - sphere.vertex = m_characterBody->GetWorldPoint(m_characterShape->m_center); - sphere.radius = m_characterShape->m_radius; - return sphere; - } - - void UpdateGrounded() - { - b3StackArray shapes; - CollectStaticShapes(shapes); - - b3StackArray planes; - CollectOverlapPlanes(planes, shapes); - - m_isGrounded = false; - - for (u32 i = 0; i < planes.Count(); ++i) - { - Plane plane = planes[i]; - - float32 cosine = b3Dot(b3Vec3_y, plane.n); - - // ~80 degrees - const float32 kTol = 0.17f; - - if (cosine > kTol) - { - m_isGrounded = true; - break; - } - } - } - - void CollectStaticShapes(b3Array& shapes) const - { - for (b3Body* b = m_world->GetBodyList().m_head; b; b = b->GetNext()) - { - if (b == m_characterBody) - { - continue; - } - - if (b->GetType() != e_staticBody) - { - continue; - } - - for (b3Shape* s = b->GetShapeList().m_head; s; s = s->GetNext()) - { - shapes.PushBack(s); - } - } - } - - void CollectOverlapPlanes(b3Array& planes, - const b3Array& shapes) const - { - b3Sphere sphere2 = GetCharacterSphere(); - - for (u32 i = 0; i < shapes.Count(); ++i) - { - b3Shape* shape1 = shapes[i]; - b3Body* body1 = shape1->GetBody(); - b3Transform xf1 = body1->GetTransform(); - - if (shape1->GetType() == e_meshShape) - { - b3MeshShape* meshShape1 = (b3MeshShape*)shape1; - const b3Mesh* mesh1 = meshShape1->m_mesh; - - for (u32 j = 0; j < mesh1->triangleCount; ++j) - { - b3TestSphereOutput output; - bool overlap = meshShape1->TestSphere(&output, sphere2, xf1, j); - - if (overlap) - { - Plane plane; - plane.n = output.normal; - plane.p = output.point; - - planes.PushBack(plane); - } - } - } - else - { - b3TestSphereOutput output; - bool overlap = shape1->TestSphere(&output, sphere2, xf1); - - if (overlap) - { - Plane plane; - plane.n = output.normal; - plane.p = output.point; - - planes.PushBack(plane); - } - } - } - } - - void CollectSweepPlanes(b3Array& planes, - const b3Array& shapes, - const b3Vec3& translation2) const - { - if (b3LengthSquared(translation2) < B3_EPSILON * B3_EPSILON) - { - return; - } - - b3Transform xf2 = m_characterBody->GetTransform(); - - b3ShapeGJKProxy proxy2; - proxy2.Set(m_characterShape, 0); - - for (u32 i = 0; i < shapes.Count(); ++i) - { - b3Shape* shape1 = shapes[i]; - b3Body* body1 = shape1->GetBody(); - - b3Transform xf1 = body1->GetTransform(); - - if (shape1->GetType() == e_meshShape) - { - b3MeshShape* meshShape1 = (b3MeshShape*)shape1; - const b3Mesh* mesh1 = meshShape1->m_mesh; - - for (u32 j = 0; j < mesh1->triangleCount; ++j) - { - b3ShapeGJKProxy proxy1; - proxy1.Set(shape1, j); - - b3GJKShapeCastOutput output; - bool hit = b3GJKShapeCast(&output, xf1, proxy1, xf2, proxy2, translation2); - - if (hit) - { - Plane plane; - plane.n = output.normal; - plane.p = output.point; - - planes.PushBack(plane); - } - } - } - else - { - b3ShapeGJKProxy proxy1; - proxy1.Set(shape1, 0); - - b3GJKShapeCastOutput output; - bool hit = b3GJKShapeCast(&output, xf1, proxy1, xf2, proxy2, translation2); - - if (hit) - { - Plane plane; - plane.n = output.normal; - plane.p = output.point; - - planes.PushBack(plane); - } - } - } - } - - b3Vec3 SolvePositionConstraints(const b3Array& planes, const b3Vec3& position) const - { - b3Vec3 localCenter = m_characterBody->GetLocalCenter(); - b3Vec3 c = position; - b3Quat q = m_characterBody->GetOrientation(); - - const u32 kMaxPositionIterations = 10; - - for (u32 i = 0; i < kMaxPositionIterations; ++i) - { - for (u32 j = 0; j < planes.Count(); ++j) - { - Plane nc = planes[j]; - b3Plane plane(nc.n, nc.p); - - b3Transform xf; - xf.rotation = b3QuatMat33(q); - xf.position = c - b3Mul(xf.rotation, localCenter); - - b3Sphere sphere; - sphere.vertex = b3Mul(xf, m_characterShape->m_center); - sphere.radius = m_characterShape->m_radius; - - float32 separation = b3Distance(sphere.vertex, plane); - - if (separation > sphere.radius) - { - continue; - } - - separation -= sphere.radius; - - float32 C = b3Clamp(B3_BAUMGARTE * (separation + B3_LINEAR_SLOP), -B3_MAX_LINEAR_CORRECTION, 0.0f); - - b3Vec3 normal = plane.normal; - b3Vec3 P = C * normal; - - c -= P; - } - } - - return c; - } - - void Solve() - { - b3StackArray shapes; - CollectStaticShapes(shapes); - - // Collect collision planes - b3StackArray planes; - CollectOverlapPlanes(planes, shapes); - - b3Vec3 startPosition = m_characterBody->GetWorldCenter(); - - b3Vec3 targetPosition = startPosition + m_translation; - - m_translation.SetZero(); - - b3Vec3 solvePosition = SolvePositionConstraints(planes, targetPosition); - - b3Vec3 oldSolvePosition = solvePosition; - - for (;;) - { - // Collect collision planes - b3Vec3 translation = solvePosition - startPosition; - - CollectSweepPlanes(planes, shapes, translation); - - solvePosition = SolvePositionConstraints(planes, targetPosition); - - const float32 tolerance = 0.05f; - - if (b3DistanceSquared(oldSolvePosition, solvePosition) < tolerance * tolerance) - { - break; - } - - oldSolvePosition = solvePosition; - } - - // Synchronize body - b3Quat orientation = m_characterBody->GetOrientation(); - - b3Vec3 axis; - float32 angle; - orientation.GetAxisAngle(&axis, &angle); - - m_characterBody->SetTransform(solvePosition, axis, angle); - } - - b3World* m_world; - b3Body* m_characterBody; - b3SphereShape* m_characterShape; - b3Vec3 m_translation; - bool m_isGrounded; -}; - -class CharacterTest : public Test -{ -public: - CharacterTest() - { - { - b3BodyDef bdef; - b3Body* body = m_world.CreateBody(bdef); - - b3HullShape hs; - hs.m_hull = &m_groundHull; - - b3ShapeDef sdef; - sdef.shape = &hs; - - b3Shape* shape = body->CreateShape(sdef); - } - - { - b3BodyDef bdef; - bdef.position.Set(-10.0f, 3.0f, 0.0f); - - b3Body* body = m_world.CreateBody(bdef); - - static b3BoxHull boxHull; - boxHull.Set(2.0f, 2.0f, 5.0f); - - b3HullShape hs; - hs.m_hull = &boxHull; - - b3ShapeDef sdef; - sdef.shape = &hs; - - b3Shape* shape = body->CreateShape(sdef); - } - - { - b3BodyDef bdef; - bdef.position.Set(-10.0f, 9.0f, 0.0f); - - b3Body* body = m_world.CreateBody(bdef); - - static b3BoxHull boxHull; - boxHull.Set(1.0f, 4.0f, 1.0f); - - b3HullShape hs; - hs.m_hull = &boxHull; - - b3ShapeDef sdef; - sdef.shape = &hs; - - b3Shape* shape = body->CreateShape(sdef); - } - - { - b3BodyDef bdef; - bdef.position.Set(-10.0f, 2.7f, 7.0f); - bdef.orientation.Set(b3Vec3_x, 0.25f * B3_PI); - - b3Body* body = m_world.CreateBody(bdef); - - static b3BoxHull boxHull; - boxHull.Set(2.0f, 0.25f, 3.0f); - - b3HullShape hs; - hs.m_hull = &boxHull; - - b3ShapeDef sdef; - sdef.shape = &hs; - - b3Shape* shape = body->CreateShape(sdef); - } - - { - b3BodyDef bdef; - bdef.position.Set(-10.0f, 2.7f, -7.0f); - bdef.orientation.Set(b3Vec3_x, -0.25f * B3_PI); - - b3Body* body = m_world.CreateBody(bdef); - - static b3BoxHull boxHull; - boxHull.Set(2.0f, 0.25f, 3.0f); - - b3HullShape hs; - hs.m_hull = &boxHull; - - b3ShapeDef sdef; - sdef.shape = &hs; - - b3Shape* shape = body->CreateShape(sdef); - } - - { - b3BodyDef bdef; - bdef.position.Set(10.0f, 3.0f, 0.0f); - - b3Body* body = m_world.CreateBody(bdef); - - static b3BoxHull boxHull; - boxHull.Set(2.0f, 2.0f, 5.0f); - - b3HullShape hs; - hs.m_hull = &boxHull; - - b3ShapeDef sdef; - sdef.shape = &hs; - - b3Shape* shape = body->CreateShape(sdef); - } - - { - b3BodyDef bdef; - bdef.position.Set(10.0f, 9.0f, 0.0f); - - b3Body* body = m_world.CreateBody(bdef); - - static b3BoxHull boxHull; - boxHull.Set(1.0f, 4.0f, 1.0f); - - b3HullShape hs; - hs.m_hull = &boxHull; - - b3ShapeDef sdef; - sdef.shape = &hs; - - b3Shape* shape = body->CreateShape(sdef); - } - - { - b3BodyDef bdef; - bdef.position.Set(10.0f, 2.7f, 7.0f); - bdef.orientation.Set(b3Vec3_x, 0.25f * B3_PI); - - b3Body* body = m_world.CreateBody(bdef); - - static b3BoxHull boxHull; - boxHull.Set(2.0f, 0.25f, 3.0f); - - b3HullShape hs; - hs.m_hull = &boxHull; - - b3ShapeDef sdef; - sdef.shape = &hs; - - b3Shape* shape = body->CreateShape(sdef); - } - - { - b3BodyDef bdef; - bdef.position.Set(10.0f, 2.7f, -7.0f); - bdef.orientation.Set(b3Vec3_x, -0.25f * B3_PI); - - b3Body* body = m_world.CreateBody(bdef); - - static b3BoxHull boxHull; - boxHull.Set(2.0f, 0.25f, 3.0f); - - b3HullShape hs; - hs.m_hull = &boxHull; - - b3ShapeDef sdef; - sdef.shape = &hs; - - b3Shape* shape = body->CreateShape(sdef); - } - - { - b3BodyDef bdef; - bdef.position.Set(0.0f, 4.0f, 0.0f); - - b3Body* body = m_world.CreateBody(bdef); - - static b3BoxHull boxHull; - boxHull.Set(8.0f, 1.0f, 2.0f); - - b3HullShape hs; - hs.m_hull = &boxHull; - - b3ShapeDef sdef; - sdef.shape = &hs; - - b3Shape* shape = body->CreateShape(sdef); - } - - { - b3BodyDef bdef; - bdef.type = b3BodyType::e_kinematicBody; - bdef.position.Set(0.0f, 1.0f, 20.0f); - - b3Body* body = m_world.CreateBody(bdef); - - b3SphereShape sphere; - sphere.m_center.SetZero(); - sphere.m_radius = 1.0f; - - b3ShapeDef sdef; - sdef.shape = &sphere; - - b3SphereShape* shape = (b3SphereShape*)body->CreateShape(sdef); - - m_characterController = new CharacterController(shape); - } - } - - ~CharacterTest() - { - delete m_characterController; - } - - void Step() - { - g_draw->DrawString(b3Color_white, "Arrows - Walk"); - g_draw->DrawString(b3Color_white, "Space - Jump"); - - float32 dt = g_testSettings->inv_hertz; - - const float32 walkSpeed = 10.0f; - const float32 jumpSpeed = 10.0f; - const float32 gravityAcc = 9.8f; - - static b3Vec3 velocity = b3Vec3_zero; - - velocity.x = 0.0f; - velocity.z = 0.0f; - - extern GLFWwindow* g_window; - - bool leftDown = glfwGetKey(g_window, GLFW_KEY_LEFT); - bool rightDown = glfwGetKey(g_window, GLFW_KEY_RIGHT); - bool downDown = glfwGetKey(g_window, GLFW_KEY_DOWN); - bool upDown = glfwGetKey(g_window, GLFW_KEY_UP); - bool spaceDown = glfwGetKey(g_window, GLFW_KEY_SPACE); - - bool isGrounded = m_characterController->IsGrounded(); - - // Walk - if (leftDown) - { - velocity.x -= walkSpeed; - } - - if (rightDown) - { - velocity.x += walkSpeed; - } - - if (upDown) - { - velocity.z -= walkSpeed; - } - - if (downDown) - { - velocity.z += walkSpeed; - } - - if (isGrounded) - { - velocity.y = 0.0f; - - // Jump - if (spaceDown) - { - velocity.y += jumpSpeed; - } - } - else - { - // Integrate gravity - velocity.y -= dt * gravityAcc; - } - - // Compute translation - b3Vec3 translation = dt * velocity; - - // Move character - m_characterController->Move(translation); - - // Step controllers - m_characterController->Step(); - - // Step world - Test::Step(); - } - - static Test* Create() - { - return new CharacterTest(); - } - - CharacterController* m_characterController; -}; - -#endif \ No newline at end of file diff --git a/examples/testbed/tests/self_collision.h b/examples/testbed/tests/cloth_capsule.h similarity index 94% rename from examples/testbed/tests/self_collision.h rename to examples/testbed/tests/cloth_capsule.h index 3051748..0a816e3 100644 --- a/examples/testbed/tests/self_collision.h +++ b/examples/testbed/tests/cloth_capsule.h @@ -16,13 +16,13 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef SELF_COLLISION_H -#define SELF_COLLISION_H +#ifndef CLOTH_CAPSULE_H +#define CLOTH_CAPSULE_H -class SelfCollision : public Test +class ClothCapsule : public Test { public: - SelfCollision() : m_rectangleGarment(5.0f, 5.0f) + ClothCapsule() : m_rectangleGarment(5.0f, 5.0f) { // Generate 2D mesh m_rectangleGarmentMesh.Set(&m_rectangleGarment, 1.0f); @@ -76,7 +76,7 @@ public: m_clothDragger = new b3ClothDragger(&m_ray, m_cloth); } - ~SelfCollision() + ~ClothCapsule() { delete m_cloth; delete m_clothDragger; @@ -141,7 +141,7 @@ public: static Test* Create() { - return new SelfCollision(); + return new ClothCapsule(); } b3RectangleGarment m_rectangleGarment; diff --git a/examples/testbed/tests/table_cloth.h b/examples/testbed/tests/table_cloth.h index 32fd016..9b9c147 100644 --- a/examples/testbed/tests/table_cloth.h +++ b/examples/testbed/tests/table_cloth.h @@ -53,7 +53,7 @@ public: for (b3Particle* p = m_cloth->GetParticleList().m_head; p; p = p->GetNext()) { - p->SetRadius(0.05f); + p->SetRadius(0.1f); p->SetFriction(0.2f); } diff --git a/src/bounce/dynamics/shapes/mesh_shape.cpp b/src/bounce/dynamics/shapes/mesh_shape.cpp index 99af62f..88f3f3d 100644 --- a/src/bounce/dynamics/shapes/mesh_shape.cpp +++ b/src/bounce/dynamics/shapes/mesh_shape.cpp @@ -17,9 +17,7 @@ */ #include -#include #include -#include b3MeshShape::b3MeshShape() { @@ -92,20 +90,11 @@ bool b3MeshShape::TestSphere(b3TestSphereOutput* output, const b3Sphere& sphere, bool b3MeshShape::TestSphere(b3TestSphereOutput* output, const b3Sphere& sphere, const b3Transform& xf, u32 index) const { - B3_ASSERT(index < m_mesh->triangleCount); - b3Triangle* triangle = m_mesh->triangles + index; - b3Vec3 v1 = m_mesh->vertices[triangle->v1]; - b3Vec3 v2 = m_mesh->vertices[triangle->v2]; - b3Vec3 v3 = m_mesh->vertices[triangle->v3]; - - b3TriangleHull hull(v1, v2, v3); - - b3HullShape hullShape; - hullShape.m_body = m_body; - hullShape.m_hull = &hull; - hullShape.m_radius = B3_HULL_RADIUS; - - return hullShape.TestSphere(output, sphere, xf); + B3_NOT_USED(output); + B3_NOT_USED(sphere); + B3_NOT_USED(xf); + B3_NOT_USED(index); + return false; } bool b3MeshShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, const b3Transform& xf, u32 index) const @@ -137,7 +126,7 @@ bool b3MeshShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, return false; } -struct b3MeshRayCastCallback +struct b3MeshShapeRayCastCallback { float32 Report(const b3RayCastInput& subInput, u32 proxyId) { @@ -169,7 +158,7 @@ struct b3MeshRayCastCallback bool b3MeshShape::RayCast(b3RayCastOutput* output, const b3RayCastInput& input, const b3Transform& xf) const { - b3MeshRayCastCallback callback; + b3MeshShapeRayCastCallback callback; callback.input = input; callback.mesh = this; callback.xf = xf; From 2e09982d3c57acb3863f5732b2401720e084fd23 Mon Sep 17 00:00:00 2001 From: Irlan Robson Date: Sun, 2 Jun 2019 11:04:40 -0300 Subject: [PATCH 118/198] Update readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 44d8d4d..4a992d9 100644 --- a/readme.md +++ b/readme.md @@ -20,7 +20,7 @@ Bounce uses [premake](https://premake.github.io/) for generating project files i * Say { premake5 vs2017 } on a command line. * Open build/vs2017/bounce.sln. * Set testbed as the startup project. -* In the testbed debugging properties, set the Working Directory to ..\..\examples\testbed. +* In the testbed debugging properties, set the Working Directory to ..\\..\examples\testbed. * Press F5 to run. ### Linux From f1429a54817e97897d5ed731ef08c450d63ae602 Mon Sep 17 00:00:00 2001 From: Irlan Robson Date: Sun, 2 Jun 2019 11:12:19 -0300 Subject: [PATCH 119/198] Update readme.md --- readme.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 4a992d9..d07e26c 100644 --- a/readme.md +++ b/readme.md @@ -104,8 +104,6 @@ Below are the external dependencies for testbed. If you don't care about testbed ### Dynamics * Rigid bodies -* Cloth -* Soft body * Contact, friction, restitution * Mouse, spring, sphere, cone, revolute joint types * Quaternion constraints @@ -119,6 +117,27 @@ Below are the external dependencies for testbed. If you don't care about testbed * Contact callbacks: begin, pre-solve, post-solve * Ray-casting and volume queries +### Rope + +* Rope +* Linear time solver + +### Cloth + +* Cloth +* Vertex contact, friction +* Linear time solver +* Unconditional simulation stability +* Ray-casting + +### Soft Body + +* Soft body +* Vertex contact, friction +* Linear time solver +* Unconditional simulation stability +* Ray-casting + ### Testbed * OpenGL with GLFW and GLAD From 46600010fedc730a87b8a48e6e44bd1e06a06bb1 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 2 Jun 2019 12:05:36 -0300 Subject: [PATCH 120/198] More consistency and pass velocity and position iterations to cloth constraint solver --- examples/testbed/tests/cloth_capsule.h | 2 +- examples/testbed/tests/pinned_cloth.h | 2 +- examples/testbed/tests/table_cloth.h | 2 +- examples/testbed/tests/tension_mapping.h | 2 +- include/bounce/cloth/cloth.h | 7 +- include/bounce/cloth/cloth_contact_solver.h | 6 +- include/bounce/cloth/cloth_solver.h | 14 +-- include/bounce/cloth/particle.h | 26 ++--- src/bounce/cloth/cloth.cpp | 109 ++------------------ src/bounce/cloth/cloth_contact_solver.cpp | 14 +-- src/bounce/cloth/cloth_solver.cpp | 84 ++------------- src/bounce/cloth/particle.cpp | 6 +- 12 files changed, 47 insertions(+), 227 deletions(-) diff --git a/examples/testbed/tests/cloth_capsule.h b/examples/testbed/tests/cloth_capsule.h index 0a816e3..84a4d0e 100644 --- a/examples/testbed/tests/cloth_capsule.h +++ b/examples/testbed/tests/cloth_capsule.h @@ -86,7 +86,7 @@ public: { Test::Step(); - m_cloth->Step(g_testSettings->inv_hertz); + m_cloth->Step(g_testSettings->inv_hertz, g_testSettings->velocityIterations, g_testSettings->positionIterations); m_cloth->Draw(); diff --git a/examples/testbed/tests/pinned_cloth.h b/examples/testbed/tests/pinned_cloth.h index b2a28ef..023e662 100644 --- a/examples/testbed/tests/pinned_cloth.h +++ b/examples/testbed/tests/pinned_cloth.h @@ -84,7 +84,7 @@ public: { Test::Step(); - m_cloth->Step(g_testSettings->inv_hertz); + m_cloth->Step(g_testSettings->inv_hertz, g_testSettings->velocityIterations, g_testSettings->positionIterations); m_cloth->Draw(); diff --git a/examples/testbed/tests/table_cloth.h b/examples/testbed/tests/table_cloth.h index 9b9c147..5f83489 100644 --- a/examples/testbed/tests/table_cloth.h +++ b/examples/testbed/tests/table_cloth.h @@ -88,7 +88,7 @@ public: { Test::Step(); - m_cloth->Step(g_testSettings->inv_hertz); + m_cloth->Step(g_testSettings->inv_hertz, g_testSettings->velocityIterations, g_testSettings->positionIterations); m_cloth->Draw(); diff --git a/examples/testbed/tests/tension_mapping.h b/examples/testbed/tests/tension_mapping.h index 74d93ae..1da11b2 100644 --- a/examples/testbed/tests/tension_mapping.h +++ b/examples/testbed/tests/tension_mapping.h @@ -110,7 +110,7 @@ public: { Test::Step(); - m_cloth->Step(g_testSettings->inv_hertz); + m_cloth->Step(g_testSettings->inv_hertz, g_testSettings->velocityIterations, g_testSettings->positionIterations); const b3ClothMesh* mesh = m_cloth->GetMesh(); diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index d4ff2a8..a200861 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -29,8 +29,7 @@ class b3Shape; class b3Particle; class b3Force; -class b3BodyContact; -class b3ParticleContact; +struct b3ParticleBodyContact; struct b3ParticleDef; struct b3ForceDef; @@ -134,7 +133,7 @@ public: float32 GetEnergy() const; // Perform a time step. - void Step(float32 dt); + void Step(float32 dt, u32 velocityIterations, u32 positionIterations); // Debug draw the cloth using the associated cloth mesh. void Draw() const; @@ -149,7 +148,7 @@ private: void UpdateContacts(); // Solve - void Solve(float32 dt, const b3Vec3& gravity); + void Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations); // Stack allocator b3StackAllocator m_stackAllocator; diff --git a/include/bounce/cloth/cloth_contact_solver.h b/include/bounce/cloth/cloth_contact_solver.h index 1fe3070..a945d03 100644 --- a/include/bounce/cloth/cloth_contact_solver.h +++ b/include/bounce/cloth/cloth_contact_solver.h @@ -27,7 +27,7 @@ class b3StackAllocator; class b3Particle; class b3Body; -class b3BodyContact; +struct b3ParticleBodyContact; struct b3DenseVec3; @@ -88,7 +88,7 @@ struct b3ClothContactSolverDef b3DenseVec3* velocities; u32 bodyContactCount; - b3BodyContact** bodyContacts; + b3ParticleBodyContact** bodyContacts; }; inline float32 b3MixFriction(float32 u1, float32 u2) @@ -118,7 +118,7 @@ protected: b3DenseVec3* m_velocities; u32 m_bodyContactCount; - b3BodyContact** m_bodyContacts; + b3ParticleBodyContact** m_bodyContacts; b3ClothSolverBodyContactVelocityConstraint* m_bodyVelocityConstraints; b3ClothSolverBodyContactPositionConstraint* m_bodyPositionConstraints; }; diff --git a/include/bounce/cloth/cloth_solver.h b/include/bounce/cloth/cloth_solver.h index 7eb4229..3a582db 100644 --- a/include/bounce/cloth/cloth_solver.h +++ b/include/bounce/cloth/cloth_solver.h @@ -33,8 +33,7 @@ struct b3DiagMat33; struct b3SparseSymMat33; struct b3SparseSymMat33View; -class b3BodyContact; -class b3ParticleContact; +struct b3ParticleBodyContact; struct b3ClothSolverDef { @@ -75,10 +74,9 @@ public: void Add(b3Particle* p); void Add(b3Force* f); - void Add(b3BodyContact* c); - void Add(b3ParticleContact* c); + void Add(b3ParticleBodyContact* c); - void Solve(float32 dt, const b3Vec3& gravity); + void Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations); private: // Apply forces. void ApplyForces(); @@ -107,11 +105,7 @@ private: u32 m_bodyContactCapacity; u32 m_bodyContactCount; - b3BodyContact** m_bodyContacts; - - u32 m_particleContactCapacity; - u32 m_particleContactCount; - b3ParticleContact** m_particleContacts; + b3ParticleBodyContact** m_bodyContacts; b3ClothSolverData m_solverData; }; diff --git a/include/bounce/cloth/particle.h b/include/bounce/cloth/particle.h index bec7da6..b496662 100644 --- a/include/bounce/cloth/particle.h +++ b/include/bounce/cloth/particle.h @@ -62,27 +62,13 @@ struct b3ParticleDef }; // A contact between a particle and a solid -class b3BodyContact +struct b3ParticleBodyContact { -public: - b3BodyContact() { } - ~b3BodyContact() { } - b3Particle* p1; b3Shape* s2; // Contact constraint - float32 s; - b3Vec3 p; - b3Vec3 n; - float32 fn0; - float32 fn; - float32 ft1, ft2; - bool nActive; - bool t1Active; - bool t2Active; - - // Contact constraint + b3Vec3 normal1; b3Vec3 localPoint1; b3Vec3 localPoint2; float32 normalImpulse; @@ -94,9 +80,9 @@ public: bool active; }; -struct b3BodyContactWorldPoint +struct b3ParticleBodyContactWorldPoint { - void Initialize(const b3BodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB); + void Initialize(const b3ParticleBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB); b3Vec3 point; b3Vec3 normal; @@ -210,8 +196,8 @@ private: // b3Cloth* m_cloth; - // - b3BodyContact m_bodyContact; + // Contact + b3ParticleBodyContact m_bodyContact; // b3Particle* m_prev; diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 90cd13b..b86c517 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -464,23 +464,14 @@ void b3Cloth::UpdateBodyContacts() b3Vec3 point = bestPoint; b3Vec3 normal = -bestNormal; - b3BodyContact* c = &p->m_bodyContact; + b3ParticleBodyContact* c = &p->m_bodyContact; - b3BodyContact c0 = *c; + b3ParticleBodyContact c0 = *c; c->active = true; c->p1 = p; c->s2 = shape; - c->s = separation; - c->p = point; - c->n = normal; - c->fn0 = 0.0f; - c->fn = 0.0f; - c->ft1 = 0.0f; - c->ft2 = 0.0f; - c->nActive = true; - c->t1Active = false; - c->t2Active = false; + c->normal1 = normal; c->localPoint1.SetZero(); c->localPoint2 = body->GetLocalPoint(point); c->t1 = b3Perp(normal); @@ -488,97 +479,15 @@ void b3Cloth::UpdateBodyContacts() c->normalImpulse = 0.0f; c->tangentImpulse.SetZero(); -#if 0 - // Apply position correction - p->m_translation += separation * normal; -#endif - - // Update contact state if (c0.active == true) { - if (c0.nActive == true) - { - c->fn0 = c0.fn0; - c->fn = c0.fn; - c->ft1 = c0.ft1; - c->ft2 = c0.ft2; - - c->normalImpulse = c0.normalImpulse; - c->tangentImpulse = c0.tangentImpulse; -#if 0 - const float32 kForceTol = 0.0f; - - // Allow the contact to release when the constraint force - // switches from a repulsive force to an attractive one. - // if (c0.fn0 < kForceTol && c0.fn > kForceTol) - if (c0.fn > kForceTol) - { - // Contact force is attractive. - c->nActive = false; - continue; - } -#endif - } + c->normalImpulse = c0.normalImpulse; + c->tangentImpulse = c0.tangentImpulse; } -#if 0 - if (c0.active == false) - { - continue; - } - - // A friction force requires an associated normal force. - if (c0.nActive == false) - { - continue; - } - - b3Vec3 v1; v1.SetZero(); - b3Vec3 v2 = p->m_velocity; - b3Vec3 dv = v2 - v1; - - const float32 kVelTol = 2.0f * B3_EPSILON; - - // Lock particle on surface - float32 dvt1 = b3Dot(dv, c->t1); - if (dvt1 * dvt1 < kVelTol * kVelTol) - { - c->t1Active = true; - } - - float32 dvt2 = b3Dot(dv, c->t2); - if (dvt2 * dvt2 < kVelTol * kVelTol) - { - c->t2Active = true; - } - - // Unlock particle off surface - float32 normalForce = c->fn; - - float32 friction = shape->GetFriction(); - float32 maxFrictionForce = friction * normalForce; - - if (c0.t1Active == true) - { - float32 tangentForce1 = c0.ft1; - if (tangentForce1 * tangentForce1 > maxFrictionForce * maxFrictionForce) - { - c->t1Active = false; - } - } - - if (c0.t2Active == true) - { - float32 tangentForce2 = c0.ft2; - if (tangentForce2 * tangentForce2 > maxFrictionForce* maxFrictionForce) - { - c->t2Active = false; - } - } -#endif } } -void b3Cloth::Solve(float32 dt, const b3Vec3& gravity) +void b3Cloth::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations) { B3_PROFILE("Cloth Solve"); @@ -610,7 +519,7 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity) } // Solve - solver.Solve(dt, gravity); + solver.Solve(dt, gravity, velocityIterations, positionIterations); } void b3Cloth::UpdateContacts() @@ -619,7 +528,7 @@ void b3Cloth::UpdateContacts() UpdateBodyContacts(); } -void b3Cloth::Step(float32 dt) +void b3Cloth::Step(float32 dt, u32 velocityIterations, u32 positionIterations) { B3_PROFILE("Cloth Step"); @@ -629,7 +538,7 @@ void b3Cloth::Step(float32 dt) // Solve constraints, integrate state, clear forces and translations. if (dt > 0.0f) { - Solve(dt, m_gravity); + Solve(dt, m_gravity, velocityIterations, positionIterations); } // Clear external applied forces and translations diff --git a/src/bounce/cloth/cloth_contact_solver.cpp b/src/bounce/cloth/cloth_contact_solver.cpp index ac97dfc..46d84ad 100644 --- a/src/bounce/cloth/cloth_contact_solver.cpp +++ b/src/bounce/cloth/cloth_contact_solver.cpp @@ -49,7 +49,7 @@ void b3ClothContactSolver::InitializeBodyContactConstraints() for (u32 i = 0; i < m_bodyContactCount; ++i) { - b3BodyContact* c = m_bodyContacts[i]; + b3ParticleBodyContact* c = m_bodyContacts[i]; b3ClothSolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; b3ClothSolverBodyContactPositionConstraint* pc = m_bodyPositionConstraints + i; @@ -79,14 +79,14 @@ void b3ClothContactSolver::InitializeBodyContactConstraints() pc->localCenterA.SetZero(); pc->localCenterB = pc->bodyB->m_sweep.localCenter; - pc->normalA = c->n; + pc->normalA = c->normal1; pc->localPointA = c->localPoint1; pc->localPointB = c->localPoint2; } for (u32 i = 0; i < m_bodyContactCount; ++i) { - b3BodyContact* c = m_bodyContacts[i]; + b3ParticleBodyContact* c = m_bodyContacts[i]; b3ClothSolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; b3ClothSolverBodyContactPositionConstraint* pc = m_bodyPositionConstraints + i; @@ -116,7 +116,7 @@ void b3ClothContactSolver::InitializeBodyContactConstraints() xfB.rotation = b3QuatMat33(qB); xfB.position = xB - b3Mul(xfB.rotation, localCenterB); - b3BodyContactWorldPoint wp; + b3ParticleBodyContactWorldPoint wp; wp.Initialize(c, pc->radiusA, xfA, pc->radiusB, xfB); vc->normal = wp.normal; @@ -310,7 +310,7 @@ void b3ClothContactSolver::StoreImpulses() { for (u32 i = 0; i < m_bodyContactCount; ++i) { - b3BodyContact* c = m_bodyContacts[i]; + b3ParticleBodyContact* c = m_bodyContacts[i]; b3ClothSolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; c->normalImpulse = vc->normalImpulse; @@ -322,14 +322,14 @@ struct b3ClothSolverBodyContactSolverPoint { void Initialize(const b3ClothSolverBodyContactPositionConstraint* pc, const b3Transform& xfA, const b3Transform& xfB) { + b3Vec3 nA = pc->normalA; + b3Vec3 cA = b3Mul(xfA, pc->localPointA); b3Vec3 cB = b3Mul(xfB, pc->localPointB); float32 rA = pc->radiusA; float32 rB = pc->radiusB; - b3Vec3 nA = pc->normalA; - b3Vec3 pA = cA + rA * nA; b3Vec3 pB = cB - rB * nA; diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index 632f04e..665b27a 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -57,7 +57,7 @@ b3ClothSolver::b3ClothSolver(const b3ClothSolverDef& def) m_bodyContactCapacity = def.bodyContactCapacity; m_bodyContactCount = 0; - m_bodyContacts = (b3BodyContact**)m_allocator->Allocate(m_bodyContactCapacity * sizeof(b3BodyContact*));; + m_bodyContacts = (b3ParticleBodyContact**)m_allocator->Allocate(m_bodyContactCapacity * sizeof(b3ParticleBodyContact*));; } b3ClothSolver::~b3ClothSolver() @@ -80,16 +80,11 @@ void b3ClothSolver::Add(b3Force* f) m_forces[m_forceCount++] = f; } -void b3ClothSolver::Add(b3BodyContact* c) +void b3ClothSolver::Add(b3ParticleBodyContact* c) { m_bodyContacts[m_bodyContactCount++] = c; } -void b3ClothSolver::Add(b3ParticleContact* c) -{ - m_particleContacts[m_particleContactCount++] = c; -} - void b3ClothSolver::ApplyForces() { for (u32 i = 0; i < m_forceCount; ++i) @@ -149,6 +144,7 @@ void b3ClothSolver::ApplyConstraints() for (u32 i = 0; i < m_particleCount; ++i) { b3Particle* p = m_particles[i]; + if (p->m_type != e_dynamicParticle) { b3AccelerationConstraint* ac = m_constraints + m_constraintCount; @@ -158,47 +154,6 @@ void b3ClothSolver::ApplyConstraints() ac->z.SetZero(); } } -#if 0 - for (u32 i = 0; i < m_bodyContactCount; ++i) - { - b3BodyContact* bc = m_bodyContacts[i]; - - if (bc->nActive == false) - { - continue; - } - - b3Particle* p1 = bc->p1; - - B3_ASSERT(p1->m_type == e_dynamicParticle); - - b3AccelerationConstraint* ac = m_constraints + m_constraintCount; - ++m_constraintCount; - ac->i1 = p1->m_solverId; - ac->ndof = 2; - ac->p = bc->n; - ac->z.SetZero(); - - if (bc->t1Active && bc->t2Active) - { - ac->ndof = 0; - } - else - { - if (bc->t1Active) - { - ac->ndof = 1; - ac->q = bc->t1; - } - - if (bc->t2Active) - { - ac->ndof = 1; - ac->q = bc->t2; - } - } - } -#endif for (u32 i = 0; i < m_constraintCount; ++i) { @@ -206,7 +161,7 @@ void b3ClothSolver::ApplyConstraints() } } -void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) +void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations) { float32 h = dt; float32 inv_h = 1.0f / h; @@ -288,23 +243,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) // x b3DenseVec3 x(m_particleCount); SolveMPCG(x, viewA, b, S, z, sx0); -#if 0 - // Copy constraint forces to the contacts - b3DenseVec3 f = A * x - b; - for (u32 i = 0; i < m_bodyContactCount; ++i) - { - b3BodyContact* c = m_bodyContacts[i]; - b3Particle* p1 = c->p1; - - b3Vec3 cf = f[p1->m_solverId]; - - c->fn0 = c->fn; - c->fn = b3Dot(cf, c->n); - c->ft1 = b3Dot(cf, c->t1); - c->ft2 = b3Dot(cf, c->t2); - } -#endif // sv = sv + x; sx = sx + sy; @@ -319,7 +258,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) p->m_velocity = sv[i]; } } -#if 1 + // Solve constraints b3ClothContactSolverDef contactSolverDef; contactSolverDef.allocator = m_allocator; @@ -338,11 +277,8 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) contactSolver.WarmStart(); } - // Solve velocity constraints { - const u32 kVelocityIterations = 10; - - for (u32 i = 0; i < kVelocityIterations; ++i) + for (u32 i = 0; i < velocityIterations; ++i) { contactSolver.SolveBodyContactVelocityConstraints(); } @@ -351,18 +287,14 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) { contactSolver.StoreImpulses(); } -#endif // Integrate positions sx = sx + h * sv; -#if 1 // Solve position constraints { - const u32 kPositionIterations = 2; - bool positionSolved = false; - for (u32 i = 0; i < kPositionIterations; ++i) + for (u32 i = 0; i < positionIterations; ++i) { bool bodyContactsSolved = contactSolver.SolveBodyContactPositionConstraints(); @@ -385,7 +317,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity) body->SynchronizeShapes(); } -#endif + // Copy state buffers back to the particles for (u32 i = 0; i < m_particleCount; ++i) { diff --git a/src/bounce/cloth/particle.cpp b/src/bounce/cloth/particle.cpp index aa0bc67..a0e9fad 100644 --- a/src/bounce/cloth/particle.cpp +++ b/src/bounce/cloth/particle.cpp @@ -22,13 +22,13 @@ #include #include -void b3BodyContactWorldPoint::Initialize(const b3BodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB) +void b3ParticleBodyContactWorldPoint::Initialize(const b3ParticleBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB) { + b3Vec3 nA = c->normal1; + b3Vec3 cA = b3Mul(xfA, c->localPoint1); b3Vec3 cB = b3Mul(xfB, c->localPoint2); - b3Vec3 nA = c->n; - b3Vec3 pA = cA + rA * nA; b3Vec3 pB = cB - rB * nA; From d81ee7b84a3edcc1111f7b2172d4ccc5d71d4455 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 3 Jun 2019 10:04:18 -0300 Subject: [PATCH 121/198] Delete unused --- include/bounce/cloth/garment/garment.h | 94 -------------------------- 1 file changed, 94 deletions(-) diff --git a/include/bounce/cloth/garment/garment.h b/include/bounce/cloth/garment/garment.h index 8e16f93..202abe2 100644 --- a/include/bounce/cloth/garment/garment.h +++ b/include/bounce/cloth/garment/garment.h @@ -69,98 +69,4 @@ struct b3CircleGarment : public b3Garment } }; -struct b3ShirtGarment : public b3Garment -{ - b3RectanglePattern frontBody; - b3RectanglePattern frontRightSleeve; - b3RectanglePattern frontLeftSleeve; - - b3RectanglePattern backBody; - b3RectanglePattern backRightSleeve; - b3RectanglePattern backLeftSleeve; - - b3SewingLine shirtSewingLines[12]; - - b3SewingPattern* shirtPatterns[6]; - - b3ShirtGarment() : - frontBody(1.0f, 1.0f), - frontRightSleeve(0.2f, 0.2f), - frontLeftSleeve(0.2f, 0.2f), - backBody(1.0f, 1.0f), - backRightSleeve(0.2f, 0.2f), - backLeftSleeve(0.2f, 0.2f) - { - b3Vec2 etr; - etr.x = 1.2f; - etr.y = 0.8f; - - b3Vec2 etl; - etl.x = -1.2f; - etl.y = 0.8f; - - for (u32 i = 0; i < 4; ++i) - { - frontRightSleeve.vertices[i] += etr; - frontLeftSleeve.vertices[i] += etl; - backRightSleeve.vertices[i] += etr; - backLeftSleeve.vertices[i] += etl; - } - - // Perform sewing - u32 count = 0; - - // Sew bodies - for (u32 i = 0; i < frontBody.vertexCount; ++i) - { - b3SewingLine line; - line.p1 = 0; - line.p2 = 3; - line.v1 = i; - line.v2 = i; - - shirtSewingLines[count++] = line; - } - - // Sew sleeves - for (u32 i = 0; i < frontRightSleeve.vertexCount; ++i) - { - b3SewingLine line; - line.p1 = 1; - line.p2 = 4; - line.v1 = i; - line.v2 = i; - - shirtSewingLines[count++] = line; - } - - for (u32 i = 0; i < frontLeftSleeve.vertexCount; ++i) - { - b3SewingLine line; - line.p1 = 2; - line.p2 = 5; - line.v1 = i; - line.v2 = i; - - shirtSewingLines[count++] = line; - } - - B3_ASSERT(12 == count); - - shirtPatterns[0] = &frontBody; - shirtPatterns[1] = &frontRightSleeve; - shirtPatterns[2] = &frontLeftSleeve; - - shirtPatterns[3] = &backBody; - shirtPatterns[4] = &backRightSleeve; - shirtPatterns[5] = &backLeftSleeve; - - patternCount = 6; - patterns = shirtPatterns; - - sewingCount = 12; - sewingLines = shirtSewingLines; - } -}; - #endif \ No newline at end of file From 3b910491c383149cea2f1bb3d1ac2e454f769766 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 3 Jun 2019 14:32:58 -0300 Subject: [PATCH 122/198] Set AABB from center and radius --- include/bounce/collision/shapes/aabb3.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/bounce/collision/shapes/aabb3.h b/include/bounce/collision/shapes/aabb3.h index a9e562e..09ffb2c 100644 --- a/include/bounce/collision/shapes/aabb3.h +++ b/include/bounce/collision/shapes/aabb3.h @@ -60,6 +60,20 @@ struct b3AABB3 } } + // Set this AABB from a center point and a radius vector. + void Set(const b3Vec3& center, const b3Vec3& r) + { + m_lower = center - r; + m_upper = center + r; + } + + // Set this AABB from a center point and a radius value. + void Set(const b3Vec3& center, float32 radius) + { + b3Vec3 r(radius, radius, radius); + Set(center, r); + } + // Extend this AABB by a scalar. void Extend(float32 s) { From 2c78e67844ff0bc7b660454e5d80a52717097a37 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 3 Jun 2019 14:33:42 -0300 Subject: [PATCH 123/198] Use a node tree. Rename damping to mass damping. --- examples/testbed/tests/pinned_softbody.h | 2 +- include/bounce/softbody/softbody.h | 7 ++ include/bounce/softbody/softbody_node.h | 48 +++++---- src/bounce/softbody/softbody.cpp | 120 ++++++++++++++--------- src/bounce/softbody/softbody_node.cpp | 43 ++++++++ src/bounce/softbody/softbody_solver.cpp | 2 +- 6 files changed, 151 insertions(+), 71 deletions(-) create mode 100644 src/bounce/softbody/softbody_node.cpp diff --git a/examples/testbed/tests/pinned_softbody.h b/examples/testbed/tests/pinned_softbody.h index 1b4eefe..e058e24 100644 --- a/examples/testbed/tests/pinned_softbody.h +++ b/examples/testbed/tests/pinned_softbody.h @@ -43,7 +43,7 @@ public: for (u32 i = 0; i < m_mesh.vertexCount; ++i) { b3SoftBodyNode* n = m_body->GetVertexNode(i); - n->SetDamping(0.2f); + n->SetMassDamping(0.2f); } u32 pinIndex = ~0; diff --git a/include/bounce/softbody/softbody.h b/include/bounce/softbody/softbody.h index 9316e95..3e17de5 100644 --- a/include/bounce/softbody/softbody.h +++ b/include/bounce/softbody/softbody.h @@ -22,6 +22,8 @@ #include #include +#include + class b3World; struct b3SoftBodyMesh; @@ -56,6 +58,7 @@ struct b3SoftBodyTriangle { u32 v1, v2, v3; u32 tetrahedron; + u32 treeId; }; // Soft body definition @@ -138,6 +141,7 @@ public: // Debug draw the body using the associated mesh. void Draw() const; private: + friend class b3SoftBodyNode; friend class b3SoftBodySolver; // Compute mass of each node. @@ -185,6 +189,9 @@ private: // Soft body triangles b3SoftBodyTriangle* m_triangles; + // Node tree + b3DynamicTree m_nodeTree; + // Attached world b3World* m_world; }; diff --git a/include/bounce/softbody/softbody_node.h b/include/bounce/softbody/softbody_node.h index 3e9021d..7b7dd6f 100644 --- a/include/bounce/softbody/softbody_node.h +++ b/include/bounce/softbody/softbody_node.h @@ -48,21 +48,8 @@ struct b3NodeBodyContact struct b3NodeBodyContactWorldPoint { - void Initialize(const b3NodeBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB) - { - b3Vec3 nA = c->normal1; - - b3Vec3 cA = xfA * c->localPoint1; - b3Vec3 cB = xfB * c->localPoint2; - - b3Vec3 pA = cA + rA * nA; - b3Vec3 pB = cB - rB * nA; - - point = 0.5f * (pA + pB); - normal = nA; - separation = b3Dot(cB - cA, nA) - rA - rB; - } - + void Initialize(const b3NodeBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB); + b3Vec3 point; b3Vec3 normal; float32 separation; @@ -108,11 +95,11 @@ public: // Get the node mass. float32 GetMass() const; - // Set the node damping. - void SetDamping(float32 damping); + // Set the node mass damping. + void SetMassDamping(float32 damping); - // Get the node damping. - float32 GetDamping() const; + // Get the node mass damping. + float32 GetMassDamping() const; // Set the node radius. void SetRadius(float32 radius); @@ -137,6 +124,9 @@ private: ~b3SoftBodyNode() { } + // Synchronize node + void Synchronize(); + // Type b3SoftBodyNodeType m_type; @@ -155,8 +145,8 @@ private: // Inverse mass float32 m_invMass; - // Damping - float32 m_damping; + // Mass damping + float32 m_massDamping; // Radius float32 m_radius; @@ -173,6 +163,9 @@ private: // Node and body contact b3NodeBodyContact m_bodyContact; + // Tree identifier + u32 m_treeId; + // Soft body b3SoftBody* m_body; }; @@ -190,6 +183,7 @@ inline void b3SoftBodyNode::SetType(b3SoftBodyNodeType type) if (type == e_staticSoftBodyNode) { m_velocity.SetZero(); + Synchronize(); } m_bodyContact.active = false; @@ -208,6 +202,8 @@ inline u32 b3SoftBodyNode::GetVertex() const inline void b3SoftBodyNode::SetPosition(const b3Vec3& position) { m_position = position; + + Synchronize(); } inline const b3Vec3& b3SoftBodyNode::GetPosition() const @@ -234,19 +230,21 @@ inline float32 b3SoftBodyNode::GetMass() const return m_mass; } -inline void b3SoftBodyNode::SetDamping(float32 damping) +inline void b3SoftBodyNode::SetMassDamping(float32 massDamping) { - m_damping = damping; + m_massDamping = massDamping; } -inline float32 b3SoftBodyNode::GetDamping() const +inline float32 b3SoftBodyNode::GetMassDamping() const { - return m_damping; + return m_massDamping; } inline void b3SoftBodyNode::SetRadius(float32 radius) { m_radius = radius; + + Synchronize(); } inline float32 b3SoftBodyNode::GetRadius() const diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index d326420..c8c2936 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -451,9 +452,8 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) const b3SoftBodyMesh* m = m_mesh; - m_nodes = (b3SoftBodyNode*)b3Alloc(m->vertexCount * sizeof(b3SoftBodyNode)); - // Initialize nodes + m_nodes = (b3SoftBodyNode*)b3Alloc(m->vertexCount * sizeof(b3SoftBodyNode)); for (u32 i = 0; i < m->vertexCount; ++i) { b3SoftBodyNode* n = m_nodes + i; @@ -465,12 +465,17 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) n->m_force.SetZero(); n->m_mass = 0.0f; n->m_invMass = 0.0f; - n->m_damping = 0.0f; + n->m_massDamping = 0.0f; n->m_radius = 0.0f; n->m_friction = 0.0f; n->m_userData = nullptr; n->m_vertex = i; n->m_bodyContact.active = false; + + b3AABB3 aabb; + aabb.Set(n->m_position, 0.0f); + + n->m_treeId = m_nodeTree.InsertNode(aabb, n); } // Compute mass @@ -703,6 +708,43 @@ void b3SoftBody::ComputeMass() } } +class b3SoftBodyUpdateContactsQueryListener : public b3QueryListener +{ +public: + bool ReportShape(b3Shape* shape) + { + b3Body* body = shape->GetBody(); + + if (body->GetType() != e_staticBody) + { + //continue; + } + + b3Transform xf = body->GetTransform(); + + b3TestSphereOutput output; + if (shape->TestSphere(&output, sphere, xf)) + { + if (output.separation < bestSeparation) + { + bestShape = shape; + bestSeparation = output.separation; + bestPoint = output.point; + bestNormal = output.normal; + } + } + + return true; + } + + b3SoftBodyNode* node; + b3Sphere sphere; + b3Shape* bestShape; + float32 bestSeparation; + b3Vec3 bestPoint; + b3Vec3 bestNormal; +}; + void b3SoftBody::UpdateContacts() { B3_PROFILE("Soft Body Update Contacts"); @@ -718,56 +760,33 @@ void b3SoftBody::UpdateContacts() { b3SoftBodyNode* n = m_nodes + i; - b3Sphere s1; - s1.vertex = n->m_position; - s1.radius = n->m_radius; - - // Find the deepest penetration - b3Shape* bestShape = nullptr; - float32 bestSeparation = 0.0f; - b3Vec3 bestPoint(0.0f, 0.0f, 0.0f); - b3Vec3 bestNormal(0.0f, 0.0f, 0.0f); - - for (b3Body* body = m_world->GetBodyList().m_head; body; body = body->GetNext()) + if (n->m_type != e_dynamicSoftBodyNode) { - if (n->m_type != e_dynamicSoftBodyNode) - { - continue; - } - - if (body->GetType() != e_staticBody) - { - //continue; - } - - b3Transform xf = body->GetTransform(); - for (b3Shape* shape = body->GetShapeList().m_head; shape; shape = shape->GetNext()) - { - b3TestSphereOutput output; - if (shape->TestSphere(&output, s1, xf)) - { - if (output.separation < bestSeparation) - { - bestShape = shape; - bestSeparation = output.separation; - bestPoint = output.point; - bestNormal = output.normal; - } - } - } + continue; } - if (bestShape == nullptr) + b3AABB3 aabb = m_nodeTree.GetAABB(n->m_treeId); + + b3SoftBodyUpdateContactsQueryListener listener; + listener.node = n; + listener.sphere.vertex = n->m_position; + listener.sphere.radius = n->m_radius; + listener.bestShape = nullptr; + listener.bestSeparation = 0.0f; + + m_world->QueryAABB(&listener, aabb); + + if (listener.bestShape == nullptr) { n->m_bodyContact.active = false; continue; } - b3Shape* shape = bestShape; + b3Shape* shape = listener.bestShape; b3Body* body = shape->GetBody(); - float32 separation = bestSeparation; - b3Vec3 point = bestPoint; - b3Vec3 normal = -bestNormal; + float32 separation = listener.bestSeparation; + b3Vec3 point = listener.bestPoint; + b3Vec3 normal = -listener.bestNormal; b3NodeBodyContact* c = &n->m_bodyContact; @@ -822,6 +841,19 @@ void b3SoftBody::Step(float32 dt, u32 velocityIterations, u32 positionIterations { m_nodes[i].m_force.SetZero(); } + + // Synchronize nodes + for (u32 i = 0; i < m_mesh->vertexCount; ++i) + { + b3SoftBodyNode* n = m_nodes + i; + + if (n->m_type == e_staticSoftBodyNode) + { + continue; + } + + n->Synchronize(); + } } void b3SoftBody::Draw() const diff --git a/src/bounce/softbody/softbody_node.cpp b/src/bounce/softbody/softbody_node.cpp new file mode 100644 index 0000000..e18ffed --- /dev/null +++ b/src/bounce/softbody/softbody_node.cpp @@ -0,0 +1,43 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include + +void b3NodeBodyContactWorldPoint::Initialize(const b3NodeBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB) +{ + b3Vec3 nA = c->normal1; + + b3Vec3 cA = xfA * c->localPoint1; + b3Vec3 cB = xfB * c->localPoint2; + + b3Vec3 pA = cA + rA * nA; + b3Vec3 pB = cB - rB * nA; + + point = 0.5f * (pA + pB); + normal = nA; + separation = b3Dot(cB - cA, nA) - rA - rB; +} + +void b3SoftBodyNode::Synchronize() +{ + b3AABB3 aabb; + aabb.Set(m_position, m_radius); + + m_body->m_nodeTree.UpdateNode(m_treeId, aabb); +} \ No newline at end of file diff --git a/src/bounce/softbody/softbody_solver.cpp b/src/bounce/softbody/softbody_solver.cpp index 9907e7a..1b831e3 100644 --- a/src/bounce/softbody/softbody_solver.cpp +++ b/src/bounce/softbody/softbody_solver.cpp @@ -216,7 +216,7 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter // Rayleigh damping // C = alpha * M + beta * K // Here the stiffness coefficient beta is zero - C(i, i) = b3Diagonal(n->m_damping * n->m_mass); + C(i, i) = b3Diagonal(n->m_massDamping * n->m_mass); x[i] = m_mesh->vertices[i]; p[i] = n->m_position; From 17bddf54265c1daa8213c8d0dc3af4ef2a1902ce Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 3 Jun 2019 17:16:49 -0300 Subject: [PATCH 124/198] Remove unused --- src/bounce/softbody/softbody.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index c8c2936..388ea3b 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -737,7 +737,6 @@ public: return true; } - b3SoftBodyNode* node; b3Sphere sphere; b3Shape* bestShape; float32 bestSeparation; @@ -768,7 +767,6 @@ void b3SoftBody::UpdateContacts() b3AABB3 aabb = m_nodeTree.GetAABB(n->m_treeId); b3SoftBodyUpdateContactsQueryListener listener; - listener.node = n; listener.sphere.vertex = n->m_position; listener.sphere.radius = n->m_radius; listener.bestShape = nullptr; From 92cdb42dca9386476bbed138596b5bf2b77b7ff3 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 3 Jun 2019 17:50:43 -0300 Subject: [PATCH 125/198] Use a particle tree for cloth --- include/bounce/cloth/cloth.h | 11 ++- include/bounce/cloth/particle.h | 12 ++- src/bounce/cloth/cloth.cpp | 140 +++++++++++++++++++----------- src/bounce/cloth/cloth_solver.cpp | 2 +- src/bounce/cloth/particle.cpp | 13 ++- src/bounce/softbody/softbody.cpp | 2 +- 6 files changed, 117 insertions(+), 63 deletions(-) diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index a200861..e24e17f 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -23,6 +23,7 @@ #include #include #include +#include class b3World; class b3Shape; @@ -138,12 +139,11 @@ public: // Debug draw the cloth using the associated cloth mesh. void Draw() const; private: + friend class b3Particle; + // Compute mass of each particle. void ComputeMass(); - // Update body contacts. - void UpdateBodyContacts(); - // Update contacts void UpdateContacts(); @@ -173,7 +173,10 @@ private: // List of particles b3List2 m_particleList; - + + // Particle tree + b3DynamicTree m_particleTree; + // List of forces b3List2 m_forceList; }; diff --git a/include/bounce/cloth/particle.h b/include/bounce/cloth/particle.h index b496662..6d96c4a 100644 --- a/include/bounce/cloth/particle.h +++ b/include/bounce/cloth/particle.h @@ -152,6 +152,9 @@ private: b3Particle(const b3ParticleDef& def, b3Cloth* cloth); ~b3Particle(); + // Synchronize AABB + void Synchronize(); + // Type b3ParticleType m_type; @@ -193,12 +196,15 @@ private: // Solution b3Vec3 m_x; - // + // Parent cloth b3Cloth* m_cloth; // Contact b3ParticleBodyContact m_bodyContact; + // Particle tree identifier + u32 m_treeId; + // b3Particle* m_prev; @@ -220,6 +226,8 @@ inline void b3Particle::SetPosition(const b3Vec3& position) { m_position = position; m_translation.SetZero(); + + Synchronize(); } inline const b3Vec3& b3Particle::GetPosition() const @@ -249,6 +257,8 @@ inline float32 b3Particle::GetMass() const inline void b3Particle::SetRadius(float32 radius) { m_radius = radius; + + Synchronize(); } inline float32 b3Particle::GetRadius() const diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index b86c517..a5be051 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -22,10 +22,14 @@ #include #include #include + #include +#include #include #include + #include + #include static B3_FORCE_INLINE u32 b3NextIndex(u32 i) @@ -157,9 +161,8 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : const b3ClothMesh* m = m_mesh; - m_vertexParticles = (b3Particle**)b3Alloc(m->vertexCount * sizeof(b3Particle*)); - // Create particles + m_vertexParticles = (b3Particle**)b3Alloc(m->vertexCount * sizeof(b3Particle*)); for (u32 i = 0; i < m->vertexCount; ++i) { b3ParticleDef pd; @@ -170,6 +173,11 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : p->m_vertex = i; + b3AABB3 aabb; + aabb.Set(p->m_position, p->m_radius); + + p->m_treeId = m_particleTree.InsertNode(aabb, p); + m_vertexParticles[i] = p; } @@ -245,6 +253,9 @@ b3Cloth::~b3Cloth() { b3Particle* p0 = p; p = p->m_next; + + m_particleTree.RemoveNode(p0->m_treeId); + p0->~b3Particle(); } @@ -264,7 +275,14 @@ b3Particle* b3Cloth::CreateParticle(const b3ParticleDef& def) { void* mem = m_particleBlocks.Allocate(); b3Particle* p = new(mem) b3Particle(def, this); + + b3AABB3 aabb; + aabb.Set(p->m_position, p->m_radius); + + p->m_treeId = m_particleTree.InsertNode(aabb, p); + m_particleList.PushFront(p); + return p; } @@ -275,6 +293,8 @@ void b3Cloth::DestroyParticle(b3Particle* particle) m_vertexParticles[particle->m_vertex] = NULL; } + m_particleTree.RemoveNode(particle->m_treeId); + m_particleList.Remove(particle); particle->~b3Particle(); m_particleBlocks.Free(particle); @@ -399,9 +419,43 @@ bool b3Cloth::RayCast(b3RayCastOutput* output, const b3RayCastInput* input, u32 return b3RayCast(output, input, v1, v2, v3); } -void b3Cloth::UpdateBodyContacts() +class b3ClothUpdateContactsQueryListener : public b3QueryListener { - B3_PROFILE("Cloth Update Body Contacts"); +public: + bool ReportShape(b3Shape* shape) + { + b3Body* body = shape->GetBody(); + + if (body->GetType() != e_staticBody) + { + return true; + } + + b3Transform xf = body->GetTransform(); + + b3TestSphereOutput output; + if (shape->TestSphere(&output, sphere, xf)) + { + if (output.separation < bestSeparation) + { + bestShape = shape; + bestSeparation = output.separation; + bestPoint = output.point; + bestNormal = output.normal; + } + } + } + + b3Sphere sphere; + b3Shape* bestShape; + float32 bestSeparation; + b3Vec3 bestPoint; + b3Vec3 bestNormal; +}; + +void b3Cloth::UpdateContacts() +{ + B3_PROFILE("Cloth Update Contacts"); // Is there a world attached to this cloth? if (m_world == nullptr) @@ -412,57 +466,32 @@ void b3Cloth::UpdateBodyContacts() // Create contacts for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) { - b3Sphere s1; - s1.vertex = p->m_position; - s1.radius = p->m_radius; - - // Find the deepest penetration - b3Shape* bestShape = nullptr; - float32 bestSeparation = 0.0f; - b3Vec3 bestPoint(0.0f, 0.0f, 0.0f); - b3Vec3 bestNormal(0.0f, 0.0f, 0.0f); - - for (b3Body* body = m_world->GetBodyList().m_head; body; body = body->GetNext()) + if (p->m_type != e_dynamicParticle) { - if (p->m_type != e_dynamicParticle) - { - continue; - } - - if (body->GetType() != e_staticBody) - { - continue; - } - - b3Transform xf = body->GetTransform(); - for (b3Shape* shape = body->GetShapeList().m_head; shape; shape = shape->GetNext()) - { - b3TestSphereOutput output; - if (shape->TestSphere(&output, s1, xf)) - { - if (output.separation < bestSeparation) - { - bestShape = shape; - bestSeparation = output.separation; - bestPoint = output.point; - bestNormal = output.normal; - } - } - } + continue; } - if (bestShape == nullptr) + b3AABB3 aabb = m_particleTree.GetAABB(p->m_treeId); + + b3ClothUpdateContactsQueryListener listener; + listener.sphere.vertex = p->m_position; + listener.sphere.radius = p->m_radius; + listener.bestShape = nullptr; + listener.bestSeparation = 0.0f; + + m_world->QueryAABB(&listener, aabb); + + if (listener.bestShape == nullptr) { p->m_bodyContact.active = false; continue; } - // Ensure the the normal points from the particle 1 to shape 2 - b3Shape* shape = bestShape; + b3Shape* shape = listener.bestShape; b3Body* body = shape->GetBody(); - float32 separation = bestSeparation; - b3Vec3 point = bestPoint; - b3Vec3 normal = -bestNormal; + float32 separation = listener.bestSeparation; + b3Vec3 point = listener.bestPoint; + b3Vec3 normal = -listener.bestNormal; b3ParticleBodyContact* c = &p->m_bodyContact; @@ -522,12 +551,6 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u solver.Solve(dt, gravity, velocityIterations, positionIterations); } -void b3Cloth::UpdateContacts() -{ - // Update body contacts - UpdateBodyContacts(); -} - void b3Cloth::Step(float32 dt, u32 velocityIterations, u32 positionIterations) { B3_PROFILE("Cloth Step"); @@ -535,7 +558,7 @@ void b3Cloth::Step(float32 dt, u32 velocityIterations, u32 positionIterations) // Update contacts UpdateContacts(); - // Solve constraints, integrate state, clear forces and translations. + // Integrate state, solve constraints. if (dt > 0.0f) { Solve(dt, m_gravity, velocityIterations, positionIterations); @@ -547,6 +570,17 @@ void b3Cloth::Step(float32 dt, u32 velocityIterations, u32 positionIterations) p->m_force.SetZero(); p->m_translation.SetZero(); } + + // Synchronize particles + for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) + { + if (p->m_type == e_staticParticle) + { + continue; + } + + p->Synchronize(); + } } void b3Cloth::Draw() const diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index 665b27a..0daf1a8 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -379,7 +379,7 @@ void b3ClothSolver::SolveMPCG(b3DenseVec3& x, u32 iteration = 0; for (;;) { - if (iteration >= maxIterations) + if (iteration == maxIterations) { break; } diff --git a/src/bounce/cloth/particle.cpp b/src/bounce/cloth/particle.cpp index a0e9fad..3316a77 100644 --- a/src/bounce/cloth/particle.cpp +++ b/src/bounce/cloth/particle.cpp @@ -18,9 +18,6 @@ #include #include -#include -#include -#include void b3ParticleBodyContactWorldPoint::Initialize(const b3ParticleBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB) { @@ -60,6 +57,14 @@ b3Particle::~b3Particle() } +void b3Particle::Synchronize() +{ + b3AABB3 aabb; + aabb.Set(m_position, m_radius); + + m_cloth->m_particleTree.UpdateNode(m_treeId, aabb); +} + void b3Particle::SetType(b3ParticleType type) { if (m_type == type) @@ -74,6 +79,8 @@ void b3Particle::SetType(b3ParticleType type) { m_velocity.SetZero(); m_translation.SetZero(); + + Synchronize(); } m_bodyContact.active = false; diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index 388ea3b..fe97ac4 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -717,7 +717,7 @@ public: if (body->GetType() != e_staticBody) { - //continue; + // return true; } b3Transform xf = body->GetTransform(); From db949eeca0ff6fdec4b873ed9284e168e23d9e18 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 3 Jun 2019 18:37:32 -0300 Subject: [PATCH 126/198] Bugfix --- src/bounce/cloth/cloth.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index a5be051..4a7c1ec 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -172,12 +172,6 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : b3Particle* p = CreateParticle(pd); p->m_vertex = i; - - b3AABB3 aabb; - aabb.Set(p->m_position, p->m_radius); - - p->m_treeId = m_particleTree.InsertNode(aabb, p); - m_vertexParticles[i] = p; } From ef1ae1230b47ec724ea09e6fa037a8f326369790 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 3 Jun 2019 18:48:55 -0300 Subject: [PATCH 127/198] Put some comments and reference code --- src/bounce/softbody/softbody.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index fe97ac4..3bd7119 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -83,13 +83,18 @@ static B3_FORCE_INLINE void b3ComputeD(float32 out[36], float32 E, float32 nu) } } +// Compute B = S * N, +// where S is the operational matrix and N are the shape functions +// This is a 6 x 12 matrix +// A derivation and corresponding simplification for this matrix +// can be found here: +// https://github.com/erleben/OpenTissue/blob/master/OpenTissue/dynamics/fem/fem_compute_b.h static B3_FORCE_INLINE void b3ComputeB(float32 out[72], const b3Vec3& e10, const b3Vec3& e20, const b3Vec3& e30, float32 V) { - // Compute derivatives of shape functions N float32 inv6V = 1.0f / (6.0f * V); b3Vec3 B[4]; @@ -125,7 +130,7 @@ static B3_FORCE_INLINE void b3ComputeB(float32 out[72], float32 c4 = B[3].y; float32 d4 = B[3].z; - float32 MB[72] = + float32 B_out[72] = { b1, 0, 0, c1, d1, 0, 0, c1, 0, b1, 0, d1, @@ -146,7 +151,7 @@ static B3_FORCE_INLINE void b3ComputeB(float32 out[72], for (u32 i = 0; i < 72; ++i) { - out[i] = MB[i]; + out[i] = B_out[i]; } } @@ -502,11 +507,11 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) B3_ASSERT(V > 0.0f); - b3Vec3 e10 = p2 - p1; - b3Vec3 e20 = p3 - p1; - b3Vec3 e30 = p4 - p1; + b3Vec3 e1 = p2 - p1; + b3Vec3 e2 = p3 - p1; + b3Vec3 e3 = p4 - p1; - b3Mat33 E(e10, e20, e30); + b3Mat33 E(e1, e2, e3); e->invE = b3Inverse(E); @@ -516,7 +521,7 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) // 6 x 12 float32* B = e->B; - b3ComputeB(B, e10, e20, e30, V); + b3ComputeB(B, e1, e2, e3, V); // 12 x 6 float32 BT[72]; From 552970cfe74072e50f1acb1e35642106756a1304 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 3 Jun 2019 19:39:06 -0300 Subject: [PATCH 128/198] Return true --- src/bounce/cloth/cloth.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 4a7c1ec..84151dd 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -438,6 +438,8 @@ public: bestNormal = output.normal; } } + + return true; } b3Sphere sphere; From 43085c8cc1665e2846433bee59a6ab763c80bde3 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 4 Jun 2019 19:38:43 -0300 Subject: [PATCH 129/198] Pushed code to draw a plane --- examples/testbed/framework/draw.cpp | 86 +++++++++++++++++------------ examples/testbed/framework/draw.h | 4 ++ include/bounce/common/draw.h | 6 ++ include/bounce/common/math/mat33.h | 16 +++--- 4 files changed, 69 insertions(+), 43 deletions(-) diff --git a/examples/testbed/framework/draw.cpp b/examples/testbed/framework/draw.cpp index 3640a76..f9a5fed 100644 --- a/examples/testbed/framework/draw.cpp +++ b/examples/testbed/framework/draw.cpp @@ -251,24 +251,9 @@ void Draw::DrawSolidPolygon(const b3Vec3& normal, const b3Vec3* vertices, u32 co void Draw::DrawCircle(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color) { - // Build a tangent vector to normal. - b3Vec3 u = b3Cross(normal, b3Vec3(1.0f, 0.0f, 0.0f)); - b3Vec3 v = b3Cross(normal, b3Vec3(0.0f, 1.0f, 0.0f)); + b3Vec3 n1, n3; + b3ComputeBasis(normal, n1, n3); - // Handle edge cases (zero cross product). - b3Vec3 n1; - if (b3LengthSquared(u) > b3LengthSquared(v)) - { - n1 = u; - } - else - { - n1 = v; - } - - n1.Normalize(); - - // Build a quaternion to rotate the tangent about the normal. u32 kEdgeCount = 20; float32 kAngleInc = 2.0f * B3_PI / float32(kEdgeCount); b3Quat q(normal, kAngleInc); @@ -292,24 +277,9 @@ void Draw::DrawSolidCircle(const b3Vec3& normal, const b3Vec3& center, float32 r b3Color fillColor(color.r, color.g, color.b, color.a); b3Color frameColor(0.5f * color.r, 0.5f * color.g, 0.5f * color.b, 1.0f); - // Build a tangent vector to normal. - b3Vec3 u = b3Cross(normal, b3Vec3(1.0f, 0.0f, 0.0f)); - b3Vec3 v = b3Cross(normal, b3Vec3(0.0f, 1.0f, 0.0f)); + b3Vec3 n1, n3; + b3ComputeBasis(normal, n1, n3); - // Handle edge cases (zero cross product). - b3Vec3 n1; - if (b3LengthSquared(u) > b3LengthSquared(v)) - { - n1 = u; - } - else - { - n1 = v; - } - - n1.Normalize(); - - // Build a quaternion to rotate the tangent about the normal. const u32 kEdgeCount = 20; const float32 kAngleInc = 2.0f * B3_PI / float32(kEdgeCount); b3Quat q(normal, kAngleInc); @@ -460,6 +430,54 @@ void Draw::DrawAABB(const b3AABB3& aabb, const b3Color& color) DrawSegment(vs[1], vs[7], color); } +void Draw::DrawPlane(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color) +{ + b3Vec3 n1, n2; + b3ComputeBasis(normal, n1, n2); + + float32 scale = 2.0f * radius; + + // v1__v4 + // | | + // v2__v3 + b3Vec3 v1 = center - scale * n1 - scale * n2; + b3Vec3 v2 = center + scale * n1 - scale * n2; + b3Vec3 v3 = center + scale * n1 + scale * n2; + b3Vec3 v4 = center - scale * n1 + scale * n2; + + DrawSegment(v1, v2, color); + DrawSegment(v2, v3, color); + DrawSegment(v3, v4, color); + DrawSegment(v4, v1, color); + + DrawSegment(center, center + normal, color); +} + +void Draw::DrawSolidPlane(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color) +{ + b3Color frameColor(0.5f * color.r, 0.5f * color.g, 0.5f * color.b, 1.0f); + + b3Vec3 n1, n2; + b3ComputeBasis(normal, n1, n2); + + float32 scale = 2.0f * radius; + + b3Vec3 v1 = center - scale * n1 - scale * n2; + b3Vec3 v2 = center + scale * n1 - scale * n2; + b3Vec3 v3 = center + scale * n1 + scale * n2; + b3Vec3 v4 = center - scale * n1 + scale * n2; + + DrawSegment(v1, v2, frameColor); + DrawSegment(v2, v3, frameColor); + DrawSegment(v3, v4, frameColor); + DrawSegment(v4, v1, frameColor); + + DrawSegment(center, center + normal, frameColor); + + DrawSolidTriangle(normal, v1, v2, v3, color); + DrawSolidTriangle(normal, v3, v4, v1, color); +} + void Draw::DrawString(const b3Color& color, const b3Vec2& ps, const char* text, ...) { va_list args; diff --git a/examples/testbed/framework/draw.h b/examples/testbed/framework/draw.h index b4db22b..cd89acb 100644 --- a/examples/testbed/framework/draw.h +++ b/examples/testbed/framework/draw.h @@ -91,6 +91,10 @@ public: void DrawSolidCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Color& color); + void DrawPlane(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color); + + void DrawSolidPlane(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color); + void DrawAABB(const b3AABB3& aabb, const b3Color& color); void DrawTransform(const b3Transform& xf); diff --git a/include/bounce/common/draw.h b/include/bounce/common/draw.h index f6790b8..c1a3ce7 100644 --- a/include/bounce/common/draw.h +++ b/include/bounce/common/draw.h @@ -107,6 +107,12 @@ public : // Draw a solid capsule with segment and radius. virtual void DrawSolidCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Color& color) = 0; + // Draw a plane with center, normal and radius. + virtual void DrawPlane(const b3Vec3& center, const b3Vec3& normal, float32 radius, const b3Color& color) = 0; + + // Draw a solid plane with center, normal and radius. + virtual void DrawSolidPlane(const b3Vec3& center, const b3Vec3& normal, float32 radius, const b3Color& color) = 0; + // Draw a AABB. virtual void DrawAABB(const b3AABB3& aabb, const b3Color& color) = 0; diff --git a/include/bounce/common/math/mat33.h b/include/bounce/common/math/mat33.h index 897fdce..89da221 100644 --- a/include/bounce/common/math/mat33.h +++ b/include/bounce/common/math/mat33.h @@ -233,22 +233,20 @@ inline b3Mat33 b3Outer(const b3Vec3& a, const b3Vec3& b) // Compute an orthogonal basis given one of its vectors. // The vector must be normalized. -inline b3Mat33 b3Basis(const b3Vec3& a) +inline void b3ComputeBasis(const b3Vec3& a, b3Vec3& b, b3Vec3& c) { - // Box2D - b3Mat33 A; + // https://box2d.org/2014/02/computing-a-basis/ if (b3Abs(a.x) >= float32(0.57735027)) { - A.y.Set(a.y, -a.x, 0.0f); + b.Set(a.y, -a.x, 0.0f); } else { - A.y.Set(0.0f, a.z, -a.y); + b.Set(0.0f, a.z, -a.y); } - A.x = a; - A.y = b3Normalize(A.y); - A.z = b3Cross(a, A.y); - return A; + + b.Normalize(); + c = b3Cross(a, b); } // Rotation about the x-axis. From 56db3517f958db6f3626be2f155a00431b492471 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 4 Jun 2019 20:56:51 -0300 Subject: [PATCH 130/198] Draw solid stuff inside b3World --- examples/testbed/framework/draw.cpp | 171 +------------------------ examples/testbed/framework/draw.h | 14 +- examples/testbed/framework/test.cpp | 2 +- examples/testbed/tests/collide_test.h | 8 +- examples/testbed/tests/distance_test.h | 4 +- examples/testbed/tests/shape_cast.h | 10 +- include/bounce/common/draw.h | 24 ++-- include/bounce/dynamics/world.h | 11 +- src/bounce/dynamics/draw_world.cpp | 135 +++++++++++++++++-- src/bounce/rope/rope.cpp | 4 +- 10 files changed, 165 insertions(+), 218 deletions(-) diff --git a/examples/testbed/framework/draw.cpp b/examples/testbed/framework/draw.cpp index f9a5fed..fe1e305 100644 --- a/examples/testbed/framework/draw.cpp +++ b/examples/testbed/framework/draw.cpp @@ -302,16 +302,16 @@ void Draw::DrawSolidCircle(const b3Vec3& normal, const b3Vec3& center, float32 r void Draw::DrawSphere(const b3Vec3& center, float32 radius, const b3Color& color) { b3Transform xf; - xf.SetIdentity(); + xf.rotation.SetIdentity(); xf.position = center; m_wire->DrawSphere(radius, color, xf); } -void Draw::DrawSolidSphere(const b3Vec3& center, float32 radius, const b3Color& color) +void Draw::DrawSolidSphere(const b3Vec3& center, float32 radius, const b3Mat33& rotation, const b3Color& color) { b3Transform xf; - xf.SetIdentity(); + xf.rotation = rotation; xf.position = center; m_solid->DrawSphere(radius, color, xf); @@ -529,171 +529,6 @@ void Draw::DrawString(const b3Color& color, const char* text, ...) va_end(args); } -void Draw::DrawSolidSphere(const b3SphereShape* s, const b3Color& c, const b3Transform& xf) -{ - b3Transform xfc; - xfc.rotation = xf.rotation; - xfc.position = xf * s->m_center; - m_solid->DrawSphere(s->m_radius, c, xfc); -} - -void Draw::DrawSolidCapsule(const b3CapsuleShape* s, const b3Color& c, const b3Transform& xf) -{ - b3Vec3 c1 = s->m_centers[0]; - b3Vec3 c2 = s->m_centers[1]; - float32 height = b3Length(c1 - c2); - float32 radius = s->m_radius; - - { - b3Transform xfc; - xfc.rotation = xf.rotation; - xfc.position = xf * c1; - m_solid->DrawSphere(radius, c, xfc); - } - - if (height > 0.0f) - { - { - b3Mat33 R; - R.y = (1.0f / height) * (c1 - c2); - R.z = b3Perp(R.y); - R.x = b3Cross(R.y, R.z); - - b3Transform xfc; - xfc.position = xf * (0.5f * (c1 + c2)); - xfc.rotation = xf.rotation * R; - - m_solid->DrawCylinder(radius, height, c, xfc); - } - - { - b3Transform xfc; - xfc.rotation = xf.rotation; - xfc.position = xf * c2; - m_solid->DrawSphere(radius, c, xfc); - } - } -} - -void Draw::DrawSolidHull(const b3HullShape* s, const b3Color& c, const b3Transform& xf) -{ - const b3Hull* hull = s->m_hull; - - for (u32 i = 0; i < hull->faceCount; ++i) - { - const b3Face* face = hull->GetFace(i); - const b3HalfEdge* begin = hull->GetEdge(face->edge); - - b3Vec3 n = xf.rotation * hull->planes[i].normal; - - const b3HalfEdge* edge = hull->GetEdge(begin->next); - do - { - u32 i1 = begin->origin; - u32 i2 = edge->origin; - const b3HalfEdge* next = hull->GetEdge(edge->next); - u32 i3 = next->origin; - - b3Vec3 p1 = xf * hull->vertices[i1]; - b3Vec3 p2 = xf * hull->vertices[i2]; - b3Vec3 p3 = xf * hull->vertices[i3]; - - m_triangles->Vertex(p1, c, n); - m_triangles->Vertex(p2, c, n); - m_triangles->Vertex(p3, c, n); - - edge = next; - } while (hull->GetEdge(edge->next) != begin); - } -} - -void Draw::DrawSolidMesh(const b3MeshShape* s, const b3Color& c, const b3Transform& xf) -{ - const b3Mesh* mesh = s->m_mesh; - for (u32 i = 0; i < mesh->triangleCount; ++i) - { - const b3Triangle* t = mesh->triangles + i; - - b3Vec3 p1 = xf * mesh->vertices[t->v1]; - b3Vec3 p2 = xf * mesh->vertices[t->v2]; - b3Vec3 p3 = xf * mesh->vertices[t->v3]; - - b3Vec3 n1 = b3Cross(p2 - p1, p3 - p1); - n1.Normalize(); - - m_triangles->Vertex(p1, c, n1); - m_triangles->Vertex(p2, c, n1); - m_triangles->Vertex(p3, c, n1); - - b3Vec3 n2 = -n1; - - m_triangles->Vertex(p1, c, n2); - m_triangles->Vertex(p3, c, n2); - m_triangles->Vertex(p2, c, n2); - } -} - -void Draw::DrawSolidShape(const b3Shape* s, const b3Color& c, const b3Transform& xf) -{ - switch (s->GetType()) - { - case e_sphereShape: - { - DrawSolidSphere((b3SphereShape*)s, c, xf); - break; - } - case e_capsuleShape: - { - DrawSolidCapsule((b3CapsuleShape*)s, c, xf); - break; - } - case e_hullShape: - { - DrawSolidHull((b3HullShape*)s, c, xf); - break; - } - case e_meshShape: - { - DrawSolidMesh((b3MeshShape*)s, c, xf); - break; - } - default: - { - break; - } - } -} - -void Draw::DrawSolidShapes(const b3World& world) -{ - for (b3Body* b = world.GetBodyList().m_head; b; b = b->GetNext()) - { - b3Color c; - if (b->IsAwake() == false) - { - c = b3Color(0.5f, 0.25f, 0.25f, 1.0f); - } - else if (b->GetType() == e_staticBody) - { - c = b3Color(0.5f, 0.5f, 0.5f, 1.0f); - } - else if (b->GetType() == e_dynamicBody) - { - c = b3Color(1.0f, 0.5f, 0.5f, 1.0f); - } - else - { - c = b3Color(0.5f, 0.5f, 1.0f, 1.0f); - } - - b3Transform xf = b->GetTransform(); - for (b3Shape* s = b->GetShapeList().m_head; s; s = s->GetNext()) - { - DrawSolidShape(s, c, xf); - } - } -} - void Draw::Flush() { m_triangles->Flush(); diff --git a/examples/testbed/framework/draw.h b/examples/testbed/framework/draw.h index cd89acb..4f5bda4 100644 --- a/examples/testbed/framework/draw.h +++ b/examples/testbed/framework/draw.h @@ -85,7 +85,7 @@ public: void DrawSphere(const b3Vec3& center, float32 radius, const b3Color& color); - void DrawSolidSphere(const b3Vec3& center, float32 radius, const b3Color& color); + void DrawSolidSphere(const b3Vec3& center, float32 radius, const b3Mat33& rotation, const b3Color& color); void DrawCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Color& color); @@ -105,18 +105,6 @@ public: void DrawString(const b3Color& color, const char* string, ...); - void DrawSolidSphere(const b3SphereShape* s, const b3Color& c, const b3Transform& xf); - - void DrawSolidCapsule(const b3CapsuleShape* s, const b3Color& c, const b3Transform& xf); - - void DrawSolidHull(const b3HullShape* s, const b3Color& c, const b3Transform& xf); - - void DrawSolidMesh(const b3MeshShape* s, const b3Color& c, const b3Transform& xf); - - void DrawSolidShape(const b3Shape* s, const b3Color& c, const b3Transform& xf); - - void DrawSolidShapes(const b3World& world); - void Flush(); private: friend struct DrawPoints; diff --git a/examples/testbed/framework/test.cpp b/examples/testbed/framework/test.cpp index d6dc8b0..fe3ff77 100644 --- a/examples/testbed/framework/test.cpp +++ b/examples/testbed/framework/test.cpp @@ -88,7 +88,7 @@ void Test::Step() if (g_settings->drawTriangles) { - g_draw->DrawSolidShapes(m_world); + m_world.DrawSolid(); } if (g_settings->drawStats) diff --git a/examples/testbed/tests/collide_test.h b/examples/testbed/tests/collide_test.h index 560df43..f76d704 100644 --- a/examples/testbed/tests/collide_test.h +++ b/examples/testbed/tests/collide_test.h @@ -50,13 +50,13 @@ public: g_draw->DrawSegment(pw, pw + wm.points[i].normal, b3Color_white); } - m_world.DrawShape(m_xfA, m_shapeA); - m_world.DrawShape(m_xfB, m_shapeB); + m_world.DrawShape(m_xfA, m_shapeA, b3Color_black); + m_world.DrawShape(m_xfB, m_shapeB, b3Color_black); g_draw->Flush(); - g_draw->DrawSolidShape(m_shapeA, b3Color(1.0f, 1.0f, 1.0f, 0.25f), m_xfA); - g_draw->DrawSolidShape(m_shapeB, b3Color(1.0f, 1.0f, 1.0f, 0.25f), m_xfB); + m_world.DrawSolidShape(m_xfA, m_shapeA, b3Color(1.0f, 1.0f, 1.0f, 0.25f)); + m_world.DrawSolidShape(m_xfB, m_shapeB, b3Color(1.0f, 1.0f, 1.0f, 0.25f)); g_draw->DrawString(b3Color_white, "Left/Right/Up/Down Arrow - Translate shape"); g_draw->DrawString(b3Color_white, "X/Y/Z - Rotate shape"); diff --git a/examples/testbed/tests/distance_test.h b/examples/testbed/tests/distance_test.h index e5ea4ac..4f659c7 100644 --- a/examples/testbed/tests/distance_test.h +++ b/examples/testbed/tests/distance_test.h @@ -68,8 +68,8 @@ public: g_draw->DrawTransform(m_xfA); g_draw->DrawTransform(m_xfB); - m_world.DrawShape(m_xfA, &m_shapeA); - m_world.DrawShape(m_xfB, &m_shapeB); + m_world.DrawShape(m_xfA, &m_shapeA, b3Color_black); + m_world.DrawShape(m_xfB, &m_shapeB, b3Color_black); g_draw->DrawString(b3Color_white, "Left/Right/Up/Down Arrow - Translate shape"); g_draw->DrawString(b3Color_white, "X/Y/Z - Rotate shape"); diff --git a/examples/testbed/tests/shape_cast.h b/examples/testbed/tests/shape_cast.h index 392ce0c..f08e083 100644 --- a/examples/testbed/tests/shape_cast.h +++ b/examples/testbed/tests/shape_cast.h @@ -30,11 +30,11 @@ public: g_draw->DrawTransform(m_xfA); g_draw->DrawTransform(m_xfB); - m_world.DrawShape(m_xfA, &m_shapeA); - m_world.DrawShape(m_xfB, &m_shapeB); + m_world.DrawShape(m_xfA, &m_shapeA, b3Color_black); + m_world.DrawShape(m_xfB, &m_shapeB, b3Color_black); - g_draw->DrawSolidShape(&m_shapeA, b3Color_white, m_xfA); - g_draw->DrawSolidShape(&m_shapeB, b3Color_white, m_xfB); + m_world.DrawSolidShape(m_xfA, &m_shapeA, b3Color_white); + m_world.DrawSolidShape(m_xfB, &m_shapeB, b3Color_white); b3Vec3 translationB = -100.0f * b3Vec3_x; g_draw->DrawSegment(m_xfB.position, m_xfB.position + translationB, b3Color_white); @@ -53,7 +53,7 @@ public: xfB.rotation = m_xfB.rotation; xfB.position = m_xfB.position + out.t * translationB; - m_world.DrawShape(xfB, &m_shapeB); + m_world.DrawShape(xfB, &m_shapeB, b3Color_black); } } diff --git a/include/bounce/common/draw.h b/include/bounce/common/draw.h index c1a3ce7..9030f44 100644 --- a/include/bounce/common/draw.h +++ b/include/bounce/common/draw.h @@ -95,24 +95,24 @@ public : // Draw a solid circle with center, normal, and radius. virtual void DrawSolidCircle(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color) = 0; - // Draw a sphere with center and radius. - virtual void DrawSphere(const b3Vec3& center, float32 radius, const b3Color& color) = 0; - - // Draw a solid sphere with center and radius. - virtual void DrawSolidSphere(const b3Vec3& center, float32 radius, const b3Color& color) = 0; - - // Draw a capsule with segment and radius. - virtual void DrawCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Color& color) = 0; - - // Draw a solid capsule with segment and radius. - virtual void DrawSolidCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Color& color) = 0; - // Draw a plane with center, normal and radius. virtual void DrawPlane(const b3Vec3& center, const b3Vec3& normal, float32 radius, const b3Color& color) = 0; // Draw a solid plane with center, normal and radius. virtual void DrawSolidPlane(const b3Vec3& center, const b3Vec3& normal, float32 radius, const b3Color& color) = 0; + // Draw a sphere with center, and radius. + virtual void DrawSphere(const b3Vec3& center, float32 radius, const b3Color& color) = 0; + + // Draw a solid sphere with center, radius, and rotation. + virtual void DrawSolidSphere(const b3Vec3& center, float32 radius, const b3Mat33& rotation, const b3Color& color) = 0; + + // Draw a capsule with segment, and radius. + virtual void DrawCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Color& color) = 0; + + // Draw a solid capsule with segment, and radius. + virtual void DrawSolidCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Color& color) = 0; + // Draw a AABB. virtual void DrawAABB(const b3AABB3& aabb, const b3Color& color) = 0; diff --git a/include/bounce/dynamics/world.h b/include/bounce/dynamics/world.h index d33a5a8..a60d72d 100644 --- a/include/bounce/dynamics/world.h +++ b/include/bounce/dynamics/world.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -121,9 +122,15 @@ public: // Draw the entities in this world. void Draw() const; + // Draw solid the entities in this world. + void DrawSolid() const; + // Draw a shape. - void DrawShape(const b3Transform& xf, const b3Shape* shape) const; -private : + void DrawShape(const b3Transform& xf, const b3Shape* shape, const b3Color& color) const; + + // Draw solid a shape. + void DrawSolidShape(const b3Transform& xf, const b3Shape* shape, const b3Color& color) const; +private: enum b3Flags { e_shapeAddedFlag = 0x0001, diff --git a/src/bounce/dynamics/draw_world.cpp b/src/bounce/dynamics/draw_world.cpp index 1d197c8..802b1e4 100644 --- a/src/bounce/dynamics/draw_world.cpp +++ b/src/bounce/dynamics/draw_world.cpp @@ -51,7 +51,7 @@ void b3World::Draw() const const b3Transform& xf = b->GetTransform(); for (b3Shape* s = b->m_shapeList.m_head; s; s = s->m_next) { - DrawShape(xf, s); + DrawShape(xf, s, b3Color_black); } } } @@ -142,16 +142,15 @@ void b3World::Draw() const } } -void b3World::DrawShape(const b3Transform& xf, const b3Shape* shape) const +void b3World::DrawShape(const b3Transform& xf, const b3Shape* shape, const b3Color& color) const { - b3Color wireColor(0.0f, 0.0f, 0.0f); switch (shape->GetType()) { case e_sphereShape: { const b3SphereShape* sphere = (b3SphereShape*)shape; b3Vec3 p = xf * sphere->m_center; - b3Draw_draw->DrawPoint(p, 4.0f, wireColor); + b3Draw_draw->DrawPoint(p, 4.0f, color); break; } case e_capsuleShape: @@ -159,9 +158,9 @@ void b3World::DrawShape(const b3Transform& xf, const b3Shape* shape) const const b3CapsuleShape* capsule = (b3CapsuleShape*)shape; b3Vec3 p1 = xf * capsule->m_centers[0]; b3Vec3 p2 = xf * capsule->m_centers[1]; - b3Draw_draw->DrawPoint(p1, 4.0f, wireColor); - b3Draw_draw->DrawPoint(p2, 4.0f, wireColor); - b3Draw_draw->DrawSegment(p1, p2, wireColor); + b3Draw_draw->DrawPoint(p1, 4.0f, color); + b3Draw_draw->DrawPoint(p2, 4.0f, color); + b3Draw_draw->DrawSegment(p1, p2, color); break; } case e_hullShape: @@ -176,7 +175,7 @@ void b3World::DrawShape(const b3Transform& xf, const b3Shape* shape) const b3Vec3 p1 = xf * hull->vertices[edge->origin]; b3Vec3 p2 = xf * hull->vertices[twin->origin]; - b3Draw_draw->DrawSegment(p1, p2, wireColor); + b3Draw_draw->DrawSegment(p1, p2, color); } break; } @@ -192,7 +191,7 @@ void b3World::DrawShape(const b3Transform& xf, const b3Shape* shape) const b3Vec3 p2 = xf * mesh->vertices[t->v2]; b3Vec3 p3 = xf * mesh->vertices[t->v3]; - b3Draw_draw->DrawTriangle(p1, p2, p3, wireColor); + b3Draw_draw->DrawTriangle(p1, p2, p3, color); } break; } @@ -201,4 +200,122 @@ void b3World::DrawShape(const b3Transform& xf, const b3Shape* shape) const break; } }; +} + +void b3World::DrawSolid() const +{ + for (b3Body* b = m_bodyList.m_head; b; b = b->GetNext()) + { + b3Color c; + if (b->IsAwake() == false) + { + c = b3Color(0.5f, 0.25f, 0.25f, 1.0f); + } + else if (b->GetType() == e_staticBody) + { + c = b3Color(0.5f, 0.5f, 0.5f, 1.0f); + } + else if (b->GetType() == e_dynamicBody) + { + c = b3Color(1.0f, 0.5f, 0.5f, 1.0f); + } + else + { + c = b3Color(0.5f, 0.5f, 1.0f, 1.0f); + } + + b3Transform xf = b->GetTransform(); + for (b3Shape* s = b->GetShapeList().m_head; s; s = s->GetNext()) + { + DrawSolidShape(xf, s, c); + } + } +} + +void b3World::DrawSolidShape(const b3Transform& xf, const b3Shape* shape, const b3Color& color) const +{ + switch (shape->GetType()) + { + case e_sphereShape: + { + const b3SphereShape* sphere = (b3SphereShape*)shape; + + b3Vec3 center = xf * sphere->m_center; + + b3Draw_draw->DrawSolidSphere(center, sphere->m_radius, xf.rotation, color); + + break; + } + case e_capsuleShape: + { + const b3CapsuleShape* capsule = (b3CapsuleShape*)shape; + + b3Vec3 c1 = xf * capsule->m_centers[0]; + b3Vec3 c2 = xf * capsule->m_centers[1]; + + b3Draw_draw->DrawSolidCapsule(c1, c2, capsule->m_radius, color); + + break; + } + case e_hullShape: + { + const b3HullShape* hullShape = (b3HullShape*)shape; + + const b3Hull* hull = hullShape->m_hull; + + for (u32 i = 0; i < hull->faceCount; ++i) + { + const b3Face* face = hull->GetFace(i); + const b3HalfEdge* begin = hull->GetEdge(face->edge); + + b3Vec3 n = xf.rotation * hull->planes[i].normal; + + const b3HalfEdge* edge = hull->GetEdge(begin->next); + do + { + u32 i1 = begin->origin; + u32 i2 = edge->origin; + const b3HalfEdge* next = hull->GetEdge(edge->next); + u32 i3 = next->origin; + + b3Vec3 p1 = xf * hull->vertices[i1]; + b3Vec3 p2 = xf * hull->vertices[i2]; + b3Vec3 p3 = xf * hull->vertices[i3]; + + b3Draw_draw->DrawSolidTriangle(n, p1, p2, p3, color); + + edge = next; + } while (hull->GetEdge(edge->next) != begin); + } + + break; + } + case e_meshShape: + { + const b3MeshShape* meshShape = (b3MeshShape*)shape; + + const b3Mesh* mesh = meshShape->m_mesh; + for (u32 i = 0; i < mesh->triangleCount; ++i) + { + const b3Triangle* t = mesh->triangles + i; + + b3Vec3 p1 = xf * mesh->vertices[t->v1]; + b3Vec3 p2 = xf * mesh->vertices[t->v2]; + b3Vec3 p3 = xf * mesh->vertices[t->v3]; + + b3Vec3 n1 = b3Cross(p2 - p1, p3 - p1); + n1.Normalize(); + b3Draw_draw->DrawSolidTriangle(n1, p1, p2, p3, color); + + b3Vec3 n2 = -n1; + b3Draw_draw->DrawSolidTriangle(n2, p3, p2, p1, color); + } + + break; + } + default: + { + break; + } + }; } \ No newline at end of file diff --git a/src/bounce/rope/rope.cpp b/src/bounce/rope/rope.cpp index 38824d7..d74582d 100644 --- a/src/bounce/rope/rope.cpp +++ b/src/bounce/rope/rope.cpp @@ -480,7 +480,7 @@ void b3Rope::Draw() const b3RopeBody* b = m_links; b3Draw_draw->DrawTransform(b->m_X); - b3Draw_draw->DrawSolidSphere(b->m_X.position, 0.2f, b3Color_green); + b3Draw_draw->DrawSolidSphere(b->m_X.position, 0.2f, b->m_X.rotation, b3Color_green); } for (u32 i = 1; i < m_count; ++i) @@ -498,6 +498,6 @@ void b3Rope::Draw() const b3Draw_draw->DrawPoint(X_J0.position, 5.0f, b3Color_red); b3Draw_draw->DrawTransform(b->m_X); - b3Draw_draw->DrawSolidSphere(b->m_X.position, 0.2f, b3Color_green); + b3Draw_draw->DrawSolidSphere(b->m_X.position, 0.2f, b->m_X.rotation, b3Color_green); } } \ No newline at end of file From 53e1f3a0be60f25a5dcce0b5e4a0a87097c4403e Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 4 Jun 2019 21:35:11 -0300 Subject: [PATCH 131/198] Bugfix --- examples/testbed/framework/draw.cpp | 26 +++++++++++++------------- examples/testbed/framework/draw.h | 2 +- include/bounce/common/draw.h | 4 ++-- src/bounce/dynamics/draw_world.cpp | 5 +---- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/examples/testbed/framework/draw.cpp b/examples/testbed/framework/draw.cpp index fe1e305..c3c431a 100644 --- a/examples/testbed/framework/draw.cpp +++ b/examples/testbed/framework/draw.cpp @@ -341,15 +341,15 @@ void Draw::DrawCapsule(const b3Vec3& c1, const b3Vec3& c2, float32 radius, const } } -void Draw::DrawSolidCapsule(const b3Vec3& c1, const b3Vec3& c2, float32 radius, const b3Color& c) +void Draw::DrawSolidCapsule(const b3Vec3& c1, const b3Vec3& c2, float32 radius, const b3Transform& transform, const b3Color& c) { float32 height = b3Length(c1 - c2); { - b3Transform xfc; - xfc.rotation.SetIdentity(); - xfc.position = c1; - m_solid->DrawSphere(radius, c, xfc); + b3Transform xf; + xf.rotation = transform.rotation; + xf.position = transform * c1; + m_solid->DrawSphere(radius, c, xf); } if (height > 0.0f) @@ -360,18 +360,18 @@ void Draw::DrawSolidCapsule(const b3Vec3& c1, const b3Vec3& c2, float32 radius, R.z = b3Perp(R.y); R.x = b3Cross(R.y, R.z); - b3Transform xfc; - xfc.position = 0.5f * (c1 + c2); - xfc.rotation = R; + b3Transform xf; + xf.position = transform * (0.5f * (c1 + c2)); + xf.rotation = transform.rotation * R; - m_solid->DrawCylinder(radius, height, c, xfc); + m_solid->DrawCylinder(radius, height, c, xf); } { - b3Transform xfc; - xfc.rotation.SetIdentity(); - xfc.position = c2; - m_solid->DrawSphere(radius, c, xfc); + b3Transform xf; + xf.rotation = transform.rotation; + xf.position = transform * c2; + m_solid->DrawSphere(radius, c, xf); } } } diff --git a/examples/testbed/framework/draw.h b/examples/testbed/framework/draw.h index 4f5bda4..34b8551 100644 --- a/examples/testbed/framework/draw.h +++ b/examples/testbed/framework/draw.h @@ -89,7 +89,7 @@ public: void DrawCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Color& color); - void DrawSolidCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Color& color); + void DrawSolidCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Transform& transform, const b3Color& color); void DrawPlane(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color); diff --git a/include/bounce/common/draw.h b/include/bounce/common/draw.h index 9030f44..adb6028 100644 --- a/include/bounce/common/draw.h +++ b/include/bounce/common/draw.h @@ -110,8 +110,8 @@ public : // Draw a capsule with segment, and radius. virtual void DrawCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Color& color) = 0; - // Draw a solid capsule with segment, and radius. - virtual void DrawSolidCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Color& color) = 0; + // Draw a solid capsule with local segment, radius, and transform. + virtual void DrawSolidCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Transform& transform, const b3Color& color) = 0; // Draw a AABB. virtual void DrawAABB(const b3AABB3& aabb, const b3Color& color) = 0; diff --git a/src/bounce/dynamics/draw_world.cpp b/src/bounce/dynamics/draw_world.cpp index 802b1e4..741286f 100644 --- a/src/bounce/dynamics/draw_world.cpp +++ b/src/bounce/dynamics/draw_world.cpp @@ -250,10 +250,7 @@ void b3World::DrawSolidShape(const b3Transform& xf, const b3Shape* shape, const { const b3CapsuleShape* capsule = (b3CapsuleShape*)shape; - b3Vec3 c1 = xf * capsule->m_centers[0]; - b3Vec3 c2 = xf * capsule->m_centers[1]; - - b3Draw_draw->DrawSolidCapsule(c1, c2, capsule->m_radius, color); + b3Draw_draw->DrawSolidCapsule(capsule->m_centers[0], capsule->m_centers[1], capsule->m_radius, xf, color); break; } From b277b8b588bd6cceade08346aec8445ca24abf5b Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 4 Jun 2019 21:47:01 -0300 Subject: [PATCH 132/198] Draw capsule in world space --- examples/testbed/framework/draw.cpp | 17 ++++++++--------- examples/testbed/framework/draw.h | 2 +- include/bounce/common/draw.h | 4 ++-- src/bounce/dynamics/draw_world.cpp | 5 ++++- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/examples/testbed/framework/draw.cpp b/examples/testbed/framework/draw.cpp index c3c431a..b1056cc 100644 --- a/examples/testbed/framework/draw.cpp +++ b/examples/testbed/framework/draw.cpp @@ -341,14 +341,14 @@ void Draw::DrawCapsule(const b3Vec3& c1, const b3Vec3& c2, float32 radius, const } } -void Draw::DrawSolidCapsule(const b3Vec3& c1, const b3Vec3& c2, float32 radius, const b3Transform& transform, const b3Color& c) +void Draw::DrawSolidCapsule(const b3Vec3& c1, const b3Vec3& c2, float32 radius, const b3Mat33& rotation, const b3Color& c) { float32 height = b3Length(c1 - c2); { b3Transform xf; - xf.rotation = transform.rotation; - xf.position = transform * c1; + xf.rotation = rotation; + xf.position = c1; m_solid->DrawSphere(radius, c, xf); } @@ -357,20 +357,19 @@ void Draw::DrawSolidCapsule(const b3Vec3& c1, const b3Vec3& c2, float32 radius, { b3Mat33 R; R.y = (1.0f / height) * (c1 - c2); - R.z = b3Perp(R.y); - R.x = b3Cross(R.y, R.z); + b3ComputeBasis(R.y, R.z, R.x); b3Transform xf; - xf.position = transform * (0.5f * (c1 + c2)); - xf.rotation = transform.rotation * R; + xf.position = (0.5f * (c1 + c2)); + xf.rotation = R; m_solid->DrawCylinder(radius, height, c, xf); } { b3Transform xf; - xf.rotation = transform.rotation; - xf.position = transform * c2; + xf.rotation = rotation; + xf.position = c2; m_solid->DrawSphere(radius, c, xf); } } diff --git a/examples/testbed/framework/draw.h b/examples/testbed/framework/draw.h index 34b8551..32acb8f 100644 --- a/examples/testbed/framework/draw.h +++ b/examples/testbed/framework/draw.h @@ -89,7 +89,7 @@ public: void DrawCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Color& color); - void DrawSolidCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Transform& transform, const b3Color& color); + void DrawSolidCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Mat33& rotation, const b3Color& color); void DrawPlane(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color); diff --git a/include/bounce/common/draw.h b/include/bounce/common/draw.h index adb6028..7865711 100644 --- a/include/bounce/common/draw.h +++ b/include/bounce/common/draw.h @@ -110,8 +110,8 @@ public : // Draw a capsule with segment, and radius. virtual void DrawCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Color& color) = 0; - // Draw a solid capsule with local segment, radius, and transform. - virtual void DrawSolidCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Transform& transform, const b3Color& color) = 0; + // Draw a solid capsule with local segment, radius, and rotation. + virtual void DrawSolidCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Mat33& rotation, const b3Color& color) = 0; // Draw a AABB. virtual void DrawAABB(const b3AABB3& aabb, const b3Color& color) = 0; diff --git a/src/bounce/dynamics/draw_world.cpp b/src/bounce/dynamics/draw_world.cpp index 741286f..5bd5477 100644 --- a/src/bounce/dynamics/draw_world.cpp +++ b/src/bounce/dynamics/draw_world.cpp @@ -250,7 +250,10 @@ void b3World::DrawSolidShape(const b3Transform& xf, const b3Shape* shape, const { const b3CapsuleShape* capsule = (b3CapsuleShape*)shape; - b3Draw_draw->DrawSolidCapsule(capsule->m_centers[0], capsule->m_centers[1], capsule->m_radius, xf, color); + b3Vec3 c1 = xf * capsule->m_centers[0]; + b3Vec3 c2 = xf * capsule->m_centers[1]; + + b3Draw_draw->DrawSolidCapsule(c1, c2, capsule->m_radius, xf.rotation, color); break; } From bb3156c3289c4edfdc71cfa93e40acdc1452ddb2 Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 4 Jun 2019 21:47:22 -0300 Subject: [PATCH 133/198] Update draw.h --- include/bounce/common/draw.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/bounce/common/draw.h b/include/bounce/common/draw.h index 7865711..7a78162 100644 --- a/include/bounce/common/draw.h +++ b/include/bounce/common/draw.h @@ -110,7 +110,7 @@ public : // Draw a capsule with segment, and radius. virtual void DrawCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Color& color) = 0; - // Draw a solid capsule with local segment, radius, and rotation. + // Draw a solid capsule with segment, radius, and rotation. virtual void DrawSolidCapsule(const b3Vec3& p1, const b3Vec3& p2, float32 radius, const b3Mat33& rotation, const b3Color& color) = 0; // Draw a AABB. From b3c2fcf3f899298b345982058e16af2db296b8de Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 5 Jun 2019 08:59:58 -0300 Subject: [PATCH 134/198] Silence Triangle --- src/bounce/cloth/garment/garment_mesh.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bounce/cloth/garment/garment_mesh.cpp b/src/bounce/cloth/garment/garment_mesh.cpp index b42be19..394eab3 100644 --- a/src/bounce/cloth/garment/garment_mesh.cpp +++ b/src/bounce/cloth/garment/garment_mesh.cpp @@ -106,10 +106,11 @@ static void b3Set(b3SewingPatternMesh* mesh, float32 desiredArea, const b3Sewing mid.segmentmarkerlist = NULL; // Run triangulation + // Q - quiet // z - zero based indices // p - PSLG // c - preserve the convex hull - triangulate("zpc", &in, &mid, NULL); + triangulate("Qzpc", &in, &mid, NULL); // Refine @@ -131,11 +132,12 @@ static void b3Set(b3SewingPatternMesh* mesh, float32 desiredArea, const b3Sewing out.segmentmarkerlist = NULL; // Run triangulation + // Q - quiet // z - zero based indices // p - PSLG // c - preserve the convex hull // r - read triangles - triangulate("zpcra", &mid, &out, NULL); + triangulate("Qzpcra", &mid, &out, NULL); // The first vertices of the output structure must be the vertices of the input structure. for (int i = 0; i < in.numberofpoints; ++i) From 2e9a8d7b249cbd7470fb0332bffe04733d3065f5 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 5 Jun 2019 09:00:15 -0300 Subject: [PATCH 135/198] Code consistency --- examples/testbed/framework/draw.cpp | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/examples/testbed/framework/draw.cpp b/examples/testbed/framework/draw.cpp index b1056cc..ba206e6 100644 --- a/examples/testbed/framework/draw.cpp +++ b/examples/testbed/framework/draw.cpp @@ -322,10 +322,10 @@ void Draw::DrawCapsule(const b3Vec3& c1, const b3Vec3& c2, float32 radius, const float32 height = b3Length(c1 - c2); { - b3Transform xfc; - xfc.rotation.SetIdentity(); - xfc.position = c1; - m_wire->DrawSphere(radius, color, xfc); + b3Transform xf; + xf.rotation.SetIdentity(); + xf.position = c1; + m_wire->DrawSphere(radius, color, xf); } if (height > 0.0f) @@ -333,10 +333,10 @@ void Draw::DrawCapsule(const b3Vec3& c1, const b3Vec3& c2, float32 radius, const DrawSegment(c1, c2, color); { - b3Transform xfc; - xfc.rotation.SetIdentity(); - xfc.position = c2; - m_wire->DrawSphere(radius, color, xfc); + b3Transform xf; + xf.rotation.SetIdentity(); + xf.position = c2; + m_wire->DrawSphere(radius, color, xf); } } } @@ -360,7 +360,7 @@ void Draw::DrawSolidCapsule(const b3Vec3& c1, const b3Vec3& c2, float32 radius, b3ComputeBasis(R.y, R.z, R.x); b3Transform xf; - xf.position = (0.5f * (c1 + c2)); + xf.position = 0.5f * (c1 + c2); xf.rotation = R; m_solid->DrawCylinder(radius, height, c, xf); @@ -379,10 +379,6 @@ void Draw::DrawTransform(const b3Transform& xf) { float32 lenght = 1.0f; - b3Color red(1.0f, 0.0f, 0.0f, 1.0f); - b3Color green(0.0f, 1.0f, 0.0f, 1.0f); - b3Color blue(0.0f, 0.0f, 1.0f, 1.0f); - b3Vec3 position = xf.position; b3Mat33 rotation = xf.rotation; @@ -390,9 +386,9 @@ void Draw::DrawTransform(const b3Transform& xf) b3Vec3 B = position + lenght * rotation.y; b3Vec3 C = position + lenght * rotation.z; - DrawSegment(position, A, red); - DrawSegment(position, B, green); - DrawSegment(position, C, blue); + DrawSegment(position, A, b3Color_red); + DrawSegment(position, B, b3Color_green); + DrawSegment(position, C, b3Color_blue); } void Draw::DrawAABB(const b3AABB3& aabb, const b3Color& color) From ad254c77c8c6fae99cee7db6e2f46ddd57a31bf1 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 5 Jun 2019 11:30:24 -0300 Subject: [PATCH 136/198] Ensure not deleting a mesh particle --- src/bounce/cloth/cloth.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 84151dd..0550732 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -247,9 +247,6 @@ b3Cloth::~b3Cloth() { b3Particle* p0 = p; p = p->m_next; - - m_particleTree.RemoveNode(p0->m_treeId); - p0->~b3Particle(); } @@ -282,10 +279,7 @@ b3Particle* b3Cloth::CreateParticle(const b3ParticleDef& def) void b3Cloth::DestroyParticle(b3Particle* particle) { - if (particle->m_vertex != ~0) - { - m_vertexParticles[particle->m_vertex] = NULL; - } + B3_ASSERT(particle->m_vertex == ~0); m_particleTree.RemoveNode(particle->m_treeId); From 5ee66d645ef5f787974ada823fa0b56b0d3b0b8b Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 5 Jun 2019 13:48:37 -0300 Subject: [PATCH 137/198] Remove include --- examples/testbed/framework/cloth_dragger.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/testbed/framework/cloth_dragger.cpp b/examples/testbed/framework/cloth_dragger.cpp index e05d447..ad18319 100644 --- a/examples/testbed/framework/cloth_dragger.cpp +++ b/examples/testbed/framework/cloth_dragger.cpp @@ -17,7 +17,6 @@ */ #include -#include b3ClothDragger::b3ClothDragger(b3Ray3* ray, b3Cloth* cloth) { From 07ee080310cc6aa3fa982fccdc75552f63dae1b1 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 5 Jun 2019 13:49:40 -0300 Subject: [PATCH 138/198] Remove particle forces if it gets destroyed --- include/bounce/cloth/force.h | 3 +++ include/bounce/cloth/spring_force.h | 7 +++++++ src/bounce/cloth/cloth.cpp | 16 ++++++++++++++-- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/include/bounce/cloth/force.h b/include/bounce/cloth/force.h index 04c456f..9e9132f 100644 --- a/include/bounce/cloth/force.h +++ b/include/bounce/cloth/force.h @@ -46,6 +46,9 @@ public: // b3Force* GetNext(); + + // + virtual bool HasParticle(const b3Particle* particle) const = 0; protected: friend class b3List2; friend class b3Cloth; diff --git a/include/bounce/cloth/spring_force.h b/include/bounce/cloth/spring_force.h index aee0425..f7da89f 100644 --- a/include/bounce/cloth/spring_force.h +++ b/include/bounce/cloth/spring_force.h @@ -67,6 +67,8 @@ public: float32 GetDampingStiffness() const; b3Vec3 GetActionForce() const; + + bool HasParticle(const b3Particle* particle) const; private: friend class b3Force; friend class b3Cloth; @@ -127,4 +129,9 @@ inline b3Vec3 b3SpringForce::GetActionForce() const return m_f; } +inline bool b3SpringForce::HasParticle(const b3Particle* particle) const +{ + return m_p1 == particle || m_p2 == particle; +} + #endif \ No newline at end of file diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 0550732..85b12dd 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -257,8 +257,7 @@ b3Cloth::~b3Cloth() { b3Force* f0 = f; f = f->m_next; - f0->~b3Force(); - b3Free(f0); + b3Force::Destroy(f0); } } @@ -280,6 +279,19 @@ b3Particle* b3Cloth::CreateParticle(const b3ParticleDef& def) void b3Cloth::DestroyParticle(b3Particle* particle) { B3_ASSERT(particle->m_vertex == ~0); + + b3Force* f = m_forceList.m_head; + while (f) + { + b3Force* f0 = f; + f = f->m_next; + + if (f0->HasParticle(particle)) + { + m_forceList.Remove(f0); + b3Force::Destroy(f0); + } + } m_particleTree.RemoveNode(particle->m_treeId); From 7b4795f0a3bca12fc90179ea0e4973c71a40c09c Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 9 Jun 2019 15:38:19 -0300 Subject: [PATCH 139/198] Set B using only E^-1 --- src/bounce/softbody/softbody.cpp | 89 ++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index 3bd7119..6dc6b94 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -62,7 +62,8 @@ static B3_FORCE_INLINE void b3Transpose(float32* B, float32* A, u32 AM, u32 AN) // Compute the elasticity matrix given Young modulus and Poisson's ratio // This is a 6 x 6 matrix -static B3_FORCE_INLINE void b3ComputeD(float32 out[36], float32 E, float32 nu) +static B3_FORCE_INLINE void b3ComputeD(float32 out[36], + float32 E, float32 nu) { float32 lambda = (nu * E) / ((1 + nu) * (1 - 2 * nu)); float32 mu = E / (2 * (1 + nu)); @@ -90,47 +91,55 @@ static B3_FORCE_INLINE void b3ComputeD(float32 out[36], float32 E, float32 nu) // can be found here: // https://github.com/erleben/OpenTissue/blob/master/OpenTissue/dynamics/fem/fem_compute_b.h static B3_FORCE_INLINE void b3ComputeB(float32 out[72], - const b3Vec3& e10, - const b3Vec3& e20, - const b3Vec3& e30, - float32 V) + const b3Mat33& invE) { - float32 inv6V = 1.0f / (6.0f * V); + // cofactor = det(E)^-1 * cofactor(E) + b3Mat33 cofactor = b3Transpose(invE); - b3Vec3 B[4]; + // minor = det(E)^-1 * minor(E) + b3Mat33 minor; - B[1][0] = (e20[2] * e30[1] - e20[1] * e30[2]) * inv6V; - B[2][0] = (e10[1] * e30[2] - e10[2] * e30[1]) * inv6V; - B[3][0] = (e10[2] * e20[1] - e10[1] * e20[2]) * inv6V; - B[0][0] = -B[1][0] - B[2][0] - B[3][0]; + minor.x.x = cofactor.x.x; + minor.x.y = -cofactor.x.y; + minor.x.z = cofactor.x.z; - B[1][1] = (e20[0] * e30[2] - e20[2] * e30[0]) * inv6V; - B[2][1] = (e10[2] * e30[0] - e10[0] * e30[2]) * inv6V; - B[3][1] = (e10[0] * e20[2] - e10[2] * e20[0]) * inv6V; - B[0][1] = -B[1][1] - B[2][1] - B[3][1]; + minor.y.x = -cofactor.y.x; + minor.y.y = cofactor.y.y; + minor.y.z = -cofactor.y.z; - B[1][2] = (e20[1] * e30[0] - e20[0] * e30[1]) * inv6V; - B[2][2] = (e10[0] * e30[1] - e10[1] * e30[0]) * inv6V; - B[3][2] = (e10[1] * e20[0] - e10[0] * e20[1]) * inv6V; - B[0][2] = -B[1][2] - B[2][2] - B[3][2]; + minor.z.x = cofactor.z.x; + minor.z.y = -cofactor.z.y; + minor.z.z = cofactor.z.z; - float32 b1 = B[0].x; - float32 c1 = B[0].y; - float32 d1 = B[0].z; + float32 e11 = -minor.x.x; // -det(E)^-1 * det(E_11) + float32 e12 = minor.y.x; // det(E)^-1 * det(E_12) + float32 e13 = -minor.z.x; // -det(E)^-1 * det(E_13) - float32 b2 = B[1].x; - float32 c2 = B[1].y; - float32 d2 = B[1].z; - - float32 b3 = B[2].x; - float32 c3 = B[2].y; - float32 d3 = B[2].z; + float32 e21 = minor.x.y; // det(E)^-1 * det(E_21) + float32 e22 = -minor.y.y; // -det(E)^-1 * det(E_22) + float32 e23 = minor.z.y; // -det(E)^-1 * det(E_23) - float32 b4 = B[3].x; - float32 c4 = B[3].y; - float32 d4 = B[3].z; + float32 e31 = -minor.x.z; // -det(E)^-1 * det(E_31) + float32 e32 = minor.y.z; // det(E)^-1 * det(E_32) + float32 e33 = -minor.z.z; // -det(E)^-1 * det(E_33) - float32 B_out[72] = + float32 b1 = -e11 - e12 - e13; + float32 c1 = -e21 - e22 - e23; + float32 d1 = -e31 - e32 - e33; + + float32 b2 = e11; + float32 c2 = e21; + float32 d2 = e31; + + float32 b3 = e12; + float32 c3 = e22; + float32 d3 = e32; + + float32 b4 = e13; + float32 c4 = e23; + float32 d4 = e33; + + float32 B[72] = { b1, 0, 0, c1, d1, 0, 0, c1, 0, b1, 0, d1, @@ -151,7 +160,7 @@ static B3_FORCE_INLINE void b3ComputeB(float32 out[72], for (u32 i = 0; i < 72; ++i) { - out[i] = B_out[i]; + out[i] = B[i]; } } @@ -521,7 +530,7 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) // 6 x 12 float32* B = e->B; - b3ComputeB(B, e1, e2, e3, V); + b3ComputeB(B, e->invE); // 12 x 6 float32 BT[72]; @@ -878,7 +887,7 @@ void b3SoftBody::Draw() const { b3Draw_draw->DrawPoint(v, 4.0f, b3Color_blue); } - + if (n->m_type == e_dynamicSoftBodyNode) { b3Draw_draw->DrawPoint(v, 4.0f, b3Color_green); @@ -905,28 +914,28 @@ void b3SoftBody::Draw() const // v1, v2, v3 b3Draw_draw->DrawTriangle(v1, v2, v3, b3Color_black); - + b3Vec3 n1 = b3Cross(v2 - v1, v3 - v1); n1.Normalize(); b3Draw_draw->DrawSolidTriangle(-n1, v1, v2, v3, b3Color_blue); // v1, v3, v4 b3Draw_draw->DrawTriangle(v1, v3, v4, b3Color_black); - + b3Vec3 n2 = b3Cross(v3 - v1, v4 - v1); n2.Normalize(); b3Draw_draw->DrawSolidTriangle(-n2, v1, v3, v4, b3Color_blue); // v1, v4, v2 b3Draw_draw->DrawTriangle(v1, v4, v2, b3Color_black); - + b3Vec3 n3 = b3Cross(v4 - v1, v2 - v1); n3.Normalize(); b3Draw_draw->DrawSolidTriangle(-n3, v1, v4, v2, b3Color_blue); // v2, v4, v3 b3Draw_draw->DrawTriangle(v2, v4, v3, b3Color_black); - + b3Vec3 n4 = b3Cross(v4 - v2, v3 - v2); n4.Normalize(); b3Draw_draw->DrawSolidTriangle(-n4, v2, v4, v3, b3Color_blue); From 80aa6b6b7f07562afd6229ab05080060ca1273d3 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 9 Jun 2019 15:40:35 -0300 Subject: [PATCH 140/198] Comment --- src/bounce/softbody/softbody.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index 6dc6b94..408338b 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -117,7 +117,7 @@ static B3_FORCE_INLINE void b3ComputeB(float32 out[72], float32 e21 = minor.x.y; // det(E)^-1 * det(E_21) float32 e22 = -minor.y.y; // -det(E)^-1 * det(E_22) - float32 e23 = minor.z.y; // -det(E)^-1 * det(E_23) + float32 e23 = minor.z.y; // det(E)^-1 * det(E_23) float32 e31 = -minor.x.z; // -det(E)^-1 * det(E_31) float32 e32 = minor.y.z; // det(E)^-1 * det(E_32) From 00819d015a46d9ae585dcd30578c20b98c613adf Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 11 Jun 2019 22:19:04 -0300 Subject: [PATCH 141/198] Pass color to shader --- examples/testbed/framework/draw_gl2.h | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/testbed/framework/draw_gl2.h b/examples/testbed/framework/draw_gl2.h index f2166c7..16dd8fe 100644 --- a/examples/testbed/framework/draw_gl2.h +++ b/examples/testbed/framework/draw_gl2.h @@ -625,6 +625,7 @@ struct DrawWire b3Mat44 m3 = g_glProjectionMatrix; b3Mat44 m = m3 * m2 * m1; + glUniform4fv(m_colorUniform, 1, &c.r); glUniformMatrix4fv(m_projectionUniform, 1, GL_FALSE, &m.x.x); glBindBuffer(GL_ARRAY_BUFFER, m_sphere.m_vboId); From cb492f7d51fcd2855e0969618fd969b5de78e487 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 12 Jun 2019 17:39:57 -0300 Subject: [PATCH 142/198] Unbuffer move if proxy gets destroyed --- include/bounce/collision/broad_phase.h | 25 +++++++-- src/bounce/collision/broad_phase.cpp | 60 ++++++++++++++------- src/bounce/collision/trees/dynamic_tree.cpp | 3 ++ src/bounce/dynamics/body.cpp | 2 +- 4 files changed, 65 insertions(+), 25 deletions(-) diff --git a/include/bounce/collision/broad_phase.h b/include/bounce/collision/broad_phase.h index b9483a2..9c37a6e 100644 --- a/include/bounce/collision/broad_phase.h +++ b/include/bounce/collision/broad_phase.h @@ -22,6 +22,8 @@ #include #include +#define B3_NULL_PROXY (0xFFFFFFFF) + // A pair of broad-phase proxies. struct b3Pair { @@ -49,9 +51,8 @@ public: // Return true if the proxy has moved. bool MoveProxy(u32 proxyId, const b3AABB3& aabb, const b3Vec3& displacement); - // Add a proxy to the list of moved proxies. - // Only moved proxies will be used internally as an AABB query reference object. - void BufferMove(u32 proxyId); + // Force move the proxy + void TouchProxy(u32 proxyId); // Get the AABB of a given proxy. const b3AABB3& GetAABB(u32 proxyId) const; @@ -59,6 +60,9 @@ public: // Get the user data attached to a proxy. void* GetUserData(u32 proxyId) const; + // Get the number of proxies. + u32 GetProxyCount() const; + // Test if two proxy AABBs are overlapping. bool TestOverlap(u32 proxy1, u32 proxy2) const; @@ -81,6 +85,9 @@ public: void Draw() const; private : friend class b3DynamicTree; + + void BufferMove(u32 proxyId); + void UnbufferMove(u32 proxyId); // The client callback used to add an overlapping pair // to the overlapping pair buffer. @@ -89,6 +96,9 @@ private : // The dynamic tree. b3DynamicTree m_tree; + // Number of proxies + u32 m_proxyCount; + // The current proxy being queried for overlap with another proxies. // It is used to avoid a proxy overlap with itself. u32 m_queryProxyId; @@ -114,6 +124,11 @@ inline void* b3BroadPhase::GetUserData(u32 proxyId) const return m_tree.GetUserData(proxyId); } +inline u32 b3BroadPhase::GetProxyCount() const +{ + return m_proxyCount; +} + template inline void b3BroadPhase::QueryAABB(T* callback, const b3AABB3& aabb) const { @@ -152,8 +167,10 @@ inline void b3BroadPhase::FindPairs(T* callback) { // Keep the current queried proxy ID to avoid self overlapping. m_queryProxyId = m_moveBuffer[i]; - if (m_queryProxyId == B3_NULL_NODE_D) + + if (m_queryProxyId == B3_NULL_PROXY) { + // Proxy was unbuffered continue; } diff --git a/src/bounce/collision/broad_phase.cpp b/src/bounce/collision/broad_phase.cpp index 58c1212..6f0d5bb 100644 --- a/src/bounce/collision/broad_phase.cpp +++ b/src/bounce/collision/broad_phase.cpp @@ -20,6 +20,8 @@ b3BroadPhase::b3BroadPhase() { + m_proxyCount = 0; + m_moveBufferCapacity = 16; m_moveBuffer = (u32*)b3Alloc(m_moveBufferCapacity * sizeof(u32)); memset(m_moveBuffer, 0, m_moveBufferCapacity * sizeof(u32)); @@ -57,6 +59,17 @@ void b3BroadPhase::BufferMove(u32 proxyId) ++m_moveBufferCount; } +void b3BroadPhase::UnbufferMove(u32 proxyId) +{ + for (u32 i = 0; i < m_moveBufferCount; ++i) + { + if (m_moveBuffer[i] == proxyId) + { + m_moveBuffer[i] = B3_NULL_PROXY; + } + } +} + bool b3BroadPhase::TestOverlap(u32 proxy1, u32 proxy2) const { return m_tree.TestOverlap(proxy1, proxy2); @@ -64,20 +77,23 @@ bool b3BroadPhase::TestOverlap(u32 proxy1, u32 proxy2) const u32 b3BroadPhase::CreateProxy(const b3AABB3& aabb, void* userData) { - // Later, if the node aabb has changed then it should be reinserted into the tree. - // However, this can be expansive due to the hierarchy reconstruction. - // Therefore, the original AABB is extended and inserted into the tree, - // so we can check later if the new (original) AABB is inside the old (fat) AABB. b3AABB3 fatAABB = aabb; fatAABB.Extend(B3_AABB_EXTENSION); + u32 proxyId = m_tree.InsertNode(fatAABB, userData); + + ++m_proxyCount; + BufferMove(proxyId); + return proxyId; } void b3BroadPhase::DestroyProxy(u32 proxyId) { - return m_tree.RemoveNode(proxyId); + UnbufferMove(proxyId); + --m_proxyCount; + m_tree.RemoveNode(proxyId); } bool b3BroadPhase::MoveProxy(u32 proxyId, const b3AABB3& aabb, const b3Vec3& displacement) @@ -88,39 +104,38 @@ bool b3BroadPhase::MoveProxy(u32 proxyId, const b3AABB3& aabb, const b3Vec3& dis return false; } - // Update the tree with a fat and motion predicted AABB. - const b3Vec3 kExtension(B3_AABB_EXTENSION, B3_AABB_EXTENSION, B3_AABB_EXTENSION); + // Extend the AABB. + b3AABB3 fatAABB = aabb; + fatAABB.Extend(B3_AABB_EXTENSION); - // Extend the new (original) AABB. - b3AABB3 fatAABB; - fatAABB.m_lower = aabb.m_lower - kExtension; - fatAABB.m_upper = aabb.m_upper + kExtension; + // Predict AABB displacement. + b3Vec3 d = B3_AABB_MULTIPLIER * displacement; - if (displacement.x < 0.0f) + if (d.x < 0.0f) { - fatAABB.m_lower.x += B3_AABB_MULTIPLIER * displacement.x; + fatAABB.m_lower.x += d.x; } else { - fatAABB.m_upper.x += B3_AABB_MULTIPLIER * displacement.x; + fatAABB.m_upper.x += d.x; } - if (displacement.y < 0.0f) + if (d.y < 0.0f) { - fatAABB.m_lower.y += B3_AABB_MULTIPLIER * displacement.y; + fatAABB.m_lower.y += d.y; } else { - fatAABB.m_upper.y += B3_AABB_MULTIPLIER * displacement.y; + fatAABB.m_upper.y += d.y; } - if (displacement.z < 0.0f) + if (d.z < 0.0f) { - fatAABB.m_lower.z += B3_AABB_MULTIPLIER * displacement.z; + fatAABB.m_lower.z += d.z; } else { - fatAABB.m_upper.z += B3_AABB_MULTIPLIER * displacement.z; + fatAABB.m_upper.z += d.z; } // Update proxy with the extented AABB. @@ -133,6 +148,11 @@ bool b3BroadPhase::MoveProxy(u32 proxyId, const b3AABB3& aabb, const b3Vec3& dis return true; } +void b3BroadPhase::TouchProxy(u32 proxyId) +{ + BufferMove(proxyId); +} + bool b3BroadPhase::Report(u32 proxyId) { if (proxyId == m_queryProxyId) diff --git a/src/bounce/collision/trees/dynamic_tree.cpp b/src/bounce/collision/trees/dynamic_tree.cpp index 99d482f..ca30f1c 100644 --- a/src/bounce/collision/trees/dynamic_tree.cpp +++ b/src/bounce/collision/trees/dynamic_tree.cpp @@ -123,6 +123,7 @@ void b3DynamicTree::RemoveNode(u32 proxyId) { // Remove from the tree. RemoveLeaf(proxyId); + // Remove from the node array and make it available. FreeNode(proxyId); } @@ -131,8 +132,10 @@ void b3DynamicTree::UpdateNode(u32 proxyId, const b3AABB3& aabb) { B3_ASSERT(m_root != B3_NULL_NODE_D); B3_ASSERT(m_nodes[proxyId].IsLeaf()); + // Remove old AABB from the tree. RemoveLeaf(proxyId); + // Insert the new AABB to the tree. m_nodes[proxyId].aabb = aabb; InsertLeaf(proxyId); diff --git a/src/bounce/dynamics/body.cpp b/src/bounce/dynamics/body.cpp index e7e010d..caf19c8 100644 --- a/src/bounce/dynamics/body.cpp +++ b/src/bounce/dynamics/body.cpp @@ -457,7 +457,7 @@ void b3Body::SetType(b3BodyType type) b3BroadPhase* phase = &m_world->m_contactMan.m_broadPhase; for (b3Shape* s = m_shapeList.m_head; s; s = s->m_next) { - phase->BufferMove(s->m_broadPhaseID); + phase->TouchProxy(s->m_broadPhaseID); } } From 078081fa3c1e9e02f3447a2438e862b9d7488890 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 12 Jun 2019 17:41:41 -0300 Subject: [PATCH 143/198] AABB stuff --- include/bounce/collision/shapes/aabb3.h | 15 +++++++++++---- src/bounce/dynamics/shapes/hull_shape.cpp | 2 +- src/bounce/dynamics/shapes/mesh_shape.cpp | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/include/bounce/collision/shapes/aabb3.h b/include/bounce/collision/shapes/aabb3.h index 09ffb2c..51ba36e 100644 --- a/include/bounce/collision/shapes/aabb3.h +++ b/include/bounce/collision/shapes/aabb3.h @@ -37,8 +37,8 @@ struct b3AABB3 return support; } - // Compute this AABB from a list of points. - void Compute(const b3Vec3* points, u32 count) + // Set this AABB from a list of points. + void Set(const b3Vec3* points, u32 count) { m_lower = m_upper = points[0]; for (u32 i = 1; i < count; ++i) @@ -48,8 +48,8 @@ struct b3AABB3 } } - // Compute this AABB from a list of points and a transform. - void Compute(const b3Vec3* points, u32 count, const b3Transform& xf) + // Set this AABB from a list of points and a transform. + void Set(const b3Vec3* points, u32 count, const b3Transform& xf) { m_lower = m_upper = b3Mul(xf, points[0]); for (u32 i = 1; i < count; ++i) @@ -60,6 +60,13 @@ struct b3AABB3 } } + // Set this AABB from a triangle. + void Set(const b3Vec3& v1, const b3Vec3& v2, const b3Vec3& v3) + { + m_lower = b3Min(v1, b3Min(v2, v3)); + m_upper = b3Max(v1, b3Max(v2, v3)); + } + // Set this AABB from a center point and a radius vector. void Set(const b3Vec3& center, const b3Vec3& r) { diff --git a/src/bounce/dynamics/shapes/hull_shape.cpp b/src/bounce/dynamics/shapes/hull_shape.cpp index 78da7bc..81be848 100644 --- a/src/bounce/dynamics/shapes/hull_shape.cpp +++ b/src/bounce/dynamics/shapes/hull_shape.cpp @@ -161,7 +161,7 @@ void b3HullShape::ComputeMass(b3MassData* massData, float32 density) const void b3HullShape::ComputeAABB(b3AABB3* aabb, const b3Transform& xf) const { - aabb->Compute(m_hull->vertices, m_hull->vertexCount, xf); + aabb->Set(m_hull->vertices, m_hull->vertexCount, xf); aabb->Extend(m_radius); } diff --git a/src/bounce/dynamics/shapes/mesh_shape.cpp b/src/bounce/dynamics/shapes/mesh_shape.cpp index 88f3f3d..55724e6 100644 --- a/src/bounce/dynamics/shapes/mesh_shape.cpp +++ b/src/bounce/dynamics/shapes/mesh_shape.cpp @@ -56,7 +56,7 @@ void b3MeshShape::ComputeMass(b3MassData* massData, float32 density) const void b3MeshShape::ComputeAABB(b3AABB3* output, const b3Transform& xf) const { - output->Compute(m_mesh->vertices, m_mesh->vertexCount, xf); + output->Set(m_mesh->vertices, m_mesh->vertexCount, xf); output->Extend(m_radius); } From 99270a70b9bd0cd566feb1872d921126a83e45e0 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 12 Jun 2019 17:42:05 -0300 Subject: [PATCH 144/198] AABB stuff --- include/bounce/collision/shapes/mesh.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/include/bounce/collision/shapes/mesh.h b/include/bounce/collision/shapes/mesh.h index 3fbde6d..60586b1 100644 --- a/include/bounce/collision/shapes/mesh.h +++ b/include/bounce/collision/shapes/mesh.h @@ -78,14 +78,10 @@ inline u32 b3Mesh::GetSize() const inline b3AABB3 b3Mesh::GetTriangleAABB(u32 index) const { const b3Triangle* triangle = triangles + index; - - u32 i1 = triangle->v1; - u32 i2 = triangle->v2; - u32 i3 = triangle->v3; b3AABB3 aabb; - aabb.m_lower = b3Min(b3Min(vertices[i1], vertices[i2]), vertices[i3]); - aabb.m_upper = b3Max(b3Max(vertices[i1], vertices[i2]), vertices[i3]); + aabb.Set(vertices[triangle->v1], vertices[triangle->v2], vertices[triangle->v3]); + return aabb; } From d3b6292afd9c8cf868a30cdd28d13462f7e3538f Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 12 Jun 2019 17:58:33 -0300 Subject: [PATCH 145/198] Keep particles and triangles in a tree --- include/bounce/cloth/cloth.h | 26 +- include/bounce/cloth/cloth_contact_manager.h | 81 +++++ include/bounce/cloth/cloth_contact_solver.h | 53 ++- include/bounce/cloth/cloth_solver.h | 7 + include/bounce/cloth/particle.h | 32 +- src/bounce/cloth/cloth.cpp | 198 ++++++++--- src/bounce/cloth/cloth_contact_manager.cpp | 336 +++++++++++++++++++ src/bounce/cloth/cloth_contact_solver.cpp | 281 +++++++++++++++- src/bounce/cloth/cloth_solver.cpp | 21 +- src/bounce/cloth/particle.cpp | 66 +++- 10 files changed, 1043 insertions(+), 58 deletions(-) create mode 100644 include/bounce/cloth/cloth_contact_manager.h create mode 100644 src/bounce/cloth/cloth_contact_manager.cpp diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index e24e17f..a84cf1a 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -19,11 +19,11 @@ #ifndef B3_CLOTH_H #define B3_CLOTH_H -#include #include #include #include -#include +#include +#include class b3World; class b3Shape; @@ -42,6 +42,8 @@ class b3RayCastListener; struct b3RayCastInput; struct b3RayCastOutput; +struct b3ClothAABBProxy; + struct b3ClothRayCastSingleOutput { u32 triangle; @@ -140,16 +142,23 @@ public: void Draw() const; private: friend class b3Particle; + friend class b3ClothContactManager; // Compute mass of each particle. void ComputeMass(); - // Update contacts - void UpdateContacts(); + // Update particle-body contacts + void UpdateParticleBodyContacts(); // Solve void Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations); + // Synchronize triangle AABB. + void SynchronizeTriangle(u32 triangleIndex); + + // Time-step + float32 m_dt; + // Stack allocator b3StackAllocator m_stackAllocator; @@ -165,6 +174,9 @@ private: // Vertex particles b3Particle** m_vertexParticles; + // Triangle proxies + b3ClothAABBProxy* m_triangleProxies; + // Cloth density float32 m_density; @@ -174,11 +186,11 @@ private: // List of particles b3List2 m_particleList; - // Particle tree - b3DynamicTree m_particleTree; - // List of forces b3List2 m_forceList; + + // Contact manager + b3ClothContactManager m_contactManager; }; inline void b3Cloth::SetGravity(const b3Vec3& gravity) diff --git a/include/bounce/cloth/cloth_contact_manager.h b/include/bounce/cloth/cloth_contact_manager.h new file mode 100644 index 0000000..3e67e2f --- /dev/null +++ b/include/bounce/cloth/cloth_contact_manager.h @@ -0,0 +1,81 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_CLOTH_CONTACT_MANAGER_H +#define B3_CLOTH_CONTACT_MANAGER_H + +#include +#include +#include + +class b3Cloth; +class b3Particle; +struct b3ClothMeshTriangle; +struct b3ClothAABBProxy; + +// Contact between particle and a triangle +class b3ParticleTriangleContact +{ +public: + b3Particle* m_p1; + + b3ClothMeshTriangle* m_triangle; + b3ClothAABBProxy* m_triangleProxy; + b3Particle* m_p2; + b3Particle* m_p3; + b3Particle* m_p4; + + bool m_front; + + bool m_active; + + float32 m_normalImpulse; + + b3ParticleTriangleContact* m_prev; + b3ParticleTriangleContact* m_next; +}; + +// Contact delegator for b3Cloth. +class b3ClothContactManager +{ +public: + b3ClothContactManager(); + + // The broad-phase callback. + void AddPair(void* data1, void* data2); + + void FindNewContacts(); + + void UpdateContacts(); + + b3ParticleTriangleContact* Create(); + + void Destroy(b3ParticleTriangleContact* c); + + void Update(b3ParticleTriangleContact* c); + + b3Cloth* m_cloth; + + b3BlockPool m_particleTriangleContactBlocks; + + b3BroadPhase m_broadPhase; + + b3List2 m_particleTriangleContactList; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/cloth/cloth_contact_solver.h b/include/bounce/cloth/cloth_contact_solver.h index a945d03..3005084 100644 --- a/include/bounce/cloth/cloth_contact_solver.h +++ b/include/bounce/cloth/cloth_contact_solver.h @@ -28,6 +28,7 @@ class b3Particle; class b3Body; struct b3ParticleBodyContact; +class b3ParticleTriangleContact; struct b3DenseVec3; @@ -80,6 +81,44 @@ struct b3ClothSolverBodyContactPositionConstraint b3Vec3 localPointB; }; +struct b3ClothSolverTriangleContactVelocityConstraint +{ + u32 indexA; + float32 invMassA; + + u32 indexB; + float32 invMassB; + u32 indexC; + float32 invMassC; + u32 indexD; + float32 invMassD; + + b3Vec3 JA; + b3Vec3 JB; + b3Vec3 JC; + b3Vec3 JD; + + float32 normalMass; + float32 normalImpulse; +}; + +struct b3ClothSolverTriangleContactPositionConstraint +{ + u32 indexA; + float32 invMassA; + float32 radiusA; + + u32 indexB; + float32 invMassB; + u32 indexC; + float32 invMassC; + u32 indexD; + float32 invMassD; + float32 triangleRadius; + + bool front; +}; + struct b3ClothContactSolverDef { b3StackAllocator* allocator; @@ -89,6 +128,9 @@ struct b3ClothContactSolverDef u32 bodyContactCount; b3ParticleBodyContact** bodyContacts; + + u32 triangleContactCount; + b3ParticleTriangleContact** triangleContacts; }; inline float32 b3MixFriction(float32 u1, float32 u2) @@ -103,14 +145,18 @@ public: ~b3ClothContactSolver(); void InitializeBodyContactConstraints(); + void InitializeTriangleContactConstraints(); - void WarmStart(); + void WarmStartBodyContactConstraints(); + void WarmStartTriangleContactConstraints(); void SolveBodyContactVelocityConstraints(); + void SolveTriangleContactVelocityConstraints(); void StoreImpulses(); bool SolveBodyContactPositionConstraints(); + bool SolveTriangleContactPositionConstraints(); protected: b3StackAllocator* m_allocator; @@ -121,6 +167,11 @@ protected: b3ParticleBodyContact** m_bodyContacts; b3ClothSolverBodyContactVelocityConstraint* m_bodyVelocityConstraints; b3ClothSolverBodyContactPositionConstraint* m_bodyPositionConstraints; + + u32 m_triangleContactCount; + b3ParticleTriangleContact** m_triangleContacts; + b3ClothSolverTriangleContactVelocityConstraint* m_triangleVelocityConstraints; + b3ClothSolverTriangleContactPositionConstraint* m_trianglePositionConstraints; }; #endif \ No newline at end of file diff --git a/include/bounce/cloth/cloth_solver.h b/include/bounce/cloth/cloth_solver.h index 3a582db..c39e417 100644 --- a/include/bounce/cloth/cloth_solver.h +++ b/include/bounce/cloth/cloth_solver.h @@ -34,6 +34,7 @@ struct b3SparseSymMat33; struct b3SparseSymMat33View; struct b3ParticleBodyContact; +class b3ParticleTriangleContact; struct b3ClothSolverDef { @@ -41,6 +42,7 @@ struct b3ClothSolverDef u32 particleCapacity; u32 forceCapacity; u32 bodyContactCapacity; + u32 triangleContactCapacity; }; struct b3ClothSolverData @@ -75,6 +77,7 @@ public: void Add(b3Particle* p); void Add(b3Force* f); void Add(b3ParticleBodyContact* c); + void Add(b3ParticleTriangleContact* c); void Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations); private: @@ -107,6 +110,10 @@ private: u32 m_bodyContactCount; b3ParticleBodyContact** m_bodyContacts; + u32 m_triangleContactCapacity; + u32 m_triangleContactCount; + b3ParticleTriangleContact** m_triangleContacts; + b3ClothSolverData m_solverData; }; diff --git a/include/bounce/cloth/particle.h b/include/bounce/cloth/particle.h index 6d96c4a..d2d13cf 100644 --- a/include/bounce/cloth/particle.h +++ b/include/bounce/cloth/particle.h @@ -44,6 +44,7 @@ struct b3ParticleDef b3ParticleDef() { type = e_staticParticle; + mass = 0.0f; position.SetZero(); velocity.SetZero(); force.SetZero(); @@ -53,6 +54,7 @@ struct b3ParticleDef } b3ParticleType type; + float32 mass; b3Vec3 position; b3Vec3 velocity; b3Vec3 force; @@ -89,6 +91,20 @@ struct b3ParticleBodyContactWorldPoint float32 separation; }; +enum b3ClothAABBProxyType +{ + e_particleProxy, + e_triangleProxy +}; + +struct b3ClothAABBProxy +{ + b3ClothAABBProxyType type; + void* data; + u32 index; + u32 broadPhaseId; +}; + // A cloth particle. class b3Particle { @@ -143,18 +159,23 @@ private: friend class b3List2; friend class b3Cloth; friend class b3ClothSolver; + friend class b3ClothContactManager; friend class b3ClothContactSolver; friend class b3Force; friend class b3SpringForce; - friend class b3BendForce; - friend class b3FrictionForce; b3Particle(const b3ParticleDef& def, b3Cloth* cloth); ~b3Particle(); - // Synchronize AABB + // Synchronize particle AABB void Synchronize(); + // Synchronize triangles AABB + void SynchronizeTriangles(); + + // Destroy contacts. + void DestroyContacts(); + // Type b3ParticleType m_type; @@ -202,8 +223,8 @@ private: // Contact b3ParticleBodyContact m_bodyContact; - // Particle tree identifier - u32 m_treeId; + // AABB Proxy + b3ClothAABBProxy m_aabbProxy; // b3Particle* m_prev; @@ -228,6 +249,7 @@ inline void b3Particle::SetPosition(const b3Vec3& position) m_translation.SetZero(); Synchronize(); + SynchronizeTriangles(); } inline const b3Vec3& b3Particle::GetPosition() const diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 85b12dd..3c66427 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -32,6 +32,8 @@ #include +#define B3_ENABLE_SELF_COLLISION 0 + static B3_FORCE_INLINE u32 b3NextIndex(u32 i) { return i + 1 < 3 ? i + 1 : 0; @@ -158,6 +160,8 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : m_mesh = def.mesh; m_density = def.density; + m_contactManager.m_cloth = this; + m_dt = 0.0f; const b3ClothMesh* m = m_mesh; @@ -167,10 +171,12 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : { b3ParticleDef pd; pd.type = e_dynamicParticle; + pd.mass = 1.0f; pd.position = m->vertices[i]; b3Particle* p = CreateParticle(pd); + p->m_aabbProxy.index = i; p->m_vertex = i; m_vertexParticles[i] = p; } @@ -200,7 +206,10 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : b3SpringForceDef fd; fd.Initialize(p1, p2, def.structural, def.damping); - CreateForce(fd); + if (def.structural > 0.0f) + { + CreateForce(fd); + } } // Bending @@ -216,9 +225,12 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : b3SpringForceDef fd; fd.Initialize(p3, p4, def.bending, def.damping); - CreateForce(fd); + if (def.bending > 0.0f) + { + CreateForce(fd); + } } - + allocator->Free(sharedEdges); allocator->Free(uniqueEdges); @@ -233,7 +245,30 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : b3SpringForceDef fd; fd.Initialize(p1, p2, def.structural, def.damping); - CreateForce(fd); + if (def.structural > 0.0f) + { + CreateForce(fd); + } + } + + // Initialize triangle proxies + m_triangleProxies = (b3ClothAABBProxy*)b3Alloc(m_mesh->triangleCount * sizeof(b3ClothAABBProxy)); + for (u32 i = 0; i < m_mesh->triangleCount; ++i) + { + b3ClothMeshTriangle* triangle = m_mesh->triangles + i; + b3ClothAABBProxy* triangleProxy = m_triangleProxies + i; + + b3Vec3 v1 = m_mesh->vertices[triangle->v1]; + b3Vec3 v2 = m_mesh->vertices[triangle->v2]; + b3Vec3 v3 = m_mesh->vertices[triangle->v3]; + + b3AABB3 aabb; + aabb.Set(v1, v2, v3); + + triangleProxy->type = e_triangleProxy; + triangleProxy->index = i; + triangleProxy->data = triangle; + triangleProxy->broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, triangleProxy); } m_gravity.SetZero(); @@ -251,6 +286,7 @@ b3Cloth::~b3Cloth() } b3Free(m_vertexParticles); + b3Free(m_triangleProxies); b3Force* f = m_forceList.m_head; while (f) @@ -265,11 +301,14 @@ b3Particle* b3Cloth::CreateParticle(const b3ParticleDef& def) { void* mem = m_particleBlocks.Allocate(); b3Particle* p = new(mem) b3Particle(def, this); - + b3AABB3 aabb; aabb.Set(p->m_position, p->m_radius); - - p->m_treeId = m_particleTree.InsertNode(aabb, p); + + p->m_aabbProxy.type = e_particleProxy; + p->m_aabbProxy.data = p; + p->m_aabbProxy.index = p->m_vertex; + p->m_aabbProxy.broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, &p->m_aabbProxy); m_particleList.PushFront(p); @@ -280,6 +319,7 @@ void b3Cloth::DestroyParticle(b3Particle* particle) { B3_ASSERT(particle->m_vertex == ~0); + // Destroy particle forces b3Force* f = m_forceList.m_head; while (f) { @@ -293,7 +333,11 @@ void b3Cloth::DestroyParticle(b3Particle* particle) } } - m_particleTree.RemoveNode(particle->m_treeId); + // Destroy particle contacts + particle->DestroyContacts(); + + // Destroy AABB proxy + m_contactManager.m_broadPhase.DestroyProxy(particle->m_aabbProxy.broadPhaseId); m_particleList.Remove(particle); particle->~b3Particle(); @@ -370,6 +414,44 @@ void b3Cloth::ComputeMass() } } +struct b3ClothRayCastSingleCallback +{ + float32 Report(const b3RayCastInput& input, u32 proxyId) + { + // Get primitive associated with the proxy. + void* userData = broadPhase->GetUserData(proxyId); + b3ClothAABBProxy* proxy = (b3ClothAABBProxy*)userData; + + if (proxy->type != e_triangleProxy) + { + // Continue search from where we stopped. + return input.maxFraction; + } + + u32 triangleIndex = proxy->index; + + b3RayCastOutput subOutput; + if (cloth->RayCast(&subOutput, &input, triangleIndex)) + { + // Ray hits triangle. + if (subOutput.fraction < output0.fraction) + { + triangle0 = proxy->index; + output0.fraction = subOutput.fraction; + output0.normal = subOutput.normal; + } + } + + // Continue search from where we stopped. + return input.maxFraction; + } + + const b3Cloth* cloth; + const b3BroadPhase* broadPhase; + u32 triangle0; + b3RayCastOutput output0; +}; + bool b3Cloth::RayCastSingle(b3ClothRayCastSingleOutput* output, const b3Vec3& p1, const b3Vec3& p2) const { b3RayCastInput input; @@ -377,29 +459,19 @@ bool b3Cloth::RayCastSingle(b3ClothRayCastSingleOutput* output, const b3Vec3& p1 input.p2 = p2; input.maxFraction = 1.0f; - u32 triangle0 = ~0; - b3RayCastOutput output0; - output0.fraction = B3_MAX_FLOAT; + b3ClothRayCastSingleCallback callback; + callback.cloth = this; + callback.broadPhase = &m_contactManager.m_broadPhase; + callback.triangle0 = ~0; + callback.output0.fraction = B3_MAX_FLOAT; - for (u32 i = 0; i < m_mesh->triangleCount; ++i) - { - b3RayCastOutput subOutput; - if (RayCast(&subOutput, &input, i)) - { - if (subOutput.fraction < output0.fraction) - { - triangle0 = i; - output0.fraction = subOutput.fraction; - output0.normal = subOutput.normal; - } - } - } + m_contactManager.m_broadPhase.RayCast(&callback, input); - if (triangle0 != ~0) + if (callback.triangle0 != ~0) { - output->triangle = triangle0; - output->fraction = output0.fraction; - output->normal = output0.normal; + output->triangle = callback.triangle0; + output->fraction = callback.output0.fraction; + output->normal = callback.output0.normal; return true; } @@ -455,9 +527,9 @@ public: b3Vec3 bestNormal; }; -void b3Cloth::UpdateContacts() +void b3Cloth::UpdateParticleBodyContacts() { - B3_PROFILE("Cloth Update Contacts"); + B3_PROFILE("Cloth Update Particle Body Contacts"); // Is there a world attached to this cloth? if (m_world == nullptr) @@ -473,7 +545,7 @@ void b3Cloth::UpdateContacts() continue; } - b3AABB3 aabb = m_particleTree.GetAABB(p->m_treeId); + b3AABB3 aabb = m_contactManager.m_broadPhase.GetAABB(p->m_aabbProxy.broadPhaseId); b3ClothUpdateContactsQueryListener listener; listener.sphere.vertex = p->m_position; @@ -528,6 +600,7 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u solverDef.particleCapacity = m_particleList.m_count; solverDef.forceCapacity = m_forceList.m_count; solverDef.bodyContactCapacity = m_particleList.m_count; + solverDef.triangleContactCapacity = m_contactManager.m_particleTriangleContactList.m_count; b3ClothSolver solver(solverDef); @@ -549,6 +622,14 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u } } + for (b3ParticleTriangleContact* c = m_contactManager.m_particleTriangleContactList.m_head; c; c = c->m_next) + { + if (c->m_active) + { + solver.Add(c); + } + } + // Solve solver.Solve(dt, gravity, velocityIterations, positionIterations); } @@ -557,8 +638,13 @@ void b3Cloth::Step(float32 dt, u32 velocityIterations, u32 positionIterations) { B3_PROFILE("Cloth Step"); - // Update contacts - UpdateContacts(); + m_dt = dt; + + // Update particle-body contacts + UpdateParticleBodyContacts(); + + // Update self-contacts + m_contactManager.UpdateContacts(); // Integrate state, solve constraints. if (dt > 0.0f) @@ -576,13 +662,49 @@ void b3Cloth::Step(float32 dt, u32 velocityIterations, u32 positionIterations) // Synchronize particles for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) { - if (p->m_type == e_staticParticle) - { - continue; - } - p->Synchronize(); } + + // Synchronize triangles + for (u32 i = 0; i < m_mesh->triangleCount; ++i) + { + SynchronizeTriangle(i); + } + +#if B3_ENABLE_SELF_COLLISION + + // Find new self-contacts + m_contactManager.FindNewContacts(); + +#endif +} + +void b3Cloth::SynchronizeTriangle(u32 triangleIndex) +{ + b3ClothMeshTriangle* triangle = m_mesh->triangles + triangleIndex; + b3ClothAABBProxy* triangleProxy = m_triangleProxies + triangleIndex; + + b3Particle* p1 = m_vertexParticles[triangle->v1]; + b3Particle* p2 = m_vertexParticles[triangle->v2]; + b3Particle* p3 = m_vertexParticles[triangle->v3]; + + b3Vec3 x1 = p1->m_position; + b3Vec3 x2 = p2->m_position; + b3Vec3 x3 = p3->m_position; + + b3Vec3 v1 = p1->m_velocity; + b3Vec3 v2 = p2->m_velocity; + b3Vec3 v3 = p3->m_velocity; + + b3AABB3 aabb; + aabb.Set(x1, x2, x3); + + const float32 kInv3 = 1.0f / 3.0f; + + b3Vec3 center_velocity = kInv3 * (v1 + v2 + v3); + b3Vec3 center_displacement = m_dt * center_velocity; + + m_contactManager.m_broadPhase.MoveProxy(triangleProxy->broadPhaseId, aabb, center_displacement); } void b3Cloth::Draw() const diff --git a/src/bounce/cloth/cloth_contact_manager.cpp b/src/bounce/cloth/cloth_contact_manager.cpp new file mode 100644 index 0000000..6f5ce47 --- /dev/null +++ b/src/bounce/cloth/cloth_contact_manager.cpp @@ -0,0 +1,336 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include + +#include + +b3ClothContactManager::b3ClothContactManager() : + m_particleTriangleContactBlocks(sizeof(b3ParticleTriangleContact)) +{ + +} + +void b3ClothContactManager::FindNewContacts() +{ + B3_PROFILE("Cloth Find New Contacts"); + + m_broadPhase.FindPairs(this); +} + +void b3ClothContactManager::AddPair(void* data1, void* data2) +{ + b3ClothAABBProxy* proxy1 = (b3ClothAABBProxy*)data1; + b3ClothAABBProxy* proxy2 = (b3ClothAABBProxy*)data2; + + if (proxy1->type == e_particleProxy && proxy2->type == e_particleProxy) + { + // Particle-particle contacts are not supported. + return; + } + + if (proxy1->type == e_triangleProxy && proxy2->type == e_triangleProxy) + { + // Triangle-triangle contacts are not supported. + return; + } + + if (proxy1->type == e_triangleProxy) + { + // Ensure proxy1 is a particle and proxy 2 a triangle. + b3Swap(proxy1, proxy2); + } + + B3_ASSERT(proxy1->type == e_particleProxy); + B3_ASSERT(proxy2->type == e_triangleProxy); + + b3Particle* p1 = (b3Particle*)proxy1->data; + + b3ClothMeshTriangle* triangle = (b3ClothMeshTriangle*)proxy2->data; + b3Particle* p2 = m_cloth->m_vertexParticles[triangle->v1]; + b3Particle* p3 = m_cloth->m_vertexParticles[triangle->v2]; + b3Particle* p4 = m_cloth->m_vertexParticles[triangle->v3]; + + // Check if there is a contact between the two entities. + for (b3ParticleTriangleContact* c = m_particleTriangleContactList.m_head; c; c = c->m_next) + { + if (c->m_p1 == p1 && c->m_triangle == triangle) + { + // A contact already exists. + return; + } + } + + bool isntDynamic1 = p1->m_type != e_dynamicParticle; + bool isntDynamic2 = p2->m_type != e_dynamicParticle && p3->m_type != e_dynamicParticle && p4->m_type != e_dynamicParticle; + + if (isntDynamic1 && isntDynamic2) + { + // The entities must not collide with each other. + return; + } + + if (triangle->v1 == p1->m_vertex || triangle->v2 == p1->m_vertex || triangle->v3 == p1->m_vertex) + { + // The entities must not collide with each other. + return; + } + + // Create a new contact. + b3ParticleTriangleContact* c = Create(); + + c->m_p1 = p1; + + c->m_p2 = p2; + c->m_p3 = p3; + c->m_p4 = p4; + c->m_triangle = triangle; + c->m_triangleProxy = proxy2; + + c->m_front = false; + c->m_active = false; + + c->m_normalImpulse = 0.0f; + + // Add the contact to the cloth contact list. + m_particleTriangleContactList.PushFront(c); +} + +b3ParticleTriangleContact* b3ClothContactManager::Create() +{ + void* block = m_particleTriangleContactBlocks.Allocate(); + return new(block) b3ParticleTriangleContact(); +} + +void b3ClothContactManager::Destroy(b3ParticleTriangleContact* c) +{ + m_particleTriangleContactList.Remove(c); + + c->~b3ParticleTriangleContact(); + + m_particleTriangleContactBlocks.Free(c); +} + +static void b3Solve3(float32 out[3], + const b3Vec3& A, const b3Vec3& B, const b3Vec3& C, + const b3Vec3& Q) +{ + // Test vertex regions + float32 wAB[3], wBC[3], wCA[3]; + b3BarycentricCoordinates(wAB, A, B, Q); + b3BarycentricCoordinates(wBC, B, C, Q); + b3BarycentricCoordinates(wCA, C, A, Q); + + // R A + if (wAB[1] <= 0.0f && wCA[0] <= 0.0f) + { + out[0] = 1.0f; + out[1] = 0.0f; + out[2] = 0.0f; + return; + } + + // R B + if (wAB[0] <= 0.0f && wBC[1] <= 0.0f) + { + out[0] = 0.0f; + out[1] = 1.0f; + out[2] = 0.0f; + return; + } + + // R C + if (wBC[0] <= 0.0f && wCA[1] <= 0.0f) + { + out[0] = 0.0f; + out[1] = 0.0f; + out[2] = 1.0f; + return; + } + + // Test edge regions + float32 wABC[4]; + b3BarycentricCoordinates(wABC, A, B, C, Q); + + // R AB + if (wAB[0] > 0.0f && wAB[1] > 0.0f && wABC[3] * wABC[2] <= 0.0f) + { + float32 divisor = wAB[2]; + B3_ASSERT(divisor > 0.0f); + float32 s = 1.0f / divisor; + out[0] = s * wAB[0]; + out[1] = s * wAB[1]; + out[2] = 0.0f; + return; + } + + // R BC + if (wBC[0] > 0.0f && wBC[1] > 0.0f && wABC[3] * wABC[0] <= 0.0f) + { + float32 divisor = wBC[2]; + B3_ASSERT(divisor > 0.0f); + float32 s = 1.0f / divisor; + out[0] = 0.0f; + out[1] = s * wBC[0]; + out[2] = s * wBC[1]; + return; + } + + // R CA + if (wCA[0] > 0.0f && wCA[1] > 0.0f && wABC[3] * wABC[1] <= 0.0f) + { + float32 divisor = wCA[2]; + B3_ASSERT(divisor > 0.0f); + float32 s = 1.0f / divisor; + out[0] = s * wCA[1]; + out[1] = 0.0f; + out[2] = s * wCA[0]; + return; + } + + // R ABC/ACB + float32 divisor = wABC[3]; + if (divisor == 0.0f) + { + float32 s = 1.0f / 3.0f; + out[0] = s; + out[1] = s; + out[2] = s; + return; + } + + B3_ASSERT(divisor > 0.0f); + float32 s = 1.0f / divisor; + out[0] = s * wABC[0]; + out[1] = s * wABC[1]; + out[2] = s * wABC[2]; +} + +void b3ClothContactManager::Update(b3ParticleTriangleContact* c) +{ + b3Particle* p1 = c->m_p1; + + b3Particle* p2 = c->m_p2; + b3Particle* p3 = c->m_p3; + b3Particle* p4 = c->m_p4; + + float32 r1 = p1->m_radius; + float32 r2 = 0.0f; + + float32 totalRadius = r1 + r2; + + b3Vec3 A = p2->m_position; + b3Vec3 B = p3->m_position; + b3Vec3 C = p4->m_position; + + b3Vec3 n = b3Cross(B - A, C - A); + float32 len = n.Normalize(); + + // Is ABC degenerate? + if (len == 0.0f) + { + c->m_active = false; + return; + } + + b3Vec3 P1 = p1->m_position; + + float32 distance = b3Dot(n, P1 - A); + + // Is P1 below the plane? + if (distance < -totalRadius) + { + c->m_active = false; + return; + } + + // Is P1 above the plane? + if (distance > totalRadius) + { + c->m_active = false; + return; + } + + // Closest point on ABC to P1 + float32 wABC[3]; + b3Solve3(wABC, A, B, C, P1); + + b3Vec3 P2 = wABC[0] * A + wABC[1] * B + wABC[2] * C; + + if (b3DistanceSquared(P1, P2) > totalRadius * totalRadius) + { + c->m_active = false; + return; + } + + // Activate the contact + c->m_active = true; + + // Is the the other point in front of the plane? + if (distance >= 0.0f) + { + c->m_front = true; + } + else + { + c->m_front = false; + } +} + +void b3ClothContactManager::UpdateContacts() +{ + B3_PROFILE("Cloth Update Contacts"); + + // Update the state of all triangle contacts. + b3ParticleTriangleContact* c = m_particleTriangleContactList.m_head; + while (c) + { + bool isntDynamic1 = c->m_p1->m_type != e_dynamicParticle; + bool isntDynamic2 = c->m_p2->m_type != e_dynamicParticle && c->m_p3->m_type != e_dynamicParticle && c->m_p4->m_type != e_dynamicParticle; + + // Destroy the contact if primitives must not collide with each other. + if (isntDynamic1 && isntDynamic2) + { + b3ParticleTriangleContact* quack = c; + c = c->m_next; + Destroy(quack); + continue; + } + + u32 proxy1 = c->m_p1->m_aabbProxy.broadPhaseId; + u32 proxy2 = c->m_triangleProxy->broadPhaseId; + + // Destroy the contact if primitive AABBs are not overlapping. + bool overlap = m_broadPhase.TestOverlap(proxy1, proxy2); + if (overlap == false) + { + b3ParticleTriangleContact* quack = c; + c = c->m_next; + Destroy(quack); + continue; + } + + // The contact persists. + Update(c); + + c = c->m_next; + } +} \ No newline at end of file diff --git a/src/bounce/cloth/cloth_contact_solver.cpp b/src/bounce/cloth/cloth_contact_solver.cpp index 46d84ad..0a7768c 100644 --- a/src/bounce/cloth/cloth_contact_solver.cpp +++ b/src/bounce/cloth/cloth_contact_solver.cpp @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -34,10 +35,18 @@ b3ClothContactSolver::b3ClothContactSolver(const b3ClothContactSolverDef& def) m_bodyContacts = def.bodyContacts; m_bodyVelocityConstraints = (b3ClothSolverBodyContactVelocityConstraint*)m_allocator->Allocate(m_bodyContactCount * sizeof(b3ClothSolverBodyContactVelocityConstraint)); m_bodyPositionConstraints = (b3ClothSolverBodyContactPositionConstraint*)m_allocator->Allocate(m_bodyContactCount * sizeof(b3ClothSolverBodyContactPositionConstraint)); + + m_triangleContactCount = def.triangleContactCount; + m_triangleContacts = def.triangleContacts; + m_triangleVelocityConstraints = (b3ClothSolverTriangleContactVelocityConstraint*)m_allocator->Allocate(m_triangleContactCount * sizeof(b3ClothSolverTriangleContactVelocityConstraint)); + m_trianglePositionConstraints = (b3ClothSolverTriangleContactPositionConstraint*)m_allocator->Allocate(m_triangleContactCount * sizeof(b3ClothSolverTriangleContactPositionConstraint)); } b3ClothContactSolver::~b3ClothContactSolver() { + m_allocator->Free(m_trianglePositionConstraints); + m_allocator->Free(m_triangleVelocityConstraints); + m_allocator->Free(m_bodyPositionConstraints); m_allocator->Free(m_bodyVelocityConstraints); } @@ -170,8 +179,93 @@ void b3ClothContactSolver::InitializeBodyContactConstraints() } } } +void b3ClothContactSolver::InitializeTriangleContactConstraints() +{ + b3DenseVec3& x = *m_positions; -void b3ClothContactSolver::WarmStart() + for (u32 i = 0; i < m_triangleContactCount; ++i) + { + b3ParticleTriangleContact* c = m_triangleContacts[i]; + b3ClothSolverTriangleContactVelocityConstraint* vc = m_triangleVelocityConstraints + i; + b3ClothSolverTriangleContactPositionConstraint* pc = m_trianglePositionConstraints + i; + + vc->indexA = c->m_p1->m_solverId; + vc->invMassA = c->m_p1->m_type == e_staticParticle ? 0.0f : c->m_p1->m_invMass; + + vc->indexB = c->m_p2->m_solverId; + vc->invMassB = c->m_p2->m_type == e_staticParticle ? 0.0f : c->m_p2->m_invMass; + + vc->indexC = c->m_p3->m_solverId; + vc->invMassC = c->m_p3->m_type == e_staticParticle ? 0.0f : c->m_p3->m_invMass; + + vc->indexD = c->m_p4->m_solverId; + vc->invMassD = c->m_p4->m_type == e_staticParticle ? 0.0f : c->m_p4->m_invMass; + + pc->indexA = c->m_p1->m_solverId; + pc->invMassA = c->m_p1->m_type == e_staticParticle ? 0.0f : c->m_p1->m_invMass; + pc->radiusA = c->m_p1->m_radius; + + pc->indexB = c->m_p2->m_solverId; + pc->invMassB = c->m_p2->m_type == e_staticParticle ? 0.0f : c->m_p2->m_invMass; + + pc->indexC = c->m_p3->m_solverId; + pc->invMassC = c->m_p3->m_type == e_staticParticle ? 0.0f : c->m_p3->m_invMass; + + pc->indexD = c->m_p4->m_solverId; + pc->invMassD = c->m_p4->m_type == e_staticParticle ? 0.0f : c->m_p4->m_invMass; + + pc->triangleRadius = 0.0f; + pc->front = c->m_front; + + u32 indexA = pc->indexA; + float32 mA = pc->invMassA; + + u32 indexB = pc->indexB; + float32 mB = pc->invMassB; + + u32 indexC = pc->indexC; + float32 mC = pc->invMassC; + + u32 indexD = pc->indexD; + float32 mD = pc->invMassD; + + b3Vec3 xA = x[indexA]; + b3Vec3 xB = x[indexB]; + b3Vec3 xC = x[indexC]; + b3Vec3 xD = x[indexD]; + + b3Vec3 n = b3Cross(xC - xB, xD - xB); + + if (pc->front == false) + { + n = -n; + } + + float32 n_len = n.Normalize(); + + b3Mat33 I; I.SetIdentity(); + + b3Mat33 N = I - b3Outer(n, n); + if (n_len > B3_EPSILON) + { + N = (1.0f / n_len) * N; + } + + b3Vec3 N_n = N * n; + + vc->JA = n; + vc->JB = b3Cross(xC - xD, N_n) - n; + vc->JC = b3Cross(xD - xB, N_n); + vc->JD = b3Cross(xC - xB, N_n); + + float32 K = mA + mB * b3Dot(vc->JB, vc->JB) + mC * b3Dot(vc->JC, vc->JC) + mD * b3Dot(vc->JD, vc->JD); + + vc->normalMass = K > 0.0f ? 1.0f / K : 0.0f; + vc->normalImpulse = c->m_normalImpulse; + } +} + +void b3ClothContactSolver::WarmStartBodyContactConstraints() { b3DenseVec3& v = *m_velocities; @@ -218,6 +312,46 @@ void b3ClothContactSolver::WarmStart() } } +void b3ClothContactSolver::WarmStartTriangleContactConstraints() +{ + b3DenseVec3& v = *m_velocities; + + for (u32 i = 0; i < m_triangleContactCount; ++i) + { + b3ClothSolverTriangleContactVelocityConstraint* vc = m_triangleVelocityConstraints + i; + + u32 indexA = vc->indexA; + u32 indexB = vc->indexB; + u32 indexC = vc->indexC; + u32 indexD = vc->indexD; + + b3Vec3 vA = v[indexA]; + b3Vec3 vB = v[indexB]; + b3Vec3 vC = v[indexC]; + b3Vec3 vD = v[indexD]; + + float32 mA = vc->invMassA; + float32 mB = vc->invMassB; + float32 mC = vc->invMassC; + float32 mD = vc->invMassD; + + b3Vec3 PA = vc->normalImpulse * vc->JA; + b3Vec3 PB = vc->normalImpulse * vc->JB; + b3Vec3 PC = vc->normalImpulse * vc->JC; + b3Vec3 PD = vc->normalImpulse * vc->JD; + + vA += mA * PA; + vB += mB * PB; + vC += mC * PC; + vD += mD * PD; + + v[indexA] = vA; + v[indexB] = vB; + v[indexC] = vC; + v[indexD] = vD; + } +} + void b3ClothContactSolver::SolveBodyContactVelocityConstraints() { b3DenseVec3& v = *m_velocities; @@ -306,6 +440,56 @@ void b3ClothContactSolver::SolveBodyContactVelocityConstraints() } } +void b3ClothContactSolver::SolveTriangleContactVelocityConstraints() +{ + b3DenseVec3& v = *m_velocities; + + for (u32 i = 0; i < m_triangleContactCount; ++i) + { + b3ClothSolverTriangleContactVelocityConstraint* vc = m_triangleVelocityConstraints + i; + + u32 indexA = vc->indexA; + float32 mA = vc->invMassA; + + u32 indexB = vc->indexB; + float32 mB = vc->invMassB; + + u32 indexC = vc->indexC; + float32 mC = vc->invMassC; + + u32 indexD = vc->indexD; + float32 mD = vc->invMassD; + + b3Vec3 vA = v[indexA]; + b3Vec3 vB = v[indexB]; + b3Vec3 vC = v[indexC]; + b3Vec3 vD = v[indexD]; + + float32 Cdot = b3Dot(vc->JA, vA) + b3Dot(vc->JB, vB) + b3Dot(vc->JC, vC) + b3Dot(vc->JD, vD); + + float32 impulse = -vc->normalMass * Cdot; + + float32 oldImpulse = vc->normalImpulse; + vc->normalImpulse = b3Max(vc->normalImpulse + impulse, 0.0f); + impulse = vc->normalImpulse - oldImpulse; + + b3Vec3 PA = impulse * vc->JA; + b3Vec3 PB = impulse * vc->JB; + b3Vec3 PC = impulse * vc->JC; + b3Vec3 PD = impulse * vc->JD; + + vA += mA * PA; + vB += mB * PB; + vC += mC * PC; + vD += mD * PD; + + v[indexA] = vA; + v[indexB] = vB; + v[indexC] = vC; + v[indexD] = vD; + } +} + void b3ClothContactSolver::StoreImpulses() { for (u32 i = 0; i < m_bodyContactCount; ++i) @@ -316,6 +500,14 @@ void b3ClothContactSolver::StoreImpulses() c->normalImpulse = vc->normalImpulse; c->tangentImpulse = vc->tangentImpulse; } + + for (u32 i = 0; i < m_triangleContactCount; ++i) + { + b3ParticleTriangleContact* c = m_triangleContacts[i]; + b3ClothSolverTriangleContactVelocityConstraint* vc = m_triangleVelocityConstraints + i; + + c->m_normalImpulse = vc->normalImpulse; + } } struct b3ClothSolverBodyContactSolverPoint @@ -417,5 +609,92 @@ bool b3ClothContactSolver::SolveBodyContactPositionConstraints() bodyB->m_sweep.orientation = qB; } + return minSeparation >= -3.0f * B3_LINEAR_SLOP; +} + +bool b3ClothContactSolver::SolveTriangleContactPositionConstraints() +{ + b3DenseVec3& x = *m_positions; + + float32 minSeparation = 0.0f; + + for (u32 i = 0; i < m_triangleContactCount; ++i) + { + b3ClothSolverTriangleContactPositionConstraint* pc = m_trianglePositionConstraints + i; + + u32 indexA = pc->indexA; + float32 mA = pc->invMassA; + float32 radiusA = pc->radiusA; + + u32 indexB = pc->indexB; + float32 mB = pc->invMassB; + + u32 indexC = pc->indexC; + float32 mC = pc->invMassC; + + u32 indexD = pc->indexD; + float32 mD = pc->invMassD; + + float32 triangleRadius = pc->triangleRadius; + + b3Vec3 xA = x[indexA]; + b3Vec3 xB = x[indexB]; + b3Vec3 xC = x[indexC]; + b3Vec3 xD = x[indexD]; + + b3Vec3 n = b3Cross(xC - xB, xD - xB); + if (pc->front == false) + { + n = -n; + } + + float32 n_len = n.Normalize(); + + float32 distance = b3Dot(n, xA - xB); + float32 separation = distance - radiusA - triangleRadius; + + // Update max constraint error. + minSeparation = b3Min(minSeparation, separation); + + // Allow some slop and prevent large corrections. + float32 C = b3Clamp(B3_BAUMGARTE * (separation + B3_LINEAR_SLOP), -B3_MAX_LINEAR_CORRECTION, 0.0f); + + b3Mat33 I; I.SetIdentity(); + + b3Mat33 N = I - b3Outer(n, n); + if (n_len > B3_EPSILON) + { + N = (1.0f / n_len) * N; + } + + b3Vec3 N_n = N * n; + + b3Vec3 JA = n; + b3Vec3 JB = b3Cross(xC - xD, N_n) - n; + b3Vec3 JC = b3Cross(xD - xB, N_n); + b3Vec3 JD = b3Cross(xC - xB, N_n); + + // Compute effective mass. + float32 K = mA + mB * b3Dot(JB, JB) + mC * b3Dot(JC, JC) + mD * b3Dot(JD, JD); + + // Compute normal impulse. + float32 impulse = K > 0.0f ? -C / K : 0.0f; + + b3Vec3 PA = impulse * JA; + b3Vec3 PB = impulse * JB; + b3Vec3 PC = impulse * JC; + b3Vec3 PD = impulse * JD; + + xA += mA * PA; + xB += mB * PB; + xC += mC * PC; + xD += mD * PD; + + x[indexA] = xA; + x[indexB] = xB; + x[indexC] = xC; + x[indexD] = xD; + } + return minSeparation >= -3.0f * B3_LINEAR_SLOP; } \ No newline at end of file diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index 0daf1a8..92c7530 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -58,10 +58,15 @@ b3ClothSolver::b3ClothSolver(const b3ClothSolverDef& def) m_bodyContactCapacity = def.bodyContactCapacity; m_bodyContactCount = 0; m_bodyContacts = (b3ParticleBodyContact**)m_allocator->Allocate(m_bodyContactCapacity * sizeof(b3ParticleBodyContact*));; + + m_triangleContactCapacity = def.triangleContactCapacity; + m_triangleContactCount = 0; + m_triangleContacts = (b3ParticleTriangleContact**)m_allocator->Allocate(m_triangleContactCapacity * sizeof(b3ParticleTriangleContact*));; } b3ClothSolver::~b3ClothSolver() { + m_allocator->Free(m_triangleContacts); m_allocator->Free(m_bodyContacts); m_allocator->Free(m_constraints); @@ -85,6 +90,11 @@ void b3ClothSolver::Add(b3ParticleBodyContact* c) m_bodyContacts[m_bodyContactCount++] = c; } +void b3ClothSolver::Add(b3ParticleTriangleContact* c) +{ + m_triangleContacts[m_triangleContactCount++] = c; +} + void b3ClothSolver::ApplyForces() { for (u32 i = 0; i < m_forceCount; ++i) @@ -266,21 +276,26 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterati contactSolverDef.velocities = m_solverData.v; contactSolverDef.bodyContactCount = m_bodyContactCount; contactSolverDef.bodyContacts = m_bodyContacts; + contactSolverDef.triangleContactCount = m_triangleContactCount; + contactSolverDef.triangleContacts = m_triangleContacts; b3ClothContactSolver contactSolver(contactSolverDef); { contactSolver.InitializeBodyContactConstraints(); + contactSolver.InitializeTriangleContactConstraints(); } { - contactSolver.WarmStart(); + contactSolver.WarmStartBodyContactConstraints(); + contactSolver.WarmStartTriangleContactConstraints(); } { for (u32 i = 0; i < velocityIterations; ++i) { contactSolver.SolveBodyContactVelocityConstraints(); + contactSolver.SolveTriangleContactVelocityConstraints(); } } @@ -297,9 +312,11 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterati for (u32 i = 0; i < positionIterations; ++i) { bool bodyContactsSolved = contactSolver.SolveBodyContactPositionConstraints(); + bool triangleContactsSolved = contactSolver.SolveTriangleContactPositionConstraints(); - if (bodyContactsSolved) + if (bodyContactsSolved && triangleContactsSolved) { + // Early out if the position errors are small. positionSolved = true; break; } diff --git a/src/bounce/cloth/particle.cpp b/src/bounce/cloth/particle.cpp index 3316a77..5eeddb0 100644 --- a/src/bounce/cloth/particle.cpp +++ b/src/bounce/cloth/particle.cpp @@ -18,6 +18,7 @@ #include #include +#include void b3ParticleBodyContactWorldPoint::Initialize(const b3ParticleBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB) { @@ -42,8 +43,20 @@ b3Particle::b3Particle(const b3ParticleDef& def, b3Cloth* cloth) m_velocity = def.velocity; m_force = def.force; m_translation.SetZero(); - m_mass = 0.0f; - m_invMass = 0.0f; + m_mass = def.mass; + + if (m_mass == 0.0f) + { + m_type = e_staticParticle; + m_mass = 1.0f; + m_invMass = 1.0f; + } + else + { + m_type = e_dynamicParticle; + m_invMass = 1.0f / m_mass; + } + m_radius = def.radius; m_friction = def.friction; m_userData = nullptr; @@ -62,7 +75,48 @@ void b3Particle::Synchronize() b3AABB3 aabb; aabb.Set(m_position, m_radius); - m_cloth->m_particleTree.UpdateNode(m_treeId, aabb); + b3Vec3 displacement = m_cloth->m_dt * m_velocity; + + m_cloth->m_contactManager.m_broadPhase.MoveProxy(m_aabbProxy.broadPhaseId, aabb, displacement); +} + +void b3Particle::SynchronizeTriangles() +{ + if (m_vertex == ~0) + { + return; + } + + for (u32 i = 0; i < m_cloth->m_mesh->triangleCount; ++i) + { + b3ClothMeshTriangle* triangle = m_cloth->m_mesh->triangles + i; + + if (triangle->v1 == m_vertex || triangle->v2 == m_vertex || triangle->v3 == m_vertex) + { + m_cloth->SynchronizeTriangle(i); + } + } +} + +void b3Particle::DestroyContacts() +{ + // Destroy body contacts + m_bodyContact.active = false; + + // Destroy triangle contacts + b3ParticleTriangleContact* c = m_cloth->m_contactManager.m_particleTriangleContactList.m_head; + while (c) + { + if (c->m_p1 == this) + { + b3ParticleTriangleContact* quack = c; + c = c->m_next; + m_cloth->m_contactManager.Destroy(quack); + continue; + } + + c = c->m_next; + } } void b3Particle::SetType(b3ParticleType type) @@ -81,7 +135,11 @@ void b3Particle::SetType(b3ParticleType type) m_translation.SetZero(); Synchronize(); + SynchronizeTriangles(); } - m_bodyContact.active = false; + DestroyContacts(); + + // Move the proxy so new contacts can be created. + m_cloth->m_contactManager.m_broadPhase.TouchProxy(m_aabbProxy.broadPhaseId); } \ No newline at end of file From e5db997fa7485ece3c5f042de2ebddfffa774702 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 12 Jun 2019 18:20:39 -0300 Subject: [PATCH 146/198] Update cloth_contact_manager.cpp --- src/bounce/cloth/cloth_contact_manager.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bounce/cloth/cloth_contact_manager.cpp b/src/bounce/cloth/cloth_contact_manager.cpp index 6f5ce47..601041d 100644 --- a/src/bounce/cloth/cloth_contact_manager.cpp +++ b/src/bounce/cloth/cloth_contact_manager.cpp @@ -21,8 +21,6 @@ #include #include -#include - b3ClothContactManager::b3ClothContactManager() : m_particleTriangleContactBlocks(sizeof(b3ParticleTriangleContact)) { From 46abe231e30f1797ea6da426f8c5d23b3f882fd4 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 12 Jun 2019 20:46:54 -0300 Subject: [PATCH 147/198] Use a modified inverse mass to avoid some instability problems --- src/bounce/cloth/cloth_contact_solver.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bounce/cloth/cloth_contact_solver.cpp b/src/bounce/cloth/cloth_contact_solver.cpp index 0a7768c..9154c8a 100644 --- a/src/bounce/cloth/cloth_contact_solver.cpp +++ b/src/bounce/cloth/cloth_contact_solver.cpp @@ -675,7 +675,8 @@ bool b3ClothContactSolver::SolveTriangleContactPositionConstraints() b3Vec3 JD = b3Cross(xC - xB, N_n); // Compute effective mass. - float32 K = mA + mB * b3Dot(JB, JB) + mC * b3Dot(JC, JC) + mD * b3Dot(JD, JD); + //float32 K = mA + mB * b3Dot(JB, JB) + mC * b3Dot(JC, JC) + mD * b3Dot(JD, JD); + float32 K = mA + mB + mC + mD; // Compute normal impulse. float32 impulse = K > 0.0f ? -C / K : 0.0f; From 6b92664c1e1f0803fa0096d1d4ded934dc9386b0 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 12 Jun 2019 20:52:39 -0300 Subject: [PATCH 148/198] Use modified mass in velocity solver too --- src/bounce/cloth/cloth_contact_solver.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bounce/cloth/cloth_contact_solver.cpp b/src/bounce/cloth/cloth_contact_solver.cpp index 9154c8a..57f3f05 100644 --- a/src/bounce/cloth/cloth_contact_solver.cpp +++ b/src/bounce/cloth/cloth_contact_solver.cpp @@ -258,7 +258,8 @@ void b3ClothContactSolver::InitializeTriangleContactConstraints() vc->JC = b3Cross(xD - xB, N_n); vc->JD = b3Cross(xC - xB, N_n); - float32 K = mA + mB * b3Dot(vc->JB, vc->JB) + mC * b3Dot(vc->JC, vc->JC) + mD * b3Dot(vc->JD, vc->JD); + //float32 K = mA + mB * b3Dot(vc->JB, vc->JB) + mC * b3Dot(vc->JC, vc->JC) + mD * b3Dot(vc->JD, vc->JD); + float32 K = mA + mB + mC + mD; vc->normalMass = K > 0.0f ? 1.0f / K : 0.0f; vc->normalImpulse = c->m_normalImpulse; From f7becc7ee78872c221d37e3fdffbc5f074072636 Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 13 Jun 2019 10:13:15 -0300 Subject: [PATCH 149/198] Organize cloth contacts --- include/bounce/cloth/cloth_contact.h | 64 +++++++ include/bounce/cloth/cloth_contact_manager.h | 39 +--- include/bounce/cloth/particle.h | 9 +- src/bounce/cloth/cloth_contact.cpp | 183 +++++++++++++++++++ src/bounce/cloth/cloth_contact_manager.cpp | 183 +------------------ 5 files changed, 264 insertions(+), 214 deletions(-) create mode 100644 include/bounce/cloth/cloth_contact.h create mode 100644 src/bounce/cloth/cloth_contact.cpp diff --git a/include/bounce/cloth/cloth_contact.h b/include/bounce/cloth/cloth_contact.h new file mode 100644 index 0000000..f913ebe --- /dev/null +++ b/include/bounce/cloth/cloth_contact.h @@ -0,0 +1,64 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_CLOTH_CONTACT_H +#define B3_CLOTH_CONTACT_H + +#include + +class b3Particle; +struct b3ClothMeshTriangle; +struct b3ClothAABBProxy; + +// Contact between particle and a triangle +class b3ParticleTriangleContact +{ +public: +private: + friend class b3Cloth; + friend class b3Particle; + friend class b3ClothContactManager; + friend class b3List2; + friend class b3ClothContactSolver; + + b3ParticleTriangleContact() { } + ~b3ParticleTriangleContact() { } + + void Update(); + + // Particle + b3Particle* m_p1; + + // Triangle + b3ClothMeshTriangle* m_triangle; + b3ClothAABBProxy* m_triangleProxy; + b3Particle* m_p2; + b3Particle* m_p3; + b3Particle* m_p4; + + float32 m_normalImpulse; + + bool m_front; + + bool m_active; + + b3ParticleTriangleContact* m_prev; + b3ParticleTriangleContact* m_next; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/cloth/cloth_contact_manager.h b/include/bounce/cloth/cloth_contact_manager.h index 3e67e2f..8f4517e 100644 --- a/include/bounce/cloth/cloth_contact_manager.h +++ b/include/bounce/cloth/cloth_contact_manager.h @@ -19,36 +19,12 @@ #ifndef B3_CLOTH_CONTACT_MANAGER_H #define B3_CLOTH_CONTACT_MANAGER_H +#include +#include #include #include -#include class b3Cloth; -class b3Particle; -struct b3ClothMeshTriangle; -struct b3ClothAABBProxy; - -// Contact between particle and a triangle -class b3ParticleTriangleContact -{ -public: - b3Particle* m_p1; - - b3ClothMeshTriangle* m_triangle; - b3ClothAABBProxy* m_triangleProxy; - b3Particle* m_p2; - b3Particle* m_p3; - b3Particle* m_p4; - - bool m_front; - - bool m_active; - - float32 m_normalImpulse; - - b3ParticleTriangleContact* m_prev; - b3ParticleTriangleContact* m_next; -}; // Contact delegator for b3Cloth. class b3ClothContactManager @@ -62,19 +38,14 @@ public: void FindNewContacts(); void UpdateContacts(); - - b3ParticleTriangleContact* Create(); - + + b3ParticleTriangleContact* CreateParticleTriangleContact(); void Destroy(b3ParticleTriangleContact* c); - void Update(b3ParticleTriangleContact* c); - - b3Cloth* m_cloth; - b3BlockPool m_particleTriangleContactBlocks; + b3Cloth* m_cloth; b3BroadPhase m_broadPhase; - b3List2 m_particleTriangleContactList; }; diff --git a/include/bounce/cloth/particle.h b/include/bounce/cloth/particle.h index d2d13cf..bdef984 100644 --- a/include/bounce/cloth/particle.h +++ b/include/bounce/cloth/particle.h @@ -19,10 +19,10 @@ #ifndef B3_PARTICLE_H #define B3_PARTICLE_H +#include #include #include #include -#include class b3Shape; class b3Cloth; @@ -91,12 +91,14 @@ struct b3ParticleBodyContactWorldPoint float32 separation; }; +// Cloth primitive type enum b3ClothAABBProxyType { e_particleProxy, e_triangleProxy }; +// Cloth primitive broadphase proxy struct b3ClothAABBProxy { b3ClothAABBProxyType type; @@ -160,6 +162,7 @@ private: friend class b3Cloth; friend class b3ClothSolver; friend class b3ClothContactManager; + friend class b3ParticleTriangleContact; friend class b3ClothContactSolver; friend class b3Force; friend class b3SpringForce; @@ -226,10 +229,8 @@ private: // AABB Proxy b3ClothAABBProxy m_aabbProxy; - // + // Links to the cloth particle list. b3Particle* m_prev; - - // b3Particle* m_next; }; diff --git a/src/bounce/cloth/cloth_contact.cpp b/src/bounce/cloth/cloth_contact.cpp new file mode 100644 index 0000000..edbcb05 --- /dev/null +++ b/src/bounce/cloth/cloth_contact.cpp @@ -0,0 +1,183 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include + +// +static void b3Solve3(float32 out[3], + const b3Vec3& A, const b3Vec3& B, const b3Vec3& C, + const b3Vec3& Q) +{ + // Test vertex regions + float32 wAB[3], wBC[3], wCA[3]; + b3BarycentricCoordinates(wAB, A, B, Q); + b3BarycentricCoordinates(wBC, B, C, Q); + b3BarycentricCoordinates(wCA, C, A, Q); + + // R A + if (wAB[1] <= 0.0f && wCA[0] <= 0.0f) + { + out[0] = 1.0f; + out[1] = 0.0f; + out[2] = 0.0f; + return; + } + + // R B + if (wAB[0] <= 0.0f && wBC[1] <= 0.0f) + { + out[0] = 0.0f; + out[1] = 1.0f; + out[2] = 0.0f; + return; + } + + // R C + if (wBC[0] <= 0.0f && wCA[1] <= 0.0f) + { + out[0] = 0.0f; + out[1] = 0.0f; + out[2] = 1.0f; + return; + } + + // Test edge regions + float32 wABC[4]; + b3BarycentricCoordinates(wABC, A, B, C, Q); + + // R AB + if (wAB[0] > 0.0f && wAB[1] > 0.0f && wABC[3] * wABC[2] <= 0.0f) + { + float32 divisor = wAB[2]; + B3_ASSERT(divisor > 0.0f); + float32 s = 1.0f / divisor; + out[0] = s * wAB[0]; + out[1] = s * wAB[1]; + out[2] = 0.0f; + return; + } + + // R BC + if (wBC[0] > 0.0f && wBC[1] > 0.0f && wABC[3] * wABC[0] <= 0.0f) + { + float32 divisor = wBC[2]; + B3_ASSERT(divisor > 0.0f); + float32 s = 1.0f / divisor; + out[0] = 0.0f; + out[1] = s * wBC[0]; + out[2] = s * wBC[1]; + return; + } + + // R CA + if (wCA[0] > 0.0f && wCA[1] > 0.0f && wABC[3] * wABC[1] <= 0.0f) + { + float32 divisor = wCA[2]; + B3_ASSERT(divisor > 0.0f); + float32 s = 1.0f / divisor; + out[0] = s * wCA[1]; + out[1] = 0.0f; + out[2] = s * wCA[0]; + return; + } + + // R ABC/ACB + float32 divisor = wABC[3]; + if (divisor == 0.0f) + { + float32 s = 1.0f / 3.0f; + out[0] = s; + out[1] = s; + out[2] = s; + return; + } + + B3_ASSERT(divisor > 0.0f); + float32 s = 1.0f / divisor; + out[0] = s * wABC[0]; + out[1] = s * wABC[1]; + out[2] = s * wABC[2]; +} + +void b3ParticleTriangleContact::Update() +{ + b3Vec3 A = m_p2->m_position; + b3Vec3 B = m_p3->m_position; + b3Vec3 C = m_p4->m_position; + + b3Vec3 N = b3Cross(B - A, C - A); + float32 len = N.Normalize(); + + // Is ABC degenerate? + if (len == 0.0f) + { + m_active = false; + return; + } + + float32 r1 = m_p1->m_radius; + float32 r2 = 0.0f; + + float32 totalRadius = r1 + r2; + + b3Vec3 P1 = m_p1->m_position; + + float32 distance = b3Dot(N, P1 - A); + + // Is P1 below the plane? + if (distance < -totalRadius) + { + m_active = false; + return; + } + + // Is P1 above the plane? + if (distance > totalRadius) + { + m_active = false; + return; + } + + // Closest point on ABC to P1 + float32 wABC[3]; + b3Solve3(wABC, A, B, C, P1); + + b3Vec3 P2 = wABC[0] * A + wABC[1] * B + wABC[2] * C; + + if (b3DistanceSquared(P1, P2) > totalRadius * totalRadius) + { + m_active = false; + return; + } + + // Activate the contact + m_active = true; + + // Is P1 in front or back of the plane? + if (distance >= 0.0f) + { + m_front = true; + } + else + { + m_front = false; + } +} \ No newline at end of file diff --git a/src/bounce/cloth/cloth_contact_manager.cpp b/src/bounce/cloth/cloth_contact_manager.cpp index 601041d..872f2c2 100644 --- a/src/bounce/cloth/cloth_contact_manager.cpp +++ b/src/bounce/cloth/cloth_contact_manager.cpp @@ -93,26 +93,23 @@ void b3ClothContactManager::AddPair(void* data1, void* data2) } // Create a new contact. - b3ParticleTriangleContact* c = Create(); + b3ParticleTriangleContact* c = CreateParticleTriangleContact(); c->m_p1 = p1; - + c->m_triangle = triangle; + c->m_triangleProxy = proxy2; c->m_p2 = p2; c->m_p3 = p3; c->m_p4 = p4; - c->m_triangle = triangle; - c->m_triangleProxy = proxy2; - + c->m_normalImpulse = 0.0f; c->m_front = false; c->m_active = false; - c->m_normalImpulse = 0.0f; - // Add the contact to the cloth contact list. m_particleTriangleContactList.PushFront(c); } -b3ParticleTriangleContact* b3ClothContactManager::Create() +b3ParticleTriangleContact* b3ClothContactManager::CreateParticleTriangleContact() { void* block = m_particleTriangleContactBlocks.Allocate(); return new(block) b3ParticleTriangleContact(); @@ -127,177 +124,11 @@ void b3ClothContactManager::Destroy(b3ParticleTriangleContact* c) m_particleTriangleContactBlocks.Free(c); } -static void b3Solve3(float32 out[3], - const b3Vec3& A, const b3Vec3& B, const b3Vec3& C, - const b3Vec3& Q) -{ - // Test vertex regions - float32 wAB[3], wBC[3], wCA[3]; - b3BarycentricCoordinates(wAB, A, B, Q); - b3BarycentricCoordinates(wBC, B, C, Q); - b3BarycentricCoordinates(wCA, C, A, Q); - - // R A - if (wAB[1] <= 0.0f && wCA[0] <= 0.0f) - { - out[0] = 1.0f; - out[1] = 0.0f; - out[2] = 0.0f; - return; - } - - // R B - if (wAB[0] <= 0.0f && wBC[1] <= 0.0f) - { - out[0] = 0.0f; - out[1] = 1.0f; - out[2] = 0.0f; - return; - } - - // R C - if (wBC[0] <= 0.0f && wCA[1] <= 0.0f) - { - out[0] = 0.0f; - out[1] = 0.0f; - out[2] = 1.0f; - return; - } - - // Test edge regions - float32 wABC[4]; - b3BarycentricCoordinates(wABC, A, B, C, Q); - - // R AB - if (wAB[0] > 0.0f && wAB[1] > 0.0f && wABC[3] * wABC[2] <= 0.0f) - { - float32 divisor = wAB[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; - out[0] = s * wAB[0]; - out[1] = s * wAB[1]; - out[2] = 0.0f; - return; - } - - // R BC - if (wBC[0] > 0.0f && wBC[1] > 0.0f && wABC[3] * wABC[0] <= 0.0f) - { - float32 divisor = wBC[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; - out[0] = 0.0f; - out[1] = s * wBC[0]; - out[2] = s * wBC[1]; - return; - } - - // R CA - if (wCA[0] > 0.0f && wCA[1] > 0.0f && wABC[3] * wABC[1] <= 0.0f) - { - float32 divisor = wCA[2]; - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; - out[0] = s * wCA[1]; - out[1] = 0.0f; - out[2] = s * wCA[0]; - return; - } - - // R ABC/ACB - float32 divisor = wABC[3]; - if (divisor == 0.0f) - { - float32 s = 1.0f / 3.0f; - out[0] = s; - out[1] = s; - out[2] = s; - return; - } - - B3_ASSERT(divisor > 0.0f); - float32 s = 1.0f / divisor; - out[0] = s * wABC[0]; - out[1] = s * wABC[1]; - out[2] = s * wABC[2]; -} - -void b3ClothContactManager::Update(b3ParticleTriangleContact* c) -{ - b3Particle* p1 = c->m_p1; - - b3Particle* p2 = c->m_p2; - b3Particle* p3 = c->m_p3; - b3Particle* p4 = c->m_p4; - - float32 r1 = p1->m_radius; - float32 r2 = 0.0f; - - float32 totalRadius = r1 + r2; - - b3Vec3 A = p2->m_position; - b3Vec3 B = p3->m_position; - b3Vec3 C = p4->m_position; - - b3Vec3 n = b3Cross(B - A, C - A); - float32 len = n.Normalize(); - - // Is ABC degenerate? - if (len == 0.0f) - { - c->m_active = false; - return; - } - - b3Vec3 P1 = p1->m_position; - - float32 distance = b3Dot(n, P1 - A); - - // Is P1 below the plane? - if (distance < -totalRadius) - { - c->m_active = false; - return; - } - - // Is P1 above the plane? - if (distance > totalRadius) - { - c->m_active = false; - return; - } - - // Closest point on ABC to P1 - float32 wABC[3]; - b3Solve3(wABC, A, B, C, P1); - - b3Vec3 P2 = wABC[0] * A + wABC[1] * B + wABC[2] * C; - - if (b3DistanceSquared(P1, P2) > totalRadius * totalRadius) - { - c->m_active = false; - return; - } - - // Activate the contact - c->m_active = true; - - // Is the the other point in front of the plane? - if (distance >= 0.0f) - { - c->m_front = true; - } - else - { - c->m_front = false; - } -} - void b3ClothContactManager::UpdateContacts() { B3_PROFILE("Cloth Update Contacts"); - // Update the state of all triangle contacts. + // Update the state of particle-triangle contacts. b3ParticleTriangleContact* c = m_particleTriangleContactList.m_head; while (c) { @@ -327,7 +158,7 @@ void b3ClothContactManager::UpdateContacts() } // The contact persists. - Update(c); + c->Update(); c = c->m_next; } From 901aa7255da4d34855260de102ed9705ded61281 Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 13 Jun 2019 13:36:31 -0300 Subject: [PATCH 150/198] Update cloth.h --- include/bounce/cloth/cloth.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index a84cf1a..330c842 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -67,7 +67,7 @@ struct b3ClothDef // Cloth mesh const b3ClothMesh* mesh; - // Cloth density in kg/m^3 + // Cloth density in kg/m^2 float32 density; // Structural stiffness From 922a5a0a7458f2d16301031c6a0595c5bd9ccb33 Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 14 Jun 2019 10:46:22 -0300 Subject: [PATCH 151/198] Swap parameters --- include/bounce/common/draw.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/bounce/common/draw.h b/include/bounce/common/draw.h index 7a78162..bc5f1d9 100644 --- a/include/bounce/common/draw.h +++ b/include/bounce/common/draw.h @@ -96,10 +96,10 @@ public : virtual void DrawSolidCircle(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color) = 0; // Draw a plane with center, normal and radius. - virtual void DrawPlane(const b3Vec3& center, const b3Vec3& normal, float32 radius, const b3Color& color) = 0; + virtual void DrawPlane(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color) = 0; // Draw a solid plane with center, normal and radius. - virtual void DrawSolidPlane(const b3Vec3& center, const b3Vec3& normal, float32 radius, const b3Color& color) = 0; + virtual void DrawSolidPlane(const b3Vec3& normal, const b3Vec3& center, float32 radius, const b3Color& color) = 0; // Draw a sphere with center, and radius. virtual void DrawSphere(const b3Vec3& center, float32 radius, const b3Color& color) = 0; From 24a86505ee15c1095e133b4eb4aadd4b3024a53b Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 14 Jun 2019 11:47:30 -0300 Subject: [PATCH 152/198] Move stuff around --- include/bounce/cloth/cloth_solver.h | 8 -- src/bounce/cloth/cloth_solver.cpp | 170 ++++++++++++++-------------- 2 files changed, 84 insertions(+), 94 deletions(-) diff --git a/include/bounce/cloth/cloth_solver.h b/include/bounce/cloth/cloth_solver.h index c39e417..85ed648 100644 --- a/include/bounce/cloth/cloth_solver.h +++ b/include/bounce/cloth/cloth_solver.h @@ -81,17 +81,9 @@ public: void Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations); private: - // Apply forces. void ApplyForces(); - - // Apply constraints. void ApplyConstraints(); - // Solve Ax = b. - void SolveMPCG(b3DenseVec3& x, - const b3SparseSymMat33View& A, const b3DenseVec3& b, - const b3DiagMat33& S, const b3DenseVec3& z, const b3DenseVec3& y, u32 maxIterations = 30) const; - b3StackAllocator* m_allocator; u32 m_particleCapacity; diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index 92c7530..c0cc90a 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -171,6 +171,84 @@ void b3ClothSolver::ApplyConstraints() } } +// +static void b3SolveMPCG(b3DenseVec3& x, + const b3SparseSymMat33View& A, const b3DenseVec3& b, + const b3DiagMat33& S, const b3DenseVec3& z, + const b3DenseVec3& y, const b3DiagMat33& I, u32 maxIterations = 20) +{ + B3_PROFILE("Cloth Solve MPCG"); + + // Jacobi preconditioner + // P = diag(A) + b3DiagMat33 inv_P(A.rowCount); + for (u32 i = 0; i < A.rowCount; ++i) + { + b3Mat33 a = A(i, i); + + // Sylvester Criterion to ensure PD-ness + B3_ASSERT(b3Det(a.x, a.y, a.z) > 0.0f); + + B3_ASSERT(a.x.x != 0.0f); + float32 xx = 1.0f / a.x.x; + + B3_ASSERT(a.y.y != 0.0f); + float32 yy = 1.0f / a.y.y; + + B3_ASSERT(a.z.z != 0.0f); + float32 zz = 1.0f / a.z.z; + + inv_P[i] = b3Diagonal(xx, yy, zz); + } + + x = (S * y) + (I - S) * z; + + b3DenseVec3 b_hat = S * (b - A * ((I - S) * z)); + + float32 b_delta = b3Dot(b_hat, inv_P * b_hat); + + b3DenseVec3 r = S * (b - A * x); + + b3DenseVec3 p = S * (inv_P * r); + + float32 delta_new = b3Dot(r, p); + + u32 iteration = 0; + for (;;) + { + if (iteration == maxIterations) + { + break; + } + + if (delta_new <= B3_EPSILON * B3_EPSILON * b_delta) + { + break; + } + + b3DenseVec3 s = S * (A * p); + + float32 alpha = delta_new / b3Dot(p, s); + + x = x + alpha * p; + r = r - alpha * s; + + b3DenseVec3 h = inv_P * r; + + float32 delta_old = delta_new; + + delta_new = b3Dot(r, h); + + float32 beta = delta_new / delta_old; + + p = S * (h + beta * p); + + ++iteration; + } + + b3_clothSolverIterations = iteration; +} + void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations) { float32 h = dt; @@ -250,11 +328,15 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterati // b b3DenseVec3 b = h * (sf + h * (dfdx * sv) + dfdx * sy); + // I + b3DiagMat33 I(m_particleCount); + I.SetIdentity(); + // x b3DenseVec3 x(m_particleCount); - SolveMPCG(x, viewA, b, S, z, sx0); + b3SolveMPCG(x, viewA, b, S, z, sx0, I); - // + // Velocity update sv = sv + x; sx = sx + sy; @@ -344,87 +426,3 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterati p->m_velocity = sv[i]; } } - -void b3ClothSolver::SolveMPCG(b3DenseVec3& x, - const b3SparseSymMat33View& A, const b3DenseVec3& b, - const b3DiagMat33& S, const b3DenseVec3& z, const b3DenseVec3& y, u32 maxIterations) const -{ - B3_PROFILE("Cloth Solve Ax = b"); - - // P = diag(A) - b3DiagMat33 inv_P(A.rowCount); - for (u32 i = 0; i < A.rowCount; ++i) - { - inv_P[i] = A(i, i); - } - - // Invert - for (u32 i = 0; i < inv_P.n; ++i) - { - b3Mat33& D = inv_P[i]; - - // Sylvester Criterion to ensure PD-ness - B3_ASSERT(b3Det(D.x, D.y, D.z) > 0.0f); - - B3_ASSERT(D.x.x != 0.0f); - float32 xx = 1.0f / D.x.x; - - B3_ASSERT(D.y.y != 0.0f); - float32 yy = 1.0f / D.y.y; - - B3_ASSERT(D.z.z != 0.0f); - float32 zz = 1.0f / D.z.z; - - D = b3Diagonal(xx, yy, zz); - } - - b3DiagMat33 I(m_particleCount); - I.SetIdentity(); - - x = (S * y) + (I - S) * z; - - b3DenseVec3 b_hat = S * (b - A * ((I - S) * z)); - - float32 b_delta = b3Dot(b_hat, inv_P * b_hat); - - b3DenseVec3 r = S * (b - A * x); - - b3DenseVec3 p = S * (inv_P * r); - - float32 delta_new = b3Dot(r, p); - - u32 iteration = 0; - for (;;) - { - if (iteration == maxIterations) - { - break; - } - - if (delta_new <= B3_EPSILON * B3_EPSILON * b_delta) - { - break; - } - - b3DenseVec3 s = S * (A * p); - - float32 alpha = delta_new / b3Dot(p, s); - - x = x + alpha * p; - r = r - alpha * s; - - b3DenseVec3 h = inv_P * r; - - float32 delta_old = delta_new; - - delta_new = b3Dot(r, h); - - float32 beta = delta_new / delta_old; - - p = S * (h + beta * p); - - ++iteration; - } - - b3_clothSolverIterations = iteration; -} \ No newline at end of file From a3a9495d881884b7beb2f282c1b57f291032456a Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 14 Jun 2019 11:48:34 -0300 Subject: [PATCH 153/198] Use Jacobi preconditioner, not inverse Jacobi --- src/bounce/softbody/softbody_solver.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/bounce/softbody/softbody_solver.cpp b/src/bounce/softbody/softbody_solver.cpp index 1b831e3..b726143 100644 --- a/src/bounce/softbody/softbody_solver.cpp +++ b/src/bounce/softbody/softbody_solver.cpp @@ -92,32 +92,35 @@ static void b3ExtractRotation(b3Mat33& out, b3Quat& q, const b3Mat33& A, u32 max out = b3QuatMat33(q); } +// static void b3SolveMPCG(b3DenseVec3& x, const b3SparseMat33View& A, const b3DenseVec3& b, const b3DenseVec3& z, const b3DiagMat33& S, u32 maxIterations = 20) { - B3_PROFILE("Soft Body Solve Ax = b"); + B3_PROFILE("Soft Body Solve MPCG"); + // Jacobi preconditioner + // P = diag(A) b3DiagMat33 P(A.rowCount); b3DiagMat33 invP(A.rowCount); for (u32 i = 0; i < A.rowCount; ++i) { - b3Mat33 D = A(i, i); + b3Mat33 a = A(i, i); // Sylvester Criterion to ensure PD-ness - B3_ASSERT(b3Det(D.x, D.y, D.z) > 0.0f); + B3_ASSERT(b3Det(a.x, a.y, a.z) > 0.0f); - B3_ASSERT(D.x.x != 0.0f); - float32 xx = 1.0f / D.x.x; + B3_ASSERT(a.x.x != 0.0f); + float32 xx = 1.0f / a.x.x; - B3_ASSERT(D.y.y != 0.0f); - float32 yy = 1.0f / D.y.y; + B3_ASSERT(a.y.y != 0.0f); + float32 yy = 1.0f / a.y.y; - B3_ASSERT(D.z.z != 0.0f); - float32 zz = 1.0f / D.z.z; + B3_ASSERT(a.z.z != 0.0f); + float32 zz = 1.0f / a.z.z; - P[i] = b3Diagonal(xx, yy, zz); - invP[i] = b3Diagonal(D.x.x, D.y.y, D.z.z); + P[i] = b3Diagonal(a.x.x, a.y.y, a.z.z); + invP[i] = b3Diagonal(xx, yy, zz); } x = z; From 11724ef5e3cf82c917df4012866df93242db42a0 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 15 Jun 2019 11:33:24 -0300 Subject: [PATCH 154/198] Put nodes in a broadphase to reduce tree updates --- include/bounce/softbody/softbody.h | 9 ++++++--- include/bounce/softbody/softbody_node.h | 4 ++-- src/bounce/softbody/softbody.cpp | 7 +++++-- src/bounce/softbody/softbody_node.cpp | 4 +++- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/include/bounce/softbody/softbody.h b/include/bounce/softbody/softbody.h index 3e17de5..9506b96 100644 --- a/include/bounce/softbody/softbody.h +++ b/include/bounce/softbody/softbody.h @@ -22,7 +22,7 @@ #include #include -#include +#include class b3World; @@ -189,8 +189,11 @@ private: // Soft body triangles b3SoftBodyTriangle* m_triangles; - // Node tree - b3DynamicTree m_nodeTree; + // Broadphase + b3BroadPhase m_broadPhase; + + // Time-step + float32 m_dt; // Attached world b3World* m_world; diff --git a/include/bounce/softbody/softbody_node.h b/include/bounce/softbody/softbody_node.h index 7b7dd6f..bc9a98b 100644 --- a/include/bounce/softbody/softbody_node.h +++ b/include/bounce/softbody/softbody_node.h @@ -163,8 +163,8 @@ private: // Node and body contact b3NodeBodyContact m_bodyContact; - // Tree identifier - u32 m_treeId; + // Broadphase proxy + u32 m_broadPhaseId; // Soft body b3SoftBody* m_body; diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index 408338b..2448d1e 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -463,6 +463,7 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) m_c_max = def.c_max; m_gravity.SetZero(); m_world = nullptr; + m_dt = 0.0f; const b3SoftBodyMesh* m = m_mesh; @@ -489,7 +490,7 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) b3AABB3 aabb; aabb.Set(n->m_position, 0.0f); - n->m_treeId = m_nodeTree.InsertNode(aabb, n); + n->m_broadPhaseId = m_broadPhase.CreateProxy(aabb, n); } // Compute mass @@ -778,7 +779,7 @@ void b3SoftBody::UpdateContacts() continue; } - b3AABB3 aabb = m_nodeTree.GetAABB(n->m_treeId); + b3AABB3 aabb = m_broadPhase.GetAABB(n->m_broadPhaseId); b3SoftBodyUpdateContactsQueryListener listener; listener.sphere.vertex = n->m_position; @@ -839,6 +840,8 @@ void b3SoftBody::Step(float32 dt, u32 velocityIterations, u32 positionIterations { B3_PROFILE("Soft Body Step"); + m_dt = dt; + // Update contacts UpdateContacts(); diff --git a/src/bounce/softbody/softbody_node.cpp b/src/bounce/softbody/softbody_node.cpp index e18ffed..9df0ad4 100644 --- a/src/bounce/softbody/softbody_node.cpp +++ b/src/bounce/softbody/softbody_node.cpp @@ -39,5 +39,7 @@ void b3SoftBodyNode::Synchronize() b3AABB3 aabb; aabb.Set(m_position, m_radius); - m_body->m_nodeTree.UpdateNode(m_treeId, aabb); + b3Vec3 displacement = m_body->m_dt * m_velocity; + + m_body->m_broadPhase.MoveProxy(m_broadPhaseId, aabb, displacement); } \ No newline at end of file From 4f34c6a95b4d4e41697d02a2634f1578bda7c482 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 15 Jun 2019 11:38:33 -0300 Subject: [PATCH 155/198] Remove unused, update comment --- include/bounce/softbody/softbody.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/bounce/softbody/softbody.h b/include/bounce/softbody/softbody.h index 9506b96..6099850 100644 --- a/include/bounce/softbody/softbody.h +++ b/include/bounce/softbody/softbody.h @@ -58,7 +58,6 @@ struct b3SoftBodyTriangle { u32 v1, v2, v3; u32 tetrahedron; - u32 treeId; }; // Soft body definition @@ -90,15 +89,16 @@ struct b3SoftBodyDef // This is a dimensionless value float32 nu; - // Material yield in [0, inf] + // Material elastic strain yield in [0, inf] // This is a dimensionless value float32 c_yield; // Material creep rate in [0, 1 / dt] - // Units are inverse seconds + // Units are Hz float32 c_creep; // Material maximum plastic strain in [0, inf] + // This is a dimensionless value float32 c_max; }; From bc90c4f30e4c530c83e924e9559aaa74c1df622d Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 16 Jun 2019 10:01:46 -0300 Subject: [PATCH 156/198] Positive diagonal check --- src/bounce/cloth/cloth_solver.cpp | 6 +++--- src/bounce/softbody/softbody_solver.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index c0cc90a..da71c12 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -189,13 +189,13 @@ static void b3SolveMPCG(b3DenseVec3& x, // Sylvester Criterion to ensure PD-ness B3_ASSERT(b3Det(a.x, a.y, a.z) > 0.0f); - B3_ASSERT(a.x.x != 0.0f); + B3_ASSERT(a.x.x > 0.0f); float32 xx = 1.0f / a.x.x; - B3_ASSERT(a.y.y != 0.0f); + B3_ASSERT(a.y.y > 0.0f); float32 yy = 1.0f / a.y.y; - B3_ASSERT(a.z.z != 0.0f); + B3_ASSERT(a.z.z > 0.0f); float32 zz = 1.0f / a.z.z; inv_P[i] = b3Diagonal(xx, yy, zz); diff --git a/src/bounce/softbody/softbody_solver.cpp b/src/bounce/softbody/softbody_solver.cpp index b726143..0eaff88 100644 --- a/src/bounce/softbody/softbody_solver.cpp +++ b/src/bounce/softbody/softbody_solver.cpp @@ -110,13 +110,13 @@ static void b3SolveMPCG(b3DenseVec3& x, // Sylvester Criterion to ensure PD-ness B3_ASSERT(b3Det(a.x, a.y, a.z) > 0.0f); - B3_ASSERT(a.x.x != 0.0f); + B3_ASSERT(a.x.x > 0.0f); float32 xx = 1.0f / a.x.x; - B3_ASSERT(a.y.y != 0.0f); + B3_ASSERT(a.y.y > 0.0f); float32 yy = 1.0f / a.y.y; - B3_ASSERT(a.z.z != 0.0f); + B3_ASSERT(a.z.z > 0.0f); float32 zz = 1.0f / a.z.z; P[i] = b3Diagonal(a.x.x, a.y.y, a.z.z); From 5f756dafca96c0f039c5472462d33947cc6afc60 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 17 Jun 2019 12:41:27 -0300 Subject: [PATCH 157/198] Use a particle versus triangle contact constraint which is, numerically, more stable --- include/bounce/cloth/cloth_contact.h | 2 +- include/bounce/cloth/cloth_contact_solver.h | 9 +- src/bounce/cloth/cloth_contact.cpp | 13 +-- src/bounce/cloth/cloth_contact_manager.cpp | 1 - src/bounce/cloth/cloth_contact_solver.cpp | 108 ++++++++++---------- 5 files changed, 62 insertions(+), 71 deletions(-) diff --git a/include/bounce/cloth/cloth_contact.h b/include/bounce/cloth/cloth_contact.h index f913ebe..d2760a8 100644 --- a/include/bounce/cloth/cloth_contact.h +++ b/include/bounce/cloth/cloth_contact.h @@ -53,7 +53,7 @@ private: float32 m_normalImpulse; - bool m_front; + float32 w2, w3, w4; bool m_active; diff --git a/include/bounce/cloth/cloth_contact_solver.h b/include/bounce/cloth/cloth_contact_solver.h index 3005084..27c0a46 100644 --- a/include/bounce/cloth/cloth_contact_solver.h +++ b/include/bounce/cloth/cloth_contact_solver.h @@ -92,11 +92,10 @@ struct b3ClothSolverTriangleContactVelocityConstraint float32 invMassC; u32 indexD; float32 invMassD; + + float32 wB, wC, wD; - b3Vec3 JA; - b3Vec3 JB; - b3Vec3 JC; - b3Vec3 JD; + b3Vec3 normal; float32 normalMass; float32 normalImpulse; @@ -116,7 +115,7 @@ struct b3ClothSolverTriangleContactPositionConstraint float32 invMassD; float32 triangleRadius; - bool front; + float32 wB, wC, wD; }; struct b3ClothContactSolverDef diff --git a/src/bounce/cloth/cloth_contact.cpp b/src/bounce/cloth/cloth_contact.cpp index edbcb05..7fafa9e 100644 --- a/src/bounce/cloth/cloth_contact.cpp +++ b/src/bounce/cloth/cloth_contact.cpp @@ -171,13 +171,8 @@ void b3ParticleTriangleContact::Update() // Activate the contact m_active = true; - // Is P1 in front or back of the plane? - if (distance >= 0.0f) - { - m_front = true; - } - else - { - m_front = false; - } + // Store Barycentric coordinates for P1 + w2 = wABC[0]; + w3 = wABC[1]; + w4 = wABC[2]; } \ No newline at end of file diff --git a/src/bounce/cloth/cloth_contact_manager.cpp b/src/bounce/cloth/cloth_contact_manager.cpp index 872f2c2..33d70d7 100644 --- a/src/bounce/cloth/cloth_contact_manager.cpp +++ b/src/bounce/cloth/cloth_contact_manager.cpp @@ -102,7 +102,6 @@ void b3ClothContactManager::AddPair(void* data1, void* data2) c->m_p3 = p3; c->m_p4 = p4; c->m_normalImpulse = 0.0f; - c->m_front = false; c->m_active = false; // Add the contact to the cloth contact list. diff --git a/src/bounce/cloth/cloth_contact_solver.cpp b/src/bounce/cloth/cloth_contact_solver.cpp index 57f3f05..cf30a5d 100644 --- a/src/bounce/cloth/cloth_contact_solver.cpp +++ b/src/bounce/cloth/cloth_contact_solver.cpp @@ -215,7 +215,10 @@ void b3ClothContactSolver::InitializeTriangleContactConstraints() pc->invMassD = c->m_p4->m_type == e_staticParticle ? 0.0f : c->m_p4->m_invMass; pc->triangleRadius = 0.0f; - pc->front = c->m_front; + + pc->wB = c->w2; + pc->wC = c->w3; + pc->wD = c->w4; u32 indexA = pc->indexA; float32 mA = pc->invMassA; @@ -234,32 +237,21 @@ void b3ClothContactSolver::InitializeTriangleContactConstraints() b3Vec3 xC = x[indexC]; b3Vec3 xD = x[indexD]; - b3Vec3 n = b3Cross(xC - xB, xD - xB); + float32 wB = pc->wB; + float32 wC = pc->wC; + float32 wD = pc->wD; - if (pc->front == false) - { - n = -n; - } + b3Vec3 cB = wB * xB + wC * xC + wD * xD; - float32 n_len = n.Normalize(); + b3Vec3 n = cB - xA; + n.Normalize(); - b3Mat33 I; I.SetIdentity(); + vc->normal = n; + vc->wB = wB; + vc->wC = wC; + vc->wD = wD; - b3Mat33 N = I - b3Outer(n, n); - if (n_len > B3_EPSILON) - { - N = (1.0f / n_len) * N; - } - - b3Vec3 N_n = N * n; - - vc->JA = n; - vc->JB = b3Cross(xC - xD, N_n) - n; - vc->JC = b3Cross(xD - xB, N_n); - vc->JD = b3Cross(xC - xB, N_n); - - //float32 K = mA + mB * b3Dot(vc->JB, vc->JB) + mC * b3Dot(vc->JC, vc->JC) + mD * b3Dot(vc->JD, vc->JD); - float32 K = mA + mB + mC + mD; + float32 K = mA + mB * wB * wB + mC * wC * wC + mD * wD * wD; vc->normalMass = K > 0.0f ? 1.0f / K : 0.0f; vc->normalImpulse = c->m_normalImpulse; @@ -336,10 +328,15 @@ void b3ClothContactSolver::WarmStartTriangleContactConstraints() float32 mC = vc->invMassC; float32 mD = vc->invMassD; - b3Vec3 PA = vc->normalImpulse * vc->JA; - b3Vec3 PB = vc->normalImpulse * vc->JB; - b3Vec3 PC = vc->normalImpulse * vc->JC; - b3Vec3 PD = vc->normalImpulse * vc->JD; + b3Vec3 JA = -vc->normal; + b3Vec3 JB = vc->wB * vc->normal; + b3Vec3 JC = vc->wC * vc->normal; + b3Vec3 JD = vc->wD * vc->normal; + + b3Vec3 PA = vc->normalImpulse * JA; + b3Vec3 PB = vc->normalImpulse * JB; + b3Vec3 PC = vc->normalImpulse * JC; + b3Vec3 PD = vc->normalImpulse * JD; vA += mA * PA; vB += mB * PB; @@ -461,12 +458,18 @@ void b3ClothContactSolver::SolveTriangleContactVelocityConstraints() u32 indexD = vc->indexD; float32 mD = vc->invMassD; + float32 wB = vc->wB; + float32 wC = vc->wC; + float32 wD = vc->wD; + b3Vec3 vA = v[indexA]; b3Vec3 vB = v[indexB]; b3Vec3 vC = v[indexC]; b3Vec3 vD = v[indexD]; - float32 Cdot = b3Dot(vc->JA, vA) + b3Dot(vc->JB, vB) + b3Dot(vc->JC, vC) + b3Dot(vc->JD, vD); + b3Vec3 vCB = wB * vB + wC * vC + wD * vD; + + float32 Cdot = b3Dot(vCB - vA, vc->normal); float32 impulse = -vc->normalMass * Cdot; @@ -474,10 +477,15 @@ void b3ClothContactSolver::SolveTriangleContactVelocityConstraints() vc->normalImpulse = b3Max(vc->normalImpulse + impulse, 0.0f); impulse = vc->normalImpulse - oldImpulse; - b3Vec3 PA = impulse * vc->JA; - b3Vec3 PB = impulse * vc->JB; - b3Vec3 PC = impulse * vc->JC; - b3Vec3 PD = impulse * vc->JD; + b3Vec3 JA = -vc->normal; + b3Vec3 JB = wB * vc->normal; + b3Vec3 JC = wC * vc->normal; + b3Vec3 JD = wD * vc->normal; + + b3Vec3 PA = impulse * JA; + b3Vec3 PB = impulse * JB; + b3Vec3 PC = impulse * JC; + b3Vec3 PD = impulse * JD; vA += mA * PA; vB += mB * PB; @@ -638,20 +646,21 @@ bool b3ClothContactSolver::SolveTriangleContactPositionConstraints() float32 triangleRadius = pc->triangleRadius; + float32 wB = pc->wB; + float32 wC = pc->wC; + float32 wD = pc->wD; + b3Vec3 xA = x[indexA]; b3Vec3 xB = x[indexB]; b3Vec3 xC = x[indexC]; b3Vec3 xD = x[indexD]; - b3Vec3 n = b3Cross(xC - xB, xD - xB); - if (pc->front == false) - { - n = -n; - } + b3Vec3 cB = wB * xB + wC * xC + wD * xD; - float32 n_len = n.Normalize(); + b3Vec3 n = cB - xA; + + float32 distance = n.Normalize(); - float32 distance = b3Dot(n, xA - xB); float32 separation = distance - radiusA - triangleRadius; // Update max constraint error. @@ -660,24 +669,13 @@ bool b3ClothContactSolver::SolveTriangleContactPositionConstraints() // Allow some slop and prevent large corrections. float32 C = b3Clamp(B3_BAUMGARTE * (separation + B3_LINEAR_SLOP), -B3_MAX_LINEAR_CORRECTION, 0.0f); - b3Mat33 I; I.SetIdentity(); - - b3Mat33 N = I - b3Outer(n, n); - if (n_len > B3_EPSILON) - { - N = (1.0f / n_len) * N; - } - - b3Vec3 N_n = N * n; - - b3Vec3 JA = n; - b3Vec3 JB = b3Cross(xC - xD, N_n) - n; - b3Vec3 JC = b3Cross(xD - xB, N_n); - b3Vec3 JD = b3Cross(xC - xB, N_n); + b3Vec3 JA = -n; + b3Vec3 JB = wB * n; + b3Vec3 JC = wC * n; + b3Vec3 JD = wD * n; // Compute effective mass. - //float32 K = mA + mB * b3Dot(JB, JB) + mC * b3Dot(JC, JC) + mD * b3Dot(JD, JD); - float32 K = mA + mB + mC + mD; + float32 K = mA + mB * wB * wB + mC * wC * wC + mD * wD * wD; // Compute normal impulse. float32 impulse = K > 0.0f ? -C / K : 0.0f; From ee8a15ddaa13c866fc930b665e1daf58cf8984fc Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 17 Jun 2019 12:41:59 -0300 Subject: [PATCH 158/198] Disable bending in a test --- examples/testbed/tests/table_cloth.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/testbed/tests/table_cloth.h b/examples/testbed/tests/table_cloth.h index 5f83489..3040ade 100644 --- a/examples/testbed/tests/table_cloth.h +++ b/examples/testbed/tests/table_cloth.h @@ -42,7 +42,7 @@ public: b3ClothDef def; def.mesh = &m_rectangleClothMesh; def.density = 0.2f; - def.bending = 10000.0f; + //def.bending = 10000.0f; def.structural = 10000.0f; def.damping = 0.0f; From 9414b7a2758fedeb62ec78e7d659f67e899d78ed Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 17 Jun 2019 14:27:22 -0300 Subject: [PATCH 159/198] Put a member variable prefix --- include/bounce/cloth/cloth_contact.h | 2 +- src/bounce/cloth/cloth_contact.cpp | 6 +++--- src/bounce/cloth/cloth_contact_solver.cpp | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/bounce/cloth/cloth_contact.h b/include/bounce/cloth/cloth_contact.h index d2760a8..86646b0 100644 --- a/include/bounce/cloth/cloth_contact.h +++ b/include/bounce/cloth/cloth_contact.h @@ -53,7 +53,7 @@ private: float32 m_normalImpulse; - float32 w2, w3, w4; + float32 m_w2, m_w3, m_w4; bool m_active; diff --git a/src/bounce/cloth/cloth_contact.cpp b/src/bounce/cloth/cloth_contact.cpp index 7fafa9e..814a728 100644 --- a/src/bounce/cloth/cloth_contact.cpp +++ b/src/bounce/cloth/cloth_contact.cpp @@ -172,7 +172,7 @@ void b3ParticleTriangleContact::Update() m_active = true; // Store Barycentric coordinates for P1 - w2 = wABC[0]; - w3 = wABC[1]; - w4 = wABC[2]; + m_w2 = wABC[0]; + m_w3 = wABC[1]; + m_w4 = wABC[2]; } \ No newline at end of file diff --git a/src/bounce/cloth/cloth_contact_solver.cpp b/src/bounce/cloth/cloth_contact_solver.cpp index cf30a5d..b713c4f 100644 --- a/src/bounce/cloth/cloth_contact_solver.cpp +++ b/src/bounce/cloth/cloth_contact_solver.cpp @@ -216,9 +216,9 @@ void b3ClothContactSolver::InitializeTriangleContactConstraints() pc->triangleRadius = 0.0f; - pc->wB = c->w2; - pc->wC = c->w3; - pc->wD = c->w4; + pc->wB = c->m_w2; + pc->wC = c->m_w3; + pc->wD = c->m_w4; u32 indexA = pc->indexA; float32 mA = pc->invMassA; From 13d8415a155030138436922b4714560a37e21f8d Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 18 Jun 2019 11:43:03 -0300 Subject: [PATCH 160/198] Support cloth self friction, thickness. Small refactor --- include/bounce/cloth/cloth.h | 24 ++- include/bounce/cloth/cloth_contact.h | 13 +- include/bounce/cloth/cloth_contact_solver.h | 10 +- include/bounce/cloth/cloth_triangle.h | 98 +++++++++++ include/bounce/cloth/particle.h | 10 +- src/bounce/cloth/cloth.cpp | 120 +++++++------- src/bounce/cloth/cloth_contact_manager.cpp | 15 +- src/bounce/cloth/cloth_contact_solver.cpp | 174 ++++++++++++++++---- src/bounce/cloth/cloth_triangle.cpp | 40 +++++ src/bounce/cloth/particle.cpp | 9 +- 10 files changed, 390 insertions(+), 123 deletions(-) create mode 100644 include/bounce/cloth/cloth_triangle.h create mode 100644 src/bounce/cloth/cloth_triangle.cpp diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index 330c842..98109b2 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -29,6 +29,8 @@ class b3World; class b3Shape; class b3Particle; +class b3ClothTriangle; + class b3Force; struct b3ParticleBodyContact; @@ -62,6 +64,8 @@ struct b3ClothDef structural = 0.0f; bending = 0.0f; damping = 0.0f; + thickness = 0.0f; + friction = 0.2f; } // Cloth mesh @@ -78,6 +82,12 @@ struct b3ClothDef // Damping stiffness float32 damping; + + // Cloth thickness + float32 thickness; + + // Cloth coefficient of friction + float32 friction; }; // A cloth represents a deformable surface as a collection of particles. @@ -126,6 +136,9 @@ public: // Return the particle associated with the given vertex. b3Particle* GetVertexParticle(u32 i); + // Return the cloth triangle given the triangle index. + b3ClothTriangle* GetTriangle(u32 i); + // Return the list of particles in this cloth. const b3List2& GetParticleList() const; @@ -142,6 +155,7 @@ public: void Draw() const; private: friend class b3Particle; + friend class b3ClothTriangle; friend class b3ClothContactManager; // Compute mass of each particle. @@ -153,12 +167,6 @@ private: // Solve void Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations); - // Synchronize triangle AABB. - void SynchronizeTriangle(u32 triangleIndex); - - // Time-step - float32 m_dt; - // Stack allocator b3StackAllocator m_stackAllocator; @@ -174,8 +182,8 @@ private: // Vertex particles b3Particle** m_vertexParticles; - // Triangle proxies - b3ClothAABBProxy* m_triangleProxies; + // Triangles + b3ClothTriangle* m_triangles; // Cloth density float32 m_density; diff --git a/include/bounce/cloth/cloth_contact.h b/include/bounce/cloth/cloth_contact.h index 86646b0..6e7e291 100644 --- a/include/bounce/cloth/cloth_contact.h +++ b/include/bounce/cloth/cloth_contact.h @@ -22,8 +22,7 @@ #include class b3Particle; -struct b3ClothMeshTriangle; -struct b3ClothAABBProxy; +class b3ClothTriangle; // Contact between particle and a triangle class b3ParticleTriangleContact @@ -32,6 +31,7 @@ public: private: friend class b3Cloth; friend class b3Particle; + friend class b3ClothTriangle; friend class b3ClothContactManager; friend class b3List2; friend class b3ClothContactSolver; @@ -45,16 +45,17 @@ private: b3Particle* m_p1; // Triangle - b3ClothMeshTriangle* m_triangle; - b3ClothAABBProxy* m_triangleProxy; + b3ClothTriangle* m_t2; b3Particle* m_p2; b3Particle* m_p3; b3Particle* m_p4; - float32 m_normalImpulse; - float32 m_w2, m_w3, m_w4; + float32 m_normalImpulse; + float32 m_tangentImpulse1; + float32 m_tangentImpulse2; + bool m_active; b3ParticleTriangleContact* m_prev; diff --git a/include/bounce/cloth/cloth_contact_solver.h b/include/bounce/cloth/cloth_contact_solver.h index 27c0a46..5d69900 100644 --- a/include/bounce/cloth/cloth_contact_solver.h +++ b/include/bounce/cloth/cloth_contact_solver.h @@ -96,9 +96,17 @@ struct b3ClothSolverTriangleContactVelocityConstraint float32 wB, wC, wD; b3Vec3 normal; - float32 normalMass; float32 normalImpulse; + + float32 friction; + + b3Vec3 tangent1; + b3Vec3 tangent2; + float32 tangentMass1; + float32 tangentMass2; + float32 tangentImpulse1; + float32 tangentImpulse2; }; struct b3ClothSolverTriangleContactPositionConstraint diff --git a/include/bounce/cloth/cloth_triangle.h b/include/bounce/cloth/cloth_triangle.h new file mode 100644 index 0000000..6d4fcc3 --- /dev/null +++ b/include/bounce/cloth/cloth_triangle.h @@ -0,0 +1,98 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_CLOTH_TRIANGLE_H +#define B3_CLOTH_TRIANGLE_H + +#include + +// A cloth triangle +class b3ClothTriangle +{ +public: + // Return the triangle index. + u32 GetTriangle() const; + + // Set the triangle radius. + void SetRadius(float32 radius); + + // Return the triangle radius. + float32 GetRadius() const; + + // Set the triangle coefficient of friction. + void SetFriction(float32 friction); + + // Return the triangle coefficient of friction. + float32 GetFriction() const; +private: + friend class b3Cloth; + friend class b3Particle; + friend class b3ClothContactManager; + friend class b3ParticleTriangleContact; + friend class b3ClothSolver; + friend class b3ClothContactSolver; + + b3ClothTriangle() { } + ~b3ClothTriangle() { } + + // Synchronize AABB + void Synchronize(const b3Vec3& displacement); + + // Cloth + b3Cloth* m_cloth; + + // Triangle index + u32 m_triangle; + + // Radius + float32 m_radius; + + // Coefficient of friction + float32 m_friction; + + // AABB Proxy + b3ClothAABBProxy m_aabbProxy; +}; + +inline u32 b3ClothTriangle::GetTriangle() const +{ + return m_triangle; +} + +inline void b3ClothTriangle::SetRadius(float32 radius) +{ + m_radius = radius; + Synchronize(b3Vec3_zero); +} + +inline float32 b3ClothTriangle::GetRadius() const +{ + return m_radius; +} + +inline void b3ClothTriangle::SetFriction(float32 friction) +{ + m_friction = friction; +} + +inline float32 b3ClothTriangle::GetFriction() const +{ + return m_friction; +} + +#endif \ No newline at end of file diff --git a/include/bounce/cloth/particle.h b/include/bounce/cloth/particle.h index bdef984..9adbf2c 100644 --- a/include/bounce/cloth/particle.h +++ b/include/bounce/cloth/particle.h @@ -103,7 +103,6 @@ struct b3ClothAABBProxy { b3ClothAABBProxyType type; void* data; - u32 index; u32 broadPhaseId; }; @@ -160,6 +159,7 @@ public: private: friend class b3List2; friend class b3Cloth; + friend class b3ClothTriangle; friend class b3ClothSolver; friend class b3ClothContactManager; friend class b3ParticleTriangleContact; @@ -171,7 +171,7 @@ private: ~b3Particle(); // Synchronize particle AABB - void Synchronize(); + void Synchronize(const b3Vec3& displacement); // Synchronize triangles AABB void SynchronizeTriangles(); @@ -246,10 +246,12 @@ inline u32 b3Particle::GetVertex() const inline void b3Particle::SetPosition(const b3Vec3& position) { + b3Vec3 displacement = position - m_position; + m_position = position; m_translation.SetZero(); - Synchronize(); + Synchronize(displacement); SynchronizeTriangles(); } @@ -281,7 +283,7 @@ inline void b3Particle::SetRadius(float32 radius) { m_radius = radius; - Synchronize(); + Synchronize(b3Vec3_zero); } inline float32 b3Particle::GetRadius() const diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 3c66427..13aa712 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -161,11 +162,10 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : m_mesh = def.mesh; m_density = def.density; m_contactManager.m_cloth = this; - m_dt = 0.0f; const b3ClothMesh* m = m_mesh; - // Create particles + // Initialize particles m_vertexParticles = (b3Particle**)b3Alloc(m->vertexCount * sizeof(b3Particle*)); for (u32 i = 0; i < m->vertexCount; ++i) { @@ -176,7 +176,6 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : b3Particle* p = CreateParticle(pd); - p->m_aabbProxy.index = i; p->m_vertex = i; m_vertexParticles[i] = p; } @@ -184,7 +183,32 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : // Compute mass ComputeMass(); - // Create forces + // Initialize triangles + m_triangles = (b3ClothTriangle*)b3Alloc(m_mesh->triangleCount * sizeof(b3ClothTriangle)); + for (u32 i = 0; i < m_mesh->triangleCount; ++i) + { + b3ClothMeshTriangle* meshTriangle = m_mesh->triangles + i; + b3ClothTriangle* triangle = m_triangles + i; + + triangle->m_cloth = this; + triangle->m_radius = def.thickness; + triangle->m_friction = def.friction; + triangle->m_triangle = i; + + b3Vec3 v1 = m_mesh->vertices[meshTriangle->v1]; + b3Vec3 v2 = m_mesh->vertices[meshTriangle->v2]; + b3Vec3 v3 = m_mesh->vertices[meshTriangle->v3]; + + b3AABB3 aabb; + aabb.Set(v1, v2, v3); + aabb.Extend(triangle->m_radius); + + triangle->m_aabbProxy.type = e_triangleProxy; + triangle->m_aabbProxy.data = triangle; + triangle->m_aabbProxy.broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, &triangle->m_aabbProxy); + } + + // Initialize forces b3StackAllocator* allocator = &m_stackAllocator; // Worst-case edge memory @@ -251,32 +275,15 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : } } - // Initialize triangle proxies - m_triangleProxies = (b3ClothAABBProxy*)b3Alloc(m_mesh->triangleCount * sizeof(b3ClothAABBProxy)); - for (u32 i = 0; i < m_mesh->triangleCount; ++i) - { - b3ClothMeshTriangle* triangle = m_mesh->triangles + i; - b3ClothAABBProxy* triangleProxy = m_triangleProxies + i; - - b3Vec3 v1 = m_mesh->vertices[triangle->v1]; - b3Vec3 v2 = m_mesh->vertices[triangle->v2]; - b3Vec3 v3 = m_mesh->vertices[triangle->v3]; - - b3AABB3 aabb; - aabb.Set(v1, v2, v3); - - triangleProxy->type = e_triangleProxy; - triangleProxy->index = i; - triangleProxy->data = triangle; - triangleProxy->broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, triangleProxy); - } - m_gravity.SetZero(); m_world = nullptr; } b3Cloth::~b3Cloth() { + b3Free(m_vertexParticles); + b3Free(m_triangles); + b3Particle* p = m_particleList.m_head; while (p) { @@ -285,9 +292,6 @@ b3Cloth::~b3Cloth() p0->~b3Particle(); } - b3Free(m_vertexParticles); - b3Free(m_triangleProxies); - b3Force* f = m_forceList.m_head; while (f) { @@ -307,7 +311,6 @@ b3Particle* b3Cloth::CreateParticle(const b3ParticleDef& def) p->m_aabbProxy.type = e_particleProxy; p->m_aabbProxy.data = p; - p->m_aabbProxy.index = p->m_vertex; p->m_aabbProxy.broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, &p->m_aabbProxy); m_particleList.PushFront(p); @@ -373,6 +376,12 @@ b3Particle* b3Cloth::GetVertexParticle(u32 i) return m_vertexParticles[i]; } +b3ClothTriangle* b3Cloth::GetTriangle(u32 i) +{ + B3_ASSERT(i < m_mesh->triangleCount); + return m_triangles + i; +} + void b3Cloth::ComputeMass() { for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) @@ -428,7 +437,8 @@ struct b3ClothRayCastSingleCallback return input.maxFraction; } - u32 triangleIndex = proxy->index; + b3ClothTriangle* triangle = (b3ClothTriangle*)proxy->data; + u32 triangleIndex = triangle->GetTriangle(); b3RayCastOutput subOutput; if (cloth->RayCast(&subOutput, &input, triangleIndex)) @@ -436,7 +446,7 @@ struct b3ClothRayCastSingleCallback // Ray hits triangle. if (subOutput.fraction < output0.fraction) { - triangle0 = proxy->index; + triangle0 = triangleIndex; output0.fraction = subOutput.fraction; output0.normal = subOutput.normal; } @@ -638,8 +648,6 @@ void b3Cloth::Step(float32 dt, u32 velocityIterations, u32 positionIterations) { B3_PROFILE("Cloth Step"); - m_dt = dt; - // Update particle-body contacts UpdateParticleBodyContacts(); @@ -662,13 +670,29 @@ void b3Cloth::Step(float32 dt, u32 velocityIterations, u32 positionIterations) // Synchronize particles for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) { - p->Synchronize(); + b3Vec3 displacement = dt * p->m_velocity; + + p->Synchronize(displacement); } // Synchronize triangles for (u32 i = 0; i < m_mesh->triangleCount; ++i) { - SynchronizeTriangle(i); + b3ClothMeshTriangle* triangle = m_mesh->triangles + i; + + b3Particle* p1 = m_vertexParticles[triangle->v1]; + b3Particle* p2 = m_vertexParticles[triangle->v2]; + b3Particle* p3 = m_vertexParticles[triangle->v3]; + + b3Vec3 v1 = p1->m_velocity; + b3Vec3 v2 = p2->m_velocity; + b3Vec3 v3 = p3->m_velocity; + + b3Vec3 velocity = (v1 + v2 + v3) / 3.0f; + + b3Vec3 displacement = dt * velocity; + + m_triangles[i].Synchronize(displacement); } #if B3_ENABLE_SELF_COLLISION @@ -679,34 +703,6 @@ void b3Cloth::Step(float32 dt, u32 velocityIterations, u32 positionIterations) #endif } -void b3Cloth::SynchronizeTriangle(u32 triangleIndex) -{ - b3ClothMeshTriangle* triangle = m_mesh->triangles + triangleIndex; - b3ClothAABBProxy* triangleProxy = m_triangleProxies + triangleIndex; - - b3Particle* p1 = m_vertexParticles[triangle->v1]; - b3Particle* p2 = m_vertexParticles[triangle->v2]; - b3Particle* p3 = m_vertexParticles[triangle->v3]; - - b3Vec3 x1 = p1->m_position; - b3Vec3 x2 = p2->m_position; - b3Vec3 x3 = p3->m_position; - - b3Vec3 v1 = p1->m_velocity; - b3Vec3 v2 = p2->m_velocity; - b3Vec3 v3 = p3->m_velocity; - - b3AABB3 aabb; - aabb.Set(x1, x2, x3); - - const float32 kInv3 = 1.0f / 3.0f; - - b3Vec3 center_velocity = kInv3 * (v1 + v2 + v3); - b3Vec3 center_displacement = m_dt * center_velocity; - - m_contactManager.m_broadPhase.MoveProxy(triangleProxy->broadPhaseId, aabb, center_displacement); -} - void b3Cloth::Draw() const { for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) diff --git a/src/bounce/cloth/cloth_contact_manager.cpp b/src/bounce/cloth/cloth_contact_manager.cpp index 33d70d7..eef9ae1 100644 --- a/src/bounce/cloth/cloth_contact_manager.cpp +++ b/src/bounce/cloth/cloth_contact_manager.cpp @@ -20,6 +20,7 @@ #include #include #include +#include b3ClothContactManager::b3ClothContactManager() : m_particleTriangleContactBlocks(sizeof(b3ParticleTriangleContact)) @@ -62,7 +63,8 @@ void b3ClothContactManager::AddPair(void* data1, void* data2) b3Particle* p1 = (b3Particle*)proxy1->data; - b3ClothMeshTriangle* triangle = (b3ClothMeshTriangle*)proxy2->data; + b3ClothTriangle* t2 = (b3ClothTriangle*)proxy2->data; + b3ClothMeshTriangle* triangle = m_cloth->m_mesh->triangles + t2->m_triangle; b3Particle* p2 = m_cloth->m_vertexParticles[triangle->v1]; b3Particle* p3 = m_cloth->m_vertexParticles[triangle->v2]; b3Particle* p4 = m_cloth->m_vertexParticles[triangle->v3]; @@ -70,7 +72,7 @@ void b3ClothContactManager::AddPair(void* data1, void* data2) // Check if there is a contact between the two entities. for (b3ParticleTriangleContact* c = m_particleTriangleContactList.m_head; c; c = c->m_next) { - if (c->m_p1 == p1 && c->m_triangle == triangle) + if (c->m_p1 == p1 && c->m_t2 == t2) { // A contact already exists. return; @@ -86,7 +88,7 @@ void b3ClothContactManager::AddPair(void* data1, void* data2) return; } - if (triangle->v1 == p1->m_vertex || triangle->v2 == p1->m_vertex || triangle->v3 == p1->m_vertex) + if (p1 == p2 || p1 == p3 || p1 == p4) { // The entities must not collide with each other. return; @@ -96,12 +98,13 @@ void b3ClothContactManager::AddPair(void* data1, void* data2) b3ParticleTriangleContact* c = CreateParticleTriangleContact(); c->m_p1 = p1; - c->m_triangle = triangle; - c->m_triangleProxy = proxy2; + c->m_t2 = t2; c->m_p2 = p2; c->m_p3 = p3; c->m_p4 = p4; c->m_normalImpulse = 0.0f; + c->m_tangentImpulse1 = 0.0f; + c->m_tangentImpulse2 = 0.0f; c->m_active = false; // Add the contact to the cloth contact list. @@ -144,7 +147,7 @@ void b3ClothContactManager::UpdateContacts() } u32 proxy1 = c->m_p1->m_aabbProxy.broadPhaseId; - u32 proxy2 = c->m_triangleProxy->broadPhaseId; + u32 proxy2 = c->m_t2->m_aabbProxy.broadPhaseId; // Destroy the contact if primitive AABBs are not overlapping. bool overlap = m_broadPhase.TestOverlap(proxy1, proxy2); diff --git a/src/bounce/cloth/cloth_contact_solver.cpp b/src/bounce/cloth/cloth_contact_solver.cpp index b713c4f..d736e7c 100644 --- a/src/bounce/cloth/cloth_contact_solver.cpp +++ b/src/bounce/cloth/cloth_contact_solver.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -214,7 +215,7 @@ void b3ClothContactSolver::InitializeTriangleContactConstraints() pc->indexD = c->m_p4->m_solverId; pc->invMassD = c->m_p4->m_type == e_staticParticle ? 0.0f : c->m_p4->m_invMass; - pc->triangleRadius = 0.0f; + pc->triangleRadius = c->m_t2->m_radius; pc->wB = c->m_w2; pc->wC = c->m_w3; @@ -255,6 +256,14 @@ void b3ClothContactSolver::InitializeTriangleContactConstraints() vc->normalMass = K > 0.0f ? 1.0f / K : 0.0f; vc->normalImpulse = c->m_normalImpulse; + + vc->friction = b3MixFriction(c->m_p1->m_friction, c->m_t2->m_friction); + vc->tangent1 = b3Perp(n); + vc->tangent2 = b3Cross(vc->tangent1, n); + vc->tangentMass1 = vc->normalMass; + vc->tangentMass2 = vc->normalMass; + vc->tangentImpulse1 = c->m_tangentImpulse1; + vc->tangentImpulse2 = c->m_tangentImpulse2; } } @@ -328,21 +337,57 @@ void b3ClothContactSolver::WarmStartTriangleContactConstraints() float32 mC = vc->invMassC; float32 mD = vc->invMassD; - b3Vec3 JA = -vc->normal; - b3Vec3 JB = vc->wB * vc->normal; - b3Vec3 JC = vc->wC * vc->normal; - b3Vec3 JD = vc->wD * vc->normal; + { + b3Vec3 JA = -vc->normal; + b3Vec3 JB = vc->wB * vc->normal; + b3Vec3 JC = vc->wC * vc->normal; + b3Vec3 JD = vc->wD * vc->normal; - b3Vec3 PA = vc->normalImpulse * JA; - b3Vec3 PB = vc->normalImpulse * JB; - b3Vec3 PC = vc->normalImpulse * JC; - b3Vec3 PD = vc->normalImpulse * JD; + b3Vec3 PA = vc->normalImpulse * JA; + b3Vec3 PB = vc->normalImpulse * JB; + b3Vec3 PC = vc->normalImpulse * JC; + b3Vec3 PD = vc->normalImpulse * JD; - vA += mA * PA; - vB += mB * PB; - vC += mC * PC; - vD += mD * PD; + vA += mA * PA; + vB += mB * PB; + vC += mC * PC; + vD += mD * PD; + } + { + b3Vec3 JA = -vc->tangent1; + b3Vec3 JB = vc->wB * vc->tangent1; + b3Vec3 JC = vc->wC * vc->tangent1; + b3Vec3 JD = vc->wD * vc->tangent1; + + b3Vec3 PA = vc->tangentImpulse1 * JA; + b3Vec3 PB = vc->tangentImpulse1 * JB; + b3Vec3 PC = vc->tangentImpulse1 * JC; + b3Vec3 PD = vc->tangentImpulse1 * JD; + + vA += mA * PA; + vB += mB * PB; + vC += mC * PC; + vD += mD * PD; + } + + { + b3Vec3 JA = -vc->tangent2; + b3Vec3 JB = vc->wB * vc->tangent2; + b3Vec3 JC = vc->wC * vc->tangent2; + b3Vec3 JD = vc->wD * vc->tangent2; + + b3Vec3 PA = vc->tangentImpulse2 * JA; + b3Vec3 PB = vc->tangentImpulse2 * JB; + b3Vec3 PC = vc->tangentImpulse2 * JC; + b3Vec3 PD = vc->tangentImpulse2 * JD; + + vA += mA * PA; + vB += mB * PB; + vC += mC * PC; + vD += mD * PD; + } + v[indexA] = vA; v[indexB] = vB; v[indexC] = vC; @@ -467,30 +512,95 @@ void b3ClothContactSolver::SolveTriangleContactVelocityConstraints() b3Vec3 vC = v[indexC]; b3Vec3 vD = v[indexD]; - b3Vec3 vCB = wB * vB + wC * vC + wD * vD; + // Solve normal constraint. + { + b3Vec3 vCB = wB * vB + wC * vC + wD * vD; - float32 Cdot = b3Dot(vCB - vA, vc->normal); + float32 Cdot = b3Dot(vCB - vA, vc->normal); - float32 impulse = -vc->normalMass * Cdot; + float32 impulse = -vc->normalMass * Cdot; - float32 oldImpulse = vc->normalImpulse; - vc->normalImpulse = b3Max(vc->normalImpulse + impulse, 0.0f); - impulse = vc->normalImpulse - oldImpulse; + float32 oldImpulse = vc->normalImpulse; + vc->normalImpulse = b3Max(vc->normalImpulse + impulse, 0.0f); + impulse = vc->normalImpulse - oldImpulse; - b3Vec3 JA = -vc->normal; - b3Vec3 JB = wB * vc->normal; - b3Vec3 JC = wC * vc->normal; - b3Vec3 JD = wD * vc->normal; + b3Vec3 JA = -vc->normal; + b3Vec3 JB = wB * vc->normal; + b3Vec3 JC = wC * vc->normal; + b3Vec3 JD = wD * vc->normal; - b3Vec3 PA = impulse * JA; - b3Vec3 PB = impulse * JB; - b3Vec3 PC = impulse * JC; - b3Vec3 PD = impulse * JD; + b3Vec3 PA = impulse * JA; + b3Vec3 PB = impulse * JB; + b3Vec3 PC = impulse * JC; + b3Vec3 PD = impulse * JD; - vA += mA * PA; - vB += mB * PB; - vC += mC * PC; - vD += mD * PD; + vA += mA * PA; + vB += mB * PB; + vC += mC * PC; + vD += mD * PD; + } + + // Solve tangent constraint. + { + float32 hi = vc->friction * vc->normalImpulse; + float32 lo = -hi; + + b3Vec3 vCB = wB * vB + wC * vC + wD * vD; + + float32 Cdot = b3Dot(vCB - vA, vc->tangent1); + + float32 impulse = -vc->tangentMass1 * Cdot; + + float32 oldImpulse = vc->tangentImpulse1; + vc->tangentImpulse1 = b3Clamp(vc->tangentImpulse1 + impulse, lo, hi); + impulse = vc->tangentImpulse1 - oldImpulse; + + b3Vec3 JA = -vc->tangent1; + b3Vec3 JB = wB * vc->tangent1; + b3Vec3 JC = wC * vc->tangent1; + b3Vec3 JD = wD * vc->tangent1; + + b3Vec3 PA = impulse * JA; + b3Vec3 PB = impulse * JB; + b3Vec3 PC = impulse * JC; + b3Vec3 PD = impulse * JD; + + vA += mA * PA; + vB += mB * PB; + vC += mC * PC; + vD += mD * PD; + } + + // Solve bitangent constraint. + { + float32 hi = vc->friction * vc->normalImpulse; + float32 lo = -hi; + + b3Vec3 vCB = wB * vB + wC * vC + wD * vD; + + float32 Cdot = b3Dot(vCB - vA, vc->tangent2); + + float32 impulse = -vc->tangentMass2 * Cdot; + + float32 oldImpulse = vc->tangentImpulse2; + vc->tangentImpulse2 = b3Clamp(vc->tangentImpulse2 + impulse, lo, hi); + impulse = vc->tangentImpulse2 - oldImpulse; + + b3Vec3 JA = -vc->tangent2; + b3Vec3 JB = wB * vc->tangent2; + b3Vec3 JC = wC * vc->tangent2; + b3Vec3 JD = wD * vc->tangent2; + + b3Vec3 PA = impulse * JA; + b3Vec3 PB = impulse * JB; + b3Vec3 PC = impulse * JC; + b3Vec3 PD = impulse * JD; + + vA += mA * PA; + vB += mB * PB; + vC += mC * PC; + vD += mD * PD; + } v[indexA] = vA; v[indexB] = vB; @@ -516,6 +626,8 @@ void b3ClothContactSolver::StoreImpulses() b3ClothSolverTriangleContactVelocityConstraint* vc = m_triangleVelocityConstraints + i; c->m_normalImpulse = vc->normalImpulse; + c->m_tangentImpulse1 = vc->tangentImpulse1; + c->m_tangentImpulse2 = vc->tangentImpulse2; } } diff --git a/src/bounce/cloth/cloth_triangle.cpp b/src/bounce/cloth/cloth_triangle.cpp new file mode 100644 index 0000000..f4776f5 --- /dev/null +++ b/src/bounce/cloth/cloth_triangle.cpp @@ -0,0 +1,40 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include + +void b3ClothTriangle::Synchronize(const b3Vec3& displacement) +{ + b3ClothMeshTriangle* triangle = m_cloth->m_mesh->triangles + m_triangle; + + b3Particle* p1 = m_cloth->m_vertexParticles[triangle->v1]; + b3Particle* p2 = m_cloth->m_vertexParticles[triangle->v2]; + b3Particle* p3 = m_cloth->m_vertexParticles[triangle->v3]; + + b3Vec3 x1 = p1->m_position; + b3Vec3 x2 = p2->m_position; + b3Vec3 x3 = p3->m_position; + + b3AABB3 aabb; + aabb.Set(x1, x2, x3); + aabb.Extend(m_radius); + + m_cloth->m_contactManager.m_broadPhase.MoveProxy(m_aabbProxy.broadPhaseId, aabb, displacement); +} \ No newline at end of file diff --git a/src/bounce/cloth/particle.cpp b/src/bounce/cloth/particle.cpp index 5eeddb0..b6caf0b 100644 --- a/src/bounce/cloth/particle.cpp +++ b/src/bounce/cloth/particle.cpp @@ -19,6 +19,7 @@ #include #include #include +#include void b3ParticleBodyContactWorldPoint::Initialize(const b3ParticleBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB) { @@ -70,13 +71,11 @@ b3Particle::~b3Particle() } -void b3Particle::Synchronize() +void b3Particle::Synchronize(const b3Vec3& displacement) { b3AABB3 aabb; aabb.Set(m_position, m_radius); - b3Vec3 displacement = m_cloth->m_dt * m_velocity; - m_cloth->m_contactManager.m_broadPhase.MoveProxy(m_aabbProxy.broadPhaseId, aabb, displacement); } @@ -93,7 +92,7 @@ void b3Particle::SynchronizeTriangles() if (triangle->v1 == m_vertex || triangle->v2 == m_vertex || triangle->v3 == m_vertex) { - m_cloth->SynchronizeTriangle(i); + m_cloth->GetTriangle(i)->Synchronize(b3Vec3_zero); } } } @@ -134,7 +133,7 @@ void b3Particle::SetType(b3ParticleType type) m_velocity.SetZero(); m_translation.SetZero(); - Synchronize(); + Synchronize(b3Vec3_zero); SynchronizeTriangles(); } From f9e8d9394bb89d798e98d39c11312a137cfe0a9f Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 18 Jun 2019 11:55:09 -0300 Subject: [PATCH 161/198] Apply a bugfix --- src/bounce/cloth/cloth_contact.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bounce/cloth/cloth_contact.cpp b/src/bounce/cloth/cloth_contact.cpp index 814a728..be57dad 100644 --- a/src/bounce/cloth/cloth_contact.cpp +++ b/src/bounce/cloth/cloth_contact.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include // @@ -134,7 +135,7 @@ void b3ParticleTriangleContact::Update() } float32 r1 = m_p1->m_radius; - float32 r2 = 0.0f; + float32 r2 = m_t2->m_radius; float32 totalRadius = r1 + r2; From 9d935ea17f4913bc408401844b319f0cd3b570fe Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 18 Jun 2019 11:58:10 -0300 Subject: [PATCH 162/198] Set particle radius to thickness --- src/bounce/cloth/cloth.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 13aa712..98da7f1 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -173,6 +173,7 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : pd.type = e_dynamicParticle; pd.mass = 1.0f; pd.position = m->vertices[i]; + pd.radius = def.thickness; b3Particle* p = CreateParticle(pd); From 170793896d48cc89e0cda305b7173751fbcd08dd Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 18 Jun 2019 12:48:34 -0300 Subject: [PATCH 163/198] Synchronize node AABB given displacement --- include/bounce/softbody/softbody.h | 3 --- include/bounce/softbody/softbody_node.h | 10 ++++++---- src/bounce/softbody/softbody.cpp | 7 +++---- src/bounce/softbody/softbody_node.cpp | 4 +--- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/include/bounce/softbody/softbody.h b/include/bounce/softbody/softbody.h index 6099850..944fa9c 100644 --- a/include/bounce/softbody/softbody.h +++ b/include/bounce/softbody/softbody.h @@ -192,9 +192,6 @@ private: // Broadphase b3BroadPhase m_broadPhase; - // Time-step - float32 m_dt; - // Attached world b3World* m_world; }; diff --git a/include/bounce/softbody/softbody_node.h b/include/bounce/softbody/softbody_node.h index bc9a98b..87d039a 100644 --- a/include/bounce/softbody/softbody_node.h +++ b/include/bounce/softbody/softbody_node.h @@ -125,7 +125,7 @@ private: ~b3SoftBodyNode() { } // Synchronize node - void Synchronize(); + void Synchronize(const b3Vec3& displacement); // Type b3SoftBodyNodeType m_type; @@ -183,7 +183,7 @@ inline void b3SoftBodyNode::SetType(b3SoftBodyNodeType type) if (type == e_staticSoftBodyNode) { m_velocity.SetZero(); - Synchronize(); + Synchronize(b3Vec3_zero); } m_bodyContact.active = false; @@ -201,9 +201,11 @@ inline u32 b3SoftBodyNode::GetVertex() const inline void b3SoftBodyNode::SetPosition(const b3Vec3& position) { + b3Vec3 displacement = position - m_position; + m_position = position; - Synchronize(); + Synchronize(displacement); } inline const b3Vec3& b3SoftBodyNode::GetPosition() const @@ -244,7 +246,7 @@ inline void b3SoftBodyNode::SetRadius(float32 radius) { m_radius = radius; - Synchronize(); + Synchronize(b3Vec3_zero); } inline float32 b3SoftBodyNode::GetRadius() const diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index 2448d1e..4a55a2d 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -463,7 +463,6 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) m_c_max = def.c_max; m_gravity.SetZero(); m_world = nullptr; - m_dt = 0.0f; const b3SoftBodyMesh* m = m_mesh; @@ -840,8 +839,6 @@ void b3SoftBody::Step(float32 dt, u32 velocityIterations, u32 positionIterations { B3_PROFILE("Soft Body Step"); - m_dt = dt; - // Update contacts UpdateContacts(); @@ -867,7 +864,9 @@ void b3SoftBody::Step(float32 dt, u32 velocityIterations, u32 positionIterations continue; } - n->Synchronize(); + b3Vec3 displacement = dt * n->m_velocity; + + n->Synchronize(displacement); } } diff --git a/src/bounce/softbody/softbody_node.cpp b/src/bounce/softbody/softbody_node.cpp index 9df0ad4..45a8eb0 100644 --- a/src/bounce/softbody/softbody_node.cpp +++ b/src/bounce/softbody/softbody_node.cpp @@ -34,12 +34,10 @@ void b3NodeBodyContactWorldPoint::Initialize(const b3NodeBodyContact* c, float32 separation = b3Dot(cB - cA, nA) - rA - rB; } -void b3SoftBodyNode::Synchronize() +void b3SoftBodyNode::Synchronize(const b3Vec3& displacement) { b3AABB3 aabb; aabb.Set(m_position, m_radius); - b3Vec3 displacement = m_body->m_dt * m_velocity; - m_body->m_broadPhase.MoveProxy(m_broadPhaseId, aabb, displacement); } \ No newline at end of file From 9765e72ab9cc469fe459c3ad83eb1c9fbbbf8c0e Mon Sep 17 00:00:00 2001 From: Irlan Date: Tue, 18 Jun 2019 21:00:03 -0300 Subject: [PATCH 164/198] Better API naming. Enable self-collision by default. --- examples/testbed/framework/cloth_dragger.cpp | 24 +++---- examples/testbed/tests/tension_mapping.h | 23 +++--- include/bounce/cloth/cloth.h | 8 +-- include/bounce/cloth/cloth_triangle.h | 3 + include/bounce/cloth/particle.h | 6 +- src/bounce/cloth/cloth.cpp | 74 +++++++++----------- src/bounce/cloth/cloth_contact_manager.cpp | 14 ++-- src/bounce/cloth/cloth_triangle.cpp | 8 +-- src/bounce/cloth/particle.cpp | 4 +- 9 files changed, 85 insertions(+), 79 deletions(-) diff --git a/examples/testbed/framework/cloth_dragger.cpp b/examples/testbed/framework/cloth_dragger.cpp index ad18319..fb208c2 100644 --- a/examples/testbed/framework/cloth_dragger.cpp +++ b/examples/testbed/framework/cloth_dragger.cpp @@ -45,9 +45,9 @@ bool b3ClothDragger::StartDragging() m_triangle = m_mesh->triangles + rayOut.triangle; m_x = rayOut.fraction; - b3Particle* p1 = m_cloth->GetVertexParticle(m_triangle->v1); - b3Particle* p2 = m_cloth->GetVertexParticle(m_triangle->v2); - b3Particle* p3 = m_cloth->GetVertexParticle(m_triangle->v3); + b3Particle* p1 = m_cloth->GetParticle(m_triangle->v1); + b3Particle* p2 = m_cloth->GetParticle(m_triangle->v2); + b3Particle* p3 = m_cloth->GetParticle(m_triangle->v3); b3Vec3 v1 = p1->GetPosition(); b3Vec3 v2 = p2->GetPosition(); @@ -133,13 +133,13 @@ void b3ClothDragger::Drag() } else { - b3Particle* p1 = m_cloth->GetVertexParticle(m_triangle->v1); + b3Particle* p1 = m_cloth->GetParticle(m_triangle->v1); p1->ApplyTranslation(dx); - b3Particle* p2 = m_cloth->GetVertexParticle(m_triangle->v2); + b3Particle* p2 = m_cloth->GetParticle(m_triangle->v2); p2->ApplyTranslation(dx); - b3Particle* p3 = m_cloth->GetVertexParticle(m_triangle->v3); + b3Particle* p3 = m_cloth->GetParticle(m_triangle->v3); p3->ApplyTranslation(dx); } } @@ -172,9 +172,9 @@ void b3ClothDragger::StopDragging() } else { - m_cloth->GetVertexParticle(m_triangle->v1)->SetType(m_t1); - m_cloth->GetVertexParticle(m_triangle->v2)->SetType(m_t2); - m_cloth->GetVertexParticle(m_triangle->v3)->SetType(m_t3); + m_cloth->GetParticle(m_triangle->v1)->SetType(m_t1); + m_cloth->GetParticle(m_triangle->v2)->SetType(m_t2); + m_cloth->GetParticle(m_triangle->v3)->SetType(m_t3); } m_triangle = nullptr; @@ -184,9 +184,9 @@ b3Vec3 b3ClothDragger::GetPointA() const { B3_ASSERT(IsDragging() == true); - b3Vec3 A = m_cloth->GetVertexParticle(m_triangle->v1)->GetPosition(); - b3Vec3 B = m_cloth->GetVertexParticle(m_triangle->v2)->GetPosition(); - b3Vec3 C = m_cloth->GetVertexParticle(m_triangle->v3)->GetPosition(); + b3Vec3 A = m_cloth->GetParticle(m_triangle->v1)->GetPosition(); + b3Vec3 B = m_cloth->GetParticle(m_triangle->v2)->GetPosition(); + b3Vec3 C = m_cloth->GetParticle(m_triangle->v3)->GetPosition(); return m_u * A + m_v * B + (1.0f - m_u - m_v) * C; } diff --git a/examples/testbed/tests/tension_mapping.h b/examples/testbed/tests/tension_mapping.h index 1da11b2..3b4f89b 100644 --- a/examples/testbed/tests/tension_mapping.h +++ b/examples/testbed/tests/tension_mapping.h @@ -130,8 +130,15 @@ public: u32 v1 = s->GetParticle1()->GetVertex(); u32 v2 = s->GetParticle2()->GetVertex(); - tension[v1] += s->GetActionForce(); - tension[v2] -= s->GetActionForce(); + if (v1 != ~0) + { + tension[v1] += s->GetActionForce(); + } + + if (v2 != ~0) + { + tension[v2] -= s->GetActionForce(); + } } } @@ -139,13 +146,13 @@ public: { b3ClothMeshTriangle* t = mesh->triangles + i; - b3Vec3 v1 = m_cloth->GetVertexParticle(t->v1)->GetPosition(); - b3Vec3 v2 = m_cloth->GetVertexParticle(t->v2)->GetPosition(); - b3Vec3 v3 = m_cloth->GetVertexParticle(t->v3)->GetPosition(); + b3Vec3 v1 = m_cloth->GetParticle(t->v1)->GetPosition(); + b3Vec3 v2 = m_cloth->GetParticle(t->v2)->GetPosition(); + b3Vec3 v3 = m_cloth->GetParticle(t->v3)->GetPosition(); - b3Draw_draw->DrawSegment(v1, v2, b3Color_black); - b3Draw_draw->DrawSegment(v2, v3, b3Color_black); - b3Draw_draw->DrawSegment(v3, v1, b3Color_black); + g_draw->DrawSegment(v1, v2, b3Color_black); + g_draw->DrawSegment(v2, v3, b3Color_black); + g_draw->DrawSegment(v3, v1, b3Color_black); b3Vec3 f1 = tension[t->v1]; float32 L1 = b3Length(f1); diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index 98109b2..1f8e365 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -133,8 +133,8 @@ public: // Return the cloth mesh proxy. const b3ClothMesh* GetMesh() const; - // Return the particle associated with the given vertex. - b3Particle* GetVertexParticle(u32 i); + // Return the cloth particle given vertex index. + b3Particle* GetParticle(u32 i); // Return the cloth triangle given the triangle index. b3ClothTriangle* GetTriangle(u32 i); @@ -179,8 +179,8 @@ private: // Proxy mesh const b3ClothMesh* m_mesh; - // Vertex particles - b3Particle** m_vertexParticles; + // Particles + b3Particle** m_particles; // Triangles b3ClothTriangle* m_triangles; diff --git a/include/bounce/cloth/cloth_triangle.h b/include/bounce/cloth/cloth_triangle.h index 6d4fcc3..ca51ccc 100644 --- a/include/bounce/cloth/cloth_triangle.h +++ b/include/bounce/cloth/cloth_triangle.h @@ -67,6 +67,9 @@ private: // AABB Proxy b3ClothAABBProxy m_aabbProxy; + + // Broadphase ID + u32 m_broadPhaseId; }; inline u32 b3ClothTriangle::GetTriangle() const diff --git a/include/bounce/cloth/particle.h b/include/bounce/cloth/particle.h index 9adbf2c..ce909e2 100644 --- a/include/bounce/cloth/particle.h +++ b/include/bounce/cloth/particle.h @@ -102,8 +102,7 @@ enum b3ClothAABBProxyType struct b3ClothAABBProxy { b3ClothAABBProxyType type; - void* data; - u32 broadPhaseId; + void* owner; }; // A cloth particle. @@ -229,6 +228,9 @@ private: // AABB Proxy b3ClothAABBProxy m_aabbProxy; + // Broadphase ID + u32 m_broadPhaseId; + // Links to the cloth particle list. b3Particle* m_prev; b3Particle* m_next; diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 98da7f1..936df66 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -33,8 +33,6 @@ #include -#define B3_ENABLE_SELF_COLLISION 0 - static B3_FORCE_INLINE u32 b3NextIndex(u32 i) { return i + 1 < 3 ? i + 1 : 0; @@ -166,7 +164,7 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : const b3ClothMesh* m = m_mesh; // Initialize particles - m_vertexParticles = (b3Particle**)b3Alloc(m->vertexCount * sizeof(b3Particle*)); + m_particles = (b3Particle**)b3Alloc(m->vertexCount * sizeof(b3Particle*)); for (u32 i = 0; i < m->vertexCount; ++i) { b3ParticleDef pd; @@ -178,7 +176,7 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : b3Particle* p = CreateParticle(pd); p->m_vertex = i; - m_vertexParticles[i] = p; + m_particles[i] = p; } // Compute mass @@ -205,8 +203,8 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : aabb.Extend(triangle->m_radius); triangle->m_aabbProxy.type = e_triangleProxy; - triangle->m_aabbProxy.data = triangle; - triangle->m_aabbProxy.broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, &triangle->m_aabbProxy); + triangle->m_aabbProxy.owner = triangle; + triangle->m_broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, &triangle->m_aabbProxy); } // Initialize forces @@ -225,8 +223,8 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : { b3UniqueEdge* e = uniqueEdges + i; - b3Particle* p1 = m_vertexParticles[e->v1]; - b3Particle* p2 = m_vertexParticles[e->v2]; + b3Particle* p1 = m_particles[e->v1]; + b3Particle* p2 = m_particles[e->v2]; b3SpringForceDef fd; fd.Initialize(p1, p2, def.structural, def.damping); @@ -242,10 +240,10 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : { b3SharedEdge* e = sharedEdges + i; - b3Particle* p1 = m_vertexParticles[e->v1]; - b3Particle* p2 = m_vertexParticles[e->v2]; - b3Particle* p3 = m_vertexParticles[e->nsv1]; - b3Particle* p4 = m_vertexParticles[e->nsv2]; + b3Particle* p1 = m_particles[e->v1]; + b3Particle* p2 = m_particles[e->v2]; + b3Particle* p3 = m_particles[e->nsv1]; + b3Particle* p4 = m_particles[e->nsv2]; b3SpringForceDef fd; fd.Initialize(p3, p4, def.bending, def.damping); @@ -264,8 +262,8 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : { b3ClothMeshSewingLine* line = m->sewingLines + i; - b3Particle* p1 = m_vertexParticles[line->v1]; - b3Particle* p2 = m_vertexParticles[line->v2]; + b3Particle* p1 = m_particles[line->v1]; + b3Particle* p2 = m_particles[line->v2]; b3SpringForceDef fd; fd.Initialize(p1, p2, def.structural, def.damping); @@ -282,7 +280,7 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : b3Cloth::~b3Cloth() { - b3Free(m_vertexParticles); + b3Free(m_particles); b3Free(m_triangles); b3Particle* p = m_particleList.m_head; @@ -311,8 +309,8 @@ b3Particle* b3Cloth::CreateParticle(const b3ParticleDef& def) aabb.Set(p->m_position, p->m_radius); p->m_aabbProxy.type = e_particleProxy; - p->m_aabbProxy.data = p; - p->m_aabbProxy.broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, &p->m_aabbProxy); + p->m_aabbProxy.owner = p; + p->m_broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, &p->m_aabbProxy); m_particleList.PushFront(p); @@ -341,7 +339,7 @@ void b3Cloth::DestroyParticle(b3Particle* particle) particle->DestroyContacts(); // Destroy AABB proxy - m_contactManager.m_broadPhase.DestroyProxy(particle->m_aabbProxy.broadPhaseId); + m_contactManager.m_broadPhase.DestroyProxy(particle->m_broadPhaseId); m_particleList.Remove(particle); particle->~b3Particle(); @@ -371,10 +369,10 @@ float32 b3Cloth::GetEnergy() const return 0.5f * E; } -b3Particle* b3Cloth::GetVertexParticle(u32 i) +b3Particle* b3Cloth::GetParticle(u32 i) { B3_ASSERT(i < m_mesh->vertexCount); - return m_vertexParticles[i]; + return m_particles[i]; } b3ClothTriangle* b3Cloth::GetTriangle(u32 i) @@ -407,9 +405,9 @@ void b3Cloth::ComputeMass() float32 mass = rho * area; - b3Particle* p1 = m_vertexParticles[triangle->v1]; - b3Particle* p2 = m_vertexParticles[triangle->v2]; - b3Particle* p3 = m_vertexParticles[triangle->v3]; + b3Particle* p1 = m_particles[triangle->v1]; + b3Particle* p2 = m_particles[triangle->v2]; + b3Particle* p3 = m_particles[triangle->v3]; p1->m_mass += inv3 * mass; p2->m_mass += inv3 * mass; @@ -438,7 +436,7 @@ struct b3ClothRayCastSingleCallback return input.maxFraction; } - b3ClothTriangle* triangle = (b3ClothTriangle*)proxy->data; + b3ClothTriangle* triangle = (b3ClothTriangle*)proxy->owner; u32 triangleIndex = triangle->GetTriangle(); b3RayCastOutput subOutput; @@ -495,9 +493,9 @@ bool b3Cloth::RayCast(b3RayCastOutput* output, const b3RayCastInput* input, u32 B3_ASSERT(triangleIndex < m_mesh->triangleCount); b3ClothMeshTriangle* triangle = m_mesh->triangles + triangleIndex; - b3Vec3 v1 = m_vertexParticles[triangle->v1]->m_position; - b3Vec3 v2 = m_vertexParticles[triangle->v2]->m_position; - b3Vec3 v3 = m_vertexParticles[triangle->v3]->m_position; + b3Vec3 v1 = m_particles[triangle->v1]->m_position; + b3Vec3 v2 = m_particles[triangle->v2]->m_position; + b3Vec3 v3 = m_particles[triangle->v3]->m_position; return b3RayCast(output, input, v1, v2, v3); } @@ -556,7 +554,7 @@ void b3Cloth::UpdateParticleBodyContacts() continue; } - b3AABB3 aabb = m_contactManager.m_broadPhase.GetAABB(p->m_aabbProxy.broadPhaseId); + b3AABB3 aabb = m_contactManager.m_broadPhase.GetAABB(p->m_broadPhaseId); b3ClothUpdateContactsQueryListener listener; listener.sphere.vertex = p->m_position; @@ -681,9 +679,9 @@ void b3Cloth::Step(float32 dt, u32 velocityIterations, u32 positionIterations) { b3ClothMeshTriangle* triangle = m_mesh->triangles + i; - b3Particle* p1 = m_vertexParticles[triangle->v1]; - b3Particle* p2 = m_vertexParticles[triangle->v2]; - b3Particle* p3 = m_vertexParticles[triangle->v3]; + b3Particle* p1 = m_particles[triangle->v1]; + b3Particle* p2 = m_particles[triangle->v2]; + b3Particle* p3 = m_particles[triangle->v3]; b3Vec3 v1 = p1->m_velocity; b3Vec3 v2 = p2->m_velocity; @@ -696,12 +694,8 @@ void b3Cloth::Step(float32 dt, u32 velocityIterations, u32 positionIterations) m_triangles[i].Synchronize(displacement); } -#if B3_ENABLE_SELF_COLLISION - // Find new self-contacts m_contactManager.FindNewContacts(); - -#endif } void b3Cloth::Draw() const @@ -741,8 +735,8 @@ void b3Cloth::Draw() const for (u32 i = 0; i < m->sewingLineCount; ++i) { b3ClothMeshSewingLine* s = m->sewingLines + i; - b3Particle* p1 = m_vertexParticles[s->v1]; - b3Particle* p2 = m_vertexParticles[s->v2]; + b3Particle* p1 = m_particles[s->v1]; + b3Particle* p2 = m_particles[s->v2]; b3Draw_draw->DrawSegment(p1->m_position, p2->m_position, b3Color_white); } @@ -751,9 +745,9 @@ void b3Cloth::Draw() const { b3ClothMeshTriangle* t = m->triangles + i; - b3Particle* p1 = m_vertexParticles[t->v1]; - b3Particle* p2 = m_vertexParticles[t->v2]; - b3Particle* p3 = m_vertexParticles[t->v3]; + b3Particle* p1 = m_particles[t->v1]; + b3Particle* p2 = m_particles[t->v2]; + b3Particle* p3 = m_particles[t->v3]; b3Vec3 v1 = p1->m_position; b3Vec3 v2 = p2->m_position; diff --git a/src/bounce/cloth/cloth_contact_manager.cpp b/src/bounce/cloth/cloth_contact_manager.cpp index eef9ae1..d41b4a8 100644 --- a/src/bounce/cloth/cloth_contact_manager.cpp +++ b/src/bounce/cloth/cloth_contact_manager.cpp @@ -61,13 +61,13 @@ void b3ClothContactManager::AddPair(void* data1, void* data2) B3_ASSERT(proxy1->type == e_particleProxy); B3_ASSERT(proxy2->type == e_triangleProxy); - b3Particle* p1 = (b3Particle*)proxy1->data; + b3Particle* p1 = (b3Particle*)proxy1->owner; - b3ClothTriangle* t2 = (b3ClothTriangle*)proxy2->data; + b3ClothTriangle* t2 = (b3ClothTriangle*)proxy2->owner; b3ClothMeshTriangle* triangle = m_cloth->m_mesh->triangles + t2->m_triangle; - b3Particle* p2 = m_cloth->m_vertexParticles[triangle->v1]; - b3Particle* p3 = m_cloth->m_vertexParticles[triangle->v2]; - b3Particle* p4 = m_cloth->m_vertexParticles[triangle->v3]; + b3Particle* p2 = m_cloth->m_particles[triangle->v1]; + b3Particle* p3 = m_cloth->m_particles[triangle->v2]; + b3Particle* p4 = m_cloth->m_particles[triangle->v3]; // Check if there is a contact between the two entities. for (b3ParticleTriangleContact* c = m_particleTriangleContactList.m_head; c; c = c->m_next) @@ -146,8 +146,8 @@ void b3ClothContactManager::UpdateContacts() continue; } - u32 proxy1 = c->m_p1->m_aabbProxy.broadPhaseId; - u32 proxy2 = c->m_t2->m_aabbProxy.broadPhaseId; + u32 proxy1 = c->m_p1->m_broadPhaseId; + u32 proxy2 = c->m_t2->m_broadPhaseId; // Destroy the contact if primitive AABBs are not overlapping. bool overlap = m_broadPhase.TestOverlap(proxy1, proxy2); diff --git a/src/bounce/cloth/cloth_triangle.cpp b/src/bounce/cloth/cloth_triangle.cpp index f4776f5..1fc5477 100644 --- a/src/bounce/cloth/cloth_triangle.cpp +++ b/src/bounce/cloth/cloth_triangle.cpp @@ -24,9 +24,9 @@ void b3ClothTriangle::Synchronize(const b3Vec3& displacement) { b3ClothMeshTriangle* triangle = m_cloth->m_mesh->triangles + m_triangle; - b3Particle* p1 = m_cloth->m_vertexParticles[triangle->v1]; - b3Particle* p2 = m_cloth->m_vertexParticles[triangle->v2]; - b3Particle* p3 = m_cloth->m_vertexParticles[triangle->v3]; + b3Particle* p1 = m_cloth->m_particles[triangle->v1]; + b3Particle* p2 = m_cloth->m_particles[triangle->v2]; + b3Particle* p3 = m_cloth->m_particles[triangle->v3]; b3Vec3 x1 = p1->m_position; b3Vec3 x2 = p2->m_position; @@ -36,5 +36,5 @@ void b3ClothTriangle::Synchronize(const b3Vec3& displacement) aabb.Set(x1, x2, x3); aabb.Extend(m_radius); - m_cloth->m_contactManager.m_broadPhase.MoveProxy(m_aabbProxy.broadPhaseId, aabb, displacement); + m_cloth->m_contactManager.m_broadPhase.MoveProxy(m_broadPhaseId, aabb, displacement); } \ No newline at end of file diff --git a/src/bounce/cloth/particle.cpp b/src/bounce/cloth/particle.cpp index b6caf0b..308188c 100644 --- a/src/bounce/cloth/particle.cpp +++ b/src/bounce/cloth/particle.cpp @@ -76,7 +76,7 @@ void b3Particle::Synchronize(const b3Vec3& displacement) b3AABB3 aabb; aabb.Set(m_position, m_radius); - m_cloth->m_contactManager.m_broadPhase.MoveProxy(m_aabbProxy.broadPhaseId, aabb, displacement); + m_cloth->m_contactManager.m_broadPhase.MoveProxy(m_broadPhaseId, aabb, displacement); } void b3Particle::SynchronizeTriangles() @@ -140,5 +140,5 @@ void b3Particle::SetType(b3ParticleType type) DestroyContacts(); // Move the proxy so new contacts can be created. - m_cloth->m_contactManager.m_broadPhase.TouchProxy(m_aabbProxy.broadPhaseId); + m_cloth->m_contactManager.m_broadPhase.TouchProxy(m_broadPhaseId); } \ No newline at end of file From 625310be71cc9f2d3de76b2849e42d9c63547429 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 19 Jun 2019 12:39:28 -0300 Subject: [PATCH 165/198] Split the solvers into force solver and contact solver --- include/bounce/cloth/cloth_contact_solver.h | 10 +- include/bounce/cloth/cloth_force_solver.h | 89 +++++ include/bounce/cloth/cloth_solver.h | 41 +- include/bounce/cloth/force.h | 6 +- include/bounce/cloth/particle.h | 1 + include/bounce/cloth/spring_force.h | 2 +- src/bounce/cloth/cloth_contact_solver.cpp | 88 ++--- src/bounce/cloth/cloth_force_solver.cpp | 293 ++++++++++++++ src/bounce/cloth/cloth_solver.cpp | 401 ++++---------------- src/bounce/cloth/spring_force.cpp | 4 +- 10 files changed, 511 insertions(+), 424 deletions(-) create mode 100644 include/bounce/cloth/cloth_force_solver.h create mode 100644 src/bounce/cloth/cloth_force_solver.cpp diff --git a/include/bounce/cloth/cloth_contact_solver.h b/include/bounce/cloth/cloth_contact_solver.h index 5d69900..76f13bc 100644 --- a/include/bounce/cloth/cloth_contact_solver.h +++ b/include/bounce/cloth/cloth_contact_solver.h @@ -30,8 +30,6 @@ class b3Body; struct b3ParticleBodyContact; class b3ParticleTriangleContact; -struct b3DenseVec3; - struct b3ClothSolverBodyContactVelocityConstraint { u32 indexA; @@ -130,8 +128,8 @@ struct b3ClothContactSolverDef { b3StackAllocator* allocator; - b3DenseVec3* positions; - b3DenseVec3* velocities; + b3Vec3* positions; + b3Vec3* velocities; u32 bodyContactCount; b3ParticleBodyContact** bodyContacts; @@ -167,8 +165,8 @@ public: protected: b3StackAllocator* m_allocator; - b3DenseVec3* m_positions; - b3DenseVec3* m_velocities; + b3Vec3* m_positions; + b3Vec3* m_velocities; u32 m_bodyContactCount; b3ParticleBodyContact** m_bodyContacts; diff --git a/include/bounce/cloth/cloth_force_solver.h b/include/bounce/cloth/cloth_force_solver.h new file mode 100644 index 0000000..763e3cc --- /dev/null +++ b/include/bounce/cloth/cloth_force_solver.h @@ -0,0 +1,89 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_CLOTH_FORCE_SOLVER_H +#define B3_CLOTH_FORCE_SOLVER_H + +#include +#include + +class b3StackAllocator; + +class b3Particle; +class b3Force; + +struct b3DenseVec3; +struct b3DiagMat33; +struct b3SparseSymMat33; +struct b3SparseSymMat33View; + +struct b3ClothForceSolverDef +{ + b3StackAllocator* stack; + u32 particleCount; + b3Particle** particles; + u32 forceCount; + b3Force** forces; +}; + +struct b3ClothForceSolverData +{ + b3DenseVec3* x; + b3DenseVec3* v; + b3DenseVec3* f; + b3DenseVec3* y; + b3SparseSymMat33* dfdx; + b3SparseSymMat33* dfdv; + b3DiagMat33* S; + b3DenseVec3* z; +}; + +struct b3AccelerationConstraint +{ + u32 i1; + u32 ndof; + b3Vec3 p, q, z; + + void Apply(const b3ClothForceSolverData* data); +}; + +class b3ClothForceSolver +{ +public: + b3ClothForceSolver(const b3ClothForceSolverDef& def); + ~b3ClothForceSolver(); + + void Solve(float32 dt, const b3Vec3& gravity); +private: + void ApplyForces(); + void ApplyConstraints(); + + b3StackAllocator* m_allocator; + + u32 m_particleCount; + b3Particle** m_particles; + + u32 m_forceCount; + b3Force** m_forces; + + b3AccelerationConstraint* m_constraints; + + b3ClothForceSolverData m_solverData; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/cloth/cloth_solver.h b/include/bounce/cloth/cloth_solver.h index 85ed648..2edf011 100644 --- a/include/bounce/cloth/cloth_solver.h +++ b/include/bounce/cloth/cloth_solver.h @@ -25,14 +25,7 @@ class b3StackAllocator; class b3Particle; -class b3Body; class b3Force; - -struct b3DenseVec3; -struct b3DiagMat33; -struct b3SparseSymMat33; -struct b3SparseSymMat33View; - struct b3ParticleBodyContact; class b3ParticleTriangleContact; @@ -45,29 +38,6 @@ struct b3ClothSolverDef u32 triangleContactCapacity; }; -struct b3ClothSolverData -{ - b3DenseVec3* x; - b3DenseVec3* v; - b3DenseVec3* f; - b3DenseVec3* y; - b3SparseSymMat33* dfdx; - b3SparseSymMat33* dfdv; - b3DiagMat33* S; - b3DenseVec3* z; - float32 dt; - float32 invdt; -}; - -struct b3AccelerationConstraint -{ - u32 i1; - u32 ndof; - b3Vec3 p, q, z; - - void Apply(const b3ClothSolverData* data); -}; - class b3ClothSolver { public: @@ -81,9 +51,6 @@ public: void Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations); private: - void ApplyForces(); - void ApplyConstraints(); - b3StackAllocator* m_allocator; u32 m_particleCapacity; @@ -93,11 +60,7 @@ private: u32 m_forceCapacity; u32 m_forceCount; b3Force** m_forces; - - u32 m_constraintCapacity; - u32 m_constraintCount; - b3AccelerationConstraint* m_constraints; - + u32 m_bodyContactCapacity; u32 m_bodyContactCount; b3ParticleBodyContact** m_bodyContacts; @@ -105,8 +68,6 @@ private: u32 m_triangleContactCapacity; u32 m_triangleContactCount; b3ParticleTriangleContact** m_triangleContacts; - - b3ClothSolverData m_solverData; }; #endif \ No newline at end of file diff --git a/include/bounce/cloth/force.h b/include/bounce/cloth/force.h index 9e9132f..392e0ef 100644 --- a/include/bounce/cloth/force.h +++ b/include/bounce/cloth/force.h @@ -22,7 +22,7 @@ #include #include -struct b3ClothSolverData; +struct b3ClothForceSolverData; class b3Particle; @@ -52,7 +52,7 @@ public: protected: friend class b3List2; friend class b3Cloth; - friend class b3ClothSolver; + friend class b3ClothForceSolver; friend class b3Particle; static b3Force* Create(const b3ForceDef* def); @@ -61,7 +61,7 @@ protected: b3Force() { } virtual ~b3Force() { } - virtual void Apply(const b3ClothSolverData* data) = 0; + virtual void Apply(const b3ClothForceSolverData* data) = 0; // Force type b3ForceType m_type; diff --git a/include/bounce/cloth/particle.h b/include/bounce/cloth/particle.h index ce909e2..6844e28 100644 --- a/include/bounce/cloth/particle.h +++ b/include/bounce/cloth/particle.h @@ -160,6 +160,7 @@ private: friend class b3Cloth; friend class b3ClothTriangle; friend class b3ClothSolver; + friend class b3ClothForceSolver; friend class b3ClothContactManager; friend class b3ParticleTriangleContact; friend class b3ClothContactSolver; diff --git a/include/bounce/cloth/spring_force.h b/include/bounce/cloth/spring_force.h index f7da89f..fa01405 100644 --- a/include/bounce/cloth/spring_force.h +++ b/include/bounce/cloth/spring_force.h @@ -76,7 +76,7 @@ private: b3SpringForce(const b3SpringForceDef* def); ~b3SpringForce(); - void Apply(const b3ClothSolverData* data); + void Apply(const b3ClothForceSolverData* data); // Solver shared diff --git a/src/bounce/cloth/cloth_contact_solver.cpp b/src/bounce/cloth/cloth_contact_solver.cpp index d736e7c..d6ef10c 100644 --- a/src/bounce/cloth/cloth_contact_solver.cpp +++ b/src/bounce/cloth/cloth_contact_solver.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -54,9 +53,6 @@ b3ClothContactSolver::~b3ClothContactSolver() void b3ClothContactSolver::InitializeBodyContactConstraints() { - b3DenseVec3& x = *m_positions; - b3DenseVec3& v = *m_velocities; - for (u32 i = 0; i < m_bodyContactCount; ++i) { b3ParticleBodyContact* c = m_bodyContacts[i]; @@ -109,7 +105,7 @@ void b3ClothContactSolver::InitializeBodyContactConstraints() b3Mat33 iA = vc->invIA; b3Mat33 iB = vc->invIB; - b3Vec3 xA = x[indexA]; + b3Vec3 xA = m_positions[indexA]; b3Vec3 xB = bodyB->m_sweep.worldCenter; b3Quat qA; qA.SetIdentity(); @@ -182,8 +178,6 @@ void b3ClothContactSolver::InitializeBodyContactConstraints() } void b3ClothContactSolver::InitializeTriangleContactConstraints() { - b3DenseVec3& x = *m_positions; - for (u32 i = 0; i < m_triangleContactCount; ++i) { b3ParticleTriangleContact* c = m_triangleContacts[i]; @@ -233,10 +227,10 @@ void b3ClothContactSolver::InitializeTriangleContactConstraints() u32 indexD = pc->indexD; float32 mD = pc->invMassD; - b3Vec3 xA = x[indexA]; - b3Vec3 xB = x[indexB]; - b3Vec3 xC = x[indexC]; - b3Vec3 xD = x[indexD]; + b3Vec3 xA = m_positions[indexA]; + b3Vec3 xB = m_positions[indexB]; + b3Vec3 xC = m_positions[indexC]; + b3Vec3 xD = m_positions[indexD]; float32 wB = pc->wB; float32 wC = pc->wC; @@ -269,8 +263,6 @@ void b3ClothContactSolver::InitializeTriangleContactConstraints() void b3ClothContactSolver::WarmStartBodyContactConstraints() { - b3DenseVec3& v = *m_velocities; - for (u32 i = 0; i < m_bodyContactCount; ++i) { b3ClothSolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; @@ -278,7 +270,7 @@ void b3ClothContactSolver::WarmStartBodyContactConstraints() u32 indexA = vc->indexA; b3Body* bodyB = vc->bodyB; - b3Vec3 vA = v[indexA]; + b3Vec3 vA = m_velocities[indexA]; b3Vec3 vB = bodyB->GetLinearVelocity(); b3Vec3 wA; wA.SetZero(); @@ -307,7 +299,7 @@ void b3ClothContactSolver::WarmStartBodyContactConstraints() vB += mB * (P1 + P2); wB += iB * b3Cross(vc->rB, P1 + P2); - v[indexA] = vA; + m_velocities[indexA] = vA; bodyB->SetLinearVelocity(vB); bodyB->SetAngularVelocity(wB); @@ -316,8 +308,6 @@ void b3ClothContactSolver::WarmStartBodyContactConstraints() void b3ClothContactSolver::WarmStartTriangleContactConstraints() { - b3DenseVec3& v = *m_velocities; - for (u32 i = 0; i < m_triangleContactCount; ++i) { b3ClothSolverTriangleContactVelocityConstraint* vc = m_triangleVelocityConstraints + i; @@ -327,10 +317,10 @@ void b3ClothContactSolver::WarmStartTriangleContactConstraints() u32 indexC = vc->indexC; u32 indexD = vc->indexD; - b3Vec3 vA = v[indexA]; - b3Vec3 vB = v[indexB]; - b3Vec3 vC = v[indexC]; - b3Vec3 vD = v[indexD]; + b3Vec3 vA = m_velocities[indexA]; + b3Vec3 vB = m_velocities[indexB]; + b3Vec3 vC = m_velocities[indexC]; + b3Vec3 vD = m_velocities[indexD]; float32 mA = vc->invMassA; float32 mB = vc->invMassB; @@ -388,17 +378,15 @@ void b3ClothContactSolver::WarmStartTriangleContactConstraints() vD += mD * PD; } - v[indexA] = vA; - v[indexB] = vB; - v[indexC] = vC; - v[indexD] = vD; + m_velocities[indexA] = vA; + m_velocities[indexB] = vB; + m_velocities[indexC] = vC; + m_velocities[indexD] = vD; } } void b3ClothContactSolver::SolveBodyContactVelocityConstraints() { - b3DenseVec3& v = *m_velocities; - for (u32 i = 0; i < m_bodyContactCount; ++i) { b3ClothSolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; @@ -406,7 +394,7 @@ void b3ClothContactSolver::SolveBodyContactVelocityConstraints() u32 indexA = vc->indexA; b3Body* bodyB = vc->bodyB; - b3Vec3 vA = v[indexA]; + b3Vec3 vA = m_velocities[indexA]; b3Vec3 vB = bodyB->GetLinearVelocity(); b3Vec3 wA; wA.SetZero(); @@ -476,7 +464,7 @@ void b3ClothContactSolver::SolveBodyContactVelocityConstraints() wB += iB * b3Cross(rB, P); } - v[indexA] = vA; + m_velocities[indexA] = vA; bodyB->SetLinearVelocity(vB); bodyB->SetAngularVelocity(wB); @@ -485,8 +473,6 @@ void b3ClothContactSolver::SolveBodyContactVelocityConstraints() void b3ClothContactSolver::SolveTriangleContactVelocityConstraints() { - b3DenseVec3& v = *m_velocities; - for (u32 i = 0; i < m_triangleContactCount; ++i) { b3ClothSolverTriangleContactVelocityConstraint* vc = m_triangleVelocityConstraints + i; @@ -507,10 +493,10 @@ void b3ClothContactSolver::SolveTriangleContactVelocityConstraints() float32 wC = vc->wC; float32 wD = vc->wD; - b3Vec3 vA = v[indexA]; - b3Vec3 vB = v[indexB]; - b3Vec3 vC = v[indexC]; - b3Vec3 vD = v[indexD]; + b3Vec3 vA = m_velocities[indexA]; + b3Vec3 vB = m_velocities[indexB]; + b3Vec3 vC = m_velocities[indexC]; + b3Vec3 vD = m_velocities[indexD]; // Solve normal constraint. { @@ -602,10 +588,10 @@ void b3ClothContactSolver::SolveTriangleContactVelocityConstraints() vD += mD * PD; } - v[indexA] = vA; - v[indexB] = vB; - v[indexC] = vC; - v[indexD] = vD; + m_velocities[indexA] = vA; + m_velocities[indexB] = vB; + m_velocities[indexC] = vC; + m_velocities[indexD] = vD; } } @@ -658,8 +644,6 @@ struct b3ClothSolverBodyContactSolverPoint bool b3ClothContactSolver::SolveBodyContactPositionConstraints() { - b3DenseVec3& x = *m_positions; - float32 minSeparation = 0.0f; for (u32 i = 0; i < m_bodyContactCount; ++i) @@ -676,7 +660,7 @@ bool b3ClothContactSolver::SolveBodyContactPositionConstraints() b3Mat33 iB = pc->invIB; b3Vec3 localCenterB = pc->localCenterB; - b3Vec3 cA = x[indexA]; + b3Vec3 cA = m_positions[indexA]; b3Quat qA; qA.SetIdentity(); b3Vec3 cB = bodyB->m_sweep.worldCenter; @@ -724,7 +708,7 @@ bool b3ClothContactSolver::SolveBodyContactPositionConstraints() qB += b3Derivative(qB, iB * b3Cross(rB, P)); qB.Normalize(); - x[indexA] = cA; + m_positions[indexA] = cA; bodyB->m_sweep.worldCenter = cB; bodyB->m_sweep.orientation = qB; @@ -735,8 +719,6 @@ bool b3ClothContactSolver::SolveBodyContactPositionConstraints() bool b3ClothContactSolver::SolveTriangleContactPositionConstraints() { - b3DenseVec3& x = *m_positions; - float32 minSeparation = 0.0f; for (u32 i = 0; i < m_triangleContactCount; ++i) @@ -762,10 +744,10 @@ bool b3ClothContactSolver::SolveTriangleContactPositionConstraints() float32 wC = pc->wC; float32 wD = pc->wD; - b3Vec3 xA = x[indexA]; - b3Vec3 xB = x[indexB]; - b3Vec3 xC = x[indexC]; - b3Vec3 xD = x[indexD]; + b3Vec3 xA = m_positions[indexA]; + b3Vec3 xB = m_positions[indexB]; + b3Vec3 xC = m_positions[indexC]; + b3Vec3 xD = m_positions[indexD]; b3Vec3 cB = wB * xB + wC * xC + wD * xD; @@ -802,10 +784,10 @@ bool b3ClothContactSolver::SolveTriangleContactPositionConstraints() xC += mC * PC; xD += mD * PD; - x[indexA] = xA; - x[indexB] = xB; - x[indexC] = xC; - x[indexD] = xD; + m_positions[indexA] = xA; + m_positions[indexB] = xB; + m_positions[indexC] = xC; + m_positions[indexD] = xD; } return minSeparation >= -3.0f * B3_LINEAR_SLOP; diff --git a/src/bounce/cloth/cloth_force_solver.cpp b/src/bounce/cloth/cloth_force_solver.cpp new file mode 100644 index 0000000..0dd373e --- /dev/null +++ b/src/bounce/cloth/cloth_force_solver.cpp @@ -0,0 +1,293 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +// Here, we solve Ax = b using the Modified Preconditioned Conjugate Gradient (MPCG) algorithm. +// described in the paper: +// "Large Steps in Cloth Simulation - David Baraff, Andrew Witkin". + +// Some improvements for the original MPCG algorithm are described in the paper: +// "On the modified conjugate gradient method in cloth simulation - Uri M. Ascher, Eddy Boxerman". + +u32 b3_clothSolverIterations = 0; + +b3ClothForceSolver::b3ClothForceSolver(const b3ClothForceSolverDef& def) +{ + m_allocator = def.stack; + + m_particleCount = def.particleCount; + m_particles = def.particles; + + m_forceCount = def.forceCount; + m_forces = def.forces; + + m_constraints = (b3AccelerationConstraint*)m_allocator->Allocate(m_particleCount * sizeof(b3AccelerationConstraint)); +} + +b3ClothForceSolver::~b3ClothForceSolver() +{ + m_allocator->Free(m_constraints); +} + +void b3ClothForceSolver::ApplyForces() +{ + for (u32 i = 0; i < m_forceCount; ++i) + { + m_forces[i]->Apply(&m_solverData); + } +} + +void b3AccelerationConstraint::Apply(const b3ClothForceSolverData* data) +{ + b3DiagMat33& sS = *data->S; + b3DenseVec3& sz = *data->z; + + sz[i1] = z; + + b3Mat33 I; I.SetIdentity(); + + switch (ndof) + { + case 3: + { + sS[i1] = I; + break; + } + case 2: + { + sS[i1] = I - b3Outer(p, p); + break; + } + case 1: + { + sS[i1] = I - b3Outer(p, p) - b3Outer(q, q); + break; + } + case 0: + { + sS[i1].SetZero(); + break; + } + default: + { + B3_ASSERT(false); + break; + } + } +} + +void b3ClothForceSolver::ApplyConstraints() +{ + for (u32 i = 0; i < m_particleCount; ++i) + { + b3Particle* p = m_particles[i]; + b3AccelerationConstraint* c = m_constraints + i; + + c->i1 = i; + + if (p->m_type != e_dynamicParticle) + { + c->ndof = 0; + c->z.SetZero(); + } + else + { + c->ndof = 3; + c->z.SetZero(); + } + } + + for (u32 i = 0; i < m_particleCount; ++i) + { + m_constraints[i].Apply(&m_solverData); + } +} + +// Solve Ax = b +static void b3SolveMPCG(b3DenseVec3& x, + const b3SparseSymMat33View& A, const b3DenseVec3& b, + const b3DiagMat33& S, const b3DenseVec3& z, + const b3DenseVec3& y, const b3DiagMat33& I, u32 maxIterations = 20) +{ + B3_PROFILE("Cloth Solve MPCG"); + + // Jacobi preconditioner + // P = diag(A) + b3DiagMat33 inv_P(A.rowCount); + for (u32 i = 0; i < A.rowCount; ++i) + { + b3Mat33 a = A(i, i); + + // Sylvester Criterion to ensure PD-ness + B3_ASSERT(b3Det(a.x, a.y, a.z) > 0.0f); + + B3_ASSERT(a.x.x > 0.0f); + float32 xx = 1.0f / a.x.x; + + B3_ASSERT(a.y.y > 0.0f); + float32 yy = 1.0f / a.y.y; + + B3_ASSERT(a.z.z > 0.0f); + float32 zz = 1.0f / a.z.z; + + inv_P[i] = b3Diagonal(xx, yy, zz); + } + + x = (S * y) + (I - S) * z; + + b3DenseVec3 b_hat = S * (b - A * ((I - S) * z)); + + float32 b_delta = b3Dot(b_hat, inv_P * b_hat); + + b3DenseVec3 r = S * (b - A * x); + + b3DenseVec3 p = S * (inv_P * r); + + float32 delta_new = b3Dot(r, p); + + u32 iteration = 0; + for (;;) + { + if (iteration == maxIterations) + { + break; + } + + if (delta_new <= B3_EPSILON * B3_EPSILON * b_delta) + { + break; + } + + b3DenseVec3 s = S * (A * p); + + float32 alpha = delta_new / b3Dot(p, s); + + x = x + alpha * p; + r = r - alpha * s; + + b3DenseVec3 h = inv_P * r; + + float32 delta_old = delta_new; + + delta_new = b3Dot(r, h); + + float32 beta = delta_new / delta_old; + + p = S * (h + beta * p); + + ++iteration; + } + + b3_clothSolverIterations = iteration; +} + +void b3ClothForceSolver::Solve(float32 dt, const b3Vec3& gravity) +{ + float32 h = dt; + + b3DenseVec3 sx(m_particleCount); + b3DenseVec3 sv(m_particleCount); + b3DenseVec3 sf(m_particleCount); + b3DenseVec3 sy(m_particleCount); + b3DenseVec3 sz(m_particleCount); + b3DenseVec3 sx0(m_particleCount); + b3SparseSymMat33 dfdx(m_particleCount); + b3SparseSymMat33 dfdv(m_particleCount); + b3DiagMat33 S(m_particleCount); + + m_solverData.x = &sx; + m_solverData.v = &sv; + m_solverData.f = &sf; + m_solverData.y = &sy; + m_solverData.z = &sz; + m_solverData.dfdx = &dfdx; + m_solverData.dfdv = &dfdv; + m_solverData.S = &S; + + for (u32 i = 0; i < m_particleCount; ++i) + { + b3Particle* p = m_particles[i]; + + sx[i] = p->m_position; + sv[i] = p->m_velocity; + sf[i] = p->m_force; + + if (p->m_type == e_dynamicParticle) + { + // Apply weight + sf[i] += p->m_mass * gravity; + } + + sy[i] = p->m_translation; + sx0[i] = p->m_x; + } + + // Apply internal forces + ApplyForces(); + + // Apply constraints + ApplyConstraints(); + + // Solve Ax = b, where + // A = M - h * dfdv - h * h * dfdx + // b = h * (f0 + h * dfdx * v0 + dfdx * y) + b3SparseSymMat33 M(m_particleCount); + for (u32 i = 0; i < m_particleCount; ++i) + { + M(i, i) = b3Diagonal(m_particles[i]->m_mass); + } + + // A + b3SparseSymMat33 A = M - h * dfdv - h * h * dfdx; + + // View for A + b3SparseSymMat33View viewA(A); + + // b + b3DenseVec3 b = h * (sf + h * (dfdx * sv) + dfdx * sy); + + // I + b3DiagMat33 I(m_particleCount); + I.SetIdentity(); + + // x + b3DenseVec3 x(m_particleCount); + b3SolveMPCG(x, viewA, b, S, sz, sx0, I); + + // Velocity update + sv = sv + x; + sx = sx + sy; + + // Copy state buffers back to the particle + for (u32 i = 0; i < m_particleCount; ++i) + { + b3Particle* p = m_particles[i]; + + p->m_x = x[i]; + p->m_position = sx[i]; + p->m_velocity = sv[i]; + } +} \ No newline at end of file diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index da71c12..5584b29 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -17,27 +17,13 @@ */ #include +#include #include #include #include -#include -#include -#include -#include -#include -#include -#include #include #include - -// Here, we solve Ax = b using the Modified Preconditioned Conjugate Gradient (MPCG) algorithm. -// described in the paper: -// "Large Steps in Cloth Simulation - David Baraff, Andrew Witkin". - -// Some improvements for the original MPCG algorithm are described in the paper: -// "On the modified conjugate gradient method in cloth simulation - Uri M. Ascher, Eddy Boxerman". - -u32 b3_clothSolverIterations = 0; +#include b3ClothSolver::b3ClothSolver(const b3ClothSolverDef& def) { @@ -51,10 +37,6 @@ b3ClothSolver::b3ClothSolver(const b3ClothSolverDef& def) m_forceCount = 0; m_forces = (b3Force**)m_allocator->Allocate(m_forceCapacity * sizeof(b3Force*));; - m_constraintCapacity = def.particleCapacity; - m_constraintCount = 0; - m_constraints = (b3AccelerationConstraint*)m_allocator->Allocate(m_constraintCapacity * sizeof(b3AccelerationConstraint)); - m_bodyContactCapacity = def.bodyContactCapacity; m_bodyContactCount = 0; m_bodyContacts = (b3ParticleBodyContact**)m_allocator->Allocate(m_bodyContactCapacity * sizeof(b3ParticleBodyContact*));; @@ -68,8 +50,6 @@ b3ClothSolver::~b3ClothSolver() { m_allocator->Free(m_triangleContacts); m_allocator->Free(m_bodyContacts); - - m_allocator->Free(m_constraints); m_allocator->Free(m_forces); m_allocator->Free(m_particles); } @@ -95,326 +75,106 @@ void b3ClothSolver::Add(b3ParticleTriangleContact* c) m_triangleContacts[m_triangleContactCount++] = c; } -void b3ClothSolver::ApplyForces() -{ - for (u32 i = 0; i < m_forceCount; ++i) - { - m_forces[i]->Apply(&m_solverData); - } -} - -void b3AccelerationConstraint::Apply(const b3ClothSolverData* data) -{ - b3DiagMat33& sS = *data->S; - b3DenseVec3& sz = *data->z; - - sz[i1] = z; - - b3Mat33 I; I.SetIdentity(); - - switch (ndof) - { - case 3: - { - sS[i1] = I; - break; - } - case 2: - { - sS[i1] = I - b3Outer(p, p); - break; - } - case 1: - { - sS[i1] = I - b3Outer(p, p) - b3Outer(q, q); - break; - } - case 0: - { - sS[i1].SetZero(); - break; - } - default: - { - B3_ASSERT(false); - break; - } - } -} - -void b3ClothSolver::ApplyConstraints() -{ - b3DiagMat33& S = *m_solverData.S; - b3DenseVec3& z = *m_solverData.z; - b3DenseVec3& x = *m_solverData.x; - - S.SetIdentity(); - z.SetZero(); - - for (u32 i = 0; i < m_particleCount; ++i) - { - b3Particle* p = m_particles[i]; - - if (p->m_type != e_dynamicParticle) - { - b3AccelerationConstraint* ac = m_constraints + m_constraintCount; - ++m_constraintCount; - ac->i1 = i; - ac->ndof = 0; - ac->z.SetZero(); - } - } - - for (u32 i = 0; i < m_constraintCount; ++i) - { - m_constraints[i].Apply(&m_solverData); - } -} - -// -static void b3SolveMPCG(b3DenseVec3& x, - const b3SparseSymMat33View& A, const b3DenseVec3& b, - const b3DiagMat33& S, const b3DenseVec3& z, - const b3DenseVec3& y, const b3DiagMat33& I, u32 maxIterations = 20) -{ - B3_PROFILE("Cloth Solve MPCG"); - - // Jacobi preconditioner - // P = diag(A) - b3DiagMat33 inv_P(A.rowCount); - for (u32 i = 0; i < A.rowCount; ++i) - { - b3Mat33 a = A(i, i); - - // Sylvester Criterion to ensure PD-ness - B3_ASSERT(b3Det(a.x, a.y, a.z) > 0.0f); - - B3_ASSERT(a.x.x > 0.0f); - float32 xx = 1.0f / a.x.x; - - B3_ASSERT(a.y.y > 0.0f); - float32 yy = 1.0f / a.y.y; - - B3_ASSERT(a.z.z > 0.0f); - float32 zz = 1.0f / a.z.z; - - inv_P[i] = b3Diagonal(xx, yy, zz); - } - - x = (S * y) + (I - S) * z; - - b3DenseVec3 b_hat = S * (b - A * ((I - S) * z)); - - float32 b_delta = b3Dot(b_hat, inv_P * b_hat); - - b3DenseVec3 r = S * (b - A * x); - - b3DenseVec3 p = S * (inv_P * r); - - float32 delta_new = b3Dot(r, p); - - u32 iteration = 0; - for (;;) - { - if (iteration == maxIterations) - { - break; - } - - if (delta_new <= B3_EPSILON * B3_EPSILON * b_delta) - { - break; - } - - b3DenseVec3 s = S * (A * p); - - float32 alpha = delta_new / b3Dot(p, s); - - x = x + alpha * p; - r = r - alpha * s; - - b3DenseVec3 h = inv_P * r; - - float32 delta_old = delta_new; - - delta_new = b3Dot(r, h); - - float32 beta = delta_new / delta_old; - - p = S * (h + beta * p); - - ++iteration; - } - - b3_clothSolverIterations = iteration; -} - void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations) { - float32 h = dt; - float32 inv_h = 1.0f / h; + { + // Solve internal dynamics + b3ClothForceSolverDef forceSolverDef; + forceSolverDef.stack = m_allocator; + forceSolverDef.particleCount = m_particleCount; + forceSolverDef.particles = m_particles; + forceSolverDef.forceCount = m_forceCount; + forceSolverDef.forces = m_forces; - b3DenseVec3 sx(m_particleCount); - b3DenseVec3 sv(m_particleCount); - b3DenseVec3 sf(m_particleCount); + b3ClothForceSolver forceSolver(forceSolverDef); - m_solverData.dt = h; - m_solverData.invdt = inv_h; - m_solverData.x = &sx; - m_solverData.v = &sv; - m_solverData.f = &sf; + forceSolver.Solve(dt, gravity); + } + + // Copy particle state to state buffer + b3Vec3* positions = (b3Vec3*)m_allocator->Allocate(m_particleCount * sizeof(b3Vec3)); + b3Vec3* velocities = (b3Vec3*)m_allocator->Allocate(m_particleCount * sizeof(b3Vec3)); for (u32 i = 0; i < m_particleCount; ++i) { - b3Particle* p = m_particles[i]; - - sx[i] = p->m_position; - sv[i] = p->m_velocity; - sf[i] = p->m_force; - - // Apply weight - if (p->m_type == e_dynamicParticle) - { - sf[i] += p->m_mass * gravity; - } + positions[i] = m_particles[i]->m_position; + velocities[i] = m_particles[i]->m_velocity; } - // Integrate velocities { - b3SparseSymMat33 dfdx(m_particleCount); - b3SparseSymMat33 dfdv(m_particleCount); - b3DiagMat33 S(m_particleCount); - b3DenseVec3 z(m_particleCount); - b3DenseVec3 sy(m_particleCount); - b3DenseVec3 sx0(m_particleCount); + // Solve constraints + b3ClothContactSolverDef contactSolverDef; + contactSolverDef.allocator = m_allocator; + contactSolverDef.positions = positions; + contactSolverDef.velocities = velocities; + contactSolverDef.bodyContactCount = m_bodyContactCount; + contactSolverDef.bodyContacts = m_bodyContacts; + contactSolverDef.triangleContactCount = m_triangleContactCount; + contactSolverDef.triangleContacts = m_triangleContacts; - m_solverData.y = &sy; - m_solverData.dfdx = &dfdx; - m_solverData.dfdv = &dfdv; - m_solverData.S = &S; - m_solverData.z = &z; + b3ClothContactSolver contactSolver(contactSolverDef); - // Apply position correction - for (u32 i = 0; i < m_particleCount; ++i) { - b3Particle* p = m_particles[i]; - - sy[i] = p->m_translation; - sx0[i] = p->m_x; + // Initialize constraints + contactSolver.InitializeBodyContactConstraints(); + contactSolver.InitializeTriangleContactConstraints(); } - // Apply internal forces - ApplyForces(); - - // Apply constraints - ApplyConstraints(); - - // Solve Ax = b, where - // A = M - h * dfdv - h * h * dfdx - // b = h * (f0 + h * dfdx * v0 + dfdx * y) - - b3SparseSymMat33 M(m_particleCount); - for (u32 i = 0; i < m_particleCount; ++i) { - M(i, i) = b3Diagonal(m_particles[i]->m_mass); + // Warm start velocity constraints + contactSolver.WarmStartBodyContactConstraints(); + contactSolver.WarmStartTriangleContactConstraints(); } - // A - b3SparseSymMat33 A = M - h * dfdv - h * h * dfdx; - - // View for A - b3SparseSymMat33View viewA(A); - - // b - b3DenseVec3 b = h * (sf + h * (dfdx * sv) + dfdx * sy); - - // I - b3DiagMat33 I(m_particleCount); - I.SetIdentity(); - - // x - b3DenseVec3 x(m_particleCount); - b3SolveMPCG(x, viewA, b, S, z, sx0, I); - - // Velocity update - sv = sv + x; - sx = sx + sy; - - // Copy state buffers back to the particle - for (u32 i = 0; i < m_particleCount; ++i) { - b3Particle* p = m_particles[i]; - - p->m_x = x[i]; - p->m_position = sx[i]; - p->m_velocity = sv[i]; - } - } - - // Solve constraints - b3ClothContactSolverDef contactSolverDef; - contactSolverDef.allocator = m_allocator; - contactSolverDef.positions = m_solverData.x; - contactSolverDef.velocities = m_solverData.v; - contactSolverDef.bodyContactCount = m_bodyContactCount; - contactSolverDef.bodyContacts = m_bodyContacts; - contactSolverDef.triangleContactCount = m_triangleContactCount; - contactSolverDef.triangleContacts = m_triangleContacts; - - b3ClothContactSolver contactSolver(contactSolverDef); - - { - contactSolver.InitializeBodyContactConstraints(); - contactSolver.InitializeTriangleContactConstraints(); - } - - { - contactSolver.WarmStartBodyContactConstraints(); - contactSolver.WarmStartTriangleContactConstraints(); - } - - { - for (u32 i = 0; i < velocityIterations; ++i) - { - contactSolver.SolveBodyContactVelocityConstraints(); - contactSolver.SolveTriangleContactVelocityConstraints(); - } - } - - { - contactSolver.StoreImpulses(); - } - - // Integrate positions - sx = sx + h * sv; - - // Solve position constraints - { - bool positionSolved = false; - for (u32 i = 0; i < positionIterations; ++i) - { - bool bodyContactsSolved = contactSolver.SolveBodyContactPositionConstraints(); - bool triangleContactsSolved = contactSolver.SolveTriangleContactPositionConstraints(); - - if (bodyContactsSolved && triangleContactsSolved) + // Solve velocity constraints + for (u32 i = 0; i < velocityIterations; ++i) { - // Early out if the position errors are small. - positionSolved = true; - break; + contactSolver.SolveBodyContactVelocityConstraints(); + contactSolver.SolveTriangleContactVelocityConstraints(); } } - } - // Synchronize bodies - for (u32 i = 0; i < m_bodyContactCount; ++i) - { - b3Body* body = m_bodyContacts[i]->s2->GetBody(); + { + // Cache impulses for warm-starting + contactSolver.StoreImpulses(); + } - body->SynchronizeTransform(); + // Integrate positions + float32 h = dt; + for (u32 i = 0; i < m_particleCount; ++i) + { + positions[i] += h * velocities[i]; + } - body->m_worldInvI = b3RotateToFrame(body->m_invI, body->m_xf.rotation); + // Solve position constraints + { + bool positionSolved = false; + for (u32 i = 0; i < positionIterations; ++i) + { + bool bodyContactsSolved = contactSolver.SolveBodyContactPositionConstraints(); + bool triangleContactsSolved = contactSolver.SolveTriangleContactPositionConstraints(); - body->SynchronizeShapes(); + if (bodyContactsSolved && triangleContactsSolved) + { + // Early out if the position errors are small. + positionSolved = true; + break; + } + } + } + + // Synchronize bodies + for (u32 i = 0; i < m_bodyContactCount; ++i) + { + b3Body* body = m_bodyContacts[i]->s2->GetBody(); + + body->SynchronizeTransform(); + + body->m_worldInvI = b3RotateToFrame(body->m_invI, body->m_xf.rotation); + + body->SynchronizeShapes(); + } } // Copy state buffers back to the particles @@ -422,7 +182,10 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterati { b3Particle* p = m_particles[i]; - p->m_position = sx[i]; - p->m_velocity = sv[i]; + p->m_position = positions[i]; + p->m_velocity = velocities[i]; } -} + + m_allocator->Free(velocities); + m_allocator->Free(positions); +} \ No newline at end of file diff --git a/src/bounce/cloth/spring_force.cpp b/src/bounce/cloth/spring_force.cpp index 723b1ac..e2d0265 100644 --- a/src/bounce/cloth/spring_force.cpp +++ b/src/bounce/cloth/spring_force.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include @@ -50,7 +50,7 @@ b3SpringForce::~b3SpringForce() } -void b3SpringForce::Apply(const b3ClothSolverData* data) +void b3SpringForce::Apply(const b3ClothForceSolverData* data) { b3DenseVec3& x = *data->x; b3DenseVec3& v = *data->v; From 02872af0dbeee6007b3cd4747dc4a0ce6ff2f64a Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 19 Jun 2019 14:08:44 -0300 Subject: [PATCH 166/198] Include cloth triangle inside Bounce headers --- include/bounce/bounce.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/bounce/bounce.h b/include/bounce/bounce.h index a36b9bb..e3f4335 100644 --- a/include/bounce/bounce.h +++ b/include/bounce/bounce.h @@ -66,6 +66,7 @@ #include #include #include +#include #include #include From 66ec7309e5598fc60a500ab4e17b62a0ef8e7249 Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 19 Jun 2019 16:07:04 -0300 Subject: [PATCH 167/198] Also split soft body solver in two --- include/bounce/softbody/softbody.h | 1 + .../bounce/softbody/softbody_contact_solver.h | 14 +- .../bounce/softbody/softbody_force_solver.h | 53 ++ include/bounce/softbody/softbody_node.h | 1 + include/bounce/softbody/softbody_solver.h | 5 + src/bounce/softbody/softbody.cpp | 9 + .../softbody/softbody_contact_solver.cpp | 37 +- src/bounce/softbody/softbody_force_solver.cpp | 396 +++++++++++++++ src/bounce/softbody/softbody_solver.cpp | 477 +++--------------- 9 files changed, 559 insertions(+), 434 deletions(-) create mode 100644 include/bounce/softbody/softbody_force_solver.h create mode 100644 src/bounce/softbody/softbody_force_solver.cpp diff --git a/include/bounce/softbody/softbody.h b/include/bounce/softbody/softbody.h index 944fa9c..08ae969 100644 --- a/include/bounce/softbody/softbody.h +++ b/include/bounce/softbody/softbody.h @@ -143,6 +143,7 @@ public: private: friend class b3SoftBodyNode; friend class b3SoftBodySolver; + friend class b3SoftBodyForceSolver; // Compute mass of each node. void ComputeMass(); diff --git a/include/bounce/softbody/softbody_contact_solver.h b/include/bounce/softbody/softbody_contact_solver.h index dea16cf..4187ab4 100644 --- a/include/bounce/softbody/softbody_contact_solver.h +++ b/include/bounce/softbody/softbody_contact_solver.h @@ -84,10 +84,11 @@ struct b3SoftBodyContactSolverDef { b3StackAllocator* allocator; - b3DenseVec3* positions; - b3DenseVec3* velocities; + b3Vec3* positions; + b3Vec3* velocities; - u32 bodyContactCapacity; + u32 bodyContactCount; + b3NodeBodyContact** bodyContacts; }; inline float32 b3MixFriction(float32 u1, float32 u2) @@ -101,8 +102,6 @@ public: b3SoftBodyContactSolver(const b3SoftBodyContactSolverDef& def); ~b3SoftBodyContactSolver(); - void Add(b3NodeBodyContact* c); - void InitializeBodyContactConstraints(); void WarmStart(); @@ -115,10 +114,9 @@ public: protected: b3StackAllocator* m_allocator; - b3DenseVec3* m_positions; - b3DenseVec3* m_velocities; + b3Vec3* m_positions; + b3Vec3* m_velocities; - u32 m_bodyContactCapacity; u32 m_bodyContactCount; b3NodeBodyContact** m_bodyContacts; diff --git a/include/bounce/softbody/softbody_force_solver.h b/include/bounce/softbody/softbody_force_solver.h new file mode 100644 index 0000000..1c2f35b --- /dev/null +++ b/include/bounce/softbody/softbody_force_solver.h @@ -0,0 +1,53 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SOFT_BODY_FORCE_SOLVER_H +#define B3_SOFT_BODY_FORCE_SOLVER_H + +#include +#include + +class b3StackAllocator; + +class b3SoftBody; +class b3SoftBodyMesh; + +struct b3SoftBodyNode; +struct b3SoftBodyElement; + +struct b3SoftBodyForceSolverDef +{ + b3SoftBody* body; +}; + +class b3SoftBodyForceSolver +{ +public: + b3SoftBodyForceSolver(const b3SoftBodyForceSolverDef& def); + ~b3SoftBodyForceSolver(); + + void Solve(float32 dt, const b3Vec3& gravity); +private: + b3SoftBody* m_body; + b3StackAllocator* m_allocator; + const b3SoftBodyMesh* m_mesh; + b3SoftBodyNode* m_nodes; + b3SoftBodyElement* m_elements; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/softbody/softbody_node.h b/include/bounce/softbody/softbody_node.h index 87d039a..604e70b 100644 --- a/include/bounce/softbody/softbody_node.h +++ b/include/bounce/softbody/softbody_node.h @@ -118,6 +118,7 @@ public: private: friend class b3SoftBody; friend class b3SoftBodySolver; + friend class b3SoftBodyForceSolver; friend class b3SoftBodyContactSolver; b3SoftBodyNode() { } diff --git a/include/bounce/softbody/softbody_solver.h b/include/bounce/softbody/softbody_solver.h index a38491b..e0a3396 100644 --- a/include/bounce/softbody/softbody_solver.h +++ b/include/bounce/softbody/softbody_solver.h @@ -43,6 +43,8 @@ public: b3SoftBodySolver(const b3SoftBodySolverDef& def); ~b3SoftBodySolver(); + void Add(b3NodeBodyContact* c); + void Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations); private: b3SoftBody* m_body; @@ -50,6 +52,9 @@ private: const b3SoftBodyMesh* m_mesh; b3SoftBodyNode* m_nodes; b3SoftBodyElement* m_elements; + u32 m_bodyContactCapacity; + u32 m_bodyContactCount; + b3NodeBodyContact** m_bodyContacts; }; #endif \ No newline at end of file diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index 4a55a2d..e0267ce 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -832,6 +832,15 @@ void b3SoftBody::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations b3SoftBodySolver solver(def); + // Push the active contacts + for (u32 i = 0; i < m_mesh->vertexCount; ++i) + { + if (m_nodes[i].m_bodyContact.active) + { + solver.Add(&m_nodes[i].m_bodyContact); + } + } + solver.Solve(dt, gravity, velocityIterations, positionIterations); } diff --git a/src/bounce/softbody/softbody_contact_solver.cpp b/src/bounce/softbody/softbody_contact_solver.cpp index c079a66..f680d11 100644 --- a/src/bounce/softbody/softbody_contact_solver.cpp +++ b/src/bounce/softbody/softbody_contact_solver.cpp @@ -18,10 +18,9 @@ #include #include -#include -#include #include #include +#include b3SoftBodyContactSolver::b3SoftBodyContactSolver(const b3SoftBodyContactSolverDef& def) { @@ -30,30 +29,20 @@ b3SoftBodyContactSolver::b3SoftBodyContactSolver(const b3SoftBodyContactSolverDe m_positions = def.positions; m_velocities = def.velocities; - m_bodyContactCapacity = def.bodyContactCapacity; - m_bodyContactCount = 0; - m_bodyContacts = (b3NodeBodyContact**)m_allocator->Allocate(m_bodyContactCapacity * sizeof(b3NodeBodyContact*)); + m_bodyContactCount = def.bodyContactCount; + m_bodyContacts = def.bodyContacts; } b3SoftBodyContactSolver::~b3SoftBodyContactSolver() { m_allocator->Free(m_bodyPositionConstraints); m_allocator->Free(m_bodyVelocityConstraints); - m_allocator->Free(m_bodyContacts); -} - -void b3SoftBodyContactSolver::Add(b3NodeBodyContact* c) -{ - m_bodyContacts[m_bodyContactCount++] = c; } void b3SoftBodyContactSolver::InitializeBodyContactConstraints() { m_bodyVelocityConstraints = (b3SoftBodySolverBodyContactVelocityConstraint*)m_allocator->Allocate(m_bodyContactCount * sizeof(b3SoftBodySolverBodyContactVelocityConstraint)); m_bodyPositionConstraints = (b3SoftBodySolverBodyContactPositionConstraint*)m_allocator->Allocate(m_bodyContactCount * sizeof(b3SoftBodySolverBodyContactPositionConstraint)); - - b3DenseVec3& x = *m_positions; - b3DenseVec3& v = *m_velocities; for (u32 i = 0; i < m_bodyContactCount; ++i) { @@ -107,7 +96,7 @@ void b3SoftBodyContactSolver::InitializeBodyContactConstraints() b3Mat33 iA = vc->invIA; b3Mat33 iB = vc->invIB; - b3Vec3 xA = x[indexA]; + b3Vec3 xA = m_positions[indexA]; b3Vec3 xB = bodyB->m_sweep.worldCenter; b3Quat qA; qA.SetIdentity(); @@ -181,8 +170,6 @@ void b3SoftBodyContactSolver::InitializeBodyContactConstraints() void b3SoftBodyContactSolver::WarmStart() { - b3DenseVec3& v = *m_velocities; - for (u32 i = 0; i < m_bodyContactCount; ++i) { b3SoftBodySolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; @@ -190,7 +177,7 @@ void b3SoftBodyContactSolver::WarmStart() u32 indexA = vc->indexA; b3Body* bodyB = vc->bodyB; - b3Vec3 vA = v[indexA]; + b3Vec3 vA = m_velocities[indexA]; b3Vec3 vB = bodyB->GetLinearVelocity(); b3Vec3 wA; wA.SetZero(); @@ -219,7 +206,7 @@ void b3SoftBodyContactSolver::WarmStart() vB += mB * (P1 + P2); wB += iB * b3Cross(vc->rB, P1 + P2); - v[indexA] = vA; + m_velocities[indexA] = vA; bodyB->SetLinearVelocity(vB); bodyB->SetAngularVelocity(wB); @@ -228,8 +215,6 @@ void b3SoftBodyContactSolver::WarmStart() void b3SoftBodyContactSolver::SolveBodyContactVelocityConstraints() { - b3DenseVec3& v = *m_velocities; - for (u32 i = 0; i < m_bodyContactCount; ++i) { b3SoftBodySolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; @@ -237,7 +222,7 @@ void b3SoftBodyContactSolver::SolveBodyContactVelocityConstraints() u32 indexA = vc->indexA; b3Body* bodyB = vc->bodyB; - b3Vec3 vA = v[indexA]; + b3Vec3 vA = m_velocities[indexA]; b3Vec3 vB = bodyB->GetLinearVelocity(); b3Vec3 wA; wA.SetZero(); @@ -307,7 +292,7 @@ void b3SoftBodyContactSolver::SolveBodyContactVelocityConstraints() wB += iB * b3Cross(rB, P); } - v[indexA] = vA; + m_velocities[indexA] = vA; bodyB->SetLinearVelocity(vB); bodyB->SetAngularVelocity(wB); @@ -353,8 +338,6 @@ struct b3SoftBodySolverBodyContactSolverPoint bool b3SoftBodyContactSolver::SolveBodyContactPositionConstraints() { - b3DenseVec3& x = *m_positions; - float32 minSeparation = 0.0f; for (u32 i = 0; i < m_bodyContactCount; ++i) @@ -371,7 +354,7 @@ bool b3SoftBodyContactSolver::SolveBodyContactPositionConstraints() b3Mat33 iB = pc->invIB; b3Vec3 localCenterB = pc->localCenterB; - b3Vec3 cA = x[indexA]; + b3Vec3 cA = m_positions[indexA]; b3Quat qA; qA.SetIdentity(); b3Vec3 cB = bodyB->m_sweep.worldCenter; @@ -419,7 +402,7 @@ bool b3SoftBodyContactSolver::SolveBodyContactPositionConstraints() qB += b3Derivative(qB, iB * b3Cross(rB, P)); qB.Normalize(); - x[indexA] = cA; + m_positions[indexA] = cA; bodyB->m_sweep.worldCenter = cB; bodyB->m_sweep.orientation = qB; diff --git a/src/bounce/softbody/softbody_force_solver.cpp b/src/bounce/softbody/softbody_force_solver.cpp new file mode 100644 index 0000000..e35307d --- /dev/null +++ b/src/bounce/softbody/softbody_force_solver.cpp @@ -0,0 +1,396 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include +#include + +// This work is based on the paper "Interactive Virtual Materials" written by +// Matthias Mueller Fischer +// The paper is available here: +// http://matthias-mueller-fischer.ch/publications/GI2004.pdf + +// In order to support velocity constraints on node velocities +// we solve Ax = b using a Modified Preconditioned Conjugate Gradient (MPCG) algorithm. + +// Number of MPCG iterations, a value that is normally small when small time steps are taken. +u32 b3_softBodySolverIterations = 0; + +// Enables the stiffness warping solver. +bool b3_enableStiffnessWarping = true; + +b3SoftBodyForceSolver::b3SoftBodyForceSolver(const b3SoftBodyForceSolverDef& def) +{ + m_body = def.body; + m_allocator = &m_body->m_stackAllocator; + m_mesh = m_body->m_mesh; + m_nodes = m_body->m_nodes; + m_elements = m_body->m_elements; +} + +b3SoftBodyForceSolver::~b3SoftBodyForceSolver() +{ + +} + +// Extract rotation from deformation +// https://animation.rwth-aachen.de/media/papers/2016-MIG-StableRotation.pdf +static void b3ExtractRotation(b3Mat33& out, b3Quat& q, const b3Mat33& A, u32 maxIterations = 20) +{ + for (u32 iteration = 0; iteration < maxIterations; ++iteration) + { + b3Mat33 R = b3QuatMat33(q); + + float32 s = b3Abs(b3Dot(R.x, A.x) + b3Dot(R.y, A.y) + b3Dot(R.z, A.z)); + + if (s == 0.0f) + { + break; + } + + float32 inv_s = 1.0f / s + 1.0e-9f; + + b3Vec3 v = b3Cross(R.x, A.x) + b3Cross(R.y, A.y) + b3Cross(R.z, A.z); + + b3Vec3 omega = inv_s * v; + + float32 w = b3Length(omega); + + if (w < 1.0e-9f) + { + break; + } + + b3Quat omega_q(omega / w, w); + + q = omega_q * q; + q.Normalize(); + } + + out = b3QuatMat33(q); +} + +// Solve A * x = b +static void b3SolveMPCG(b3DenseVec3& x, + const b3SparseMat33View& A, const b3DenseVec3& b, + const b3DenseVec3& z, const b3DiagMat33& S, u32 maxIterations = 20) +{ + B3_PROFILE("Soft Body Solve MPCG"); + + // Jacobi preconditioner + // P = diag(A) + b3DiagMat33 P(A.rowCount); + b3DiagMat33 invP(A.rowCount); + for (u32 i = 0; i < A.rowCount; ++i) + { + b3Mat33 a = A(i, i); + + // Sylvester Criterion to ensure PD-ness + B3_ASSERT(b3Det(a.x, a.y, a.z) > 0.0f); + + B3_ASSERT(a.x.x > 0.0f); + float32 xx = 1.0f / a.x.x; + + B3_ASSERT(a.y.y > 0.0f); + float32 yy = 1.0f / a.y.y; + + B3_ASSERT(a.z.z > 0.0f); + float32 zz = 1.0f / a.z.z; + + P[i] = b3Diagonal(a.x.x, a.y.y, a.z.z); + invP[i] = b3Diagonal(xx, yy, zz); + } + + x = z; + + float32 delta_0 = b3Dot(S * b, P * (S * b)); + + b3DenseVec3 r = S * (b - A * x); + b3DenseVec3 c = S * (invP * r); + + float32 delta_new = b3Dot(r, c); + + u32 iteration = 0; + for (;;) + { + if (iteration == maxIterations) + { + break; + } + + if (delta_new <= B3_EPSILON * B3_EPSILON * delta_0) + { + break; + } + + b3DenseVec3 q = S * (A * c); + + float32 alpha = delta_new / b3Dot(c, q); + + x = x + alpha * c; + r = r - alpha * q; + + b3DenseVec3 s = invP * r; + + float32 delta_old = delta_new; + + delta_new = b3Dot(r, s); + + float32 beta = delta_new / delta_old; + + c = S * (s + beta * c); + + ++iteration; + } + + b3_softBodySolverIterations = iteration; +} + +// C = A * B +static B3_FORCE_INLINE void b3Mul(float32* C, float32* A, u32 AM, u32 AN, float32* B, u32 BM, u32 BN) +{ + B3_ASSERT(AN == BM); + + for (u32 i = 0; i < AM; ++i) + { + for (u32 j = 0; j < BN; ++j) + { + C[i + AM * j] = 0.0f; + + for (u32 k = 0; k < AN; ++k) + { + C[i + AM * j] += A[i + AM * k] * B[k + BM * j]; + } + } + } +} + +// ||v|| +static B3_FORCE_INLINE float32 b3Length(float32* v, u32 n) +{ + float32 result = 0.0f; + for (u32 i = 0; i < n; ++i) + { + result += v[i] * v[i]; + } + return b3Sqrt(result); +} + +void b3SoftBodyForceSolver::Solve(float32 dt, const b3Vec3& gravity) +{ + float32 h = dt; + float32 inv_h = 1.0f / h; + + b3SparseMat33 M(m_mesh->vertexCount); + b3SparseMat33 C(m_mesh->vertexCount); + b3SparseMat33 K(m_mesh->vertexCount); + b3DenseVec3 x(m_mesh->vertexCount); + b3DenseVec3 p(m_mesh->vertexCount); + b3DenseVec3 v(m_mesh->vertexCount); + b3DenseVec3 fe(m_mesh->vertexCount); + b3DenseVec3 f0(m_mesh->vertexCount); + b3DenseVec3 f_plastic(m_mesh->vertexCount); + b3DenseVec3 z(m_mesh->vertexCount); + b3DiagMat33 S(m_mesh->vertexCount); + + for (u32 i = 0; i < m_mesh->vertexCount; ++i) + { + b3SoftBodyNode* n = m_nodes + i; + + M(i, i) = b3Diagonal(n->m_mass); + + // Rayleigh damping + // C = alpha * M + beta * K + // Here the stiffness coefficient beta is zero + C(i, i) = b3Diagonal(n->m_massDamping * n->m_mass); + + x[i] = m_mesh->vertices[i]; + p[i] = n->m_position; + v[i] = n->m_velocity; + fe[i] = n->m_force; + z[i] = n->m_velocity; + + // Apply gravity + if (n->m_type == e_dynamicSoftBodyNode) + { + fe[i] += n->m_mass * gravity; + S[i].SetIdentity(); + } + else + { + S[i].SetZero(); + } + } + + // Element assembly + f0.SetZero(); + f_plastic.SetZero(); + + for (u32 ei = 0; ei < m_mesh->tetrahedronCount; ++ei) + { + b3SoftBodyMeshTetrahedron* mt = m_mesh->tetrahedrons + ei; + b3SoftBodyElement* e = m_elements + ei; + + b3Mat33* Ke = e->K; + + float32* Be = e->B; + float32* Pe = e->P; + float32* epsilon_plastic = e->epsilon_plastic; + + u32 v1 = mt->v1; + u32 v2 = mt->v2; + u32 v3 = mt->v3; + u32 v4 = mt->v4; + + b3Vec3 p1 = p[v1]; + b3Vec3 p2 = p[v2]; + b3Vec3 p3 = p[v3]; + b3Vec3 p4 = p[v4]; + + b3Mat33 R; + if (b3_enableStiffnessWarping) + { + b3Vec3 e1 = p2 - p1; + b3Vec3 e2 = p3 - p1; + b3Vec3 e3 = p4 - p1; + + b3Mat33 E(e1, e2, e3); + + b3Mat33 A = E * e->invE; + + b3ExtractRotation(R, e->q, A); + } + else + { + R.SetIdentity(); + } + + b3Mat33 RT = b3Transpose(R); + + u32 vs[4] = { v1, v2, v3, v4 }; + + for (u32 i = 0; i < 4; ++i) + { + u32 vi = vs[i]; + + for (u32 j = 0; j < 4; ++j) + { + u32 vj = vs[j]; + + K(vi, vj) += R * Ke[i + 4 * j] * RT; + } + } + + // Elasticity + b3Vec3 x1 = x[v1]; + b3Vec3 x2 = x[v2]; + b3Vec3 x3 = x[v3]; + b3Vec3 x4 = x[v4]; + + b3Vec3 xs[4] = { x1, x2, x3, x4 }; + + b3Vec3 f0s[4]; + + for (u32 i = 0; i < 4; ++i) + { + f0s[i].SetZero(); + + for (u32 j = 0; j < 4; ++j) + { + f0s[i] += R * Ke[i + 4 * j] * xs[j]; + } + } + + f0[v1] += f0s[0]; + f0[v2] += f0s[1]; + f0[v3] += f0s[2]; + f0[v4] += f0s[3]; + + // Plasticity + b3Vec3 ps[4] = { p1, p2, p3, p4 }; + + b3Vec3 RT_x_x0[4]; + for (u32 i = 0; i < 4; ++i) + { + RT_x_x0[i] = RT * ps[i] - xs[i]; + } + + // 6 x 1 + float32 epsilon_total[6]; + b3Mul(epsilon_total, Be, 6, 12, &RT_x_x0[0].x, 12, 1); + + // 6 x 1 + float32 epsilon_elastic[6]; + for (u32 i = 0; i < 6; ++i) + { + epsilon_elastic[i] = epsilon_total[i] - epsilon_plastic[i]; + } + + float32 len_epsilon_elastic = b3Length(epsilon_elastic, 6); + if (len_epsilon_elastic > m_body->m_c_yield) + { + float32 amount = h * b3Min(m_body->m_c_creep, inv_h); + for (u32 i = 0; i < 6; ++i) + { + epsilon_plastic[i] += amount * epsilon_elastic[i]; + } + } + + float32 len_epsilon_plastic = b3Length(epsilon_plastic, 6); + if (len_epsilon_plastic > m_body->m_c_max) + { + float32 scale = m_body->m_c_max / len_epsilon_plastic; + for (u32 i = 0; i < 6; ++i) + { + epsilon_plastic[i] *= scale; + } + } + + b3Vec3 fs_plastic[4]; + b3Mul(&fs_plastic[0].x, Pe, 12, 6, epsilon_plastic, 6, 1); + for (u32 i = 0; i < 4; ++i) + { + fs_plastic[i] = R * fs_plastic[i]; + } + + f_plastic[v1] += fs_plastic[0]; + f_plastic[v2] += fs_plastic[1]; + f_plastic[v3] += fs_plastic[2]; + f_plastic[v4] += fs_plastic[3]; + } + + f0 = -f0; + + b3SparseMat33 A = M + h * C + h * h * K; + + b3SparseMat33View viewA(A); + + b3DenseVec3 b = M * v - h * (K * p + f0 - (f_plastic + fe)); + + b3DenseVec3 sx(m_mesh->vertexCount); + b3SolveMPCG(sx, viewA, b, z, S); + + // Copy velocity back to the particle + for (u32 i = 0; i < m_mesh->vertexCount; ++i) + { + m_nodes[i].m_velocity = sx[i]; + } +} \ No newline at end of file diff --git a/src/bounce/softbody/softbody_solver.cpp b/src/bounce/softbody/softbody_solver.cpp index 0eaff88..54c369d 100644 --- a/src/bounce/softbody/softbody_solver.cpp +++ b/src/bounce/softbody/softbody_solver.cpp @@ -20,28 +20,11 @@ #include #include #include +#include #include - #include #include -#include -#include - -// This work is based on the paper "Interactive Virtual Materials" written by -// Matthias Mueller Fischer -// The paper is available here: -// http://matthias-mueller-fischer.ch/publications/GI2004.pdf - -// In order to support velocity constraints on node velocities -// we solve Ax = b using a Modified Preconditioned Conjugate Gradient (MPCG) algorithm. - -// Number of MPCG iterations, a value that is normally small when small time steps are taken. -u32 b3_softBodySolverIterations = 0; - -// Enables the stiffness warping solver. -bool b3_enableStiffnessWarping = true; - b3SoftBodySolver::b3SoftBodySolver(const b3SoftBodySolverDef& def) { m_body = def.body; @@ -49,432 +32,128 @@ b3SoftBodySolver::b3SoftBodySolver(const b3SoftBodySolverDef& def) m_mesh = m_body->m_mesh; m_nodes = m_body->m_nodes; m_elements = m_body->m_elements; + m_bodyContactCapacity = m_mesh->vertexCount; + m_bodyContactCount = 0; + m_bodyContacts = (b3NodeBodyContact**)m_allocator->Allocate(m_bodyContactCapacity * sizeof(b3NodeBodyContact*)); } b3SoftBodySolver::~b3SoftBodySolver() { - + m_allocator->Free(m_bodyContacts); } -// https://animation.rwth-aachen.de/media/papers/2016-MIG-StableRotation.pdf -static void b3ExtractRotation(b3Mat33& out, b3Quat& q, const b3Mat33& A, u32 maxIterations = 20) +void b3SoftBodySolver::Add(b3NodeBodyContact* c) { - for (u32 iteration = 0; iteration < maxIterations; ++iteration) - { - b3Mat33 R = b3QuatMat33(q); - - float32 s = b3Abs(b3Dot(R.x, A.x) + b3Dot(R.y, A.y) + b3Dot(R.z, A.z)); - - if (s == 0.0f) - { - break; - } - - float32 inv_s = 1.0f / s + 1.0e-9f; - - b3Vec3 v = b3Cross(R.x, A.x) + b3Cross(R.y, A.y) + b3Cross(R.z, A.z); - - b3Vec3 omega = inv_s * v; - - float32 w = b3Length(omega); - - if (w < 1.0e-9f) - { - break; - } - - b3Quat omega_q(omega / w, w); - - q = omega_q * q; - q.Normalize(); - } - - out = b3QuatMat33(q); -} - -// -static void b3SolveMPCG(b3DenseVec3& x, - const b3SparseMat33View& A, const b3DenseVec3& b, - const b3DenseVec3& z, const b3DiagMat33& S, u32 maxIterations = 20) -{ - B3_PROFILE("Soft Body Solve MPCG"); - - // Jacobi preconditioner - // P = diag(A) - b3DiagMat33 P(A.rowCount); - b3DiagMat33 invP(A.rowCount); - for (u32 i = 0; i < A.rowCount; ++i) - { - b3Mat33 a = A(i, i); - - // Sylvester Criterion to ensure PD-ness - B3_ASSERT(b3Det(a.x, a.y, a.z) > 0.0f); - - B3_ASSERT(a.x.x > 0.0f); - float32 xx = 1.0f / a.x.x; - - B3_ASSERT(a.y.y > 0.0f); - float32 yy = 1.0f / a.y.y; - - B3_ASSERT(a.z.z > 0.0f); - float32 zz = 1.0f / a.z.z; - - P[i] = b3Diagonal(a.x.x, a.y.y, a.z.z); - invP[i] = b3Diagonal(xx, yy, zz); - } - - x = z; - - float32 delta_0 = b3Dot(S * b, P * (S * b)); - - b3DenseVec3 r = S * (b - A * x); - b3DenseVec3 c = S * (invP * r); - - float32 delta_new = b3Dot(r, c); - - u32 iteration = 0; - for (;;) - { - if (iteration == maxIterations) - { - break; - } - - if (delta_new <= B3_EPSILON * B3_EPSILON * delta_0) - { - break; - } - - b3DenseVec3 q = S * (A * c); - - float32 alpha = delta_new / b3Dot(c, q); - - x = x + alpha * c; - r = r - alpha * q; - - b3DenseVec3 s = invP * r; - - float32 delta_old = delta_new; - - delta_new = b3Dot(r, s); - - float32 beta = delta_new / delta_old; - - c = S * (s + beta * c); - - ++iteration; - } - - b3_softBodySolverIterations = iteration; -} - -static B3_FORCE_INLINE void b3Mul(float32* C, float32* A, u32 AM, u32 AN, float32* B, u32 BM, u32 BN) -{ - B3_ASSERT(AN == BM); - - for (u32 i = 0; i < AM; ++i) - { - for (u32 j = 0; j < BN; ++j) - { - C[i + AM * j] = 0.0f; - - for (u32 k = 0; k < AN; ++k) - { - C[i + AM * j] += A[i + AM * k] * B[k + BM * j]; - } - } - } -} - -static B3_FORCE_INLINE float32 b3Length(float32* a, u32 an) -{ - float32 result = 0.0f; - for (u32 i = 0; i < an; ++i) - { - result += a[i] * a[i]; - } - return b3Sqrt(result); + m_bodyContacts[m_bodyContactCount++] = c; } void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations) { - float32 h = dt; - float32 inv_h = 1.0f / h; + { + // Solve internal dynamics + b3SoftBodyForceSolverDef forceSolverDef; + forceSolverDef.body = m_body; - b3SparseMat33 M(m_mesh->vertexCount); - b3SparseMat33 C(m_mesh->vertexCount); - b3DenseVec3 x(m_mesh->vertexCount); - b3DenseVec3 p(m_mesh->vertexCount); - b3DenseVec3 v(m_mesh->vertexCount); - b3DenseVec3 fe(m_mesh->vertexCount); - b3DenseVec3 z(m_mesh->vertexCount); - b3DiagMat33 S(m_mesh->vertexCount); + b3SoftBodyForceSolver forceSolver(forceSolverDef); + + forceSolver.Solve(dt, gravity); + } + + // Copy node state to state buffer + b3Vec3* positions = (b3Vec3*)m_allocator->Allocate(m_mesh->vertexCount * sizeof(b3Vec3)); + b3Vec3* velocities = (b3Vec3*)m_allocator->Allocate(m_mesh->vertexCount * sizeof(b3Vec3)); for (u32 i = 0; i < m_mesh->vertexCount; ++i) { - b3SoftBodyNode* n = m_nodes + i; - - M(i, i) = b3Diagonal(n->m_mass); - - // Rayleigh damping - // C = alpha * M + beta * K - // Here the stiffness coefficient beta is zero - C(i, i) = b3Diagonal(n->m_massDamping * n->m_mass); - - x[i] = m_mesh->vertices[i]; - p[i] = n->m_position; - v[i] = n->m_velocity; - fe[i] = n->m_force; - z[i] = n->m_velocity; - - // Apply gravity - if (n->m_type == e_dynamicSoftBodyNode) - { - fe[i] += n->m_mass * gravity; - S[i].SetIdentity(); - } - else - { - S[i].SetZero(); - } + positions[i] = m_nodes[i].m_position; + velocities[i] = m_nodes[i].m_velocity; } - // Element assembly - b3SparseMat33 K(m_mesh->vertexCount); - - b3DenseVec3 f0(m_mesh->vertexCount); - f0.SetZero(); - - b3DenseVec3 f_plastic(m_mesh->vertexCount); - f_plastic.SetZero(); - - for (u32 ei = 0; ei < m_mesh->tetrahedronCount; ++ei) { - b3SoftBodyMeshTetrahedron* mt = m_mesh->tetrahedrons + ei; - b3SoftBodyElement* e = m_elements + ei; + // Solve constraints + b3SoftBodyContactSolverDef contactSolverDef; + contactSolverDef.allocator = m_allocator; + contactSolverDef.positions = positions; + contactSolverDef.velocities = velocities; + contactSolverDef.bodyContactCount = m_bodyContactCount; + contactSolverDef.bodyContacts = m_bodyContacts; - b3Mat33* Ke = e->K; + b3SoftBodyContactSolver contactSolver(contactSolverDef); - float32* Be = e->B; - float32* Pe = e->P; - float32* epsilon_plastic = e->epsilon_plastic; - - u32 v1 = mt->v1; - u32 v2 = mt->v2; - u32 v3 = mt->v3; - u32 v4 = mt->v4; - - b3Vec3 p1 = p[v1]; - b3Vec3 p2 = p[v2]; - b3Vec3 p3 = p[v3]; - b3Vec3 p4 = p[v4]; - - b3Mat33 R; - if (b3_enableStiffnessWarping) { - b3Vec3 e1 = p2 - p1; - b3Vec3 e2 = p3 - p1; - b3Vec3 e3 = p4 - p1; - - b3Mat33 E(e1, e2, e3); - - b3Mat33 A = E * e->invE; - - b3ExtractRotation(R, e->q, A); - } - else - { - R.SetIdentity(); + // Inititalize constraints + contactSolver.InitializeBodyContactConstraints(); } - b3Mat33 RT = b3Transpose(R); - - u32 vs[4] = { v1, v2, v3, v4 }; - - for (u32 i = 0; i < 4; ++i) { - u32 vi = vs[i]; + // Warm-start velocity constraint solver + contactSolver.WarmStart(); + } - for (u32 j = 0; j < 4; ++j) + { + // Solve velocity constraints + for (u32 i = 0; i < velocityIterations; ++i) { - u32 vj = vs[j]; - - K(vi, vj) += R * Ke[i + 4 * j] * RT; + contactSolver.SolveBodyContactVelocityConstraints(); } } - // Elasticity - b3Vec3 x1 = x[v1]; - b3Vec3 x2 = x[v2]; - b3Vec3 x3 = x[v3]; - b3Vec3 x4 = x[v4]; - - b3Vec3 xs[4] = { x1, x2, x3, x4 }; - - b3Vec3 f0s[4]; - - for (u32 i = 0; i < 4; ++i) { - f0s[i].SetZero(); + // Cache impulses for warm-starting + contactSolver.StoreImpulses(); + } - for (u32 j = 0; j < 4; ++j) + // Integrate velocities + float32 h = dt; + for (u32 i = 0; i < m_mesh->vertexCount; ++i) + { + positions[i] += h * velocities[i]; + } + + // Solve position constraints + { + bool positionSolved = false; + for (u32 i = 0; i < positionIterations; ++i) { - f0s[i] += R * Ke[i + 4 * j] * xs[j]; + bool bodyContactsSolved = contactSolver.SolveBodyContactPositionConstraints(); + + if (bodyContactsSolved) + { + positionSolved = true; + break; + } } } - f0[v1] += f0s[0]; - f0[v2] += f0s[1]; - f0[v3] += f0s[2]; - f0[v4] += f0s[3]; - - // Plasticity - b3Vec3 ps[4] = { p1, p2, p3, p4 }; - - b3Vec3 RT_x_x0[4]; - for (u32 i = 0; i < 4; ++i) + // Synchronize bodies + for (u32 i = 0; i < m_mesh->vertexCount; ++i) { - RT_x_x0[i] = RT * ps[i] - xs[i]; - } + b3SoftBodyNode* n = m_nodes + i; - // 6 x 1 - float32 epsilon_total[6]; - b3Mul(epsilon_total, Be, 6, 12, &RT_x_x0[0].x, 12, 1); + b3NodeBodyContact* c = &n->m_bodyContact; - // 6 x 1 - float32 epsilon_elastic[6]; - for (u32 i = 0; i < 6; ++i) - { - epsilon_elastic[i] = epsilon_total[i] - epsilon_plastic[i]; - } - - float32 len_epsilon_elastic = b3Length(epsilon_elastic, 6); - if (len_epsilon_elastic > m_body->m_c_yield) - { - float32 amount = h * b3Min(m_body->m_c_creep, inv_h); - for (u32 i = 0; i < 6; ++i) + if (c->active == false) { - epsilon_plastic[i] += amount * epsilon_elastic[i]; + continue; } + + b3Body* body = c->s2->GetBody(); + + body->SynchronizeTransform(); + + body->m_worldInvI = b3RotateToFrame(body->m_invI, body->m_xf.rotation); + + body->SynchronizeShapes(); } - - float32 len_epsilon_plastic = b3Length(epsilon_plastic, 6); - if (len_epsilon_plastic > m_body->m_c_max) - { - float32 scale = m_body->m_c_max / len_epsilon_plastic; - for (u32 i = 0; i < 6; ++i) - { - epsilon_plastic[i] *= scale; - } - } - - b3Vec3 fs_plastic[4]; - b3Mul(&fs_plastic[0].x, Pe, 12, 6, epsilon_plastic, 6, 1); - for (u32 i = 0; i < 4; ++i) - { - fs_plastic[i] = R * fs_plastic[i]; - } - - f_plastic[v1] += fs_plastic[0]; - f_plastic[v2] += fs_plastic[1]; - f_plastic[v3] += fs_plastic[2]; - f_plastic[v4] += fs_plastic[3]; - } - - f0 = -f0; - - b3SparseMat33 A = M + h * C + h * h * K; - - b3SparseMat33View viewA(A); - - b3DenseVec3 b = M * v - h * (K * p + f0 - (f_plastic + fe)); - - // Solve Ax = b - b3DenseVec3 sx(m_mesh->vertexCount); - b3SolveMPCG(sx, viewA, b, z, S); - - // Solve constraints - b3SoftBodyContactSolverDef contactSolverDef; - contactSolverDef.allocator = m_allocator; - contactSolverDef.positions = &p; - contactSolverDef.velocities = &sx; - contactSolverDef.bodyContactCapacity = m_mesh->vertexCount; - - b3SoftBodyContactSolver contactSolver(contactSolverDef); - - for (u32 i = 0; i < m_mesh->vertexCount; ++i) - { - if (m_nodes[i].m_bodyContact.active) - { - contactSolver.Add(&m_nodes[i].m_bodyContact); - } - } - - { - contactSolver.InitializeBodyContactConstraints(); - } - - { - contactSolver.WarmStart(); - } - - // Solve velocity constraints - { - for (u32 i = 0; i < velocityIterations; ++i) - { - contactSolver.SolveBodyContactVelocityConstraints(); - } - } - - { - contactSolver.StoreImpulses(); - } - - // Integrate velocities - p = p + h * sx; - - // Solve position constraints - { - bool positionSolved = false; - for (u32 i = 0; i < positionIterations; ++i) - { - bool bodyContactsSolved = contactSolver.SolveBodyContactPositionConstraints(); - - if (bodyContactsSolved) - { - positionSolved = true; - break; - } - } - } - - // Synchronize bodies - for (u32 i = 0; i < m_mesh->vertexCount; ++i) - { - b3SoftBodyNode* n = m_nodes + i; - - b3NodeBodyContact* c = &n->m_bodyContact; - - if (c->active == false) - { - continue; - } - - b3Body* body = c->s2->GetBody(); - - body->SynchronizeTransform(); - - body->m_worldInvI = b3RotateToFrame(body->m_invI, body->m_xf.rotation); - - body->SynchronizeShapes(); } // Copy state buffers back to the nodes for (u32 i = 0; i < m_mesh->vertexCount; ++i) { - b3SoftBodyNode* n = m_nodes + i; - - n->m_position = p[i]; - n->m_velocity = sx[i]; + m_nodes[i].m_position = positions[i]; + m_nodes[i].m_velocity = velocities[i]; } + + m_allocator->Free(velocities); + m_allocator->Free(positions); } \ No newline at end of file From 7c1a72c5726b650f01a667f89dc5b468a3429701 Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 21 Jun 2019 10:58:04 -0300 Subject: [PATCH 168/198] Add b3GridClothMesh. Set particle friction to global cloth friction. Update the tests. Also simplified some code. --- examples/testbed/framework/test_entries.cpp | 4 +- ...cloth_capsule.h => cloth_self_collision.h} | 45 +++------ examples/testbed/tests/pinned_cloth.h | 22 +---- examples/testbed/tests/table_cloth.h | 33 ++----- examples/testbed/tests/tension_mapping.h | 22 +---- include/bounce/bounce.h | 1 + include/bounce/cloth/grid_cloth_mesh.h | 96 +++++++++++++++++++ include/bounce/collision/shapes/grid_mesh.h | 63 ++++++------ include/bounce/softbody/block_softbody_mesh.h | 2 +- include/bounce/softbody/softbody.h | 2 +- src/bounce/cloth/cloth.cpp | 3 +- 11 files changed, 161 insertions(+), 132 deletions(-) rename examples/testbed/tests/{cloth_capsule.h => cloth_self_collision.h} (75%) create mode 100644 include/bounce/cloth/grid_cloth_mesh.h diff --git a/examples/testbed/framework/test_entries.cpp b/examples/testbed/framework/test_entries.cpp index b2b6596..9224561 100644 --- a/examples/testbed/framework/test_entries.cpp +++ b/examples/testbed/framework/test_entries.cpp @@ -63,7 +63,7 @@ #include #include #include -#include +#include #include #include #include @@ -116,7 +116,7 @@ TestEntry g_tests[] = { "Pinned Cloth", &PinnedCloth::Create }, { "Particle Types", &ParticleTypes::Create }, { "Tension Mapping", &TensionMapping::Create }, - { "Cloth and Capsule", &ClothCapsule::Create }, + { "Cloth Self-Collision", &ClothSelfCollision::Create }, { "Beam", &Beam::Create }, { "Pinned Soft Body", &PinnedSoftBody::Create }, { "Smash Soft Body", &SmashSoftBody::Create }, diff --git a/examples/testbed/tests/cloth_capsule.h b/examples/testbed/tests/cloth_self_collision.h similarity index 75% rename from examples/testbed/tests/cloth_capsule.h rename to examples/testbed/tests/cloth_self_collision.h index 84a4d0e..dfc088e 100644 --- a/examples/testbed/tests/cloth_capsule.h +++ b/examples/testbed/tests/cloth_self_collision.h @@ -16,45 +16,33 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef CLOTH_CAPSULE_H -#define CLOTH_CAPSULE_H +#ifndef CLOTH_SELF_COLLISION_H +#define CLOTH_SELF_COLLISION_H -class ClothCapsule : public Test +class ClothSelfCollision : public Test { public: - ClothCapsule() : m_rectangleGarment(5.0f, 5.0f) + ClothSelfCollision() { - // Generate 2D mesh - m_rectangleGarmentMesh.Set(&m_rectangleGarment, 1.0f); - - // Create 3D mesh - m_rectangleClothMesh.Set(&m_rectangleGarmentMesh); - - // Rotate and translate the mesh - b3Mat33 rotation = b3Mat33RotationX(0.5f * B3_PI); - for (u32 i = 0; i < m_rectangleClothMesh.vertexCount; ++i) + // Translate the mesh + for (u32 i = 0; i < m_clothMesh.vertexCount; ++i) { - m_rectangleClothMesh.vertices[i] = rotation * m_rectangleClothMesh.vertices[i]; - m_rectangleClothMesh.vertices[i].y += 5.0f; + m_clothMesh.vertices[i].y += 5.0f; } // Create cloth b3ClothDef def; - def.mesh = &m_rectangleClothMesh; + def.mesh = &m_clothMesh; def.density = 1.0f; def.structural = 100000.0f; - + def.thickness = 0.2f; + def.friction = 0.3f; + m_cloth = new b3Cloth(def); m_cloth->SetGravity(b3Vec3(0.0f, -9.8f, 0.0f)); m_cloth->SetWorld(&m_world); - for (b3Particle* p = m_cloth->GetParticleList().m_head; p; p = p->GetNext()) - { - p->SetRadius(0.2f); - p->SetFriction(0.2f); - } - { b3BodyDef bd; bd.type = e_staticBody; @@ -76,7 +64,7 @@ public: m_clothDragger = new b3ClothDragger(&m_ray, m_cloth); } - ~ClothCapsule() + ~ClothSelfCollision() { delete m_cloth; delete m_clothDragger; @@ -141,15 +129,12 @@ public: static Test* Create() { - return new ClothCapsule(); + return new ClothSelfCollision(); } - b3RectangleGarment m_rectangleGarment; - b3GarmentMesh m_rectangleGarmentMesh; - b3GarmentClothMesh m_rectangleClothMesh; - + b3GridClothMesh<10, 10> m_clothMesh; b3Cloth* m_cloth; b3ClothDragger* m_clothDragger; - }; +}; #endif \ No newline at end of file diff --git a/examples/testbed/tests/pinned_cloth.h b/examples/testbed/tests/pinned_cloth.h index 023e662..b6c8543 100644 --- a/examples/testbed/tests/pinned_cloth.h +++ b/examples/testbed/tests/pinned_cloth.h @@ -22,24 +22,11 @@ class PinnedCloth : public Test { public: - PinnedCloth() : m_rectangleGarment(5.0f, 5.0f) + PinnedCloth() { - // Generate 2D mesh - m_rectangleGarmentMesh.Set(&m_rectangleGarment, 1.0f); - - // Create 3D mesh - m_rectangleClothMesh.Set(&m_rectangleGarmentMesh); - - // Rotate the mesh - b3Mat33 rotation = b3Mat33RotationX(0.5f * B3_PI); - for (u32 i = 0; i < m_rectangleClothMesh.vertexCount; ++i) - { - m_rectangleClothMesh.vertices[i] = rotation * m_rectangleClothMesh.vertices[i]; - } - // Create cloth b3ClothDef def; - def.mesh = &m_rectangleClothMesh; + def.mesh = &m_clothMesh; def.density = 0.2f; def.structural = 100000.0f; def.damping = 0.0f; @@ -142,10 +129,7 @@ public: return new PinnedCloth(); } - b3RectangleGarment m_rectangleGarment; - b3GarmentMesh m_rectangleGarmentMesh; - b3GarmentClothMesh m_rectangleClothMesh; - + b3GridClothMesh<10, 10> m_clothMesh; b3Cloth* m_cloth; b3ClothDragger* m_clothDragger; }; diff --git a/examples/testbed/tests/table_cloth.h b/examples/testbed/tests/table_cloth.h index 3040ade..f3329bc 100644 --- a/examples/testbed/tests/table_cloth.h +++ b/examples/testbed/tests/table_cloth.h @@ -22,41 +22,28 @@ class TableCloth : public Test { public: - TableCloth() : m_rectangleGarment(5.0f, 5.0f) + TableCloth() { - // Generate 2D mesh - m_rectangleGarmentMesh.Set(&m_rectangleGarment, 1.0f); - - // Create 3D mesh - m_rectangleClothMesh.Set(&m_rectangleGarmentMesh); - - // Rotate the mesh - b3Mat33 rotation = b3Mat33RotationX(0.5f * B3_PI); - for (u32 i = 0; i < m_rectangleClothMesh.vertexCount; ++i) + // Translate the mesh + for (u32 i = 0; i < m_clothMesh.vertexCount; ++i) { - m_rectangleClothMesh.vertices[i] = rotation * m_rectangleClothMesh.vertices[i]; - m_rectangleClothMesh.vertices[i].y += 5.0f; + m_clothMesh.vertices[i].y += 5.0f; } - + // Create cloth b3ClothDef def; - def.mesh = &m_rectangleClothMesh; + def.mesh = &m_clothMesh; def.density = 0.2f; - //def.bending = 10000.0f; def.structural = 10000.0f; def.damping = 0.0f; + def.thickness = 0.2f; + def.friction = 0.1f; m_cloth = new b3Cloth(def); m_cloth->SetGravity(b3Vec3(0.0f, -9.8f, 0.0f)); m_cloth->SetWorld(&m_world); - for (b3Particle* p = m_cloth->GetParticleList().m_head; p; p = p->GetNext()) - { - p->SetRadius(0.1f); - p->SetFriction(0.2f); - } - { b3BodyDef bd; bd.type = e_staticBody; @@ -146,9 +133,7 @@ public: return new TableCloth(); } - b3RectangleGarment m_rectangleGarment; - b3GarmentMesh m_rectangleGarmentMesh; - b3GarmentClothMesh m_rectangleClothMesh; + b3GridClothMesh<10, 10> m_clothMesh; b3Cloth* m_cloth; b3ClothDragger* m_clothDragger; diff --git a/examples/testbed/tests/tension_mapping.h b/examples/testbed/tests/tension_mapping.h index 3b4f89b..1f074cb 100644 --- a/examples/testbed/tests/tension_mapping.h +++ b/examples/testbed/tests/tension_mapping.h @@ -58,24 +58,11 @@ static inline b3Color Color(float32 x, float32 a, float32 b) class TensionMapping : public Test { public: - TensionMapping() : m_rectangleGarment(5.0f, 5.0f) + TensionMapping() { - // Generate 2D mesh - m_rectangleGarmentMesh.Set(&m_rectangleGarment, 1.0f); - - // Create 3D mesh - m_rectangleClothMesh.Set(&m_rectangleGarmentMesh); - - // Rotate the mesh - b3Mat33 rotation = b3Mat33RotationX(0.5f * B3_PI); - for (u32 i = 0; i < m_rectangleClothMesh.vertexCount; ++i) - { - m_rectangleClothMesh.vertices[i] = rotation * m_rectangleClothMesh.vertices[i]; - } - // Create cloth b3ClothDef def; - def.mesh = &m_rectangleClothMesh; + def.mesh = &m_clothMesh; def.density = 0.2f; def.structural = 10000.0f; @@ -230,10 +217,7 @@ public: return new TensionMapping(); } - b3RectangleGarment m_rectangleGarment; - b3GarmentMesh m_rectangleGarmentMesh; - b3GarmentClothMesh m_rectangleClothMesh; - + b3GridClothMesh<10, 10> m_clothMesh; b3Cloth* m_cloth; b3ClothDragger* m_clothDragger; }; diff --git a/include/bounce/bounce.h b/include/bounce/bounce.h index e3f4335..d392a9d 100644 --- a/include/bounce/bounce.h +++ b/include/bounce/bounce.h @@ -64,6 +64,7 @@ #include #include +#include #include #include #include diff --git a/include/bounce/cloth/grid_cloth_mesh.h b/include/bounce/cloth/grid_cloth_mesh.h new file mode 100644 index 0000000..474daeb --- /dev/null +++ b/include/bounce/cloth/grid_cloth_mesh.h @@ -0,0 +1,96 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_GRID_CLOTH_MESH_H +#define B3_GRID_CLOTH_MESH_H + +#include + +// A (H + 1) x (W + 1) grid mesh stored in row-major order. +// v(i, j) = i + (W + 1) + j +template +struct b3GridClothMesh : public b3ClothMesh +{ + b3Vec3 gridVertices[(H + 1) * (W + 1)]; + b3ClothMeshTriangle gridTriangles[2 * H * W]; + b3ClothMeshMesh gridMesh; + + // Set this grid to a W (width) per H (height) dimensioned grid centered at the origin and aligned + // with the world x-z axes. + b3GridClothMesh() + { + vertexCount = 0; + for (u32 i = 0; i <= H; ++i) + { + for (u32 j = 0; j <= W; ++j) + { + gridVertices[vertexCount++].Set(float32(j), 0.0f, float32(i)); + } + } + + B3_ASSERT(vertexCount == (W + 1) * (H + 1)); + + b3Vec3 translation; + translation.x = -0.5f * float32(W); + translation.y = 0.0f; + translation.z = -0.5f * float32(H); + + for (u32 i = 0; i < vertexCount; ++i) + { + gridVertices[i] += translation; + } + + triangleCount = 0; + for (u32 i = 0; i < H; ++i) + { + for (u32 j = 0; j < W; ++j) + { + u32 v1 = i * (W + 1) + j; + u32 v2 = (i + 1) * (W + 1) + j; + u32 v3 = (i + 1) * (W + 1) + (j + 1); + u32 v4 = i * (W + 1) + (j + 1); + + b3ClothMeshTriangle* t1 = gridTriangles + triangleCount++; + t1->v1 = v3; + t1->v2 = v2; + t1->v3 = v1; + + b3ClothMeshTriangle* t2 = gridTriangles + triangleCount++; + t2->v1 = v1; + t2->v2 = v4; + t2->v3 = v3; + } + } + + B3_ASSERT(triangleCount == 2 * H * W); + + gridMesh.startTriangle = 0; + gridMesh.triangleCount = triangleCount; + gridMesh.startVertex = 0; + gridMesh.vertexCount = vertexCount; + + vertices = gridVertices; + triangles = gridTriangles; + meshCount = 1; + meshes = &gridMesh; + sewingLineCount = 0; + sewingLines = nullptr; + } +}; + +#endif \ No newline at end of file diff --git a/include/bounce/collision/shapes/grid_mesh.h b/include/bounce/collision/shapes/grid_mesh.h index 358416d..f334569 100644 --- a/include/bounce/collision/shapes/grid_mesh.h +++ b/include/bounce/collision/shapes/grid_mesh.h @@ -22,6 +22,7 @@ #include // A (H + 1) x (W + 1) grid mesh stored in row-major order. +// v(i, j) = i * (W + 1) + j template struct b3GridMesh : public b3Mesh { @@ -32,61 +33,53 @@ struct b3GridMesh : public b3Mesh // with the world x-z axes. b3GridMesh() { - u32 h = H + 1; - u32 w = W + 1; - - b3Vec3 t; - t.x = -0.5f * float32(w) + 0.5f; - t.y = 0.0f; - t.z = -0.5f * float32(h) + 0.5f; - - for (u32 i = 0; i < h; ++i) + vertexCount = 0; + for (u32 i = 0; i <= H; ++i) { - for (u32 j = 0; j < w; ++j) + for (u32 j = 0; j <= W; ++j) { - u32 v1 = i * w + j; - - b3Vec3 v; - v.x = float32(j); - v.y = 0.0f; - v.z = float32(i); - - v += t; - - gridVertices[v1] = v; + gridVertices[vertexCount++].Set(float32(j), 0.0f, float32(i)); } } - u32 triangleIndex = 0; - for (u32 i = 0; i < h - 1; ++i) + B3_ASSERT(vertexCount == (H + 1) * (W + 1)); + + b3Vec3 translation; + translation.x = -0.5f * float32(W); + translation.y = 0.0f; + translation.z = -0.5f * float32(H); + + for (u32 i = 0; i < vertexCount; ++i) { - for (u32 j = 0; j < w - 1; ++j) + gridVertices[i] += translation; + } + + triangleCount = 0; + for (u32 i = 0; i < H; ++i) + { + for (u32 j = 0; j < W; ++j) { - u32 v1 = i * w + j; - u32 v2 = (i + 1) * w + j; - u32 v3 = (i + 1) * w + (j + 1); - u32 v4 = i * w + (j + 1); - - b3Triangle* t1 = gridTriangles + triangleIndex; - ++triangleIndex; + u32 v1 = i * (W + 1) + j; + u32 v2 = (i + 1) * (W + 1) + j; + u32 v3 = (i + 1) * (W + 1) + (j + 1); + u32 v4 = i * (W + 1) + (j + 1); + b3Triangle* t1 = gridTriangles + triangleCount++; t1->v1 = v3; t1->v2 = v2; t1->v3 = v1; - b3Triangle* t2 = gridTriangles + triangleIndex; - ++triangleIndex; - + b3Triangle* t2 = gridTriangles + triangleCount++; t2->v1 = v1; t2->v2 = v4; t2->v3 = v3; } } + B3_ASSERT(triangleCount == 2 * H * W); + vertices = gridVertices; - vertexCount = (H + 1) * (W + 1); triangles = gridTriangles; - triangleCount = 2 * H * W; } }; diff --git a/include/bounce/softbody/block_softbody_mesh.h b/include/bounce/softbody/block_softbody_mesh.h index 618caaf..19dba1e 100644 --- a/include/bounce/softbody/block_softbody_mesh.h +++ b/include/bounce/softbody/block_softbody_mesh.h @@ -36,7 +36,7 @@ struct b3BlockSoftBodyMesh : public b3SoftBodyMesh { for (u32 z = 0; z <= D; ++z) { - blockVertices[vertexCount++].Set(x, y, z); + blockVertices[vertexCount++].Set(float32(x), float32(y), float32(z)); } } } diff --git a/include/bounce/softbody/softbody.h b/include/bounce/softbody/softbody.h index 08ae969..18508f1 100644 --- a/include/bounce/softbody/softbody.h +++ b/include/bounce/softbody/softbody.h @@ -141,7 +141,7 @@ public: // Debug draw the body using the associated mesh. void Draw() const; private: - friend class b3SoftBodyNode; + friend struct b3SoftBodyNode; friend class b3SoftBodySolver; friend class b3SoftBodyForceSolver; diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 936df66..8cac18f 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -170,8 +170,9 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : b3ParticleDef pd; pd.type = e_dynamicParticle; pd.mass = 1.0f; - pd.position = m->vertices[i]; pd.radius = def.thickness; + pd.friction = def.friction; + pd.position = m->vertices[i]; b3Particle* p = CreateParticle(pd); From 39b27c86d2068b9bb095d457c25b00eed70b3ace Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 22 Jun 2019 19:32:20 -0300 Subject: [PATCH 169/198] Copy mass one loop above --- src/bounce/cloth/cloth_force_solver.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/bounce/cloth/cloth_force_solver.cpp b/src/bounce/cloth/cloth_force_solver.cpp index 0dd373e..2370c2a 100644 --- a/src/bounce/cloth/cloth_force_solver.cpp +++ b/src/bounce/cloth/cloth_force_solver.cpp @@ -214,9 +214,12 @@ void b3ClothForceSolver::Solve(float32 dt, const b3Vec3& gravity) b3DenseVec3 sy(m_particleCount); b3DenseVec3 sz(m_particleCount); b3DenseVec3 sx0(m_particleCount); + b3SparseSymMat33 M(m_particleCount); b3SparseSymMat33 dfdx(m_particleCount); b3SparseSymMat33 dfdv(m_particleCount); b3DiagMat33 S(m_particleCount); + b3DiagMat33 I(m_particleCount); + I.SetIdentity(); m_solverData.x = &sx; m_solverData.v = &sv; @@ -231,6 +234,8 @@ void b3ClothForceSolver::Solve(float32 dt, const b3Vec3& gravity) { b3Particle* p = m_particles[i]; + M(i, i) = b3Diagonal(p->m_mass); + sx[i] = p->m_position; sv[i] = p->m_velocity; sf[i] = p->m_force; @@ -254,12 +259,7 @@ void b3ClothForceSolver::Solve(float32 dt, const b3Vec3& gravity) // Solve Ax = b, where // A = M - h * dfdv - h * h * dfdx // b = h * (f0 + h * dfdx * v0 + dfdx * y) - b3SparseSymMat33 M(m_particleCount); - for (u32 i = 0; i < m_particleCount; ++i) - { - M(i, i) = b3Diagonal(m_particles[i]->m_mass); - } - + // A b3SparseSymMat33 A = M - h * dfdv - h * h * dfdx; @@ -269,10 +269,6 @@ void b3ClothForceSolver::Solve(float32 dt, const b3Vec3& gravity) // b b3DenseVec3 b = h * (sf + h * (dfdx * sv) + dfdx * sy); - // I - b3DiagMat33 I(m_particleCount); - I.SetIdentity(); - // x b3DenseVec3 x(m_particleCount); b3SolveMPCG(x, viewA, b, S, sz, sx0, I); From 13eab5d00dc82ae2c7c1ef32de79cd3b2d875241 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 23 Jun 2019 10:52:41 -0300 Subject: [PATCH 170/198] Small refactor. Allow cloth particle to collide with multiple shapes. This is a more general solution. --- include/bounce/cloth/cloth.h | 17 +- include/bounce/cloth/cloth_collision.h | 38 +++++ include/bounce/cloth/cloth_contact_manager.h | 21 ++- include/bounce/cloth/cloth_contact_solver.h | 2 +- .../cloth/cloth_particle_body_contact.h | 76 +++++++++ ...ct.h => cloth_particle_triangle_contact.h} | 6 +- include/bounce/cloth/cloth_solver.h | 2 +- include/bounce/cloth/cloth_triangle.h | 2 +- include/bounce/cloth/particle.h | 54 +----- include/bounce/dynamics/shapes/shape.h | 3 + src/bounce/cloth/cloth.cpp | 130 ++------------- src/bounce/cloth/cloth_contact_manager.cpp | 154 +++++++++++++++++- src/bounce/cloth/cloth_contact_solver.cpp | 34 ++-- .../cloth/cloth_particle_body_contact.cpp | 62 +++++++ ...pp => cloth_particle_triangle_contact.cpp} | 2 +- src/bounce/cloth/cloth_solver.cpp | 2 +- src/bounce/cloth/cloth_triangle.cpp | 1 + src/bounce/cloth/particle.cpp | 62 +++---- src/bounce/dynamics/shapes/shape.cpp | 5 + 19 files changed, 426 insertions(+), 247 deletions(-) create mode 100644 include/bounce/cloth/cloth_collision.h create mode 100644 include/bounce/cloth/cloth_particle_body_contact.h rename include/bounce/cloth/{cloth_contact.h => cloth_particle_triangle_contact.h} (94%) create mode 100644 src/bounce/cloth/cloth_particle_body_contact.cpp rename src/bounce/cloth/{cloth_contact.cpp => cloth_particle_triangle_contact.cpp} (98%) diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index 1f8e365..1b49752 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -26,16 +26,14 @@ #include class b3World; -class b3Shape; - -class b3Particle; -class b3ClothTriangle; - -class b3Force; -struct b3ParticleBodyContact; struct b3ParticleDef; +class b3Particle; + struct b3ForceDef; +class b3Force; + +class b3ClothTriangle; struct b3ClothMesh; @@ -44,8 +42,6 @@ class b3RayCastListener; struct b3RayCastInput; struct b3RayCastOutput; -struct b3ClothAABBProxy; - struct b3ClothRayCastSingleOutput { u32 triangle; @@ -161,9 +157,6 @@ private: // Compute mass of each particle. void ComputeMass(); - // Update particle-body contacts - void UpdateParticleBodyContacts(); - // Solve void Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations); diff --git a/include/bounce/cloth/cloth_collision.h b/include/bounce/cloth/cloth_collision.h new file mode 100644 index 0000000..58335f5 --- /dev/null +++ b/include/bounce/cloth/cloth_collision.h @@ -0,0 +1,38 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_CLOTH_COLLISION_H +#define B3_CLOTH_COLLISION_H + +#include + +// Cloth primitive type +enum b3ClothAABBProxyType +{ + e_particleProxy, + e_triangleProxy +}; + +// Cloth primitive broadphase proxy +struct b3ClothAABBProxy +{ + b3ClothAABBProxyType type; + void* owner; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/cloth/cloth_contact_manager.h b/include/bounce/cloth/cloth_contact_manager.h index 8f4517e..eccf419 100644 --- a/include/bounce/cloth/cloth_contact_manager.h +++ b/include/bounce/cloth/cloth_contact_manager.h @@ -19,7 +19,8 @@ #ifndef B3_CLOTH_CONTACT_MANAGER_H #define B3_CLOTH_CONTACT_MANAGER_H -#include +#include +#include #include #include #include @@ -32,21 +33,31 @@ class b3ClothContactManager public: b3ClothContactManager(); - // The broad-phase callback. - void AddPair(void* data1, void* data2); - void FindNewContacts(); + + void AddPair(void* data1, void* data2); + void FindNewClothContacts(); + + void AddPSPair(b3Particle* p1, b3Shape* s2); + void FindNewBodyContacts(); void UpdateContacts(); + void UpdateClothContacts(); + void UpdateBodyContacts(); b3ParticleTriangleContact* CreateParticleTriangleContact(); void Destroy(b3ParticleTriangleContact* c); + b3ParticleBodyContact* CreateParticleBodyContact(); + void Destroy(b3ParticleBodyContact* c); + b3BlockPool m_particleTriangleContactBlocks; - + b3BlockPool m_particleBodyContactBlocks; + b3Cloth* m_cloth; b3BroadPhase m_broadPhase; b3List2 m_particleTriangleContactList; + b3List2 m_particleBodyContactList; }; #endif \ No newline at end of file diff --git a/include/bounce/cloth/cloth_contact_solver.h b/include/bounce/cloth/cloth_contact_solver.h index 76f13bc..2b33265 100644 --- a/include/bounce/cloth/cloth_contact_solver.h +++ b/include/bounce/cloth/cloth_contact_solver.h @@ -27,7 +27,7 @@ class b3StackAllocator; class b3Particle; class b3Body; -struct b3ParticleBodyContact; +class b3ParticleBodyContact; class b3ParticleTriangleContact; struct b3ClothSolverBodyContactVelocityConstraint diff --git a/include/bounce/cloth/cloth_particle_body_contact.h b/include/bounce/cloth/cloth_particle_body_contact.h new file mode 100644 index 0000000..c774329 --- /dev/null +++ b/include/bounce/cloth/cloth_particle_body_contact.h @@ -0,0 +1,76 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_CLOTH_PARTICLE_BODY_CONTACT_H +#define B3_CLOTH_PARTICLE_BODY_CONTACT_H + +#include +#include +#include +#include + +class b3Particle; +class b3Shape; + +// A contact between a particle and a body +class b3ParticleBodyContact +{ +public: +private: + friend class b3List2; + friend class b3Cloth; + friend class b3Particle; + friend class b3ClothContactManager; + friend class b3ClothSolver; + friend class b3ClothContactSolver; + friend struct b3ParticleBodyContactWorldPoint; + + b3ParticleBodyContact() { } + ~b3ParticleBodyContact() { } + + void Update(); + + b3Particle* m_p1; + b3Shape* m_s2; + + bool m_active; + + // Contact constraint + b3Vec3 m_normal1; + b3Vec3 m_localPoint1; + b3Vec3 m_localPoint2; + float32 m_normalImpulse; + + // Friction constraint + b3Vec3 m_tangent1, m_tangent2; + b3Vec2 m_tangentImpulse; + + b3ParticleBodyContact* m_prev; + b3ParticleBodyContact* m_next; +}; + +struct b3ParticleBodyContactWorldPoint +{ + void Initialize(const b3ParticleBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB); + + b3Vec3 point; + b3Vec3 normal; + float32 separation; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/cloth/cloth_contact.h b/include/bounce/cloth/cloth_particle_triangle_contact.h similarity index 94% rename from include/bounce/cloth/cloth_contact.h rename to include/bounce/cloth/cloth_particle_triangle_contact.h index 6e7e291..a5cc347 100644 --- a/include/bounce/cloth/cloth_contact.h +++ b/include/bounce/cloth/cloth_particle_triangle_contact.h @@ -16,8 +16,8 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef B3_CLOTH_CONTACT_H -#define B3_CLOTH_CONTACT_H +#ifndef B3_CLOTH_PARTICLE_TRIANGLE_CONTACT_H +#define B3_CLOTH_PARTICLE_TRIANGLE_CONTACT_H #include @@ -29,11 +29,11 @@ class b3ParticleTriangleContact { public: private: + friend class b3List2; friend class b3Cloth; friend class b3Particle; friend class b3ClothTriangle; friend class b3ClothContactManager; - friend class b3List2; friend class b3ClothContactSolver; b3ParticleTriangleContact() { } diff --git a/include/bounce/cloth/cloth_solver.h b/include/bounce/cloth/cloth_solver.h index 2edf011..75636f6 100644 --- a/include/bounce/cloth/cloth_solver.h +++ b/include/bounce/cloth/cloth_solver.h @@ -26,7 +26,7 @@ class b3StackAllocator; class b3Particle; class b3Force; -struct b3ParticleBodyContact; +class b3ParticleBodyContact; class b3ParticleTriangleContact; struct b3ClothSolverDef diff --git a/include/bounce/cloth/cloth_triangle.h b/include/bounce/cloth/cloth_triangle.h index ca51ccc..1a1e6db 100644 --- a/include/bounce/cloth/cloth_triangle.h +++ b/include/bounce/cloth/cloth_triangle.h @@ -19,7 +19,7 @@ #ifndef B3_CLOTH_TRIANGLE_H #define B3_CLOTH_TRIANGLE_H -#include +#include // A cloth triangle class b3ClothTriangle diff --git a/include/bounce/cloth/particle.h b/include/bounce/cloth/particle.h index 6844e28..225b280 100644 --- a/include/bounce/cloth/particle.h +++ b/include/bounce/cloth/particle.h @@ -19,14 +19,10 @@ #ifndef B3_PARTICLE_H #define B3_PARTICLE_H -#include -#include -#include +#include #include -class b3Shape; class b3Cloth; -class b3Particle; // Static particle: Can be moved manually. // Kinematic particle: Non-zero velocity, can be moved by the solver. @@ -38,7 +34,7 @@ enum b3ParticleType e_dynamicParticle }; -// +// Particle definition struct b3ParticleDef { b3ParticleDef() @@ -63,48 +59,6 @@ struct b3ParticleDef void* userData; }; -// A contact between a particle and a solid -struct b3ParticleBodyContact -{ - b3Particle* p1; - b3Shape* s2; - - // Contact constraint - b3Vec3 normal1; - b3Vec3 localPoint1; - b3Vec3 localPoint2; - float32 normalImpulse; - - // Friction constraint - b3Vec3 t1, t2; - b3Vec2 tangentImpulse; - - bool active; -}; - -struct b3ParticleBodyContactWorldPoint -{ - void Initialize(const b3ParticleBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB); - - b3Vec3 point; - b3Vec3 normal; - float32 separation; -}; - -// Cloth primitive type -enum b3ClothAABBProxyType -{ - e_particleProxy, - e_triangleProxy -}; - -// Cloth primitive broadphase proxy -struct b3ClothAABBProxy -{ - b3ClothAABBProxyType type; - void* owner; -}; - // A cloth particle. class b3Particle { @@ -162,6 +116,7 @@ private: friend class b3ClothSolver; friend class b3ClothForceSolver; friend class b3ClothContactManager; + friend class b3ParticleBodyContact; friend class b3ParticleTriangleContact; friend class b3ClothContactSolver; friend class b3Force; @@ -223,9 +178,6 @@ private: // Parent cloth b3Cloth* m_cloth; - // Contact - b3ParticleBodyContact m_bodyContact; - // AABB Proxy b3ClothAABBProxy m_aabbProxy; diff --git a/include/bounce/dynamics/shapes/shape.h b/include/bounce/dynamics/shapes/shape.h index a11619c..b397f58 100644 --- a/include/bounce/dynamics/shapes/shape.h +++ b/include/bounce/dynamics/shapes/shape.h @@ -140,6 +140,9 @@ public: // Set the user data associated with this shape. void SetUserData(void* data); + // Get broadphase AABB + const b3AABB3& GetAABB() const; + // Dump this shape to the log file. void Dump(u32 bodyIndex) const; diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 8cac18f..0152bac 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -23,14 +23,6 @@ #include #include #include - -#include -#include -#include -#include - -#include - #include static B3_FORCE_INLINE u32 b3NextIndex(u32 i) @@ -501,105 +493,6 @@ bool b3Cloth::RayCast(b3RayCastOutput* output, const b3RayCastInput* input, u32 return b3RayCast(output, input, v1, v2, v3); } -class b3ClothUpdateContactsQueryListener : public b3QueryListener -{ -public: - bool ReportShape(b3Shape* shape) - { - b3Body* body = shape->GetBody(); - - if (body->GetType() != e_staticBody) - { - return true; - } - - b3Transform xf = body->GetTransform(); - - b3TestSphereOutput output; - if (shape->TestSphere(&output, sphere, xf)) - { - if (output.separation < bestSeparation) - { - bestShape = shape; - bestSeparation = output.separation; - bestPoint = output.point; - bestNormal = output.normal; - } - } - - return true; - } - - b3Sphere sphere; - b3Shape* bestShape; - float32 bestSeparation; - b3Vec3 bestPoint; - b3Vec3 bestNormal; -}; - -void b3Cloth::UpdateParticleBodyContacts() -{ - B3_PROFILE("Cloth Update Particle Body Contacts"); - - // Is there a world attached to this cloth? - if (m_world == nullptr) - { - return; - } - - // Create contacts - for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) - { - if (p->m_type != e_dynamicParticle) - { - continue; - } - - b3AABB3 aabb = m_contactManager.m_broadPhase.GetAABB(p->m_broadPhaseId); - - b3ClothUpdateContactsQueryListener listener; - listener.sphere.vertex = p->m_position; - listener.sphere.radius = p->m_radius; - listener.bestShape = nullptr; - listener.bestSeparation = 0.0f; - - m_world->QueryAABB(&listener, aabb); - - if (listener.bestShape == nullptr) - { - p->m_bodyContact.active = false; - continue; - } - - b3Shape* shape = listener.bestShape; - b3Body* body = shape->GetBody(); - float32 separation = listener.bestSeparation; - b3Vec3 point = listener.bestPoint; - b3Vec3 normal = -listener.bestNormal; - - b3ParticleBodyContact* c = &p->m_bodyContact; - - b3ParticleBodyContact c0 = *c; - - c->active = true; - c->p1 = p; - c->s2 = shape; - c->normal1 = normal; - c->localPoint1.SetZero(); - c->localPoint2 = body->GetLocalPoint(point); - c->t1 = b3Perp(normal); - c->t2 = b3Cross(c->t1, normal); - c->normalImpulse = 0.0f; - c->tangentImpulse.SetZero(); - - if (c0.active == true) - { - c->normalImpulse = c0.normalImpulse; - c->tangentImpulse = c0.tangentImpulse; - } - } -} - void b3Cloth::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations) { B3_PROFILE("Cloth Solve"); @@ -624,14 +517,6 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u solver.Add(f); } - for (b3Particle* p = m_particleList.m_head; p; p = p->m_next) - { - if (p->m_bodyContact.active) - { - solver.Add(&p->m_bodyContact); - } - } - for (b3ParticleTriangleContact* c = m_contactManager.m_particleTriangleContactList.m_head; c; c = c->m_next) { if (c->m_active) @@ -640,6 +525,14 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u } } + for (b3ParticleBodyContact* c = m_contactManager.m_particleBodyContactList.m_head; c; c = c->m_next) + { + if (c->m_active) + { + solver.Add(c); + } + } + // Solve solver.Solve(dt, gravity, velocityIterations, positionIterations); } @@ -648,10 +541,7 @@ void b3Cloth::Step(float32 dt, u32 velocityIterations, u32 positionIterations) { B3_PROFILE("Cloth Step"); - // Update particle-body contacts - UpdateParticleBodyContacts(); - - // Update self-contacts + // Update contacts m_contactManager.UpdateContacts(); // Integrate state, solve constraints. @@ -695,7 +585,7 @@ void b3Cloth::Step(float32 dt, u32 velocityIterations, u32 positionIterations) m_triangles[i].Synchronize(displacement); } - // Find new self-contacts + // Find new contacts m_contactManager.FindNewContacts(); } diff --git a/src/bounce/cloth/cloth_contact_manager.cpp b/src/bounce/cloth/cloth_contact_manager.cpp index d41b4a8..86731c9 100644 --- a/src/bounce/cloth/cloth_contact_manager.cpp +++ b/src/bounce/cloth/cloth_contact_manager.cpp @@ -21,20 +21,107 @@ #include #include #include +#include +#include +#include +#include b3ClothContactManager::b3ClothContactManager() : - m_particleTriangleContactBlocks(sizeof(b3ParticleTriangleContact)) + m_particleTriangleContactBlocks(sizeof(b3ParticleTriangleContact)), + m_particleBodyContactBlocks(sizeof(b3ParticleBodyContact)) { } void b3ClothContactManager::FindNewContacts() { - B3_PROFILE("Cloth Find New Contacts"); + FindNewClothContacts(); + FindNewBodyContacts(); +} + +void b3ClothContactManager::FindNewClothContacts() +{ + B3_PROFILE("Cloth Find New Cloth Contacts"); m_broadPhase.FindPairs(this); } +class b3ClothContactManagerFindNewBodyContactsQueryListener : public b3QueryListener +{ +public: + virtual bool ReportShape(b3Shape* s2) + { + cm->AddPSPair(p1, s2); + + // Keep looking for overlaps + return true; + } + + b3ClothContactManager* cm; + b3Particle* p1; +}; + +void b3ClothContactManager::FindNewBodyContacts() +{ + B3_PROFILE("Cloth Find New Body Contacts"); + + // Is there a world attached to this cloth? + if (m_cloth->m_world == nullptr) + { + return; + } + + for (b3Particle* p = m_cloth->m_particleList.m_head; p; p = p->m_next) + { + if (p->m_type != e_dynamicParticle) + { + continue; + } + + b3AABB3 aabb = m_broadPhase.GetAABB(p->m_broadPhaseId); + + b3ClothContactManagerFindNewBodyContactsQueryListener listener; + listener.cm = this; + listener.p1 = p; + + m_cloth->m_world->QueryAABB(&listener, aabb); + } +} + +void b3ClothContactManager::AddPSPair(b3Particle* p1, b3Shape* s2) +{ + // Check if there is a contact between the two entities. + for (b3ParticleBodyContact* c = m_particleBodyContactList.m_head; c; c = c->m_next) + { + if (c->m_p1 == p1 && c->m_s2 == s2) + { + // A contact already exists. + return; + } + } + + bool isntDynamic1 = p1->m_type != e_dynamicParticle; + bool isntDynamic2 = s2->GetBody()->GetType() != e_dynamicBody; + + if (isntDynamic1 && isntDynamic2) + { + // The entities must not collide with each other. + return; + } + + // Create a new contact. + b3ParticleBodyContact* c = CreateParticleBodyContact(); + + c->m_p1 = p1; + c->m_s2 = s2; + c->m_active = false; + c->m_normalImpulse = 0.0f; + c->m_tangentImpulse.SetZero(); + + // Add the contact to the body contact list. + m_particleBodyContactList.PushFront(c); +} + void b3ClothContactManager::AddPair(void* data1, void* data2) { b3ClothAABBProxy* proxy1 = (b3ClothAABBProxy*)data1; @@ -126,9 +213,30 @@ void b3ClothContactManager::Destroy(b3ParticleTriangleContact* c) m_particleTriangleContactBlocks.Free(c); } +b3ParticleBodyContact* b3ClothContactManager::CreateParticleBodyContact() +{ + void* block = m_particleBodyContactBlocks.Allocate(); + return new(block) b3ParticleBodyContact(); +} + +void b3ClothContactManager::Destroy(b3ParticleBodyContact* c) +{ + m_particleBodyContactList.Remove(c); + + c->~b3ParticleBodyContact(); + + m_particleBodyContactBlocks.Free(c); +} + void b3ClothContactManager::UpdateContacts() { - B3_PROFILE("Cloth Update Contacts"); + UpdateClothContacts(); + UpdateBodyContacts(); +} + +void b3ClothContactManager::UpdateClothContacts() +{ + B3_PROFILE("Cloth Update Cloth Contacts"); // Update the state of particle-triangle contacts. b3ParticleTriangleContact* c = m_particleTriangleContactList.m_head; @@ -162,6 +270,46 @@ void b3ClothContactManager::UpdateContacts() // The contact persists. c->Update(); + c = c->m_next; + } +} + +void b3ClothContactManager::UpdateBodyContacts() +{ + B3_PROFILE("Cloth Update Body Contacts"); + + // Update the state of particle-body contacts. + b3ParticleBodyContact* c = m_particleBodyContactList.m_head; + while (c) + { + bool isntDynamic1 = c->m_p1->m_type != e_dynamicParticle; + bool isntDynamic2 = c->m_s2->GetType() != e_dynamicBody; + + // Cease the contact if entities must not collide with each other. + if (isntDynamic1 && isntDynamic2) + { + b3ParticleBodyContact* quack = c; + c = c->m_next; + Destroy(quack); + continue; + } + + b3AABB3 aabb1 = m_broadPhase.GetAABB(c->m_p1->m_broadPhaseId); + b3AABB3 aabb2 = c->m_s2->GetAABB(); + + // Destroy the contact if entities AABBs are not overlapping. + bool overlap = b3TestOverlap(aabb1, aabb2); + if (overlap == false) + { + b3ParticleBodyContact* quack = c; + c = c->m_next; + Destroy(quack); + continue; + } + + // The contact persists. + c->Update(); + c = c->m_next; } } \ No newline at end of file diff --git a/src/bounce/cloth/cloth_contact_solver.cpp b/src/bounce/cloth/cloth_contact_solver.cpp index d6ef10c..8825734 100644 --- a/src/bounce/cloth/cloth_contact_solver.cpp +++ b/src/bounce/cloth/cloth_contact_solver.cpp @@ -59,35 +59,35 @@ void b3ClothContactSolver::InitializeBodyContactConstraints() b3ClothSolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; b3ClothSolverBodyContactPositionConstraint* pc = m_bodyPositionConstraints + i; - vc->indexA = c->p1->m_solverId; - vc->bodyB = c->s2->GetBody(); + vc->indexA = c->m_p1->m_solverId; + vc->bodyB = c->m_s2->GetBody(); - vc->invMassA = c->p1->m_type == e_staticParticle ? 0.0f : c->p1->m_invMass; + vc->invMassA = c->m_p1->m_type == e_staticParticle ? 0.0f : c->m_p1->m_invMass; vc->invMassB = vc->bodyB->GetInverseMass(); vc->invIA.SetZero(); vc->invIB = vc->bodyB->GetWorldInverseInertia(); - vc->friction = b3MixFriction(c->p1->m_friction, c->s2->GetFriction()); + vc->friction = b3MixFriction(c->m_p1->m_friction, c->m_s2->GetFriction()); - pc->indexA = c->p1->m_solverId; + pc->indexA = c->m_p1->m_solverId; pc->bodyB = vc->bodyB; - pc->invMassA = c->p1->m_type == e_staticParticle ? 0.0f : c->p1->m_invMass; + pc->invMassA = c->m_p1->m_type == e_staticParticle ? 0.0f : c->m_p1->m_invMass; pc->invMassB = vc->bodyB->m_invMass; pc->invIA.SetZero(); pc->invIB = vc->bodyB->m_worldInvI; - pc->radiusA = c->p1->m_radius; - pc->radiusB = c->s2->m_radius; + pc->radiusA = c->m_p1->m_radius; + pc->radiusB = c->m_s2->m_radius; pc->localCenterA.SetZero(); pc->localCenterB = pc->bodyB->m_sweep.localCenter; - pc->normalA = c->normal1; - pc->localPointA = c->localPoint1; - pc->localPointB = c->localPoint2; + pc->normalA = c->m_normal1; + pc->localPointA = c->m_localPoint1; + pc->localPointB = c->m_localPoint2; } for (u32 i = 0; i < m_bodyContactCount; ++i) @@ -126,8 +126,8 @@ void b3ClothContactSolver::InitializeBodyContactConstraints() wp.Initialize(c, pc->radiusA, xfA, pc->radiusB, xfB); vc->normal = wp.normal; - vc->tangent1 = c->t1; - vc->tangent2 = c->t2; + vc->tangent1 = c->m_tangent1; + vc->tangent2 = c->m_tangent2; vc->point = wp.point; b3Vec3 point = vc->point; @@ -138,8 +138,8 @@ void b3ClothContactSolver::InitializeBodyContactConstraints() vc->rA = rA; vc->rB = rB; - vc->normalImpulse = c->normalImpulse; - vc->tangentImpulse = c->tangentImpulse; + vc->normalImpulse = c->m_normalImpulse; + vc->tangentImpulse = c->m_tangentImpulse; { b3Vec3 n = vc->normal; @@ -602,8 +602,8 @@ void b3ClothContactSolver::StoreImpulses() b3ParticleBodyContact* c = m_bodyContacts[i]; b3ClothSolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; - c->normalImpulse = vc->normalImpulse; - c->tangentImpulse = vc->tangentImpulse; + c->m_normalImpulse = vc->normalImpulse; + c->m_tangentImpulse = vc->tangentImpulse; } for (u32 i = 0; i < m_triangleContactCount; ++i) diff --git a/src/bounce/cloth/cloth_particle_body_contact.cpp b/src/bounce/cloth/cloth_particle_body_contact.cpp new file mode 100644 index 0000000..54b3ed5 --- /dev/null +++ b/src/bounce/cloth/cloth_particle_body_contact.cpp @@ -0,0 +1,62 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include + +void b3ParticleBodyContact::Update() +{ + b3Sphere sphere; + sphere.radius = m_p1->m_radius; + sphere.vertex = m_p1->m_position; + + b3Shape* shape = m_s2; + b3Body* body = shape->GetBody(); + b3Transform xf = body->GetTransform(); + + b3TestSphereOutput out; + if (shape->TestSphere(&out, sphere, xf) == false) + { + m_active = false; + return; + } + + m_active = true; + m_normal1 = -out.normal; + m_localPoint1.SetZero(); + m_localPoint2 = body->GetLocalPoint(out.point); + m_tangent1 = b3Perp(m_normal1); + m_tangent2 = b3Cross(m_tangent1, m_normal1); +} + +void b3ParticleBodyContactWorldPoint::Initialize(const b3ParticleBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB) +{ + b3Vec3 nA = c->m_normal1; + + b3Vec3 cA = xfA * c->m_localPoint1; + b3Vec3 cB = xfB * c->m_localPoint2; + + b3Vec3 pA = cA + rA * nA; + b3Vec3 pB = cB - rB * nA; + + point = 0.5f * (pA + pB); + normal = nA; + separation = b3Dot(cB - cA, nA) - rA - rB; +} diff --git a/src/bounce/cloth/cloth_contact.cpp b/src/bounce/cloth/cloth_particle_triangle_contact.cpp similarity index 98% rename from src/bounce/cloth/cloth_contact.cpp rename to src/bounce/cloth/cloth_particle_triangle_contact.cpp index be57dad..8be1b1b 100644 --- a/src/bounce/cloth/cloth_contact.cpp +++ b/src/bounce/cloth/cloth_particle_triangle_contact.cpp @@ -16,7 +16,7 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include +#include #include #include #include diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index 5584b29..9cdc4c8 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -167,7 +167,7 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterati // Synchronize bodies for (u32 i = 0; i < m_bodyContactCount; ++i) { - b3Body* body = m_bodyContacts[i]->s2->GetBody(); + b3Body* body = m_bodyContacts[i]->m_s2->GetBody(); body->SynchronizeTransform(); diff --git a/src/bounce/cloth/cloth_triangle.cpp b/src/bounce/cloth/cloth_triangle.cpp index 1fc5477..bd5771a 100644 --- a/src/bounce/cloth/cloth_triangle.cpp +++ b/src/bounce/cloth/cloth_triangle.cpp @@ -18,6 +18,7 @@ #include #include +#include #include void b3ClothTriangle::Synchronize(const b3Vec3& displacement) diff --git a/src/bounce/cloth/particle.cpp b/src/bounce/cloth/particle.cpp index 308188c..c1da933 100644 --- a/src/bounce/cloth/particle.cpp +++ b/src/bounce/cloth/particle.cpp @@ -21,21 +21,6 @@ #include #include -void b3ParticleBodyContactWorldPoint::Initialize(const b3ParticleBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB) -{ - b3Vec3 nA = c->normal1; - - b3Vec3 cA = b3Mul(xfA, c->localPoint1); - b3Vec3 cB = b3Mul(xfB, c->localPoint2); - - b3Vec3 pA = cA + rA * nA; - b3Vec3 pB = cB - rB * nA; - - point = 0.5f * (pA + pB); - normal = nA; - separation = b3Dot(cB - cA, nA) - rA - rB; -} - b3Particle::b3Particle(const b3ParticleDef& def, b3Cloth* cloth) { m_cloth = cloth; @@ -63,7 +48,6 @@ b3Particle::b3Particle(const b3ParticleDef& def, b3Cloth* cloth) m_userData = nullptr; m_x.SetZero(); m_vertex = ~0; - m_bodyContact.active = false; } b3Particle::~b3Particle() @@ -99,22 +83,38 @@ void b3Particle::SynchronizeTriangles() void b3Particle::DestroyContacts() { - // Destroy body contacts - m_bodyContact.active = false; - - // Destroy triangle contacts - b3ParticleTriangleContact* c = m_cloth->m_contactManager.m_particleTriangleContactList.m_head; - while (c) { - if (c->m_p1 == this) + // Destroy body contacts + b3ParticleBodyContact* c = m_cloth->m_contactManager.m_particleBodyContactList.m_head; + while (c) { - b3ParticleTriangleContact* quack = c; - c = c->m_next; - m_cloth->m_contactManager.Destroy(quack); - continue; - } + if (c->m_p1 == this) + { + b3ParticleBodyContact* quack = c; + c = c->m_next; + m_cloth->m_contactManager.Destroy(quack); + continue; + } - c = c->m_next; + c = c->m_next; + } + } + + { + // Destroy triangle contacts + b3ParticleTriangleContact* c = m_cloth->m_contactManager.m_particleTriangleContactList.m_head; + while (c) + { + if (c->m_p1 == this) + { + b3ParticleTriangleContact* quack = c; + c = c->m_next; + m_cloth->m_contactManager.Destroy(quack); + continue; + } + + c = c->m_next; + } } } @@ -132,13 +132,13 @@ void b3Particle::SetType(b3ParticleType type) { m_velocity.SetZero(); m_translation.SetZero(); - + Synchronize(b3Vec3_zero); SynchronizeTriangles(); } DestroyContacts(); - + // Move the proxy so new contacts can be created. m_cloth->m_contactManager.m_broadPhase.TouchProxy(m_broadPhaseId); } \ No newline at end of file diff --git a/src/bounce/dynamics/shapes/shape.cpp b/src/bounce/dynamics/shapes/shape.cpp index da720b5..077b206 100644 --- a/src/bounce/dynamics/shapes/shape.cpp +++ b/src/bounce/dynamics/shapes/shape.cpp @@ -65,6 +65,11 @@ void b3Shape::DestroyContacts() } } +const b3AABB3& b3Shape::GetAABB() const +{ + return m_body->GetWorld()->m_contactMan.m_broadPhase.GetAABB(m_broadPhaseID); +} + void b3Shape::Dump(u32 bodyIndex) const { switch (m_type) From 441e8fd63fbd4e1f74c432da24756bc76b862830 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 23 Jun 2019 11:05:53 -0300 Subject: [PATCH 171/198] Update comments --- include/bounce/cloth/cloth.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index 1b49752..288c724 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -87,7 +87,7 @@ struct b3ClothDef }; // A cloth represents a deformable surface as a collection of particles. -// Particles may be connected with each other. +// Particles may be connected with each other by springs. class b3Cloth { public: @@ -101,7 +101,7 @@ public: b3Vec3 GetGravity() const; // Attach a world to this cloth. - // The cloth will be able to respond to collisions with the static shapes in the attached world. + // The cloth will be able to respond to collisions with the rigid bodies in the attached world. void SetWorld(b3World* world); // Get the world attached to this cloth. @@ -129,7 +129,7 @@ public: // Return the cloth mesh proxy. const b3ClothMesh* GetMesh() const; - // Return the cloth particle given vertex index. + // Return the cloth particle given the vertex index. b3Particle* GetParticle(u32 i); // Return the cloth triangle given the triangle index. From fe6c2a3b0ebd10a8fc205338f394564e3f002723 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 23 Jun 2019 15:28:44 -0300 Subject: [PATCH 172/198] Bugfix --- src/bounce/cloth/cloth_contact_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bounce/cloth/cloth_contact_manager.cpp b/src/bounce/cloth/cloth_contact_manager.cpp index 86731c9..77c09bd 100644 --- a/src/bounce/cloth/cloth_contact_manager.cpp +++ b/src/bounce/cloth/cloth_contact_manager.cpp @@ -283,7 +283,7 @@ void b3ClothContactManager::UpdateBodyContacts() while (c) { bool isntDynamic1 = c->m_p1->m_type != e_dynamicParticle; - bool isntDynamic2 = c->m_s2->GetType() != e_dynamicBody; + bool isntDynamic2 = c->m_s2->GetBody()->GetType() != e_dynamicBody; // Cease the contact if entities must not collide with each other. if (isntDynamic1 && isntDynamic2) From 1876592d225edf097e9e56726c088c602fa8c825 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 23 Jun 2019 15:29:47 -0300 Subject: [PATCH 173/198] Delete world body contacts if world gets detached. --- include/bounce/cloth/cloth.h | 5 ----- src/bounce/cloth/cloth.cpp | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index 288c724..4e7136c 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -204,11 +204,6 @@ inline b3Vec3 b3Cloth::GetGravity() const return m_gravity; } -inline void b3Cloth::SetWorld(b3World* world) -{ - m_world = world; -} - inline const b3World* b3Cloth::GetWorld() const { return m_world; diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 0152bac..cdec9be 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -293,6 +293,23 @@ b3Cloth::~b3Cloth() } } +void b3Cloth::SetWorld(b3World* world) +{ + if (!world && m_world) + { + // Destroy body contacts + b3ParticleBodyContact* c = m_contactManager.m_particleBodyContactList.m_head; + while (c) + { + b3ParticleBodyContact* boom = c; + c = c->m_next; + m_contactManager.Destroy(boom); + } + } + + m_world = world; +} + b3Particle* b3Cloth::CreateParticle(const b3ParticleDef& def) { void* mem = m_particleBlocks.Allocate(); From b18dae8f9d40c380753a7d20f29fe1bb9f31bdce Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 24 Jun 2019 10:16:16 -0300 Subject: [PATCH 174/198] Reestructured folders --- include/bounce/cloth/cloth_contact_manager.h | 4 ++-- .../bounce/cloth/{ => contacts}/cloth_contact_solver.h | 0 .../cloth/{ => contacts}/cloth_particle_body_contact.h | 0 .../{ => contacts}/cloth_particle_triangle_contact.h | 0 include/bounce/{cloth => sparse}/dense_vec3.h | 0 include/bounce/{cloth => sparse}/diag_mat33.h | 2 +- include/bounce/{cloth => sparse}/sparse_mat33.h | 4 ++-- include/bounce/{cloth => sparse}/sparse_mat33_view.h | 2 +- include/bounce/{cloth => sparse}/sparse_sym_mat33.h | 2 +- include/bounce/{cloth => sparse}/sparse_sym_mat33_view.h | 4 ++-- src/bounce/cloth/cloth_force_solver.cpp | 8 ++++---- src/bounce/cloth/cloth_solver.cpp | 2 +- src/bounce/cloth/{ => contacts}/cloth_contact_solver.cpp | 2 +- .../cloth/{ => contacts}/cloth_particle_body_contact.cpp | 2 +- .../{ => contacts}/cloth_particle_triangle_contact.cpp | 4 ++-- src/bounce/cloth/spring_force.cpp | 4 ++-- src/bounce/softbody/softbody_force_solver.cpp | 4 ++-- 17 files changed, 22 insertions(+), 22 deletions(-) rename include/bounce/cloth/{ => contacts}/cloth_contact_solver.h (100%) rename include/bounce/cloth/{ => contacts}/cloth_particle_body_contact.h (100%) rename include/bounce/cloth/{ => contacts}/cloth_particle_triangle_contact.h (100%) rename include/bounce/{cloth => sparse}/dense_vec3.h (100%) rename include/bounce/{cloth => sparse}/diag_mat33.h (98%) rename include/bounce/{cloth => sparse}/sparse_mat33.h (98%) rename include/bounce/{cloth => sparse}/sparse_mat33_view.h (98%) rename include/bounce/{cloth => sparse}/sparse_sym_mat33.h (99%) rename include/bounce/{cloth => sparse}/sparse_sym_mat33_view.h (96%) rename src/bounce/cloth/{ => contacts}/cloth_contact_solver.cpp (99%) rename src/bounce/cloth/{ => contacts}/cloth_particle_body_contact.cpp (96%) rename src/bounce/cloth/{ => contacts}/cloth_particle_triangle_contact.cpp (96%) diff --git a/include/bounce/cloth/cloth_contact_manager.h b/include/bounce/cloth/cloth_contact_manager.h index eccf419..c1e8bd7 100644 --- a/include/bounce/cloth/cloth_contact_manager.h +++ b/include/bounce/cloth/cloth_contact_manager.h @@ -19,8 +19,8 @@ #ifndef B3_CLOTH_CONTACT_MANAGER_H #define B3_CLOTH_CONTACT_MANAGER_H -#include -#include +#include +#include #include #include #include diff --git a/include/bounce/cloth/cloth_contact_solver.h b/include/bounce/cloth/contacts/cloth_contact_solver.h similarity index 100% rename from include/bounce/cloth/cloth_contact_solver.h rename to include/bounce/cloth/contacts/cloth_contact_solver.h diff --git a/include/bounce/cloth/cloth_particle_body_contact.h b/include/bounce/cloth/contacts/cloth_particle_body_contact.h similarity index 100% rename from include/bounce/cloth/cloth_particle_body_contact.h rename to include/bounce/cloth/contacts/cloth_particle_body_contact.h diff --git a/include/bounce/cloth/cloth_particle_triangle_contact.h b/include/bounce/cloth/contacts/cloth_particle_triangle_contact.h similarity index 100% rename from include/bounce/cloth/cloth_particle_triangle_contact.h rename to include/bounce/cloth/contacts/cloth_particle_triangle_contact.h diff --git a/include/bounce/cloth/dense_vec3.h b/include/bounce/sparse/dense_vec3.h similarity index 100% rename from include/bounce/cloth/dense_vec3.h rename to include/bounce/sparse/dense_vec3.h diff --git a/include/bounce/cloth/diag_mat33.h b/include/bounce/sparse/diag_mat33.h similarity index 98% rename from include/bounce/cloth/diag_mat33.h rename to include/bounce/sparse/diag_mat33.h index b08065e..f7a1486 100644 --- a/include/bounce/cloth/diag_mat33.h +++ b/include/bounce/sparse/diag_mat33.h @@ -20,7 +20,7 @@ #define B3_DIAG_MAT_33_H #include -#include +#include // Diagonal matrix storing only the diagonal elements of the // original matrix. diff --git a/include/bounce/cloth/sparse_mat33.h b/include/bounce/sparse/sparse_mat33.h similarity index 98% rename from include/bounce/cloth/sparse_mat33.h rename to include/bounce/sparse/sparse_mat33.h index 273b7ad..7bebd1a 100644 --- a/include/bounce/cloth/sparse_mat33.h +++ b/include/bounce/sparse/sparse_mat33.h @@ -20,8 +20,8 @@ #define B3_SPARSE_MAT_33_H #include -#include -#include +#include +#include // An element in a sparse matrix. struct b3RowValue diff --git a/include/bounce/cloth/sparse_mat33_view.h b/include/bounce/sparse/sparse_mat33_view.h similarity index 98% rename from include/bounce/cloth/sparse_mat33_view.h rename to include/bounce/sparse/sparse_mat33_view.h index 414808c..9d92520 100644 --- a/include/bounce/cloth/sparse_mat33_view.h +++ b/include/bounce/sparse/sparse_mat33_view.h @@ -19,7 +19,7 @@ #ifndef B3_SPARSE_MAT_33_VIEW_H #define B3_SPARSE_MAT_33_VIEW_H -#include +#include struct b3ArrayRowValue { diff --git a/include/bounce/cloth/sparse_sym_mat33.h b/include/bounce/sparse/sparse_sym_mat33.h similarity index 99% rename from include/bounce/cloth/sparse_sym_mat33.h rename to include/bounce/sparse/sparse_sym_mat33.h index c8af4a2..e4d4b3e 100644 --- a/include/bounce/cloth/sparse_sym_mat33.h +++ b/include/bounce/sparse/sparse_sym_mat33.h @@ -19,7 +19,7 @@ #ifndef B3_SPARSE_SYM_MAT_33_H #define B3_SPARSE_SYM_MAT_33_H -#include +#include // A sparse symmetric matrix. // Each row is a list of non-zero elements in the row. diff --git a/include/bounce/cloth/sparse_sym_mat33_view.h b/include/bounce/sparse/sparse_sym_mat33_view.h similarity index 96% rename from include/bounce/cloth/sparse_sym_mat33_view.h rename to include/bounce/sparse/sparse_sym_mat33_view.h index 269d4dc..7d944d6 100644 --- a/include/bounce/cloth/sparse_sym_mat33_view.h +++ b/include/bounce/sparse/sparse_sym_mat33_view.h @@ -19,8 +19,8 @@ #ifndef B3_SPARSE_SYM_MAT_33_VIEW_H #define B3_SPARSE_SYM_MAT_33_VIEW_H -#include -#include +#include +#include // A read-only sparse symmetric matrix. struct b3SparseSymMat33View diff --git a/src/bounce/cloth/cloth_force_solver.cpp b/src/bounce/cloth/cloth_force_solver.cpp index 2370c2a..e530d54 100644 --- a/src/bounce/cloth/cloth_force_solver.cpp +++ b/src/bounce/cloth/cloth_force_solver.cpp @@ -19,10 +19,10 @@ #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include // Here, we solve Ax = b using the Modified Preconditioned Conjugate Gradient (MPCG) algorithm. diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index 9cdc4c8..6bf66ab 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/bounce/cloth/cloth_contact_solver.cpp b/src/bounce/cloth/contacts/cloth_contact_solver.cpp similarity index 99% rename from src/bounce/cloth/cloth_contact_solver.cpp rename to src/bounce/cloth/contacts/cloth_contact_solver.cpp index 8825734..ae715c6 100644 --- a/src/bounce/cloth/cloth_contact_solver.cpp +++ b/src/bounce/cloth/contacts/cloth_contact_solver.cpp @@ -16,7 +16,7 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include +#include #include #include #include diff --git a/src/bounce/cloth/cloth_particle_body_contact.cpp b/src/bounce/cloth/contacts/cloth_particle_body_contact.cpp similarity index 96% rename from src/bounce/cloth/cloth_particle_body_contact.cpp rename to src/bounce/cloth/contacts/cloth_particle_body_contact.cpp index 54b3ed5..fb580f9 100644 --- a/src/bounce/cloth/cloth_particle_body_contact.cpp +++ b/src/bounce/cloth/contacts/cloth_particle_body_contact.cpp @@ -16,7 +16,7 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include +#include #include #include #include diff --git a/src/bounce/cloth/cloth_particle_triangle_contact.cpp b/src/bounce/cloth/contacts/cloth_particle_triangle_contact.cpp similarity index 96% rename from src/bounce/cloth/cloth_particle_triangle_contact.cpp rename to src/bounce/cloth/contacts/cloth_particle_triangle_contact.cpp index 8be1b1b..64d4073 100644 --- a/src/bounce/cloth/cloth_particle_triangle_contact.cpp +++ b/src/bounce/cloth/contacts/cloth_particle_triangle_contact.cpp @@ -16,13 +16,13 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include +#include #include #include #include #include -// +// Solve constrained Barycentric coordinates for point Q static void b3Solve3(float32 out[3], const b3Vec3& A, const b3Vec3& B, const b3Vec3& C, const b3Vec3& Q) diff --git a/src/bounce/cloth/spring_force.cpp b/src/bounce/cloth/spring_force.cpp index e2d0265..1f687ae 100644 --- a/src/bounce/cloth/spring_force.cpp +++ b/src/bounce/cloth/spring_force.cpp @@ -19,8 +19,8 @@ #include #include #include -#include -#include +#include +#include void b3SpringForceDef::Initialize(b3Particle* particle1, b3Particle* particle2, float32 structuralStiffness, float32 dampingStiffness) { diff --git a/src/bounce/softbody/softbody_force_solver.cpp b/src/bounce/softbody/softbody_force_solver.cpp index e35307d..3d7adbf 100644 --- a/src/bounce/softbody/softbody_force_solver.cpp +++ b/src/bounce/softbody/softbody_force_solver.cpp @@ -20,8 +20,8 @@ #include #include #include -#include -#include +#include +#include // This work is based on the paper "Interactive Virtual Materials" written by // Matthias Mueller Fischer From 5bb247c79bed3391a919065d548af815c1eed459 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 24 Jun 2019 11:15:02 -0300 Subject: [PATCH 175/198] Small refactor --- include/bounce/softbody/softbody.h | 19 +- .../softbody/softbody_contact_manager.h | 48 +++++ include/bounce/softbody/softbody_node.h | 40 +---- .../softbody/softbody_node_body_contact.h | 78 +++++++++ src/bounce/cloth/cloth_solver.cpp | 11 +- src/bounce/softbody/softbody.cpp | 147 ++++------------ .../softbody/softbody_contact_manager.cpp | 165 ++++++++++++++++++ .../softbody/softbody_contact_solver.cpp | 39 +++-- src/bounce/softbody/softbody_node.cpp | 35 ++-- .../softbody/softbody_node_body_contact.cpp | 62 +++++++ src/bounce/softbody/softbody_solver.cpp | 10 +- 11 files changed, 443 insertions(+), 211 deletions(-) create mode 100644 include/bounce/softbody/softbody_contact_manager.h create mode 100644 include/bounce/softbody/softbody_node_body_contact.h create mode 100644 src/bounce/softbody/softbody_contact_manager.cpp create mode 100644 src/bounce/softbody/softbody_node_body_contact.cpp diff --git a/include/bounce/softbody/softbody.h b/include/bounce/softbody/softbody.h index 18508f1..fc28e27 100644 --- a/include/bounce/softbody/softbody.h +++ b/include/bounce/softbody/softbody.h @@ -19,11 +19,9 @@ #ifndef B3_SOFT_BODY_H #define B3_SOFT_BODY_H -#include +#include #include -#include - class b3World; struct b3SoftBodyMesh; @@ -141,16 +139,14 @@ public: // Debug draw the body using the associated mesh. void Draw() const; private: - friend struct b3SoftBodyNode; + friend class b3SoftBodyContactManager; friend class b3SoftBodySolver; friend class b3SoftBodyForceSolver; + friend struct b3SoftBodyNode; // Compute mass of each node. void ComputeMass(); - // Update contacts. - void UpdateContacts(); - // Solve void Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations); @@ -190,8 +186,8 @@ private: // Soft body triangles b3SoftBodyTriangle* m_triangles; - // Broadphase - b3BroadPhase m_broadPhase; + // Contact manager + b3SoftBodyContactManager m_contactManager; // Attached world b3World* m_world; @@ -207,11 +203,6 @@ inline b3Vec3 b3SoftBody::GetGravity() const return m_gravity; } -inline void b3SoftBody::SetWorld(b3World* world) -{ - m_world = world; -} - inline const b3World* b3SoftBody::GetWorld() const { return m_world; diff --git a/include/bounce/softbody/softbody_contact_manager.h b/include/bounce/softbody/softbody_contact_manager.h new file mode 100644 index 0000000..60c1f01 --- /dev/null +++ b/include/bounce/softbody/softbody_contact_manager.h @@ -0,0 +1,48 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SOFTBODY_CONTACT_MANAGER_H +#define B3_SOFTBODY_CONTACT_MANAGER_H + +#include +#include +#include +#include + +class b3SoftBody; + +// Contact delegator for b3SoftBody. +class b3SoftBodyContactManager +{ +public: + b3SoftBodyContactManager(); + + void FindNewBodyContacts(); + void AddNSPair(b3SoftBodyNode* n1, b3Shape* s2); + void UpdateBodyContacts(); + + b3NodeBodyContact* CreateNodeBodyContact(); + void Destroy(b3NodeBodyContact* c); + + b3BlockPool m_nodeBodyContactBlocks; + b3SoftBody* m_body; + b3BroadPhase m_broadPhase; + b3List2 m_nodeBodyContactList; +}; + +#endif \ No newline at end of file diff --git a/include/bounce/softbody/softbody_node.h b/include/bounce/softbody/softbody_node.h index 604e70b..8e53062 100644 --- a/include/bounce/softbody/softbody_node.h +++ b/include/bounce/softbody/softbody_node.h @@ -23,37 +23,7 @@ #include #include -class b3Shape; class b3SoftBody; -struct b3SoftBodyNode; - -// A contact between a node and a body -struct b3NodeBodyContact -{ - b3SoftBodyNode* n1; - b3Shape* s2; - - // Contact constraint - b3Vec3 normal1; - b3Vec3 localPoint1; - b3Vec3 localPoint2; - float32 normalImpulse; - - // Friction constraint - b3Vec3 t1, t2; - b3Vec2 tangentImpulse; - - bool active; -}; - -struct b3NodeBodyContactWorldPoint -{ - void Initialize(const b3NodeBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB); - - b3Vec3 point; - b3Vec3 normal; - float32 separation; -}; // Static node: Can be moved manually. // Kinematic node: Non-zero velocity, can be moved by the solver. @@ -117,9 +87,11 @@ public: void ApplyForce(const b3Vec3& force); private: friend class b3SoftBody; + friend class b3SoftBodyContactManager; friend class b3SoftBodySolver; friend class b3SoftBodyForceSolver; friend class b3SoftBodyContactSolver; + friend class b3NodeBodyContact; b3SoftBodyNode() { } @@ -128,6 +100,9 @@ private: // Synchronize node void Synchronize(const b3Vec3& displacement); + // Destroy associated contacts + void DestroyContacts(); + // Type b3SoftBodyNodeType m_type; @@ -161,9 +136,6 @@ private: // Soft body mesh vertex index. u32 m_vertex; - // Node and body contact - b3NodeBodyContact m_bodyContact; - // Broadphase proxy u32 m_broadPhaseId; @@ -187,7 +159,7 @@ inline void b3SoftBodyNode::SetType(b3SoftBodyNodeType type) Synchronize(b3Vec3_zero); } - m_bodyContact.active = false; + DestroyContacts(); } inline b3SoftBodyNodeType b3SoftBodyNode::GetType() const diff --git a/include/bounce/softbody/softbody_node_body_contact.h b/include/bounce/softbody/softbody_node_body_contact.h new file mode 100644 index 0000000..8a8bb5e --- /dev/null +++ b/include/bounce/softbody/softbody_node_body_contact.h @@ -0,0 +1,78 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SOFT_BODY_NODE_BODY_CONTACT_H +#define B3_SOFT_BODY_NODE_BODY_CONTACT_H + +#include +#include +#include +#include + +struct b3SoftBodyNode; +class b3Shape; + +// A contact between a node and a body +class b3NodeBodyContact +{ +public: +private: + friend class b3List2; + friend class b3SoftBody; + friend class b3SoftBodyContactManager; + friend struct b3SoftBodyNode; + friend class b3SoftBodySolver; + friend class b3SoftBodyContactSolver; + friend struct b3NodeBodyContactWorldPoint; + + b3NodeBodyContact() { } + ~b3NodeBodyContact() { } + + void Update(); + + b3SoftBodyNode* m_n1; + b3Shape* m_s2; + + // Is the contact active? + bool m_active; + + // Contact constraint + b3Vec3 m_normal1; + b3Vec3 m_localPoint1; + b3Vec3 m_localPoint2; + float32 m_normalImpulse; + + // Friction constraint + b3Vec3 m_tangent1, m_tangent2; + b3Vec2 m_tangentImpulse; + + // List pointers into the soft body + b3NodeBodyContact* m_prev; + b3NodeBodyContact* m_next; +}; + +struct b3NodeBodyContactWorldPoint +{ + void Initialize(const b3NodeBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB); + + b3Vec3 point; + b3Vec3 normal; + float32 separation; +}; + +#endif \ No newline at end of file diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index 6bf66ab..2cae7b4 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -168,6 +168,11 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterati for (u32 i = 0; i < m_bodyContactCount; ++i) { b3Body* body = m_bodyContacts[i]->m_s2->GetBody(); + + if (body->GetType() == e_staticBody) + { + continue; + } body->SynchronizeTransform(); @@ -180,10 +185,8 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterati // Copy state buffers back to the particles for (u32 i = 0; i < m_particleCount; ++i) { - b3Particle* p = m_particles[i]; - - p->m_position = positions[i]; - p->m_velocity = velocities[i]; + m_particles[i]->m_position = positions[i]; + m_particles[i]->m_velocity = velocities[i]; } m_allocator->Free(velocities); diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index e0267ce..3f78684 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -19,18 +19,11 @@ #include #include #include - #include - #include - -#include -#include -#include -#include - #include +// C = A * B static B3_FORCE_INLINE void b3Mul(float32* C, float32* A, u32 AM, u32 AN, float32* B, u32 BM, u32 BN) { B3_ASSERT(AN == BM); @@ -49,6 +42,7 @@ static B3_FORCE_INLINE void b3Mul(float32* C, float32* A, u32 AM, u32 AN, float3 } } +// B = A^T static B3_FORCE_INLINE void b3Transpose(float32* B, float32* A, u32 AM, u32 AN) { for (u32 i = 0; i < AM; ++i) @@ -62,7 +56,7 @@ static B3_FORCE_INLINE void b3Transpose(float32* B, float32* A, u32 AM, u32 AN) // Compute the elasticity matrix given Young modulus and Poisson's ratio // This is a 6 x 6 matrix -static B3_FORCE_INLINE void b3ComputeD(float32 out[36], +static B3_FORCE_INLINE void b3ComputeD(float32 out[36], float32 E, float32 nu) { float32 lambda = (nu * E) / ((1 + nu) * (1 - 2 * nu)); @@ -463,6 +457,7 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) m_c_max = def.c_max; m_gravity.SetZero(); m_world = nullptr; + m_contactManager.m_body = this; const b3SoftBodyMesh* m = m_mesh; @@ -484,12 +479,11 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) n->m_friction = 0.0f; n->m_userData = nullptr; n->m_vertex = i; - n->m_bodyContact.active = false; b3AABB3 aabb; aabb.Set(n->m_position, 0.0f); - n->m_broadPhaseId = m_broadPhase.CreateProxy(aabb, n); + n->m_broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, n); } // Compute mass @@ -722,107 +716,6 @@ void b3SoftBody::ComputeMass() } } -class b3SoftBodyUpdateContactsQueryListener : public b3QueryListener -{ -public: - bool ReportShape(b3Shape* shape) - { - b3Body* body = shape->GetBody(); - - if (body->GetType() != e_staticBody) - { - // return true; - } - - b3Transform xf = body->GetTransform(); - - b3TestSphereOutput output; - if (shape->TestSphere(&output, sphere, xf)) - { - if (output.separation < bestSeparation) - { - bestShape = shape; - bestSeparation = output.separation; - bestPoint = output.point; - bestNormal = output.normal; - } - } - - return true; - } - - b3Sphere sphere; - b3Shape* bestShape; - float32 bestSeparation; - b3Vec3 bestPoint; - b3Vec3 bestNormal; -}; - -void b3SoftBody::UpdateContacts() -{ - B3_PROFILE("Soft Body Update Contacts"); - - // Is there a world attached to this soft body? - if (m_world == nullptr) - { - return; - } - - // Create contacts - for (u32 i = 0; i < m_mesh->vertexCount; ++i) - { - b3SoftBodyNode* n = m_nodes + i; - - if (n->m_type != e_dynamicSoftBodyNode) - { - continue; - } - - b3AABB3 aabb = m_broadPhase.GetAABB(n->m_broadPhaseId); - - b3SoftBodyUpdateContactsQueryListener listener; - listener.sphere.vertex = n->m_position; - listener.sphere.radius = n->m_radius; - listener.bestShape = nullptr; - listener.bestSeparation = 0.0f; - - m_world->QueryAABB(&listener, aabb); - - if (listener.bestShape == nullptr) - { - n->m_bodyContact.active = false; - continue; - } - - b3Shape* shape = listener.bestShape; - b3Body* body = shape->GetBody(); - float32 separation = listener.bestSeparation; - b3Vec3 point = listener.bestPoint; - b3Vec3 normal = -listener.bestNormal; - - b3NodeBodyContact* c = &n->m_bodyContact; - - b3NodeBodyContact c0 = *c; - - c->active = true; - c->n1 = n; - c->s2 = shape; - c->normal1 = normal; - c->localPoint1.SetZero(); - c->localPoint2 = body->GetLocalPoint(point); - c->t1 = b3Perp(normal); - c->t2 = b3Cross(c->t1, normal); - c->normalImpulse = 0.0f; - c->tangentImpulse.SetZero(); - - if (c0.active == true) - { - c->normalImpulse = c0.normalImpulse; - c->tangentImpulse = c0.tangentImpulse; - } - } -} - void b3SoftBody::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u32 positionIterations) { B3_PROFILE("Soft Body Solve"); @@ -832,12 +725,12 @@ void b3SoftBody::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations b3SoftBodySolver solver(def); - // Push the active contacts - for (u32 i = 0; i < m_mesh->vertexCount; ++i) + // Push the body contacts + for (b3NodeBodyContact* c = m_contactManager.m_nodeBodyContactList.m_head; c; c = c->m_next) { - if (m_nodes[i].m_bodyContact.active) + if (c->m_active) { - solver.Add(&m_nodes[i].m_bodyContact); + solver.Add(c); } } @@ -849,7 +742,7 @@ void b3SoftBody::Step(float32 dt, u32 velocityIterations, u32 positionIterations B3_PROFILE("Soft Body Step"); // Update contacts - UpdateContacts(); + m_contactManager.UpdateBodyContacts(); // Integrate state, solve constraints. if (dt > 0.0f) @@ -877,6 +770,26 @@ void b3SoftBody::Step(float32 dt, u32 velocityIterations, u32 positionIterations n->Synchronize(displacement); } + + // Find new contacts + m_contactManager.FindNewBodyContacts(); +} + +void b3SoftBody::SetWorld(b3World* world) +{ + if (!world && m_world) + { + // Destroy body contacts + b3NodeBodyContact* c = m_contactManager.m_nodeBodyContactList.m_head; + while (c) + { + b3NodeBodyContact* kaboom = c; + c = c->m_next; + m_contactManager.Destroy(kaboom); + } + } + + m_world = world; } void b3SoftBody::Draw() const diff --git a/src/bounce/softbody/softbody_contact_manager.cpp b/src/bounce/softbody/softbody_contact_manager.cpp new file mode 100644 index 0000000..68bbab4 --- /dev/null +++ b/src/bounce/softbody/softbody_contact_manager.cpp @@ -0,0 +1,165 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +b3SoftBodyContactManager::b3SoftBodyContactManager() : + m_nodeBodyContactBlocks(sizeof(b3NodeBodyContact)) +{ + +} + +class b3SoftBodyContactManagerFindNewBodyContactsQueryListener : public b3QueryListener +{ +public: + virtual bool ReportShape(b3Shape* s2) + { + cm->AddNSPair(n1, s2); + + // Keep looking for overlaps + return true; + } + + b3SoftBodyContactManager* cm; + b3SoftBodyNode* n1; +}; + +void b3SoftBodyContactManager::FindNewBodyContacts() +{ + B3_PROFILE("Soft Body Find New Body Contacts"); + + // Is there a world attached to this body? + if (m_body->m_world == nullptr) + { + return; + } + + for (u32 i = 0; i < m_body->m_mesh->vertexCount; ++i) + { + b3SoftBodyNode* n = m_body->m_nodes + i; + + if (n->m_type != e_dynamicSoftBodyNode) + { + continue; + } + + b3AABB3 aabb = m_broadPhase.GetAABB(n->m_broadPhaseId); + + b3SoftBodyContactManagerFindNewBodyContactsQueryListener listener; + listener.cm = this; + listener.n1 = n; + + m_body->m_world->QueryAABB(&listener, aabb); + } +} + +void b3SoftBodyContactManager::AddNSPair(b3SoftBodyNode* n1, b3Shape* s2) +{ + // Check if there is a contact between the two entities. + for (b3NodeBodyContact* c = m_nodeBodyContactList.m_head; c; c = c->m_next) + { + if (c->m_n1 == n1 && c->m_s2 == s2) + { + // A contact already exists. + return; + } + } + + bool isntDynamic1 = n1->m_type != e_dynamicSoftBodyNode; + bool isntDynamic2 = s2->GetBody()->GetType() != e_dynamicBody; + + if (isntDynamic1 && isntDynamic2) + { + // The entities must not collide with each other. + return; + } + + // Create a new contact. + b3NodeBodyContact* c = CreateNodeBodyContact(); + + c->m_n1 = n1; + c->m_s2 = s2; + c->m_active = false; + c->m_normalImpulse = 0.0f; + c->m_tangentImpulse.SetZero(); + + // Add the contact to the body contact list. + m_nodeBodyContactList.PushFront(c); +} + +void b3SoftBodyContactManager::UpdateBodyContacts() +{ + B3_PROFILE("Soft Body Update Body Contacts"); + + // Update the state of node-body contacts. + b3NodeBodyContact* c = m_nodeBodyContactList.m_head; + while (c) + { + bool isntDynamic1 = c->m_n1->m_type != e_dynamicSoftBodyNode; + bool isntDynamic2 = c->m_s2->GetBody()->GetType() != e_dynamicBody; + + // Cease the contact if entities must not collide with each other. + if (isntDynamic1 && isntDynamic2) + { + b3NodeBodyContact* quack = c; + c = c->m_next; + Destroy(quack); + continue; + } + + b3AABB3 aabb1 = m_broadPhase.GetAABB(c->m_n1->m_broadPhaseId); + b3AABB3 aabb2 = c->m_s2->GetAABB(); + + // Destroy the contact if entities AABBs are not overlapping. + bool overlap = b3TestOverlap(aabb1, aabb2); + if (overlap == false) + { + b3NodeBodyContact* quack = c; + c = c->m_next; + Destroy(quack); + continue; + } + + // The contact persists. + c->Update(); + + c = c->m_next; + } +} + +b3NodeBodyContact* b3SoftBodyContactManager::CreateNodeBodyContact() +{ + void* block = m_nodeBodyContactBlocks.Allocate(); + return new(block) b3NodeBodyContact(); +} + +void b3SoftBodyContactManager::Destroy(b3NodeBodyContact* c) +{ + m_nodeBodyContactList.Remove(c); + + c->~b3NodeBodyContact(); + + m_nodeBodyContactBlocks.Free(c); +} \ No newline at end of file diff --git a/src/bounce/softbody/softbody_contact_solver.cpp b/src/bounce/softbody/softbody_contact_solver.cpp index f680d11..bd69ab8 100644 --- a/src/bounce/softbody/softbody_contact_solver.cpp +++ b/src/bounce/softbody/softbody_contact_solver.cpp @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -50,35 +51,35 @@ void b3SoftBodyContactSolver::InitializeBodyContactConstraints() b3SoftBodySolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; b3SoftBodySolverBodyContactPositionConstraint* pc = m_bodyPositionConstraints + i; - vc->indexA = c->n1->m_vertex; - vc->bodyB = c->s2->GetBody(); + vc->indexA = c->m_n1->m_vertex; + vc->bodyB = c->m_s2->GetBody(); - vc->invMassA = c->n1->m_type == e_staticSoftBodyNode ? 0.0f : c->n1->m_invMass; + vc->invMassA = c->m_n1->m_type == e_staticSoftBodyNode ? 0.0f : c->m_n1->m_invMass; vc->invMassB = vc->bodyB->GetInverseMass(); vc->invIA.SetZero(); vc->invIB = vc->bodyB->GetWorldInverseInertia(); - vc->friction = b3MixFriction(c->n1->m_friction, c->s2->GetFriction()); + vc->friction = b3MixFriction(c->m_n1->m_friction, c->m_s2->GetFriction()); - pc->indexA = c->n1->m_vertex; + pc->indexA = c->m_n1->m_vertex; pc->bodyB = vc->bodyB; - pc->invMassA = c->n1->m_type == e_staticSoftBodyNode ? 0.0f : c->n1->m_invMass; + pc->invMassA = c->m_n1->m_type == e_staticSoftBodyNode ? 0.0f : c->m_n1->m_invMass; pc->invMassB = vc->bodyB->m_invMass; pc->invIA.SetZero(); pc->invIB = vc->bodyB->m_worldInvI; - pc->radiusA = c->n1->m_radius; - pc->radiusB = c->s2->m_radius; + pc->radiusA = c->m_n1->m_radius; + pc->radiusB = c->m_s2->m_radius; pc->localCenterA.SetZero(); pc->localCenterB = pc->bodyB->m_sweep.localCenter; - pc->normalA = c->normal1; - pc->localPointA = c->localPoint1; - pc->localPointB = c->localPoint2; + pc->normalA = c->m_normal1; + pc->localPointA = c->m_localPoint1; + pc->localPointB = c->m_localPoint2; } for (u32 i = 0; i < m_bodyContactCount; ++i) @@ -117,8 +118,8 @@ void b3SoftBodyContactSolver::InitializeBodyContactConstraints() wp.Initialize(c, pc->radiusA, xfA, pc->radiusB, xfB); vc->normal = wp.normal; - vc->tangent1 = c->t1; - vc->tangent2 = c->t2; + vc->tangent1 = c->m_tangent1; + vc->tangent2 = c->m_tangent2; vc->point = wp.point; b3Vec3 point = vc->point; @@ -129,8 +130,8 @@ void b3SoftBodyContactSolver::InitializeBodyContactConstraints() vc->rA = rA; vc->rB = rB; - vc->normalImpulse = c->normalImpulse; - vc->tangentImpulse = c->tangentImpulse; + vc->normalImpulse = c->m_normalImpulse; + vc->tangentImpulse = c->m_tangentImpulse; { b3Vec3 n = vc->normal; @@ -306,8 +307,8 @@ void b3SoftBodyContactSolver::StoreImpulses() b3NodeBodyContact* c = m_bodyContacts[i]; b3SoftBodySolverBodyContactVelocityConstraint* vc = m_bodyVelocityConstraints + i; - c->normalImpulse = vc->normalImpulse; - c->tangentImpulse = vc->tangentImpulse; + c->m_normalImpulse = vc->normalImpulse; + c->m_tangentImpulse = vc->tangentImpulse; } } @@ -315,14 +316,14 @@ struct b3SoftBodySolverBodyContactSolverPoint { void Initialize(const b3SoftBodySolverBodyContactPositionConstraint* pc, const b3Transform& xfA, const b3Transform& xfB) { + b3Vec3 nA = pc->normalA; + b3Vec3 cA = xfA * pc->localPointA; b3Vec3 cB = xfB * pc->localPointB; float32 rA = pc->radiusA; float32 rB = pc->radiusB; - b3Vec3 nA = pc->normalA; - b3Vec3 pA = cA + rA * nA; b3Vec3 pB = cB - rB * nA; diff --git a/src/bounce/softbody/softbody_node.cpp b/src/bounce/softbody/softbody_node.cpp index 45a8eb0..cd7bb55 100644 --- a/src/bounce/softbody/softbody_node.cpp +++ b/src/bounce/softbody/softbody_node.cpp @@ -19,25 +19,28 @@ #include #include -void b3NodeBodyContactWorldPoint::Initialize(const b3NodeBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB) -{ - b3Vec3 nA = c->normal1; - - b3Vec3 cA = xfA * c->localPoint1; - b3Vec3 cB = xfB * c->localPoint2; - - b3Vec3 pA = cA + rA * nA; - b3Vec3 pB = cB - rB * nA; - - point = 0.5f * (pA + pB); - normal = nA; - separation = b3Dot(cB - cA, nA) - rA - rB; -} - void b3SoftBodyNode::Synchronize(const b3Vec3& displacement) { b3AABB3 aabb; aabb.Set(m_position, m_radius); - m_body->m_broadPhase.MoveProxy(m_broadPhaseId, aabb, displacement); + m_body->m_contactManager.m_broadPhase.MoveProxy(m_broadPhaseId, aabb, displacement); +} + +void b3SoftBodyNode::DestroyContacts() +{ + // Destroy body contacts + b3NodeBodyContact* c = m_body->m_contactManager.m_nodeBodyContactList.m_head; + while (c) + { + if (c->m_n1 == this) + { + b3NodeBodyContact* quack = c; + c = c->m_next; + m_body->m_contactManager.Destroy(quack); + continue; + } + + c = c->m_next; + } } \ No newline at end of file diff --git a/src/bounce/softbody/softbody_node_body_contact.cpp b/src/bounce/softbody/softbody_node_body_contact.cpp new file mode 100644 index 0000000..9cf6401 --- /dev/null +++ b/src/bounce/softbody/softbody_node_body_contact.cpp @@ -0,0 +1,62 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include + +void b3NodeBodyContact::Update() +{ + b3Sphere sphere; + sphere.radius = m_n1->m_radius; + sphere.vertex = m_n1->m_position; + + b3Shape* shape = m_s2; + b3Body* body = shape->GetBody(); + b3Transform xf = body->GetTransform(); + + b3TestSphereOutput out; + if (shape->TestSphere(&out, sphere, xf) == false) + { + m_active = false; + return; + } + + m_active = true; + m_normal1 = -out.normal; + m_localPoint1.SetZero(); + m_localPoint2 = body->GetLocalPoint(out.point); + m_tangent1 = b3Perp(m_normal1); + m_tangent2 = b3Cross(m_tangent1, m_normal1); +} + +void b3NodeBodyContactWorldPoint::Initialize(const b3NodeBodyContact* c, float32 rA, const b3Transform& xfA, float32 rB, const b3Transform& xfB) +{ + b3Vec3 nA = c->m_normal1; + + b3Vec3 cA = xfA * c->m_localPoint1; + b3Vec3 cB = xfB * c->m_localPoint2; + + b3Vec3 pA = cA + rA * nA; + b3Vec3 pB = cB - rB * nA; + + point = 0.5f * (pA + pB); + normal = nA; + separation = b3Dot(cB - cA, nA) - rA - rB; +} diff --git a/src/bounce/softbody/softbody_solver.cpp b/src/bounce/softbody/softbody_solver.cpp index 54c369d..c7a25ca 100644 --- a/src/bounce/softbody/softbody_solver.cpp +++ b/src/bounce/softbody/softbody_solver.cpp @@ -126,19 +126,15 @@ void b3SoftBodySolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIter } // Synchronize bodies - for (u32 i = 0; i < m_mesh->vertexCount; ++i) + for (u32 i = 0; i < m_bodyContactCount; ++i) { - b3SoftBodyNode* n = m_nodes + i; + b3Body* body = m_bodyContacts[i]->m_s2->GetBody(); - b3NodeBodyContact* c = &n->m_bodyContact; - - if (c->active == false) + if (body->GetType() == e_staticBody) { continue; } - b3Body* body = c->s2->GetBody(); - body->SynchronizeTransform(); body->m_worldInvI = b3RotateToFrame(body->m_invI, body->m_xf.rotation); From 028d8e101c47c9641a44afc90c9109937a7354d4 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 24 Jun 2019 11:33:05 -0300 Subject: [PATCH 176/198] Reestructured folders and applied a bugfix --- .../bounce/softbody/{ => contacts}/softbody_contact_solver.h | 0 .../softbody/{ => contacts}/softbody_node_body_contact.h | 0 include/bounce/softbody/softbody_contact_manager.h | 2 +- .../softbody/{ => contacts}/softbody_contact_solver.cpp | 4 ++-- .../softbody/{ => contacts}/softbody_node_body_contact.cpp | 2 +- src/bounce/softbody/softbody_solver.cpp | 4 ++-- 6 files changed, 6 insertions(+), 6 deletions(-) rename include/bounce/softbody/{ => contacts}/softbody_contact_solver.h (100%) rename include/bounce/softbody/{ => contacts}/softbody_node_body_contact.h (100%) rename src/bounce/softbody/{ => contacts}/softbody_contact_solver.cpp (98%) rename src/bounce/softbody/{ => contacts}/softbody_node_body_contact.cpp (96%) diff --git a/include/bounce/softbody/softbody_contact_solver.h b/include/bounce/softbody/contacts/softbody_contact_solver.h similarity index 100% rename from include/bounce/softbody/softbody_contact_solver.h rename to include/bounce/softbody/contacts/softbody_contact_solver.h diff --git a/include/bounce/softbody/softbody_node_body_contact.h b/include/bounce/softbody/contacts/softbody_node_body_contact.h similarity index 100% rename from include/bounce/softbody/softbody_node_body_contact.h rename to include/bounce/softbody/contacts/softbody_node_body_contact.h diff --git a/include/bounce/softbody/softbody_contact_manager.h b/include/bounce/softbody/softbody_contact_manager.h index 60c1f01..5e57310 100644 --- a/include/bounce/softbody/softbody_contact_manager.h +++ b/include/bounce/softbody/softbody_contact_manager.h @@ -19,7 +19,7 @@ #ifndef B3_SOFTBODY_CONTACT_MANAGER_H #define B3_SOFTBODY_CONTACT_MANAGER_H -#include +#include #include #include #include diff --git a/src/bounce/softbody/softbody_contact_solver.cpp b/src/bounce/softbody/contacts/softbody_contact_solver.cpp similarity index 98% rename from src/bounce/softbody/softbody_contact_solver.cpp rename to src/bounce/softbody/contacts/softbody_contact_solver.cpp index bd69ab8..21721c9 100644 --- a/src/bounce/softbody/softbody_contact_solver.cpp +++ b/src/bounce/softbody/contacts/softbody_contact_solver.cpp @@ -16,8 +16,8 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include -#include +#include +#include #include #include #include diff --git a/src/bounce/softbody/softbody_node_body_contact.cpp b/src/bounce/softbody/contacts/softbody_node_body_contact.cpp similarity index 96% rename from src/bounce/softbody/softbody_node_body_contact.cpp rename to src/bounce/softbody/contacts/softbody_node_body_contact.cpp index 9cf6401..483ae3a 100644 --- a/src/bounce/softbody/softbody_node_body_contact.cpp +++ b/src/bounce/softbody/contacts/softbody_node_body_contact.cpp @@ -16,7 +16,7 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include +#include #include #include #include diff --git a/src/bounce/softbody/softbody_solver.cpp b/src/bounce/softbody/softbody_solver.cpp index c7a25ca..42b4ad6 100644 --- a/src/bounce/softbody/softbody_solver.cpp +++ b/src/bounce/softbody/softbody_solver.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include @@ -32,7 +32,7 @@ b3SoftBodySolver::b3SoftBodySolver(const b3SoftBodySolverDef& def) m_mesh = m_body->m_mesh; m_nodes = m_body->m_nodes; m_elements = m_body->m_elements; - m_bodyContactCapacity = m_mesh->vertexCount; + m_bodyContactCapacity = m_body->m_contactManager.m_nodeBodyContactList.m_count; m_bodyContactCount = 0; m_bodyContacts = (b3NodeBodyContact**)m_allocator->Allocate(m_bodyContactCapacity * sizeof(b3NodeBodyContact*)); } From 9189b6dfef0ea68b0c52793015c002b95da9a2fe Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 24 Jun 2019 18:11:19 -0300 Subject: [PATCH 177/198] Scale down the cloth triangles for rendering --- examples/testbed/tests/tension_mapping.h | 17 +++++++++++++---- src/bounce/cloth/cloth.cpp | 10 +++++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/examples/testbed/tests/tension_mapping.h b/examples/testbed/tests/tension_mapping.h index 1f074cb..bb53a8f 100644 --- a/examples/testbed/tests/tension_mapping.h +++ b/examples/testbed/tests/tension_mapping.h @@ -126,6 +126,11 @@ public: { tension[v2] -= s->GetActionForce(); } + + b3Vec3 p1 = s->GetParticle1()->GetPosition(); + b3Vec3 p2 = s->GetParticle2()->GetPosition(); + + g_draw->DrawSegment(p1, p2, b3Color_black); } } @@ -136,10 +141,14 @@ public: b3Vec3 v1 = m_cloth->GetParticle(t->v1)->GetPosition(); b3Vec3 v2 = m_cloth->GetParticle(t->v2)->GetPosition(); b3Vec3 v3 = m_cloth->GetParticle(t->v3)->GetPosition(); + + b3Vec3 c = (v1 + v2 + v3) / 3.0f; - g_draw->DrawSegment(v1, v2, b3Color_black); - g_draw->DrawSegment(v2, v3, b3Color_black); - g_draw->DrawSegment(v3, v1, b3Color_black); + float32 s = 0.9f; + + v1 = s * (v1 - c) + c; + v2 = s * (v2 - c) + c; + v3 = s * (v3 - c) + c; b3Vec3 f1 = tension[t->v1]; float32 L1 = b3Length(f1); @@ -160,7 +169,7 @@ public: g_draw->DrawSolidTriangle(n1, v1, v2, v3, color); b3Vec3 n2 = -n1; - g_draw->DrawSolidTriangle(n2, v1, v3, v2, color); + g_draw->DrawSolidTriangle(n2, v3, v2, v1, color); } if (m_clothDragger->IsDragging()) diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index cdec9be..abc9d02 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -661,11 +661,19 @@ void b3Cloth::Draw() const b3Vec3 v2 = p2->m_position; b3Vec3 v3 = p3->m_position; + b3Vec3 c = (v1 + v2 + v3) / 3.0f; + + float32 s = 0.9f; + + v1 = s * (v1 - c) + c; + v2 = s * (v2 - c) + c; + v3 = s * (v3 - c) + c; + b3Vec3 n1 = b3Cross(v2 - v1, v3 - v1); n1.Normalize(); b3Draw_draw->DrawSolidTriangle(n1, v1, v2, v3, b3Color_blue); b3Vec3 n2 = -n1; - b3Draw_draw->DrawSolidTriangle(n2, v1, v3, v2, b3Color_blue); + b3Draw_draw->DrawSolidTriangle(n2, v3, v2, v1, b3Color_blue); } } \ No newline at end of file From 52439f3414485f9a18595d062291df274ec0180f Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 24 Jun 2019 18:39:10 -0300 Subject: [PATCH 178/198] Bugfix --- src/bounce/cloth/cloth.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index abc9d02..5f2c825 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -519,7 +519,7 @@ void b3Cloth::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterations, u solverDef.stack = &m_stackAllocator; solverDef.particleCapacity = m_particleList.m_count; solverDef.forceCapacity = m_forceList.m_count; - solverDef.bodyContactCapacity = m_particleList.m_count; + solverDef.bodyContactCapacity = m_contactManager.m_particleBodyContactList.m_count; solverDef.triangleContactCapacity = m_contactManager.m_particleTriangleContactList.m_count; b3ClothSolver solver(solverDef); From 0733ebd3be330169473598623a3e1ce3bcb10b0f Mon Sep 17 00:00:00 2001 From: Irlan Date: Wed, 26 Jun 2019 20:30:33 -0300 Subject: [PATCH 179/198] Added per triangle stretching force and damping. This gives more realistics results. Also updated the tests. Saying goodbye to mass-spring system! --- examples/testbed/tests/cloth_self_collision.h | 2 +- examples/testbed/tests/pinned_cloth.h | 3 +- examples/testbed/tests/table_cloth.h | 3 +- examples/testbed/tests/tension_mapping.h | 41 +-- include/bounce/bounce.h | 1 + include/bounce/cloth/cloth.h | 12 +- include/bounce/cloth/cloth_triangle.h | 9 + include/bounce/cloth/force.h | 1 + include/bounce/cloth/particle.h | 1 + include/bounce/cloth/strech_force.h | 126 +++++++++ src/bounce/cloth/cloth.cpp | 138 +++++----- src/bounce/cloth/cloth_solver.cpp | 5 +- src/bounce/cloth/force.cpp | 14 + src/bounce/cloth/strech_force.cpp | 247 ++++++++++++++++++ 14 files changed, 495 insertions(+), 108 deletions(-) create mode 100644 include/bounce/cloth/strech_force.h create mode 100644 src/bounce/cloth/strech_force.cpp diff --git a/examples/testbed/tests/cloth_self_collision.h b/examples/testbed/tests/cloth_self_collision.h index dfc088e..b363025 100644 --- a/examples/testbed/tests/cloth_self_collision.h +++ b/examples/testbed/tests/cloth_self_collision.h @@ -34,7 +34,7 @@ public: b3ClothDef def; def.mesh = &m_clothMesh; def.density = 1.0f; - def.structural = 100000.0f; + def.streching = 100000.0f; def.thickness = 0.2f; def.friction = 0.3f; diff --git a/examples/testbed/tests/pinned_cloth.h b/examples/testbed/tests/pinned_cloth.h index b6c8543..e856ac5 100644 --- a/examples/testbed/tests/pinned_cloth.h +++ b/examples/testbed/tests/pinned_cloth.h @@ -28,8 +28,7 @@ public: b3ClothDef def; def.mesh = &m_clothMesh; def.density = 0.2f; - def.structural = 100000.0f; - def.damping = 0.0f; + def.streching = 100000.0f; m_cloth = new b3Cloth(def); diff --git a/examples/testbed/tests/table_cloth.h b/examples/testbed/tests/table_cloth.h index f3329bc..b88edf4 100644 --- a/examples/testbed/tests/table_cloth.h +++ b/examples/testbed/tests/table_cloth.h @@ -34,8 +34,7 @@ public: b3ClothDef def; def.mesh = &m_clothMesh; def.density = 0.2f; - def.structural = 10000.0f; - def.damping = 0.0f; + def.streching = 10000.0f; def.thickness = 0.2f; def.friction = 0.1f; diff --git a/examples/testbed/tests/tension_mapping.h b/examples/testbed/tests/tension_mapping.h index bb53a8f..531512c 100644 --- a/examples/testbed/tests/tension_mapping.h +++ b/examples/testbed/tests/tension_mapping.h @@ -64,7 +64,8 @@ public: b3ClothDef def; def.mesh = &m_clothMesh; def.density = 0.2f; - def.structural = 10000.0f; + def.streching = 10000.0f; + def.damping = 100.0f; m_cloth = new b3Cloth(def); @@ -110,27 +111,25 @@ public: for (b3Force* f = m_cloth->GetForceList().m_head; f; f = f->GetNext()) { - if (f->GetType() == e_springForce) + if (f->GetType() == e_strechForce) { - b3SpringForce* s = (b3SpringForce*)f; + b3StrechForce* s = (b3StrechForce*)f; + + b3ClothTriangle* triangle = s->GetTriangle(); + u32 triangleIndex = triangle->GetTriangle(); + b3ClothMeshTriangle* mesh_triangle = m_clothMesh.triangles + triangleIndex; + + u32 v1 = mesh_triangle->v1; + u32 v2 = mesh_triangle->v2; + u32 v3 = mesh_triangle->v3; - u32 v1 = s->GetParticle1()->GetVertex(); - u32 v2 = s->GetParticle2()->GetVertex(); + b3Vec3 f1 = s->GetActionForce1(); + b3Vec3 f2 = s->GetActionForce2(); + b3Vec3 f3 = s->GetActionForce3(); - if (v1 != ~0) - { - tension[v1] += s->GetActionForce(); - } - - if (v2 != ~0) - { - tension[v2] -= s->GetActionForce(); - } - - b3Vec3 p1 = s->GetParticle1()->GetPosition(); - b3Vec3 p2 = s->GetParticle2()->GetPosition(); - - g_draw->DrawSegment(p1, p2, b3Color_black); + tension[v1] += f1; + tension[v2] += f2; + tension[v3] += f3; } } @@ -142,6 +141,8 @@ public: b3Vec3 v2 = m_cloth->GetParticle(t->v2)->GetPosition(); b3Vec3 v3 = m_cloth->GetParticle(t->v3)->GetPosition(); + g_draw->DrawTriangle(v1, v2, v3, b3Color_black); + b3Vec3 c = (v1 + v2 + v3) / 3.0f; float32 s = 0.9f; @@ -161,7 +162,7 @@ public: float32 L = (L1 + L2 + L3) / 3.0f; - const float32 kMaxT = 100000.0f; + const float32 kMaxT = 10000.0f; b3Color color = Color(L, 0.0f, kMaxT); b3Vec3 n1 = b3Cross(v2 - v1, v3 - v1); diff --git a/include/bounce/bounce.h b/include/bounce/bounce.h index d392a9d..33ca02b 100644 --- a/include/bounce/bounce.h +++ b/include/bounce/bounce.h @@ -68,6 +68,7 @@ #include #include #include +#include #include #include diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index 4e7136c..4483df2 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -57,7 +57,8 @@ struct b3ClothDef { mesh = nullptr; density = 0.0f; - structural = 0.0f; + streching = 0.0f; + sewing = 0.0f; bending = 0.0f; damping = 0.0f; thickness = 0.0f; @@ -70,12 +71,15 @@ struct b3ClothDef // Cloth density in kg/m^2 float32 density; - // Structural stiffness - float32 structural; + // Streching stiffness + float32 streching; // Bending stiffness float32 bending; + // Sewing stiffness + float32 sewing; + // Damping stiffness float32 damping; @@ -152,6 +156,8 @@ public: private: friend class b3Particle; friend class b3ClothTriangle; + friend class b3StrechForce; + friend class b3SpringForce; friend class b3ClothContactManager; // Compute mass of each particle. diff --git a/include/bounce/cloth/cloth_triangle.h b/include/bounce/cloth/cloth_triangle.h index 1a1e6db..5a11fe7 100644 --- a/include/bounce/cloth/cloth_triangle.h +++ b/include/bounce/cloth/cloth_triangle.h @@ -42,6 +42,7 @@ public: private: friend class b3Cloth; friend class b3Particle; + friend class b3StrechForce; friend class b3ClothContactManager; friend class b3ParticleTriangleContact; friend class b3ClothSolver; @@ -70,6 +71,14 @@ private: // Broadphase ID u32 m_broadPhaseId; + + // Alpha + float32 m_alpha; + + // Strech matrix + float32 m_du1, m_dv1; + float32 m_du2, m_dv2; + float32 m_inv_det; }; inline u32 b3ClothTriangle::GetTriangle() const diff --git a/include/bounce/cloth/force.h b/include/bounce/cloth/force.h index 392e0ef..5c0b283 100644 --- a/include/bounce/cloth/force.h +++ b/include/bounce/cloth/force.h @@ -29,6 +29,7 @@ class b3Particle; // Force types enum b3ForceType { + e_strechForce, e_springForce, }; diff --git a/include/bounce/cloth/particle.h b/include/bounce/cloth/particle.h index 225b280..71fe08f 100644 --- a/include/bounce/cloth/particle.h +++ b/include/bounce/cloth/particle.h @@ -120,6 +120,7 @@ private: friend class b3ParticleTriangleContact; friend class b3ClothContactSolver; friend class b3Force; + friend class b3StrechForce; friend class b3SpringForce; b3Particle(const b3ParticleDef& def, b3Cloth* cloth); diff --git a/include/bounce/cloth/strech_force.h b/include/bounce/cloth/strech_force.h new file mode 100644 index 0000000..b183c05 --- /dev/null +++ b/include/bounce/cloth/strech_force.h @@ -0,0 +1,126 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_STRECH_FORCE_H +#define B3_STRECH_FORCE_H + +#include + +class b3ClothTriangle; + +struct b3StrechForceDef : public b3ForceDef +{ + b3StrechForceDef() + { + type = e_strechForce; + } + + // Triangle + b3ClothTriangle* triangle; + + // Streching stiffness + float32 streching; + + // Damping stiffness + float32 damping; + + // Desired strechiness in u direction + float32 bu; + + // Desired strechiness in v direction + float32 bv; +}; + +// Strech force acting on a cloth triangle. +class b3StrechForce : public b3Force +{ +public: + bool HasParticle(const b3Particle* particle) const; + + b3ClothTriangle* GetTriangle() const; + + float32 GetStrechingStiffness() const; + + float32 GetDampingStiffness() const; + + b3Vec3 GetActionForce1() const; + + b3Vec3 GetActionForce2() const; + + b3Vec3 GetActionForce3() const; +private: + friend class b3Force; + friend class b3Cloth; + + b3StrechForce(const b3StrechForceDef* def); + ~b3StrechForce(); + + void Apply(const b3ClothForceSolverData* data); + + // Solver shared + + // Triangle + b3ClothTriangle* m_triangle; + + // Streching stiffness + float32 m_ks; + + // Damping stiffness + float32 m_kd; + + // bu + float32 m_bu; + + // bv + float32 m_bv; + + // Action forces + b3Vec3 m_f1, m_f2, m_f3; +}; + +inline b3ClothTriangle* b3StrechForce::GetTriangle() const +{ + return m_triangle; +} + +inline float32 b3StrechForce::GetStrechingStiffness() const +{ + return m_ks; +} + +inline float32 b3StrechForce::GetDampingStiffness() const +{ + return m_kd; +} + +inline b3Vec3 b3StrechForce::GetActionForce1() const +{ + return m_f1; +} + +inline b3Vec3 b3StrechForce::GetActionForce2() const +{ + return m_f2; +} + +inline b3Vec3 b3StrechForce::GetActionForce3() const +{ + return m_f3; +} + +#endif \ No newline at end of file diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 5f2c825..d2a3af6 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -30,57 +31,6 @@ static B3_FORCE_INLINE u32 b3NextIndex(u32 i) return i + 1 < 3 ? i + 1 : 0; } -struct b3UniqueEdge -{ - u32 v1, v2; -}; - -static u32 b3FindUniqueEdges(b3UniqueEdge* uniqueEdges, const b3ClothMesh* m) -{ - u32 uniqueCount = 0; - - for (u32 i = 0; i < m->triangleCount; ++i) - { - b3ClothMeshTriangle* t1 = m->triangles + i; - u32 i1s[3] = { t1->v1, t1->v2, t1->v3 }; - - for (u32 j1 = 0; j1 < 3; ++j1) - { - u32 t1v1 = i1s[j1]; - u32 t1v2 = i1s[b3NextIndex(j1)]; - - bool unique = true; - - for (u32 j = 0; j < uniqueCount; ++j) - { - b3UniqueEdge* ue = uniqueEdges + j; - - if (ue->v1 == t1v1 && ue->v2 == t1v2) - { - unique = false; - break; - } - - if (ue->v2 == t1v1 && ue->v1 == t1v2) - { - unique = false; - break; - } - } - - if (unique) - { - b3UniqueEdge ue; - ue.v1 = t1v1; - ue.v2 = t1v2; - uniqueEdges[uniqueCount++] = ue; - } - } - } - - return uniqueCount; -} - struct b3SharedEdge { u32 v1, v2; @@ -187,17 +137,69 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : triangle->m_friction = def.friction; triangle->m_triangle = i; - b3Vec3 v1 = m_mesh->vertices[meshTriangle->v1]; - b3Vec3 v2 = m_mesh->vertices[meshTriangle->v2]; - b3Vec3 v3 = m_mesh->vertices[meshTriangle->v3]; + b3Vec3 A = m_mesh->vertices[meshTriangle->v1]; + b3Vec3 B = m_mesh->vertices[meshTriangle->v2]; + b3Vec3 C = m_mesh->vertices[meshTriangle->v3]; b3AABB3 aabb; - aabb.Set(v1, v2, v3); + aabb.Set(A, B, C); aabb.Extend(triangle->m_radius); triangle->m_aabbProxy.type = e_triangleProxy; triangle->m_aabbProxy.owner = triangle; triangle->m_broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, &triangle->m_aabbProxy); + + // uv coordinates for triangle + + // v1 + b3Vec2 uv1; + uv1.SetZero(); + + // v2 + b3Vec3 AB = B - A; + + b3Vec2 uv2; + uv2.x = b3Length(AB); + uv2.y = 0.0f; + + // v3 + b3Vec3 n_AB = b3Normalize(AB); + b3Vec3 AC = C - A; + float32 len_AC = b3Length(AC); + + b3Vec2 uv3; + uv3.x = b3Dot(n_AB, AC); + uv3.y = b3Sqrt(len_AC * len_AC - uv3.x * uv3.x); + + // Strech matrix + float32 du1 = uv2.x - uv1.x; + float32 dv1 = uv2.y - uv1.y; + float32 du2 = uv3.x - uv1.x; + float32 dv2 = uv3.y - uv1.y; + + triangle->m_du1 = du1; + triangle->m_dv1 = dv1; + triangle->m_du2 = du2; + triangle->m_dv2 = dv2; + + float32 det = du1 * dv2 - du2 * dv1; + B3_ASSERT(det != 0.0f); + triangle->m_inv_det = 1.0f / det; + + // Triangle area + b3Vec3 v1(du1, dv1, 0.0f); + b3Vec3 v2(du2, dv2, 0.0f); + triangle->m_alpha = 0.5f * b3Length(b3Cross(v1, v2)); + + // Create strech force + b3StrechForceDef sfdef; + sfdef.triangle = triangle; + sfdef.streching = def.streching; + sfdef.damping = def.damping; + sfdef.bu = 1.0f; + sfdef.bv = 1.0f; + + CreateForce(sfdef); } // Initialize forces @@ -206,28 +208,9 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : // Worst-case edge memory u32 edgeCount = 3 * m->triangleCount; - b3UniqueEdge* uniqueEdges = (b3UniqueEdge*)allocator->Allocate(edgeCount * sizeof(b3UniqueEdge)); - u32 uniqueCount = b3FindUniqueEdges(uniqueEdges, m); - b3SharedEdge* sharedEdges = (b3SharedEdge*)allocator->Allocate(edgeCount * sizeof(b3SharedEdge)); u32 sharedCount = b3FindSharedEdges(sharedEdges, m); - for (u32 i = 0; i < uniqueCount; ++i) - { - b3UniqueEdge* e = uniqueEdges + i; - - b3Particle* p1 = m_particles[e->v1]; - b3Particle* p2 = m_particles[e->v2]; - - b3SpringForceDef fd; - fd.Initialize(p1, p2, def.structural, def.damping); - - if (def.structural > 0.0f) - { - CreateForce(fd); - } - } - // Bending for (u32 i = 0; i < sharedCount; ++i) { @@ -248,7 +231,6 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : } allocator->Free(sharedEdges); - allocator->Free(uniqueEdges); // Sewing for (u32 i = 0; i < m->sewingLineCount; ++i) @@ -259,9 +241,9 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : b3Particle* p2 = m_particles[line->v2]; b3SpringForceDef fd; - fd.Initialize(p1, p2, def.structural, def.damping); + fd.Initialize(p1, p2, def.sewing, def.damping); - if (def.structural > 0.0f) + if (def.sewing > 0.0f) { CreateForce(fd); } @@ -661,6 +643,8 @@ void b3Cloth::Draw() const b3Vec3 v2 = p2->m_position; b3Vec3 v3 = p3->m_position; + b3Draw_draw->DrawTriangle(v1, v2, v3, b3Color_black); + b3Vec3 c = (v1 + v2 + v3) / 3.0f; float32 s = 0.9f; diff --git a/src/bounce/cloth/cloth_solver.cpp b/src/bounce/cloth/cloth_solver.cpp index 2cae7b4..44203a2 100644 --- a/src/bounce/cloth/cloth_solver.cpp +++ b/src/bounce/cloth/cloth_solver.cpp @@ -90,11 +90,10 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterati forceSolver.Solve(dt, gravity); } - + // Copy particle state to state buffer b3Vec3* positions = (b3Vec3*)m_allocator->Allocate(m_particleCount * sizeof(b3Vec3)); b3Vec3* velocities = (b3Vec3*)m_allocator->Allocate(m_particleCount * sizeof(b3Vec3)); - for (u32 i = 0; i < m_particleCount; ++i) { positions[i] = m_particles[i]->m_position; @@ -147,8 +146,8 @@ void b3ClothSolver::Solve(float32 dt, const b3Vec3& gravity, u32 velocityIterati positions[i] += h * velocities[i]; } - // Solve position constraints { + // Solve position constraints bool positionSolved = false; for (u32 i = 0; i < positionIterations; ++i) { diff --git a/src/bounce/cloth/force.cpp b/src/bounce/cloth/force.cpp index 4f658df..acc3227 100644 --- a/src/bounce/cloth/force.cpp +++ b/src/bounce/cloth/force.cpp @@ -17,6 +17,7 @@ */ #include +#include #include b3Force* b3Force::Create(const b3ForceDef* def) @@ -24,6 +25,12 @@ b3Force* b3Force::Create(const b3ForceDef* def) b3Force* force = NULL; switch (def->type) { + case e_strechForce: + { + void* block = b3Alloc(sizeof(b3StrechForce)); + force = new (block) b3StrechForce((b3StrechForceDef*)def); + break; + } case e_springForce: { void* block = b3Alloc(sizeof(b3SpringForce)); @@ -46,6 +53,13 @@ void b3Force::Destroy(b3Force* force) b3ForceType type = force->GetType(); switch (type) { + case e_strechForce: + { + b3StrechForce* o = (b3StrechForce*)force; + o->~b3StrechForce(); + b3Free(force); + break; + } case e_springForce: { b3SpringForce* o = (b3SpringForce*)force; diff --git a/src/bounce/cloth/strech_force.cpp b/src/bounce/cloth/strech_force.cpp new file mode 100644 index 0000000..cc4513a --- /dev/null +++ b/src/bounce/cloth/strech_force.cpp @@ -0,0 +1,247 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +b3StrechForce::b3StrechForce(const b3StrechForceDef* def) +{ + m_type = e_strechForce; + m_triangle = def->triangle; + m_ks = def->streching; + m_kd = def->damping; + m_bu = def->bu; + m_bv = def->bv; + m_f1.SetZero(); + m_f2.SetZero(); + m_f3.SetZero(); +} + +b3StrechForce::~b3StrechForce() +{ + +} + +bool b3StrechForce::HasParticle(const b3Particle* particle) const +{ + b3Cloth* cloth = m_triangle->m_cloth; + u32 triangleIndex = m_triangle->m_triangle; + b3ClothMeshTriangle* triangle = cloth->m_mesh->triangles + triangleIndex; + + b3Particle* p1 = cloth->m_particles[triangle->v1]; + b3Particle* p2 = cloth->m_particles[triangle->v2]; + b3Particle* p3 = cloth->m_particles[triangle->v3]; + + return p1 == particle || p2 == particle || p3 == particle; +} + +void b3StrechForce::Apply(const b3ClothForceSolverData* data) +{ + b3Cloth* cloth = m_triangle->m_cloth; + u32 triangleIndex = m_triangle->m_triangle; + b3ClothMeshTriangle* triangle = cloth->m_mesh->triangles + triangleIndex; + + float32 alpha = m_triangle->m_alpha; + float32 du1 = m_triangle->m_du1; + float32 dv1 = m_triangle->m_dv1; + float32 du2 = m_triangle->m_du2; + float32 dv2 = m_triangle->m_dv2; + float32 inv_det = m_triangle->m_inv_det; + + b3Particle* p1 = cloth->m_particles[triangle->v1]; + b3Particle* p2 = cloth->m_particles[triangle->v2]; + b3Particle* p3 = cloth->m_particles[triangle->v3]; + + u32 i1 = p1->m_solverId; + u32 i2 = p2->m_solverId; + u32 i3 = p3->m_solverId; + + b3DenseVec3& x = *data->x; + b3DenseVec3& v = *data->v; + b3DenseVec3& f = *data->f; + b3SparseSymMat33& dfdx = *data->dfdx; + b3SparseSymMat33& dfdv = *data->dfdv; + + b3Vec3 x1 = x[i1]; + b3Vec3 x2 = x[i2]; + b3Vec3 x3 = x[i3]; + + b3Vec3 v1 = v[i1]; + b3Vec3 v2 = v[i2]; + b3Vec3 v3 = v[i3]; + + b3Mat33 I; I.SetIdentity(); + + b3Vec3 dx1 = x2 - x1; + b3Vec3 dx2 = x3 - x1; + + b3Vec3 wu = inv_det * (dv2 * dx1 - dv1 * dx2); + float32 len_wu = b3Length(wu); + if (len_wu == 0.0f) + { + return; + } + B3_ASSERT(len_wu > 0.0f); + float32 inv_len_wu = 1.0f / len_wu; + b3Vec3 n_wu = inv_len_wu * wu; + + b3Vec3 wv = inv_det * (-du2 * dx1 + du1 * dx2); + float32 len_wv = b3Length(wv); + if (len_wv == 0.0f) + { + return; + } + B3_ASSERT(len_wv > 0.0f); + float32 inv_len_wv = 1.0f / len_wv; + b3Vec3 n_wv = inv_len_wv * wv; + + b3Vec3 dwudx; + dwudx[0] = inv_det * (dv1 - dv2); + dwudx[1] = inv_det * dv2; + dwudx[2] = -inv_det * dv1; + + b3Vec3 dwvdx; + dwvdx[0] = inv_det * (du2 - du1); + dwvdx[1] = -inv_det * du2; + dwvdx[2] = inv_det * du1; + + float32 Cu = alpha * (len_wu - m_bu); + float32 Cv = alpha * (len_wv - m_bv); + + // This is a small trick to ensure PD-ness of the linear system + Cu = b3Max(Cu, 0.0f); + Cv = b3Max(Cv, 0.0f); + + if (Cu == 0.0f && Cv == 0.0f) + { + return; + } + + // Jacobian + b3Vec3 dCudx[3]; + b3Vec3 dCvdx[3]; + for (u32 i = 0; i < 3; ++i) + { + dCudx[i] = alpha * dwudx[i] * n_wu; + dCvdx[i] = alpha * dwvdx[i] * n_wv; + } + + m_f1.SetZero(); + m_f2.SetZero(); + m_f3.SetZero(); + + if (m_ks > 0.0f) + { + // Force + b3Vec3 fs[3]; + for (u32 i = 0; i < 3; ++i) + { + fs[i] = -m_ks * (Cu * dCudx[i] + Cv * dCvdx[i]); + } + + m_f1 += fs[0]; + m_f2 += fs[1]; + m_f3 += fs[2]; + + // Jacobian + b3Mat33 J[3][3]; + for (u32 i = 0; i < 3; ++i) + { + for (u32 j = 0; j < 3; ++j) + { + b3Mat33 d2Cuxij = (alpha * inv_len_wu * dwudx[i] * dwudx[j]) * (I - b3Outer(n_wu, n_wu)); + + b3Mat33 d2Cvxij = (alpha * inv_len_wv * dwvdx[i] * dwvdx[j]) * (I - b3Outer(n_wv, n_wv)); + + b3Mat33 Jij = -m_ks * (b3Outer(dCudx[i], dCudx[j]) + b3Outer(dCvdx[i], dCvdx[j]) + Cu * d2Cuxij + Cv * d2Cvxij); + + J[i][j] = Jij; + } + } + + dfdx(i1, i1) += J[0][0]; + dfdx(i1, i2) += J[0][1]; + dfdx(i1, i3) += J[0][2]; + + //dfdx(i2, i1) += J[1][0]; + dfdx(i2, i2) += J[1][1]; + dfdx(i2, i3) += J[1][2]; + + //dfdx(i3, i1) += J[2][0]; + //dfdx(i3, i2) += J[2][1]; + dfdx(i3, i3) += J[2][2]; + } + + if (m_kd > 0.0f) + { + float32 dCudt = 0.0f; + float32 dCvdt = 0.0f; + + b3Vec3 vs[3] = { v1, v2, v3 }; + for (u32 i = 0; i < 3; ++i) + { + dCudt += b3Dot(dCudx[i], vs[i]); + dCvdt += b3Dot(dCvdx[i], vs[i]); + } + + // Force + b3Vec3 fs[3]; + for (u32 i = 0; i < 3; ++i) + { + fs[i] = -m_kd * (dCudt * dCudx[i] + dCvdt * dCvdx[i]); + } + + m_f1 += fs[0]; + m_f2 += fs[1]; + m_f3 += fs[2]; + + // Jacobian + b3Mat33 J[3][3]; + for (u32 i = 0; i < 3; ++i) + { + for (u32 j = 0; j < 3; ++j) + { + b3Mat33 Jij = -m_kd * (b3Outer(dCudx[i], dCudx[j]) + b3Outer(dCvdx[i], dCvdx[j])); + + J[i][j] = Jij; + } + } + + dfdv(i1, i1) += J[0][0]; + dfdv(i1, i2) += J[0][1]; + dfdv(i1, i3) += J[0][2]; + + //dfdv(i2, i1) += J[1][0]; + dfdv(i2, i2) += J[1][1]; + dfdv(i2, i3) += J[1][2]; + + //dfdv(i3, i1) += J[2][0]; + //dfdv(i3, i2) += J[2][1]; + dfdv(i3, i3) += J[2][2]; + } + + f[i1] += m_f1; + f[i2] += m_f2; + f[i3] += m_f3; +} \ No newline at end of file From a47c8e3e754aa5767793f241b2a92e5c92dc95c2 Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 27 Jun 2019 03:59:59 -0300 Subject: [PATCH 180/198] Update strech_force.cpp --- src/bounce/cloth/strech_force.cpp | 284 +++++++++++++++++++----------- 1 file changed, 180 insertions(+), 104 deletions(-) diff --git a/src/bounce/cloth/strech_force.cpp b/src/bounce/cloth/strech_force.cpp index cc4513a..86d7e40 100644 --- a/src/bounce/cloth/strech_force.cpp +++ b/src/bounce/cloth/strech_force.cpp @@ -90,7 +90,7 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) b3Vec3 v1 = v[i1]; b3Vec3 v2 = v[i2]; b3Vec3 v3 = v[i3]; - + b3Mat33 I; I.SetIdentity(); b3Vec3 dx1 = x2 - x1; @@ -98,23 +98,9 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) b3Vec3 wu = inv_det * (dv2 * dx1 - dv1 * dx2); float32 len_wu = b3Length(wu); - if (len_wu == 0.0f) - { - return; - } - B3_ASSERT(len_wu > 0.0f); - float32 inv_len_wu = 1.0f / len_wu; - b3Vec3 n_wu = inv_len_wu * wu; b3Vec3 wv = inv_det * (-du2 * dx1 + du1 * dx2); float32 len_wv = b3Length(wv); - if (len_wv == 0.0f) - { - return; - } - B3_ASSERT(len_wv > 0.0f); - float32 inv_len_wv = 1.0f / len_wv; - b3Vec3 n_wv = inv_len_wv * wv; b3Vec3 dwudx; dwudx[0] = inv_det * (dv1 - dv2); @@ -126,119 +112,209 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) dwvdx[1] = -inv_det * du2; dwvdx[2] = inv_det * du1; - float32 Cu = alpha * (len_wu - m_bu); - float32 Cv = alpha * (len_wv - m_bv); - - // This is a small trick to ensure PD-ness of the linear system - Cu = b3Max(Cu, 0.0f); - Cv = b3Max(Cv, 0.0f); - - if (Cu == 0.0f && Cv == 0.0f) - { - return; - } - - // Jacobian - b3Vec3 dCudx[3]; - b3Vec3 dCvdx[3]; - for (u32 i = 0; i < 3; ++i) - { - dCudx[i] = alpha * dwudx[i] * n_wu; - dCvdx[i] = alpha * dwvdx[i] * n_wv; - } - m_f1.SetZero(); m_f2.SetZero(); m_f3.SetZero(); - if (m_ks > 0.0f) + if (len_wu > m_bu) { - // Force - b3Vec3 fs[3]; - for (u32 i = 0; i < 3; ++i) - { - fs[i] = -m_ks * (Cu * dCudx[i] + Cv * dCvdx[i]); - } - - m_f1 += fs[0]; - m_f2 += fs[1]; - m_f3 += fs[2]; + float32 inv_len_wu = 1.0f / len_wu; + b3Vec3 n_wu = inv_len_wu * wu; // Jacobian - b3Mat33 J[3][3]; + b3Vec3 dCudx[3]; for (u32 i = 0; i < 3; ++i) { - for (u32 j = 0; j < 3; ++j) - { - b3Mat33 d2Cuxij = (alpha * inv_len_wu * dwudx[i] * dwudx[j]) * (I - b3Outer(n_wu, n_wu)); - - b3Mat33 d2Cvxij = (alpha * inv_len_wv * dwvdx[i] * dwvdx[j]) * (I - b3Outer(n_wv, n_wv)); - - b3Mat33 Jij = -m_ks * (b3Outer(dCudx[i], dCudx[j]) + b3Outer(dCvdx[i], dCvdx[j]) + Cu * d2Cuxij + Cv * d2Cvxij); - - J[i][j] = Jij; - } + dCudx[i] = alpha * dwudx[i] * n_wu; } - dfdx(i1, i1) += J[0][0]; - dfdx(i1, i2) += J[0][1]; - dfdx(i1, i3) += J[0][2]; + if (m_ks > 0.0f) + { + float32 Cu = alpha * (len_wu - m_bu); - //dfdx(i2, i1) += J[1][0]; - dfdx(i2, i2) += J[1][1]; - dfdx(i2, i3) += J[1][2]; + // Force + b3Vec3 fs[3]; + for (u32 i = 0; i < 3; ++i) + { + fs[i] = -m_ks * Cu * dCudx[i]; + } - //dfdx(i3, i1) += J[2][0]; - //dfdx(i3, i2) += J[2][1]; - dfdx(i3, i3) += J[2][2]; + m_f1 += fs[0]; + m_f2 += fs[1]; + m_f3 += fs[2]; + + // Jacobian + b3Mat33 J[3][3]; + for (u32 i = 0; i < 3; ++i) + { + for (u32 j = 0; j < 3; ++j) + { + b3Mat33 d2Cuxij = (alpha * inv_len_wu * dwudx[i] * dwudx[j]) * (I - b3Outer(n_wu, n_wu)); + + b3Mat33 Jij = -m_ks * (b3Outer(dCudx[i], dCudx[j]) + Cu * d2Cuxij); + + J[i][j] = Jij; + } + } + + dfdx(i1, i1) += J[0][0]; + dfdx(i1, i2) += J[0][1]; + dfdx(i1, i3) += J[0][2]; + + //dfdx(i2, i1) += J[1][0]; + dfdx(i2, i2) += J[1][1]; + dfdx(i2, i3) += J[1][2]; + + //dfdx(i3, i1) += J[2][0]; + //dfdx(i3, i2) += J[2][1]; + dfdx(i3, i3) += J[2][2]; + } + + if (m_kd > 0.0f) + { + b3Vec3 vs[3] = { v1, v2, v3 }; + float32 dCudt = 0.0f; + for (u32 i = 0; i < 3; ++i) + { + dCudt += b3Dot(dCudx[i], vs[i]); + } + + // Force + b3Vec3 fs[3]; + for (u32 i = 0; i < 3; ++i) + { + fs[i] = -m_kd * dCudt * dCudx[i]; + } + + m_f1 += fs[0]; + m_f2 += fs[1]; + m_f3 += fs[2]; + + // Jacobian + b3Mat33 J[3][3]; + for (u32 i = 0; i < 3; ++i) + { + for (u32 j = 0; j < 3; ++j) + { + b3Mat33 Jij = -m_kd * b3Outer(dCudx[i], dCudx[j]); + + J[i][j] = Jij; + } + } + + dfdv(i1, i1) += J[0][0]; + dfdv(i1, i2) += J[0][1]; + dfdv(i1, i3) += J[0][2]; + + //dfdv(i2, i1) += J[1][0]; + dfdv(i2, i2) += J[1][1]; + dfdv(i2, i3) += J[1][2]; + + //dfdv(i3, i1) += J[2][0]; + //dfdv(i3, i2) += J[2][1]; + dfdv(i3, i3) += J[2][2]; + } } - if (m_kd > 0.0f) + if (len_wv > m_bv) { - float32 dCudt = 0.0f; - float32 dCvdt = 0.0f; - - b3Vec3 vs[3] = { v1, v2, v3 }; - for (u32 i = 0; i < 3; ++i) - { - dCudt += b3Dot(dCudx[i], vs[i]); - dCvdt += b3Dot(dCvdx[i], vs[i]); - } - - // Force - b3Vec3 fs[3]; - for (u32 i = 0; i < 3; ++i) - { - fs[i] = -m_kd * (dCudt * dCudx[i] + dCvdt * dCvdx[i]); - } - - m_f1 += fs[0]; - m_f2 += fs[1]; - m_f3 += fs[2]; + float32 inv_len_wv = 1.0f / len_wv; + b3Vec3 n_wv = inv_len_wv * wv; // Jacobian - b3Mat33 J[3][3]; + b3Vec3 dCvdx[3]; for (u32 i = 0; i < 3; ++i) { - for (u32 j = 0; j < 3; ++j) - { - b3Mat33 Jij = -m_kd * (b3Outer(dCudx[i], dCudx[j]) + b3Outer(dCvdx[i], dCvdx[j])); - - J[i][j] = Jij; - } + dCvdx[i] = alpha * dwvdx[i] * n_wv; } - dfdv(i1, i1) += J[0][0]; - dfdv(i1, i2) += J[0][1]; - dfdv(i1, i3) += J[0][2]; + if (m_ks > 0.0f) + { + float32 Cv = alpha * (len_wv - m_bv); - //dfdv(i2, i1) += J[1][0]; - dfdv(i2, i2) += J[1][1]; - dfdv(i2, i3) += J[1][2]; + // Force + b3Vec3 fs[3]; + for (u32 i = 0; i < 3; ++i) + { + fs[i] = -m_ks * Cv * dCvdx[i]; + } - //dfdv(i3, i1) += J[2][0]; - //dfdv(i3, i2) += J[2][1]; - dfdv(i3, i3) += J[2][2]; + m_f1 += fs[0]; + m_f2 += fs[1]; + m_f3 += fs[2]; + + // Jacobian + b3Mat33 J[3][3]; + for (u32 i = 0; i < 3; ++i) + { + for (u32 j = 0; j < 3; ++j) + { + b3Mat33 d2Cvxij = (alpha * inv_len_wv * dwvdx[i] * dwvdx[j]) * (I - b3Outer(n_wv, n_wv)); + + b3Mat33 Jij = -m_ks * (b3Outer(dCvdx[i], dCvdx[j]) + Cv * d2Cvxij); + + J[i][j] = Jij; + } + } + + dfdx(i1, i1) += J[0][0]; + dfdx(i1, i2) += J[0][1]; + dfdx(i1, i3) += J[0][2]; + + //dfdx(i2, i1) += J[1][0]; + dfdx(i2, i2) += J[1][1]; + dfdx(i2, i3) += J[1][2]; + + //dfdx(i3, i1) += J[2][0]; + //dfdx(i3, i2) += J[2][1]; + dfdx(i3, i3) += J[2][2]; + } + + if (m_kd > 0.0f) + { + b3Vec3 vs[3] = { v1, v2, v3 }; + + float32 dCvdt = 0.0f; + for (u32 i = 0; i < 3; ++i) + { + dCvdt += b3Dot(dCvdx[i], vs[i]); + } + + // Force + b3Vec3 fs[3]; + for (u32 i = 0; i < 3; ++i) + { + fs[i] = -m_kd * dCvdt * dCvdx[i]; + } + + m_f1 += fs[0]; + m_f2 += fs[1]; + m_f3 += fs[2]; + + // Jacobian + b3Mat33 J[3][3]; + for (u32 i = 0; i < 3; ++i) + { + for (u32 j = 0; j < 3; ++j) + { + b3Mat33 Jij = -m_kd * b3Outer(dCvdx[i], dCvdx[j]); + + J[i][j] = Jij; + } + } + + dfdv(i1, i1) += J[0][0]; + dfdv(i1, i2) += J[0][1]; + dfdv(i1, i3) += J[0][2]; + + //dfdv(i2, i1) += J[1][0]; + dfdv(i2, i2) += J[1][1]; + dfdv(i2, i3) += J[1][2]; + + //dfdv(i3, i1) += J[2][0]; + //dfdv(i3, i2) += J[2][1]; + dfdv(i3, i3) += J[2][2]; + } } f[i1] += m_f1; From 774c0741bd33063fb4e19796a8ab4b73e2695501 Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 27 Jun 2019 04:19:56 -0300 Subject: [PATCH 181/198] Update strech_force.cpp --- examples/testbed/tests/table_cloth.h | 1 + src/bounce/cloth/strech_force.cpp | 146 ++++++++++++++------------- 2 files changed, 77 insertions(+), 70 deletions(-) diff --git a/examples/testbed/tests/table_cloth.h b/examples/testbed/tests/table_cloth.h index b88edf4..08adff6 100644 --- a/examples/testbed/tests/table_cloth.h +++ b/examples/testbed/tests/table_cloth.h @@ -35,6 +35,7 @@ public: def.mesh = &m_clothMesh; def.density = 0.2f; def.streching = 10000.0f; + def.damping = 100.0f; def.thickness = 0.2f; def.friction = 0.1f; diff --git a/src/bounce/cloth/strech_force.cpp b/src/bounce/cloth/strech_force.cpp index 86d7e40..d208441 100644 --- a/src/bounce/cloth/strech_force.cpp +++ b/src/bounce/cloth/strech_force.cpp @@ -116,7 +116,7 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) m_f2.SetZero(); m_f3.SetZero(); - if (len_wu > m_bu) + if (len_wu > 0.0f) { float32 inv_len_wu = 1.0f / len_wu; b3Vec3 n_wu = inv_len_wu * wu; @@ -130,44 +130,47 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) if (m_ks > 0.0f) { - float32 Cu = alpha * (len_wu - m_bu); - - // Force - b3Vec3 fs[3]; - for (u32 i = 0; i < 3; ++i) + if (len_wu > m_bu) { - fs[i] = -m_ks * Cu * dCudx[i]; - } + float32 Cu = alpha * (len_wu - m_bu); - m_f1 += fs[0]; - m_f2 += fs[1]; - m_f3 += fs[2]; - - // Jacobian - b3Mat33 J[3][3]; - for (u32 i = 0; i < 3; ++i) - { - for (u32 j = 0; j < 3; ++j) + // Force + b3Vec3 fs[3]; + for (u32 i = 0; i < 3; ++i) { - b3Mat33 d2Cuxij = (alpha * inv_len_wu * dwudx[i] * dwudx[j]) * (I - b3Outer(n_wu, n_wu)); - - b3Mat33 Jij = -m_ks * (b3Outer(dCudx[i], dCudx[j]) + Cu * d2Cuxij); - - J[i][j] = Jij; + fs[i] = -m_ks * Cu * dCudx[i]; } + + m_f1 += fs[0]; + m_f2 += fs[1]; + m_f3 += fs[2]; + + // Jacobian + b3Mat33 J[3][3]; + for (u32 i = 0; i < 3; ++i) + { + for (u32 j = 0; j < 3; ++j) + { + b3Mat33 d2Cuxij = (alpha * inv_len_wu * dwudx[i] * dwudx[j]) * (I - b3Outer(n_wu, n_wu)); + + b3Mat33 Jij = -m_ks * (b3Outer(dCudx[i], dCudx[j]) + Cu * d2Cuxij); + + J[i][j] = Jij; + } + } + + dfdx(i1, i1) += J[0][0]; + dfdx(i1, i2) += J[0][1]; + dfdx(i1, i3) += J[0][2]; + + //dfdx(i2, i1) += J[1][0]; + dfdx(i2, i2) += J[1][1]; + dfdx(i2, i3) += J[1][2]; + + //dfdx(i3, i1) += J[2][0]; + //dfdx(i3, i2) += J[2][1]; + dfdx(i3, i3) += J[2][2]; } - - dfdx(i1, i1) += J[0][0]; - dfdx(i1, i2) += J[0][1]; - dfdx(i1, i3) += J[0][2]; - - //dfdx(i2, i1) += J[1][0]; - dfdx(i2, i2) += J[1][1]; - dfdx(i2, i3) += J[1][2]; - - //dfdx(i3, i1) += J[2][0]; - //dfdx(i3, i2) += J[2][1]; - dfdx(i3, i3) += J[2][2]; } if (m_kd > 0.0f) @@ -216,7 +219,7 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) } } - if (len_wv > m_bv) + if (len_wv > 0.0f) { float32 inv_len_wv = 1.0f / len_wv; b3Vec3 n_wv = inv_len_wv * wv; @@ -230,50 +233,53 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) if (m_ks > 0.0f) { - float32 Cv = alpha * (len_wv - m_bv); - - // Force - b3Vec3 fs[3]; - for (u32 i = 0; i < 3; ++i) + if (len_wv > m_bv) { - fs[i] = -m_ks * Cv * dCvdx[i]; - } + float32 Cv = alpha * (len_wv - m_bv); - m_f1 += fs[0]; - m_f2 += fs[1]; - m_f3 += fs[2]; - - // Jacobian - b3Mat33 J[3][3]; - for (u32 i = 0; i < 3; ++i) - { - for (u32 j = 0; j < 3; ++j) + // Force + b3Vec3 fs[3]; + for (u32 i = 0; i < 3; ++i) { - b3Mat33 d2Cvxij = (alpha * inv_len_wv * dwvdx[i] * dwvdx[j]) * (I - b3Outer(n_wv, n_wv)); - - b3Mat33 Jij = -m_ks * (b3Outer(dCvdx[i], dCvdx[j]) + Cv * d2Cvxij); - - J[i][j] = Jij; + fs[i] = -m_ks * Cv * dCvdx[i]; } + + m_f1 += fs[0]; + m_f2 += fs[1]; + m_f3 += fs[2]; + + // Jacobian + b3Mat33 J[3][3]; + for (u32 i = 0; i < 3; ++i) + { + for (u32 j = 0; j < 3; ++j) + { + b3Mat33 d2Cvxij = (alpha * inv_len_wv * dwvdx[i] * dwvdx[j]) * (I - b3Outer(n_wv, n_wv)); + + b3Mat33 Jij = -m_ks * (b3Outer(dCvdx[i], dCvdx[j]) + Cv * d2Cvxij); + + J[i][j] = Jij; + } + } + + dfdx(i1, i1) += J[0][0]; + dfdx(i1, i2) += J[0][1]; + dfdx(i1, i3) += J[0][2]; + + //dfdx(i2, i1) += J[1][0]; + dfdx(i2, i2) += J[1][1]; + dfdx(i2, i3) += J[1][2]; + + //dfdx(i3, i1) += J[2][0]; + //dfdx(i3, i2) += J[2][1]; + dfdx(i3, i3) += J[2][2]; } - - dfdx(i1, i1) += J[0][0]; - dfdx(i1, i2) += J[0][1]; - dfdx(i1, i3) += J[0][2]; - - //dfdx(i2, i1) += J[1][0]; - dfdx(i2, i2) += J[1][1]; - dfdx(i2, i3) += J[1][2]; - - //dfdx(i3, i1) += J[2][0]; - //dfdx(i3, i2) += J[2][1]; - dfdx(i3, i3) += J[2][2]; } if (m_kd > 0.0f) - { + { b3Vec3 vs[3] = { v1, v2, v3 }; - + float32 dCvdt = 0.0f; for (u32 i = 0; i < 3; ++i) { From b6d9f56583b1f804a952c1df9ba285a04e577b06 Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 27 Jun 2019 16:19:42 -0300 Subject: [PATCH 182/198] Small refactor. Removed b3SparseSymMat33 since it is very particular. Pushed experimental b3ShearForce. --- examples/testbed/tests/table_cloth.h | 2 +- include/bounce/bounce.h | 1 + include/bounce/cloth/cloth.h | 7 +- include/bounce/cloth/cloth_force_solver.h | 8 +- include/bounce/cloth/cloth_triangle.h | 1 + include/bounce/cloth/force.h | 1 + include/bounce/cloth/particle.h | 5 +- include/bounce/cloth/shear_force.h | 114 ++++++ include/bounce/sparse/sparse_sym_mat33.h | 337 ------------------ include/bounce/sparse/sparse_sym_mat33_view.h | 132 ------- src/bounce/cloth/cloth.cpp | 18 +- src/bounce/cloth/cloth_force_solver.cpp | 16 +- src/bounce/cloth/force.cpp | 14 + src/bounce/cloth/shear_force.cpp | 219 ++++++++++++ src/bounce/cloth/spring_force.cpp | 14 +- src/bounce/cloth/strech_force.cpp | 30 +- 16 files changed, 410 insertions(+), 509 deletions(-) create mode 100644 include/bounce/cloth/shear_force.h delete mode 100644 include/bounce/sparse/sparse_sym_mat33.h delete mode 100644 include/bounce/sparse/sparse_sym_mat33_view.h create mode 100644 src/bounce/cloth/shear_force.cpp diff --git a/examples/testbed/tests/table_cloth.h b/examples/testbed/tests/table_cloth.h index 08adff6..9460ffe 100644 --- a/examples/testbed/tests/table_cloth.h +++ b/examples/testbed/tests/table_cloth.h @@ -35,6 +35,7 @@ public: def.mesh = &m_clothMesh; def.density = 0.2f; def.streching = 10000.0f; + //def.shearing = 10000.0f; def.damping = 100.0f; def.thickness = 0.2f; def.friction = 0.1f; @@ -134,7 +135,6 @@ public: } b3GridClothMesh<10, 10> m_clothMesh; - b3Cloth* m_cloth; b3ClothDragger* m_clothDragger; diff --git a/include/bounce/bounce.h b/include/bounce/bounce.h index 33ca02b..1fdc46f 100644 --- a/include/bounce/bounce.h +++ b/include/bounce/bounce.h @@ -69,6 +69,7 @@ #include #include #include +#include #include #include diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index 4483df2..b4fda69 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -58,8 +58,9 @@ struct b3ClothDef mesh = nullptr; density = 0.0f; streching = 0.0f; - sewing = 0.0f; + shearing = 0.0f; bending = 0.0f; + sewing = 0.0f; damping = 0.0f; thickness = 0.0f; friction = 0.2f; @@ -74,6 +75,9 @@ struct b3ClothDef // Streching stiffness float32 streching; + // Shearing stiffness + float32 shearing; + // Bending stiffness float32 bending; @@ -156,6 +160,7 @@ public: private: friend class b3Particle; friend class b3ClothTriangle; + friend class b3ShearForce; friend class b3StrechForce; friend class b3SpringForce; friend class b3ClothContactManager; diff --git a/include/bounce/cloth/cloth_force_solver.h b/include/bounce/cloth/cloth_force_solver.h index 763e3cc..d91786c 100644 --- a/include/bounce/cloth/cloth_force_solver.h +++ b/include/bounce/cloth/cloth_force_solver.h @@ -29,8 +29,8 @@ class b3Force; struct b3DenseVec3; struct b3DiagMat33; -struct b3SparseSymMat33; -struct b3SparseSymMat33View; +struct b3SparseMat33; +struct b3SparseMat33View; struct b3ClothForceSolverDef { @@ -47,8 +47,8 @@ struct b3ClothForceSolverData b3DenseVec3* v; b3DenseVec3* f; b3DenseVec3* y; - b3SparseSymMat33* dfdx; - b3SparseSymMat33* dfdv; + b3SparseMat33* dfdx; + b3SparseMat33* dfdv; b3DiagMat33* S; b3DenseVec3* z; }; diff --git a/include/bounce/cloth/cloth_triangle.h b/include/bounce/cloth/cloth_triangle.h index 5a11fe7..03750d5 100644 --- a/include/bounce/cloth/cloth_triangle.h +++ b/include/bounce/cloth/cloth_triangle.h @@ -42,6 +42,7 @@ public: private: friend class b3Cloth; friend class b3Particle; + friend class b3ShearForce; friend class b3StrechForce; friend class b3ClothContactManager; friend class b3ParticleTriangleContact; diff --git a/include/bounce/cloth/force.h b/include/bounce/cloth/force.h index 5c0b283..3fb51cd 100644 --- a/include/bounce/cloth/force.h +++ b/include/bounce/cloth/force.h @@ -30,6 +30,7 @@ class b3Particle; enum b3ForceType { e_strechForce, + e_shearForce, e_springForce, }; diff --git a/include/bounce/cloth/particle.h b/include/bounce/cloth/particle.h index 71fe08f..4de14cb 100644 --- a/include/bounce/cloth/particle.h +++ b/include/bounce/cloth/particle.h @@ -112,15 +112,16 @@ public: private: friend class b3List2; friend class b3Cloth; - friend class b3ClothTriangle; + friend class b3ClothContactManager; friend class b3ClothSolver; friend class b3ClothForceSolver; - friend class b3ClothContactManager; + friend class b3ClothTriangle; friend class b3ParticleBodyContact; friend class b3ParticleTriangleContact; friend class b3ClothContactSolver; friend class b3Force; friend class b3StrechForce; + friend class b3ShearForce; friend class b3SpringForce; b3Particle(const b3ParticleDef& def, b3Cloth* cloth); diff --git a/include/bounce/cloth/shear_force.h b/include/bounce/cloth/shear_force.h new file mode 100644 index 0000000..8485795 --- /dev/null +++ b/include/bounce/cloth/shear_force.h @@ -0,0 +1,114 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_SHEAR_FORCE_H +#define B3_SHEAR_FORCE_H + +#include + +class b3ClothTriangle; + +struct b3ShearForceDef : public b3ForceDef +{ + b3ShearForceDef() + { + type = e_shearForce; + } + + // Triangle + b3ClothTriangle* triangle; + + // Shearing stiffness + float32 shearing; + + // Damping stiffness + float32 damping; +}; + +// Shear force acting on a cloth triangle. +class b3ShearForce : public b3Force +{ +public: + bool HasParticle(const b3Particle* particle) const; + + b3ClothTriangle* GetTriangle() const; + + float32 GetShearingStiffness() const; + + float32 GetDampingStiffness() const; + + b3Vec3 GetActionForce1() const; + + b3Vec3 GetActionForce2() const; + + b3Vec3 GetActionForce3() const; +private: + friend class b3Force; + friend class b3Cloth; + + b3ShearForce(const b3ShearForceDef* def); + ~b3ShearForce(); + + void Apply(const b3ClothForceSolverData* data); + + // Solver shared + + // Triangle + b3ClothTriangle* m_triangle; + + // Shearing stiffness + float32 m_ks; + + // Damping stiffness + float32 m_kd; + + // Action forces + b3Vec3 m_f1, m_f2, m_f3; +}; + +inline b3ClothTriangle* b3ShearForce::GetTriangle() const +{ + return m_triangle; +} + +inline float32 b3ShearForce::GetShearingStiffness() const +{ + return m_ks; +} + +inline float32 b3ShearForce::GetDampingStiffness() const +{ + return m_kd; +} + +inline b3Vec3 b3ShearForce::GetActionForce1() const +{ + return m_f1; +} + +inline b3Vec3 b3ShearForce::GetActionForce2() const +{ + return m_f2; +} + +inline b3Vec3 b3ShearForce::GetActionForce3() const +{ + return m_f3; +} + +#endif \ No newline at end of file diff --git a/include/bounce/sparse/sparse_sym_mat33.h b/include/bounce/sparse/sparse_sym_mat33.h deleted file mode 100644 index e4d4b3e..0000000 --- a/include/bounce/sparse/sparse_sym_mat33.h +++ /dev/null @@ -1,337 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef B3_SPARSE_SYM_MAT_33_H -#define B3_SPARSE_SYM_MAT_33_H - -#include - -// A sparse symmetric matrix. -// Each row is a list of non-zero elements in the row. -// The total matrix capacity is bounded by -// M * (M + 1) / 2 -// You must write only the upper triangle elements of the original -// matrix to this matrix because a_ji = a_ij. -struct b3SparseSymMat33 -{ - // - b3SparseSymMat33(); - - // - b3SparseSymMat33(u32 m); - - // - b3SparseSymMat33(const b3SparseSymMat33& _m); - - // - ~b3SparseSymMat33(); - - // - b3SparseSymMat33& operator=(const b3SparseSymMat33& _m); - - // - void Copy(const b3SparseSymMat33& _m); - - // - void Destroy(); - - // - b3Mat33& operator()(u32 i, u32 j); - - // - const b3Mat33& operator()(u32 i, u32 j) const; - - // - void operator+=(const b3SparseSymMat33& m); - - // - void operator-=(const b3SparseSymMat33& m); - - u32 rowCount; - b3RowValueList* rows; -}; - -inline b3SparseSymMat33::b3SparseSymMat33() -{ - rowCount = 0; - rows = nullptr; -} - -inline b3SparseSymMat33::b3SparseSymMat33(u32 m) -{ - rowCount = m; - rows = (b3RowValueList*)b3Alloc(rowCount * sizeof(b3RowValueList)); - for (u32 i = 0; i < rowCount; ++i) - { - new (rows + i)b3RowValueList(); - } -} - -inline b3SparseSymMat33::b3SparseSymMat33(const b3SparseSymMat33& m) -{ - rowCount = m.rowCount; - rows = (b3RowValueList*)b3Alloc(rowCount * sizeof(b3RowValueList)); - for (u32 i = 0; i < rowCount; ++i) - { - new (rows + i)b3RowValueList(); - } - - Copy(m); -} - -inline b3SparseSymMat33::~b3SparseSymMat33() -{ - Destroy(); -} - -inline void b3SparseSymMat33::Destroy() -{ - for (u32 i = 0; i < rowCount; ++i) - { - b3RowValueList* vs = rows + i; - - b3RowValue* v = vs->head; - while (v) - { - b3RowValue* v0 = v->next; - b3Free(v); - v = v0; - } - - vs->~b3RowValueList(); - } - - b3Free(rows); -} - -inline b3SparseSymMat33& b3SparseSymMat33::operator=(const b3SparseSymMat33& _m) -{ - if (_m.rows == rows) - { - return *this; - } - - Destroy(); - - rowCount = _m.rowCount; - rows = (b3RowValueList*)b3Alloc(rowCount * sizeof(b3RowValueList)); - for (u32 i = 0; i < rowCount; ++i) - { - new (rows + i)b3RowValueList(); - } - - Copy(_m); - - return *this; -} - -inline void b3SparseSymMat33::Copy(const b3SparseSymMat33& _m) -{ - B3_ASSERT(rowCount == _m.rowCount); - - for (u32 i = 0; i < rowCount; ++i) - { - b3RowValueList* vs1 = _m.rows + i; - b3RowValueList* vs2 = rows + i; - - B3_ASSERT(vs2->count == 0); - - for (b3RowValue* v1 = vs1->head; v1; v1 = v1->next) - { - b3RowValue* v2 = (b3RowValue*)b3Alloc(sizeof(b3RowValue)); - - v2->column = v1->column; - v2->value = v1->value; - - vs2->PushFront(v2); - } - } -} - -inline const b3Mat33& b3SparseSymMat33::operator()(u32 i, u32 j) const -{ - B3_ASSERT(i < rowCount); - B3_ASSERT(j < rowCount); - - if (i > j) - { - b3Swap(i, j); - } - - b3RowValueList* vs = rows + i; - for (b3RowValue* v = vs->head; v; v = v->next) - { - if (v->column == j) - { - return v->value; - } - } - - return b3Mat33_zero; -} - -inline b3Mat33& b3SparseSymMat33::operator()(u32 i, u32 j) -{ - B3_ASSERT(i < rowCount); - B3_ASSERT(j < rowCount); - - if (i > j) - { - b3Swap(i, j); - } - - b3RowValueList* vs = rows + i; - for (b3RowValue* v = vs->head; v; v = v->next) - { - if (v->column == j) - { - return v->value; - } - } - - b3RowValue* v = (b3RowValue*)b3Alloc(sizeof(b3RowValue)); - v->column = j; - v->value.SetZero(); - - vs->PushFront(v); - - return v->value; -} - -inline void b3SparseSymMat33::operator+=(const b3SparseSymMat33& m) -{ - B3_ASSERT(rowCount == m.rowCount); - - for (u32 i = 0; i < m.rowCount; ++i) - { - b3RowValueList* mvs = m.rows + i; - - for (b3RowValue* v = mvs->head; v; v = v->next) - { - u32 j = v->column; - - (*this)(i, j) += v->value; - } - } -} - -inline void b3SparseSymMat33::operator-=(const b3SparseSymMat33& m) -{ - B3_ASSERT(rowCount == m.rowCount); - - for (u32 i = 0; i < m.rowCount; ++i) - { - b3RowValueList* mvs = m.rows + i; - - for (b3RowValue* v = mvs->head; v; v = v->next) - { - u32 j = v->column; - - (*this)(i, j) -= v->value; - } - } -} - -inline void b3Add(b3SparseSymMat33& out, const b3SparseSymMat33& a, const b3SparseSymMat33& b) -{ - out = a; - out += b; -} - -inline void b3Sub(b3SparseSymMat33& out, const b3SparseSymMat33& a, const b3SparseSymMat33& b) -{ - out = a; - out -= b; -} - -inline void b3Mul(b3DenseVec3& out, const b3SparseSymMat33& A, const b3DenseVec3& v) -{ - B3_ASSERT(A.rowCount == out.n); - - out.SetZero(); - - for (u32 i = 0; i < A.rowCount; ++i) - { - b3RowValueList* vs = A.rows + i; - - for (b3RowValue* vA = vs->head; vA; vA = vA->next) - { - u32 j = vA->column; - b3Mat33 a = vA->value; - - out[i] += a * v[j]; - - if (i != j) - { - // a_ij == a_ji - out[j] += a * v[i]; - } - } - } -} - -inline void b3Mul(b3SparseSymMat33& out, float32 s, const b3SparseSymMat33& B) -{ - B3_ASSERT(out.rowCount == B.rowCount); - - if (s == 0.0f) - { - return; - } - - out = B; - - for (u32 i = 0; i < out.rowCount; ++i) - { - b3RowValueList* vs = out.rows + i; - for (b3RowValue* v = vs->head; v; v = v->next) - { - v->value = s * v->value; - } - } -} - -inline b3SparseSymMat33 operator+(const b3SparseSymMat33& A, const b3SparseSymMat33& B) -{ - b3SparseSymMat33 result(A.rowCount); - b3Add(result, A, B); - return result; -} - -inline b3SparseSymMat33 operator-(const b3SparseSymMat33& A, const b3SparseSymMat33& B) -{ - b3SparseSymMat33 result(A.rowCount); - b3Sub(result, A, B); - return result; -} - -inline b3SparseSymMat33 operator*(float32 A, const b3SparseSymMat33& B) -{ - b3SparseSymMat33 result(B.rowCount); - b3Mul(result, A, B); - return result; -} - -inline b3DenseVec3 operator*(const b3SparseSymMat33& A, const b3DenseVec3& v) -{ - b3DenseVec3 result(v.n); - b3Mul(result, A, v); - return result; -} - -#endif diff --git a/include/bounce/sparse/sparse_sym_mat33_view.h b/include/bounce/sparse/sparse_sym_mat33_view.h deleted file mode 100644 index 7d944d6..0000000 --- a/include/bounce/sparse/sparse_sym_mat33_view.h +++ /dev/null @@ -1,132 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef B3_SPARSE_SYM_MAT_33_VIEW_H -#define B3_SPARSE_SYM_MAT_33_VIEW_H - -#include -#include - -// A read-only sparse symmetric matrix. -struct b3SparseSymMat33View -{ - // - b3SparseSymMat33View(const b3SparseSymMat33& _m); - - // - ~b3SparseSymMat33View(); - - // - const b3Mat33& operator()(u32 i, u32 j) const; - - u32 rowCount; - b3RowValueArray* rows; -}; - -inline b3SparseSymMat33View::b3SparseSymMat33View(const b3SparseSymMat33& _m) -{ - rowCount = _m.rowCount; - rows = (b3RowValueArray*)b3Alloc(rowCount * sizeof(b3RowValueArray)); - for (u32 i = 0; i < _m.rowCount; ++i) - { - b3RowValueList* rowList = _m.rows + i; - b3RowValueArray* rowArray = rows + i; - - rowArray->count = rowList->count; - rowArray->values = (b3ArrayRowValue*)b3Alloc(rowArray->count * sizeof(b3ArrayRowValue)); - - u32 valueIndex = 0; - for (b3RowValue* v = rowList->head; v; v = v->next) - { - rowArray->values[valueIndex].column = v->column; - rowArray->values[valueIndex].value = v->value; - ++valueIndex; - } - } -} - -inline b3SparseSymMat33View::~b3SparseSymMat33View() -{ - for (u32 i = 0; i < rowCount; ++i) - { - b3RowValueArray* rowArray = rows + i; - b3Free(rowArray->values); - } - b3Free(rows); -} - -inline const b3Mat33& b3SparseSymMat33View::operator()(u32 i, u32 j) const -{ - B3_ASSERT(i < rowCount); - B3_ASSERT(j < rowCount); - - if (i > j) - { - b3Swap(i, j); - } - - b3RowValueArray* vs = rows + i; - - for (u32 c = 0; c < vs->count; ++c) - { - b3ArrayRowValue* rv = vs->values + c; - - if (rv->column == j) - { - return rv->value; - } - } - - return b3Mat33_zero; -} - -inline void b3Mul(b3DenseVec3& out, const b3SparseSymMat33View& A, const b3DenseVec3& v) -{ - B3_ASSERT(A.rowCount == out.n); - - out.SetZero(); - - for (u32 i = 0; i < A.rowCount; ++i) - { - b3RowValueArray* rowArray = A.rows + i; - - for (u32 c = 0; c < rowArray->count; ++c) - { - b3ArrayRowValue* rv = rowArray->values + c; - - u32 j = rv->column; - b3Mat33 a = rv->value; - - out[i] += a * v[j]; - - if (i != j) - { - out[j] += a * v[i]; - } - } - } -} - -inline b3DenseVec3 operator*(const b3SparseSymMat33View& A, const b3DenseVec3& v) -{ - b3DenseVec3 result(v.n); - b3Mul(result, A, v); - return result; -} - -#endif \ No newline at end of file diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index d2a3af6..2e442ab 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -21,8 +21,9 @@ #include #include #include -#include #include +#include +#include #include #include @@ -199,7 +200,20 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : sfdef.bu = 1.0f; sfdef.bv = 1.0f; - CreateForce(sfdef); + if (def.streching > 0.0f) + { + CreateForce(sfdef); + } + + b3ShearForceDef shdef; + shdef.triangle = triangle; + shdef.shearing = def.shearing; + shdef.damping = def.damping; + + if (def.shearing > 0.0f) + { + CreateForce(shdef); + } } // Initialize forces diff --git a/src/bounce/cloth/cloth_force_solver.cpp b/src/bounce/cloth/cloth_force_solver.cpp index e530d54..8da4703 100644 --- a/src/bounce/cloth/cloth_force_solver.cpp +++ b/src/bounce/cloth/cloth_force_solver.cpp @@ -21,8 +21,8 @@ #include #include #include -#include -#include +#include +#include #include // Here, we solve Ax = b using the Modified Preconditioned Conjugate Gradient (MPCG) algorithm. @@ -128,7 +128,7 @@ void b3ClothForceSolver::ApplyConstraints() // Solve Ax = b static void b3SolveMPCG(b3DenseVec3& x, - const b3SparseSymMat33View& A, const b3DenseVec3& b, + const b3SparseMat33View& A, const b3DenseVec3& b, const b3DiagMat33& S, const b3DenseVec3& z, const b3DenseVec3& y, const b3DiagMat33& I, u32 maxIterations = 20) { @@ -214,9 +214,9 @@ void b3ClothForceSolver::Solve(float32 dt, const b3Vec3& gravity) b3DenseVec3 sy(m_particleCount); b3DenseVec3 sz(m_particleCount); b3DenseVec3 sx0(m_particleCount); - b3SparseSymMat33 M(m_particleCount); - b3SparseSymMat33 dfdx(m_particleCount); - b3SparseSymMat33 dfdv(m_particleCount); + b3SparseMat33 M(m_particleCount); + b3SparseMat33 dfdx(m_particleCount); + b3SparseMat33 dfdv(m_particleCount); b3DiagMat33 S(m_particleCount); b3DiagMat33 I(m_particleCount); I.SetIdentity(); @@ -261,10 +261,10 @@ void b3ClothForceSolver::Solve(float32 dt, const b3Vec3& gravity) // b = h * (f0 + h * dfdx * v0 + dfdx * y) // A - b3SparseSymMat33 A = M - h * dfdv - h * h * dfdx; + b3SparseMat33 A = M - h * dfdv - h * h * dfdx; // View for A - b3SparseSymMat33View viewA(A); + b3SparseMat33View viewA(A); // b b3DenseVec3 b = h * (sf + h * (dfdx * sv) + dfdx * sy); diff --git a/src/bounce/cloth/force.cpp b/src/bounce/cloth/force.cpp index acc3227..4de3bf5 100644 --- a/src/bounce/cloth/force.cpp +++ b/src/bounce/cloth/force.cpp @@ -18,6 +18,7 @@ #include #include +#include #include b3Force* b3Force::Create(const b3ForceDef* def) @@ -31,6 +32,12 @@ b3Force* b3Force::Create(const b3ForceDef* def) force = new (block) b3StrechForce((b3StrechForceDef*)def); break; } + case e_shearForce: + { + void* block = b3Alloc(sizeof(b3ShearForce)); + force = new (block) b3ShearForce((b3ShearForceDef*)def); + break; + } case e_springForce: { void* block = b3Alloc(sizeof(b3SpringForce)); @@ -60,6 +67,13 @@ void b3Force::Destroy(b3Force* force) b3Free(force); break; } + case e_shearForce: + { + b3ShearForce* o = (b3ShearForce*)force; + o->~b3ShearForce(); + b3Free(force); + break; + } case e_springForce: { b3SpringForce* o = (b3SpringForce*)force; diff --git a/src/bounce/cloth/shear_force.cpp b/src/bounce/cloth/shear_force.cpp new file mode 100644 index 0000000..b79b549 --- /dev/null +++ b/src/bounce/cloth/shear_force.cpp @@ -0,0 +1,219 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +b3ShearForce::b3ShearForce(const b3ShearForceDef* def) +{ + m_type = e_shearForce; + m_triangle = def->triangle; + m_ks = def->shearing; + m_kd = def->damping; + m_f1.SetZero(); + m_f2.SetZero(); + m_f3.SetZero(); +} + +b3ShearForce::~b3ShearForce() +{ + +} + +bool b3ShearForce::HasParticle(const b3Particle* particle) const +{ + b3Cloth* cloth = m_triangle->m_cloth; + u32 triangleIndex = m_triangle->m_triangle; + b3ClothMeshTriangle* triangle = cloth->m_mesh->triangles + triangleIndex; + + b3Particle* p1 = cloth->m_particles[triangle->v1]; + b3Particle* p2 = cloth->m_particles[triangle->v2]; + b3Particle* p3 = cloth->m_particles[triangle->v3]; + + return p1 == particle || p2 == particle || p3 == particle; +} + +void b3ShearForce::Apply(const b3ClothForceSolverData* data) +{ + b3Cloth* cloth = m_triangle->m_cloth; + u32 triangleIndex = m_triangle->m_triangle; + b3ClothMeshTriangle* triangle = cloth->m_mesh->triangles + triangleIndex; + + float32 alpha = m_triangle->m_alpha; + float32 du1 = m_triangle->m_du1; + float32 dv1 = m_triangle->m_dv1; + float32 du2 = m_triangle->m_du2; + float32 dv2 = m_triangle->m_dv2; + float32 inv_det = m_triangle->m_inv_det; + + b3Particle* p1 = cloth->m_particles[triangle->v1]; + b3Particle* p2 = cloth->m_particles[triangle->v2]; + b3Particle* p3 = cloth->m_particles[triangle->v3]; + + u32 i1 = p1->m_solverId; + u32 i2 = p2->m_solverId; + u32 i3 = p3->m_solverId; + + b3DenseVec3& x = *data->x; + b3DenseVec3& v = *data->v; + b3DenseVec3& f = *data->f; + b3SparseMat33& dfdx = *data->dfdx; + b3SparseMat33& dfdv = *data->dfdv; + + b3Vec3 x1 = x[i1]; + b3Vec3 x2 = x[i2]; + b3Vec3 x3 = x[i3]; + + b3Vec3 v1 = v[i1]; + b3Vec3 v2 = v[i2]; + b3Vec3 v3 = v[i3]; + + b3Mat33 I; I.SetIdentity(); + + b3Vec3 dx1 = x2 - x1; + b3Vec3 dx2 = x3 - x1; + + b3Vec3 wu = inv_det * (dv2 * dx1 - dv1 * dx2); + b3Vec3 wv = inv_det * (-du2 * dx1 + du1 * dx2); + + b3Vec3 dwudx; + dwudx[0] = inv_det * (dv1 - dv2); + dwudx[1] = inv_det * dv2; + dwudx[2] = -inv_det * dv1; + + b3Vec3 dwvdx; + dwvdx[0] = inv_det * (du2 - du1); + dwvdx[1] = -inv_det * du2; + dwvdx[2] = inv_det * du1; + + m_f1.SetZero(); + m_f2.SetZero(); + m_f3.SetZero(); + + // Jacobian + b3Vec3 dCdx[3]; + for (u32 i = 0; i < 3; ++i) + { + dCdx[i] = alpha * (dwudx[i] * wv + dwvdx[i] * wu); + } + + if (m_ks > 0.0f) + { + float32 C = alpha * b3Dot(wu, wv); + + // Force + b3Vec3 fs[3]; + for (u32 i = 0; i < 3; ++i) + { + fs[i] = -m_ks * C * dCdx[i]; + } + + m_f1 += fs[0]; + m_f2 += fs[1]; + m_f3 += fs[2]; + + // Jacobian + b3Mat33 d2Cxij; + for (u32 i = 0; i < 3; ++i) + { + for (u32 j = 0; j < 3; ++j) + { + d2Cxij(i, j) = alpha * (dwudx[i] * dwvdx[j] + dwudx[j] * dwvdx[i]); + } + } + + b3Mat33 J[3][3]; + for (u32 i = 0; i < 3; ++i) + { + for (u32 j = 0; j < 3; ++j) + { + b3Mat33 Jij = -m_ks * (b3Outer(dCdx[i], dCdx[j]) + (C * d2Cxij(i, j) * I)); + + J[i][j] = Jij; + } + } + + dfdx(i1, i1) += J[0][0]; + dfdx(i1, i2) += J[0][1]; + dfdx(i1, i3) += J[0][2]; + + dfdx(i2, i1) += J[1][0]; + dfdx(i2, i2) += J[1][1]; + dfdx(i2, i3) += J[1][2]; + + dfdx(i3, i1) += J[2][0]; + dfdx(i3, i2) += J[2][1]; + dfdx(i3, i3) += J[2][2]; + } + + if (m_kd > 0.0f) + { + b3Vec3 vs[3] = { v1, v2, v3 }; + + float32 dCdt = 0.0f; + for (u32 i = 0; i < 3; ++i) + { + dCdt += b3Dot(dCdx[i], vs[i]); + } + + // Force + b3Vec3 fs[3]; + for (u32 i = 0; i < 3; ++i) + { + fs[i] = -m_kd * dCdt * dCdx[i]; + } + + m_f1 += fs[0]; + m_f2 += fs[1]; + m_f3 += fs[2]; + + // Jacobian + b3Mat33 J[3][3]; + for (u32 i = 0; i < 3; ++i) + { + for (u32 j = 0; j < 3; ++j) + { + b3Mat33 Jij = -m_kd * b3Outer(dCdx[i], dCdx[j]); + + J[i][j] = Jij; + } + } + + dfdv(i1, i1) += J[0][0]; + dfdv(i1, i2) += J[0][1]; + dfdv(i1, i3) += J[0][2]; + + dfdv(i2, i1) += J[1][0]; + dfdv(i2, i2) += J[1][1]; + dfdv(i2, i3) += J[1][2]; + + dfdv(i3, i1) += J[2][0]; + dfdv(i3, i2) += J[2][1]; + dfdv(i3, i3) += J[2][2]; + } + + f[i1] += m_f1; + f[i2] += m_f2; + f[i3] += m_f3; +} \ No newline at end of file diff --git a/src/bounce/cloth/spring_force.cpp b/src/bounce/cloth/spring_force.cpp index 1f687ae..8248c6d 100644 --- a/src/bounce/cloth/spring_force.cpp +++ b/src/bounce/cloth/spring_force.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include void b3SpringForceDef::Initialize(b3Particle* particle1, b3Particle* particle2, float32 structuralStiffness, float32 dampingStiffness) { @@ -55,8 +55,8 @@ void b3SpringForce::Apply(const b3ClothForceSolverData* data) b3DenseVec3& x = *data->x; b3DenseVec3& v = *data->v; b3DenseVec3& f = *data->f; - b3SparseSymMat33& dfdx = *data->dfdx; - b3SparseSymMat33& dfdv = *data->dfdv; + b3SparseMat33& dfdx = *data->dfdx; + b3SparseMat33& dfdv = *data->dfdv; u32 i1 = m_p1->m_solverId; u32 i2 = m_p2->m_solverId; @@ -87,12 +87,12 @@ void b3SpringForce::Apply(const b3ClothForceSolverData* data) // Jacobian b3Mat33 Jx11 = -m_ks * (b3Outer(dx, dx) + (1.0f - m_L0 / L) * (I - b3Outer(dx, dx))); b3Mat33 Jx12 = -Jx11; - //b3Mat33 Jx21 = Jx12; + b3Mat33 Jx21 = Jx12; b3Mat33 Jx22 = Jx11; dfdx(i1, i1) += Jx11; dfdx(i1, i2) += Jx12; - //dfdx(i2, i1) += Jx21; + dfdx(i2, i1) += Jx21; dfdx(i2, i2) += Jx22; } } @@ -106,12 +106,12 @@ void b3SpringForce::Apply(const b3ClothForceSolverData* data) b3Mat33 Jv11 = -m_kd * I; b3Mat33 Jv12 = -Jv11; - //b3Mat33 Jv21 = Jv12; + b3Mat33 Jv21 = Jv12; b3Mat33 Jv22 = Jv11; dfdv(i1, i1) += Jv11; dfdv(i1, i2) += Jv12; - //dfdv(i2, i1) += Jv21; + dfdv(i2, i1) += Jv21; dfdv(i2, i2) += Jv22; } diff --git a/src/bounce/cloth/strech_force.cpp b/src/bounce/cloth/strech_force.cpp index d208441..e918809 100644 --- a/src/bounce/cloth/strech_force.cpp +++ b/src/bounce/cloth/strech_force.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include b3StrechForce::b3StrechForce(const b3StrechForceDef* def) { @@ -80,8 +80,8 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) b3DenseVec3& x = *data->x; b3DenseVec3& v = *data->v; b3DenseVec3& f = *data->f; - b3SparseSymMat33& dfdx = *data->dfdx; - b3SparseSymMat33& dfdv = *data->dfdv; + b3SparseMat33& dfdx = *data->dfdx; + b3SparseMat33& dfdv = *data->dfdv; b3Vec3 x1 = x[i1]; b3Vec3 x2 = x[i2]; @@ -163,12 +163,12 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) dfdx(i1, i2) += J[0][1]; dfdx(i1, i3) += J[0][2]; - //dfdx(i2, i1) += J[1][0]; + dfdx(i2, i1) += J[1][0]; dfdx(i2, i2) += J[1][1]; dfdx(i2, i3) += J[1][2]; - //dfdx(i3, i1) += J[2][0]; - //dfdx(i3, i2) += J[2][1]; + dfdx(i3, i1) += J[2][0]; + dfdx(i3, i2) += J[2][1]; dfdx(i3, i3) += J[2][2]; } } @@ -209,12 +209,12 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) dfdv(i1, i2) += J[0][1]; dfdv(i1, i3) += J[0][2]; - //dfdv(i2, i1) += J[1][0]; + dfdv(i2, i1) += J[1][0]; dfdv(i2, i2) += J[1][1]; dfdv(i2, i3) += J[1][2]; - //dfdv(i3, i1) += J[2][0]; - //dfdv(i3, i2) += J[2][1]; + dfdv(i3, i1) += J[2][0]; + dfdv(i3, i2) += J[2][1]; dfdv(i3, i3) += J[2][2]; } } @@ -266,12 +266,12 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) dfdx(i1, i2) += J[0][1]; dfdx(i1, i3) += J[0][2]; - //dfdx(i2, i1) += J[1][0]; + dfdx(i2, i1) += J[1][0]; dfdx(i2, i2) += J[1][1]; dfdx(i2, i3) += J[1][2]; - //dfdx(i3, i1) += J[2][0]; - //dfdx(i3, i2) += J[2][1]; + dfdx(i3, i1) += J[2][0]; + dfdx(i3, i2) += J[2][1]; dfdx(i3, i3) += J[2][2]; } } @@ -313,12 +313,12 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) dfdv(i1, i2) += J[0][1]; dfdv(i1, i3) += J[0][2]; - //dfdv(i2, i1) += J[1][0]; + dfdv(i2, i1) += J[1][0]; dfdv(i2, i2) += J[1][1]; dfdv(i2, i3) += J[1][2]; - //dfdv(i3, i1) += J[2][0]; - //dfdv(i3, i2) += J[2][1]; + dfdv(i3, i1) += J[2][0]; + dfdv(i3, i2) += J[2][1]; dfdv(i3, i3) += J[2][2]; } } From 51d45ae3725182dbbcfc07b653e7da797919cf6a Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 27 Jun 2019 17:03:24 -0300 Subject: [PATCH 183/198] Optimization --- src/bounce/cloth/cloth_force_solver.cpp | 2 +- src/bounce/cloth/shear_force.cpp | 13 +++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/bounce/cloth/cloth_force_solver.cpp b/src/bounce/cloth/cloth_force_solver.cpp index 8da4703..2fb3953 100644 --- a/src/bounce/cloth/cloth_force_solver.cpp +++ b/src/bounce/cloth/cloth_force_solver.cpp @@ -261,7 +261,7 @@ void b3ClothForceSolver::Solve(float32 dt, const b3Vec3& gravity) // b = h * (f0 + h * dfdx * v0 + dfdx * y) // A - b3SparseMat33 A = M - h * dfdv - h * h * dfdx; + b3SparseMat33 A = M - h * dfdv - (h * h) * dfdx; // View for A b3SparseMat33View viewA(A); diff --git a/src/bounce/cloth/shear_force.cpp b/src/bounce/cloth/shear_force.cpp index b79b549..fd0d506 100644 --- a/src/bounce/cloth/shear_force.cpp +++ b/src/bounce/cloth/shear_force.cpp @@ -134,21 +134,14 @@ void b3ShearForce::Apply(const b3ClothForceSolverData* data) m_f3 += fs[2]; // Jacobian - b3Mat33 d2Cxij; - for (u32 i = 0; i < 3; ++i) - { - for (u32 j = 0; j < 3; ++j) - { - d2Cxij(i, j) = alpha * (dwudx[i] * dwvdx[j] + dwudx[j] * dwvdx[i]); - } - } - b3Mat33 J[3][3]; for (u32 i = 0; i < 3; ++i) { for (u32 j = 0; j < 3; ++j) { - b3Mat33 Jij = -m_ks * (b3Outer(dCdx[i], dCdx[j]) + (C * d2Cxij(i, j) * I)); + b3Mat33 d2Cxij = alpha * (dwudx[i] * dwvdx[j] + dwudx[j] * dwvdx[i]) * I; + + b3Mat33 Jij = -m_ks * (b3Outer(dCdx[i], dCdx[j]) + C * d2Cxij); J[i][j] = Jij; } From 4bb367b27a6832cdaec6b2a5296d24fa5f0ebd82 Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 27 Jun 2019 19:18:28 -0300 Subject: [PATCH 184/198] Tossed out a term that makes the system non PD --- src/bounce/cloth/shear_force.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bounce/cloth/shear_force.cpp b/src/bounce/cloth/shear_force.cpp index fd0d506..a6c28a7 100644 --- a/src/bounce/cloth/shear_force.cpp +++ b/src/bounce/cloth/shear_force.cpp @@ -139,9 +139,9 @@ void b3ShearForce::Apply(const b3ClothForceSolverData* data) { for (u32 j = 0; j < 3; ++j) { - b3Mat33 d2Cxij = alpha * (dwudx[i] * dwvdx[j] + dwudx[j] * dwvdx[i]) * I; - - b3Mat33 Jij = -m_ks * (b3Outer(dCdx[i], dCdx[j]) + C * d2Cxij); + //b3Mat33 d2Cxij = alpha * (dwudx[i] * dwvdx[j] + dwudx[j] * dwvdx[i]) * I; + //b3Mat33 Jij = -m_ks * (b3Outer(dCdx[i], dCdx[j]) + C * d2Cxij); + b3Mat33 Jij = -m_ks * b3Outer(dCdx[i], dCdx[j]); J[i][j] = Jij; } From cc5d062ca5352b14ce4f4f0c5884b2d2cc4a1440 Mon Sep 17 00:00:00 2001 From: Irlan Date: Thu, 27 Jun 2019 19:36:45 -0300 Subject: [PATCH 185/198] Enable shearing in test --- examples/testbed/tests/tension_mapping.h | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/testbed/tests/tension_mapping.h b/examples/testbed/tests/tension_mapping.h index 531512c..c328fd5 100644 --- a/examples/testbed/tests/tension_mapping.h +++ b/examples/testbed/tests/tension_mapping.h @@ -65,6 +65,7 @@ public: def.mesh = &m_clothMesh; def.density = 0.2f; def.streching = 10000.0f; + def.shearing = 5000.0f; def.damping = 100.0f; m_cloth = new b3Cloth(def); From 774805e2646d0ec173c46bd429f9a8b78404531f Mon Sep 17 00:00:00 2001 From: Irlan Robson Date: Thu, 27 Jun 2019 19:41:47 -0300 Subject: [PATCH 186/198] Update readme.md --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index d07e26c..3e25bfa 100644 --- a/readme.md +++ b/readme.md @@ -126,6 +126,7 @@ Below are the external dependencies for testbed. If you don't care about testbed * Cloth * Vertex contact, friction +* Strech, shear, spring force types * Linear time solver * Unconditional simulation stability * Ray-casting From 15d5a3b303bde760e5ff6ced0ba1f212c3b33d9b Mon Sep 17 00:00:00 2001 From: Irlan Robson Date: Thu, 27 Jun 2019 20:22:55 -0300 Subject: [PATCH 187/198] Update readme.md --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index 3e25bfa..5e3717e 100644 --- a/readme.md +++ b/readme.md @@ -135,6 +135,7 @@ Below are the external dependencies for testbed. If you don't care about testbed * Soft body * Vertex contact, friction +* Elasticity, plasticity * Linear time solver * Unconditional simulation stability * Ray-casting From 55c4f190b183b4a6bead4f51295fb30694c2cfd1 Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 28 Jun 2019 14:53:09 -0300 Subject: [PATCH 188/198] Improved the code that computes the (u, v) coordinates for a triangle vertices. --- src/bounce/cloth/cloth.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 2e442ab..69a673c 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -150,27 +150,30 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : triangle->m_aabbProxy.owner = triangle; triangle->m_broadPhaseId = m_contactManager.m_broadPhase.CreateProxy(aabb, &triangle->m_aabbProxy); - // uv coordinates for triangle - + b3Vec3 AB = B - A; + b3Vec3 AC = C - A; + // v1 b3Vec2 uv1; uv1.SetZero(); // v2 - b3Vec3 AB = B - A; - b3Vec2 uv2; uv2.x = b3Length(AB); uv2.y = 0.0f; // v3 - b3Vec3 n_AB = b3Normalize(AB); - b3Vec3 AC = C - A; - float32 len_AC = b3Length(AC); + B3_ASSERT(uv2.x > 0.0f); + b3Vec3 n_AB = AB / uv2.x; + + // A = b * h / 2 + // h = (A * 2) / b + float32 A2 = b3Length(b3Cross(AB, AC)); + B3_ASSERT(A2 > 0.0f); b3Vec2 uv3; uv3.x = b3Dot(n_AB, AC); - uv3.y = b3Sqrt(len_AC * len_AC - uv3.x * uv3.x); + uv3.y = A2 / uv2.x; // Strech matrix float32 du1 = uv2.x - uv1.x; From 0e4b1c4d1ef43b3f5c0493204964093c2736327a Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 28 Jun 2019 15:19:22 -0300 Subject: [PATCH 189/198] Reuse triangle area since we use the triangle plane as the (u, v) plane --- src/bounce/cloth/cloth.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 69a673c..cacc89c 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -190,10 +190,8 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : B3_ASSERT(det != 0.0f); triangle->m_inv_det = 1.0f / det; - // Triangle area - b3Vec3 v1(du1, dv1, 0.0f); - b3Vec3 v2(du2, dv2, 0.0f); - triangle->m_alpha = 0.5f * b3Length(b3Cross(v1, v2)); + // Area + triangle->m_alpha = 0.5f * A2; // Create strech force b3StrechForceDef sfdef; From bc050769a5cd231c0f973ccad507a210b3f8145a Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 28 Jun 2019 15:30:13 -0300 Subject: [PATCH 190/198] Update cloth.cpp --- src/bounce/cloth/cloth.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index cacc89c..270ff15 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -172,7 +172,7 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : B3_ASSERT(A2 > 0.0f); b3Vec2 uv3; - uv3.x = b3Dot(n_AB, AC); + uv3.x = b3Dot(AC, n_AB); uv3.y = A2 / uv2.x; // Strech matrix From dddfba5b2159b1641ec2243ece7cefa54393f413 Mon Sep 17 00:00:00 2001 From: Irlan Date: Fri, 28 Jun 2019 21:12:58 -0300 Subject: [PATCH 191/198] Make code clearer --- src/bounce/cloth/cloth.cpp | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index 270ff15..e45cd81 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -153,33 +153,31 @@ b3Cloth::b3Cloth(const b3ClothDef& def) : b3Vec3 AB = B - A; b3Vec3 AC = C - A; - // v1 - b3Vec2 uv1; - uv1.SetZero(); + // uv1 + float32 u1 = 0.0f; + float32 v1 = 0.0f; - // v2 - b3Vec2 uv2; - uv2.x = b3Length(AB); - uv2.y = 0.0f; + // uv2 + float32 u2 = b3Length(AB); + float32 v2 = 0.0f; - // v3 - B3_ASSERT(uv2.x > 0.0f); - b3Vec3 n_AB = AB / uv2.x; + // uv3 + B3_ASSERT(u2 > 0.0f); + b3Vec3 n_AB = AB / u2; // A = b * h / 2 // h = (A * 2) / b float32 A2 = b3Length(b3Cross(AB, AC)); B3_ASSERT(A2 > 0.0f); - b3Vec2 uv3; - uv3.x = b3Dot(AC, n_AB); - uv3.y = A2 / uv2.x; + float32 u3 = b3Dot(AC, n_AB); + float32 v3 = A2 / u2; // Strech matrix - float32 du1 = uv2.x - uv1.x; - float32 dv1 = uv2.y - uv1.y; - float32 du2 = uv3.x - uv1.x; - float32 dv2 = uv3.y - uv1.y; + float32 du1 = u2 - u1; + float32 dv1 = v2 - v1; + float32 du2 = u3 - u1; + float32 dv2 = v3 - v1; triangle->m_du1 = du1; triangle->m_dv1 = dv1; From 0393933ecfd01767fe4d1cf4c57f24efac316c6e Mon Sep 17 00:00:00 2001 From: Irlan Date: Sat, 29 Jun 2019 14:51:35 -0300 Subject: [PATCH 192/198] Put forces into a folder. Added soft b3MouseForce --- examples/testbed/framework/cloth_dragger.cpp | 95 ++++---- examples/testbed/framework/cloth_dragger.h | 49 +++- include/bounce/bounce.h | 8 +- include/bounce/cloth/cloth.h | 1 + include/bounce/cloth/cloth_triangle.h | 1 + include/bounce/cloth/{ => forces}/force.h | 1 + include/bounce/cloth/forces/mouse_force.h | 140 +++++++++++ .../bounce/cloth/{ => forces}/shear_force.h | 2 +- .../bounce/cloth/{ => forces}/spring_force.h | 2 +- .../bounce/cloth/{ => forces}/strech_force.h | 2 +- include/bounce/cloth/particle.h | 1 + src/bounce/cloth/cloth.cpp | 7 +- src/bounce/cloth/cloth_force_solver.cpp | 2 +- src/bounce/cloth/{ => forces}/force.cpp | 22 +- src/bounce/cloth/forces/mouse_force.cpp | 228 ++++++++++++++++++ src/bounce/cloth/{ => forces}/shear_force.cpp | 2 +- .../cloth/{ => forces}/spring_force.cpp | 2 +- .../cloth/{ => forces}/strech_force.cpp | 2 +- 18 files changed, 485 insertions(+), 82 deletions(-) rename include/bounce/cloth/{ => forces}/force.h (98%) create mode 100644 include/bounce/cloth/forces/mouse_force.h rename include/bounce/cloth/{ => forces}/shear_force.h (98%) rename include/bounce/cloth/{ => forces}/spring_force.h (98%) rename include/bounce/cloth/{ => forces}/strech_force.h (98%) rename src/bounce/cloth/{ => forces}/force.cpp (80%) create mode 100644 src/bounce/cloth/forces/mouse_force.cpp rename src/bounce/cloth/{ => forces}/shear_force.cpp (99%) rename src/bounce/cloth/{ => forces}/spring_force.cpp (98%) rename src/bounce/cloth/{ => forces}/strech_force.cpp (99%) diff --git a/examples/testbed/framework/cloth_dragger.cpp b/examples/testbed/framework/cloth_dragger.cpp index fb208c2..40467c9 100644 --- a/examples/testbed/framework/cloth_dragger.cpp +++ b/examples/testbed/framework/cloth_dragger.cpp @@ -20,10 +20,12 @@ b3ClothDragger::b3ClothDragger(b3Ray3* ray, b3Cloth* cloth) { - m_spring = false; + m_staticDrag = true; m_ray = ray; m_cloth = cloth; m_triangle = nullptr; + m_km = 10000.0f; + m_kd = 0.0f; } b3ClothDragger::~b3ClothDragger() @@ -42,7 +44,8 @@ bool b3ClothDragger::StartDragging() } m_mesh = m_cloth->GetMesh(); - m_triangle = m_mesh->triangles + rayOut.triangle; + m_triangleIndex = rayOut.triangle; + m_triangle = m_mesh->triangles + m_triangleIndex; m_x = rayOut.fraction; b3Particle* p1 = m_cloth->GetParticle(m_triangle->v1); @@ -68,42 +71,7 @@ bool b3ClothDragger::StartDragging() m_u = m_v = 0.0f; } - if (m_spring) - { - b3ParticleDef pd; - pd.type = e_staticParticle; - pd.position = B; - - m_particle = m_cloth->CreateParticle(pd); - - { - b3SpringForceDef sfd; - sfd.p1 = m_particle; - sfd.p2 = p1; - sfd.restLength = 0.0f; - sfd.structural = 10000.0f; - m_s1 = (b3SpringForce*)m_cloth->CreateForce(sfd); - } - - { - b3SpringForceDef sfd; - sfd.p1 = m_particle; - sfd.p2 = p2; - sfd.restLength = 0.0f; - sfd.structural = 10000.0f; - m_s2 = (b3SpringForce*)m_cloth->CreateForce(sfd); - } - - { - b3SpringForceDef sfd; - sfd.p1 = m_particle; - sfd.p2 = p3; - sfd.restLength = 0.0f; - sfd.structural = 10000.0f; - m_s3 = (b3SpringForce*)m_cloth->CreateForce(sfd); - } - } - else + if (m_staticDrag) { m_t1 = p1->GetType(); p1->SetType(e_staticParticle); @@ -114,6 +82,27 @@ bool b3ClothDragger::StartDragging() m_t3 = p3->GetType(); p3->SetType(e_staticParticle); } + else + { + b3ParticleDef pd; + pd.type = e_staticParticle; + pd.position = GetPointA(); + + m_particle = m_cloth->CreateParticle(pd); + + b3ClothTriangle* triangle = m_cloth->GetTriangle(m_triangleIndex); + + b3MouseForceDef def; + def.particle = m_particle; + def.triangle = triangle; + def.w2 = m_u; + def.w3 = m_v; + def.w4 = (1.0f - m_u - m_v); + def.mouse = m_km; + def.damping = m_kd; + + m_mf = (b3MouseForce*)m_cloth->CreateForce(def); + } return true; } @@ -127,11 +116,7 @@ void b3ClothDragger::Drag() b3Vec3 dx = B - A; - if (m_spring) - { - m_particle->SetPosition(B); - } - else + if (m_staticDrag) { b3Particle* p1 = m_cloth->GetParticle(m_triangle->v1); p1->ApplyTranslation(dx); @@ -142,11 +127,15 @@ void b3ClothDragger::Drag() b3Particle* p3 = m_cloth->GetParticle(m_triangle->v3); p3->ApplyTranslation(dx); } + else + { + m_particle->SetPosition(B); + } } -void b3ClothDragger::SetSpring(bool bit) +void b3ClothDragger::SetStaticDrag(bool bit) { - if (bit == m_spring) + if (bit == m_staticDrag) { return; } @@ -156,26 +145,24 @@ void b3ClothDragger::SetSpring(bool bit) StopDragging(); } - m_spring = bit; + m_staticDrag = bit; } void b3ClothDragger::StopDragging() { B3_ASSERT(IsDragging() == true); - if (m_spring) - { - m_cloth->DestroyForce(m_s1); - m_cloth->DestroyForce(m_s2); - m_cloth->DestroyForce(m_s3); - m_cloth->DestroyParticle(m_particle); - } - else + if (m_staticDrag) { m_cloth->GetParticle(m_triangle->v1)->SetType(m_t1); m_cloth->GetParticle(m_triangle->v2)->SetType(m_t2); m_cloth->GetParticle(m_triangle->v3)->SetType(m_t3); } + else + { + m_cloth->DestroyForce(m_mf); + m_cloth->DestroyParticle(m_particle); + } m_triangle = nullptr; } diff --git a/examples/testbed/framework/cloth_dragger.h b/examples/testbed/framework/cloth_dragger.h index 6bbb797..9661570 100644 --- a/examples/testbed/framework/cloth_dragger.h +++ b/examples/testbed/framework/cloth_dragger.h @@ -23,7 +23,8 @@ #include #include #include -#include +#include +#include // A cloth triangle dragger. class b3ClothDragger @@ -32,10 +33,18 @@ public: b3ClothDragger(b3Ray3* ray, b3Cloth* cloth); ~b3ClothDragger(); - void SetSpring(bool bit); + void SetStaticDrag(bool bit); - bool GetSpring() const; + bool GetStaticDrag() const; + void SetMouseStiffness(float32 k); + + float32 GetMouseStiffness(); + + void SetMouseDamping(float32 k); + + float32 GetMouseDamping(); + bool IsDragging() const; bool StartDragging(); @@ -53,22 +62,42 @@ private: b3Cloth* m_cloth; const b3ClothMesh* m_mesh; + u32 m_triangleIndex; b3ClothMeshTriangle* m_triangle; float32 m_u, m_v; - bool m_spring; - + float32 m_km; + float32 m_kd; b3Particle* m_particle; - b3SpringForce* m_s1; - b3SpringForce* m_s2; - b3SpringForce* m_s3; + b3MouseForce* m_mf; + bool m_staticDrag; b3ParticleType m_t1, m_t2, m_t3; }; -inline bool b3ClothDragger::GetSpring() const +inline bool b3ClothDragger::GetStaticDrag() const { - return m_spring; + return m_staticDrag; +} + +inline void b3ClothDragger::SetMouseStiffness(float32 k) +{ + m_km = k; +} + +inline float32 b3ClothDragger::GetMouseStiffness() +{ + return m_km; +} + +inline void b3ClothDragger::SetMouseDamping(float32 k) +{ + m_kd = k; +} + +inline float32 b3ClothDragger::GetMouseDamping() +{ + return m_kd; } inline bool b3ClothDragger::IsDragging() const diff --git a/include/bounce/bounce.h b/include/bounce/bounce.h index 1fdc46f..58e0faa 100644 --- a/include/bounce/bounce.h +++ b/include/bounce/bounce.h @@ -68,9 +68,11 @@ #include #include #include -#include -#include -#include + +#include +#include +#include +#include #include #include diff --git a/include/bounce/cloth/cloth.h b/include/bounce/cloth/cloth.h index b4fda69..9419b23 100644 --- a/include/bounce/cloth/cloth.h +++ b/include/bounce/cloth/cloth.h @@ -163,6 +163,7 @@ private: friend class b3ShearForce; friend class b3StrechForce; friend class b3SpringForce; + friend class b3MouseForce; friend class b3ClothContactManager; // Compute mass of each particle. diff --git a/include/bounce/cloth/cloth_triangle.h b/include/bounce/cloth/cloth_triangle.h index 03750d5..4b3f4aa 100644 --- a/include/bounce/cloth/cloth_triangle.h +++ b/include/bounce/cloth/cloth_triangle.h @@ -44,6 +44,7 @@ private: friend class b3Particle; friend class b3ShearForce; friend class b3StrechForce; + friend class b3MouseForce; friend class b3ClothContactManager; friend class b3ParticleTriangleContact; friend class b3ClothSolver; diff --git a/include/bounce/cloth/force.h b/include/bounce/cloth/forces/force.h similarity index 98% rename from include/bounce/cloth/force.h rename to include/bounce/cloth/forces/force.h index 3fb51cd..27873f6 100644 --- a/include/bounce/cloth/force.h +++ b/include/bounce/cloth/forces/force.h @@ -32,6 +32,7 @@ enum b3ForceType e_strechForce, e_shearForce, e_springForce, + e_mouseForce, }; struct b3ForceDef diff --git a/include/bounce/cloth/forces/mouse_force.h b/include/bounce/cloth/forces/mouse_force.h new file mode 100644 index 0000000..9646359 --- /dev/null +++ b/include/bounce/cloth/forces/mouse_force.h @@ -0,0 +1,140 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_MOUSE_FORCE_H +#define B3_MOUSE_FORCE_H + +#include + +class b3ClothTriangle; + +struct b3MouseForceDef : public b3ForceDef +{ + b3MouseForceDef() + { + type = e_mouseForce; + } + + // Particle + b3Particle* particle; + + // Triangle + b3ClothTriangle* triangle; + + // Barycentric coordinates on triangle + float32 w2, w3, w4; + + // Mouse stiffness + float32 mouse; + + // Damping stiffness + float32 damping; +}; + +// Mouse force acting on a particle and triangle. +class b3MouseForce : public b3Force +{ +public: + bool HasParticle(const b3Particle* particle) const; + + b3Particle* GetParticle() const; + + b3ClothTriangle* GetTriangle() const; + + float32 GetMouseStiffness() const; + + float32 GetDampingStiffness() const; + + b3Vec3 GetActionForce1() const; + + b3Vec3 GetActionForce2() const; + + b3Vec3 GetActionForce3() const; + + b3Vec3 GetActionForce4() const; +private: + friend class b3Force; + friend class b3Cloth; + + b3MouseForce(const b3MouseForceDef* def); + ~b3MouseForce(); + + void Apply(const b3ClothForceSolverData* data); + + // Solver shared + + // Particle + b3Particle* m_particle; + + // Triangle + b3ClothTriangle* m_triangle; + + // Barycentric coordinates + float32 m_w2, m_w3, m_w4; + + // Mouse stiffness + float32 m_km; + + // Damping stiffness + float32 m_kd; + + // Action forces + b3Vec3 m_f1, m_f2, m_f3, m_f4; +}; + +inline b3Particle* b3MouseForce::GetParticle() const +{ + return m_particle; +} + +inline b3ClothTriangle* b3MouseForce::GetTriangle() const +{ + return m_triangle; +} + +inline float32 b3MouseForce::GetMouseStiffness() const +{ + return m_km; +} + +inline float32 b3MouseForce::GetDampingStiffness() const +{ + return m_kd; +} + +inline b3Vec3 b3MouseForce::GetActionForce1() const +{ + return m_f1; +} + +inline b3Vec3 b3MouseForce::GetActionForce2() const +{ + return m_f2; +} + +inline b3Vec3 b3MouseForce::GetActionForce3() const +{ + return m_f3; +} + +inline b3Vec3 b3MouseForce::GetActionForce4() const +{ + return m_f4; +} + +#endif \ No newline at end of file diff --git a/include/bounce/cloth/shear_force.h b/include/bounce/cloth/forces/shear_force.h similarity index 98% rename from include/bounce/cloth/shear_force.h rename to include/bounce/cloth/forces/shear_force.h index 8485795..9092100 100644 --- a/include/bounce/cloth/shear_force.h +++ b/include/bounce/cloth/forces/shear_force.h @@ -19,7 +19,7 @@ #ifndef B3_SHEAR_FORCE_H #define B3_SHEAR_FORCE_H -#include +#include class b3ClothTriangle; diff --git a/include/bounce/cloth/spring_force.h b/include/bounce/cloth/forces/spring_force.h similarity index 98% rename from include/bounce/cloth/spring_force.h rename to include/bounce/cloth/forces/spring_force.h index fa01405..3cd0a41 100644 --- a/include/bounce/cloth/spring_force.h +++ b/include/bounce/cloth/forces/spring_force.h @@ -19,7 +19,7 @@ #ifndef B3_SPRING_FORCE_H #define B3_SPRING_FORCE_H -#include +#include struct b3SpringForceDef : public b3ForceDef { diff --git a/include/bounce/cloth/strech_force.h b/include/bounce/cloth/forces/strech_force.h similarity index 98% rename from include/bounce/cloth/strech_force.h rename to include/bounce/cloth/forces/strech_force.h index b183c05..7d8dde9 100644 --- a/include/bounce/cloth/strech_force.h +++ b/include/bounce/cloth/forces/strech_force.h @@ -19,7 +19,7 @@ #ifndef B3_STRECH_FORCE_H #define B3_STRECH_FORCE_H -#include +#include class b3ClothTriangle; diff --git a/include/bounce/cloth/particle.h b/include/bounce/cloth/particle.h index 4de14cb..a4d9aef 100644 --- a/include/bounce/cloth/particle.h +++ b/include/bounce/cloth/particle.h @@ -123,6 +123,7 @@ private: friend class b3StrechForce; friend class b3ShearForce; friend class b3SpringForce; + friend class b3MouseForce; b3Particle(const b3ParticleDef& def, b3Cloth* cloth); ~b3Particle(); diff --git a/src/bounce/cloth/cloth.cpp b/src/bounce/cloth/cloth.cpp index e45cd81..b5f957b 100644 --- a/src/bounce/cloth/cloth.cpp +++ b/src/bounce/cloth/cloth.cpp @@ -20,10 +20,9 @@ #include #include #include -#include -#include -#include -#include +#include +#include +#include #include #include diff --git a/src/bounce/cloth/cloth_force_solver.cpp b/src/bounce/cloth/cloth_force_solver.cpp index 2fb3953..c31aaab 100644 --- a/src/bounce/cloth/cloth_force_solver.cpp +++ b/src/bounce/cloth/cloth_force_solver.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/bounce/cloth/force.cpp b/src/bounce/cloth/forces/force.cpp similarity index 80% rename from src/bounce/cloth/force.cpp rename to src/bounce/cloth/forces/force.cpp index 4de3bf5..c1368b5 100644 --- a/src/bounce/cloth/force.cpp +++ b/src/bounce/cloth/forces/force.cpp @@ -16,10 +16,11 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include -#include -#include -#include +#include +#include +#include +#include +#include b3Force* b3Force::Create(const b3ForceDef* def) { @@ -44,6 +45,12 @@ b3Force* b3Force::Create(const b3ForceDef* def) force = new (block) b3SpringForce((b3SpringForceDef*)def); break; } + case e_mouseForce: + { + void* block = b3Alloc(sizeof(b3MouseForce)); + force = new (block) b3MouseForce((b3MouseForceDef*)def); + break; + } default: { B3_ASSERT(false); @@ -81,6 +88,13 @@ void b3Force::Destroy(b3Force* force) b3Free(force); break; } + case e_mouseForce: + { + b3MouseForce* o = (b3MouseForce*)force; + o->~b3MouseForce(); + b3Free(force); + break; + } default: { B3_ASSERT(false); diff --git a/src/bounce/cloth/forces/mouse_force.cpp b/src/bounce/cloth/forces/mouse_force.cpp new file mode 100644 index 0000000..c15b01b --- /dev/null +++ b/src/bounce/cloth/forces/mouse_force.cpp @@ -0,0 +1,228 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +b3MouseForce::b3MouseForce(const b3MouseForceDef* def) +{ + m_type = e_mouseForce; + m_particle = def->particle; + m_triangle = def->triangle; + m_w2 = def->w2; + m_w3 = def->w3; + m_w4 = def->w4; + m_km = def->mouse; + m_kd = def->damping; + m_f1.SetZero(); + m_f2.SetZero(); + m_f3.SetZero(); + m_f4.SetZero(); +} + +b3MouseForce::~b3MouseForce() +{ + +} + +bool b3MouseForce::HasParticle(const b3Particle* particle) const +{ + b3Cloth* cloth = m_triangle->m_cloth; + u32 triangleIndex = m_triangle->m_triangle; + b3ClothMeshTriangle* triangle = cloth->m_mesh->triangles + triangleIndex; + + b3Particle* p1 = cloth->m_particles[triangle->v1]; + b3Particle* p2 = cloth->m_particles[triangle->v2]; + b3Particle* p3 = cloth->m_particles[triangle->v3]; + + return m_particle == particle || p1 == particle || p2 == particle || p3 == particle; +} + +void b3MouseForce::Apply(const b3ClothForceSolverData* data) +{ + b3Cloth* cloth = m_triangle->m_cloth; + u32 triangleIndex = m_triangle->m_triangle; + b3ClothMeshTriangle* triangle = cloth->m_mesh->triangles + triangleIndex; + + b3Particle* p1 = m_particle; + b3Particle* p2 = cloth->m_particles[triangle->v1]; + b3Particle* p3 = cloth->m_particles[triangle->v2]; + b3Particle* p4 = cloth->m_particles[triangle->v3]; + + u32 i1 = p1->m_solverId; + u32 i2 = p2->m_solverId; + u32 i3 = p3->m_solverId; + u32 i4 = p4->m_solverId; + + b3DenseVec3& x = *data->x; + b3DenseVec3& v = *data->v; + b3DenseVec3& f = *data->f; + b3SparseMat33& dfdx = *data->dfdx; + b3SparseMat33& dfdv = *data->dfdv; + + b3Vec3 x1 = x[i1]; + b3Vec3 x2 = x[i2]; + b3Vec3 x3 = x[i3]; + b3Vec3 x4 = x[i4]; + + b3Vec3 v1 = v[i1]; + b3Vec3 v2 = v[i2]; + b3Vec3 v3 = v[i3]; + b3Vec3 v4 = v[i4]; + + b3Mat33 I; I.SetIdentity(); + + m_f1.SetZero(); + m_f2.SetZero(); + m_f3.SetZero(); + m_f4.SetZero(); + + b3Vec3 c2 = m_w2 * x2 + m_w3 * x3 + m_w4 * x4; + + b3Vec3 d = x1 - c2; + float32 len = b3Length(d); + + if (len > 0.0f) + { + b3Vec3 n = d / len; + + // Jacobian + b3Vec3 dCdx[4]; + dCdx[0] = n; + dCdx[1] = -m_w2 * n; + dCdx[2] = -m_w3 * n; + dCdx[3] = -m_w4 * n; + + if (m_km > 0.0f) + { + float32 C = len; + + // Force + b3Vec3 fs[4]; + for (u32 i = 0; i < 4; ++i) + { + fs[i] = -m_km * C * dCdx[i]; + } + + m_f1 += fs[0]; + m_f2 += fs[1]; + m_f3 += fs[2]; + m_f4 += fs[3]; + + // Jacobian + b3Mat33 J[4][4]; + for (u32 i = 0; i < 4; ++i) + { + for (u32 j = 0; j < 4; ++j) + { + //b3Mat33 d2Cvxij; + //b3Mat33 Jij = -m_km * (b3Outer(dCdx[i], dCdx[j]) + C * d2Cvxij); + b3Mat33 Jij = -m_km * b3Outer(dCdx[i], dCdx[j]); + + J[i][j] = Jij; + } + } + + dfdx(i1, i1) += J[0][0]; + dfdx(i1, i2) += J[0][1]; + dfdx(i1, i3) += J[0][2]; + dfdx(i1, i4) += J[0][3]; + + dfdx(i2, i1) += J[1][0]; + dfdx(i2, i2) += J[1][1]; + dfdx(i2, i3) += J[1][2]; + dfdx(i2, i4) += J[1][3]; + + dfdx(i3, i1) += J[2][0]; + dfdx(i3, i2) += J[2][1]; + dfdx(i3, i3) += J[2][2]; + dfdx(i3, i4) += J[2][3]; + + dfdx(i4, i1) += J[3][0]; + dfdx(i4, i2) += J[3][1]; + dfdx(i4, i3) += J[3][2]; + dfdx(i4, i4) += J[3][3]; + } + + if (m_kd > 0.0f) + { + b3Vec3 vs[4] = { v1, v2, v3, v4 }; + + float32 dCdt = 0.0f; + for (u32 i = 0; i < 4; ++i) + { + dCdt += b3Dot(dCdx[i], vs[i]); + } + + // Force + b3Vec3 fs[4]; + for (u32 i = 0; i < 4; ++i) + { + fs[i] = -m_kd * dCdt * dCdx[i]; + } + + m_f1 += fs[0]; + m_f2 += fs[1]; + m_f3 += fs[2]; + m_f4 += fs[3]; + + // Jacobian + b3Mat33 J[4][4]; + for (u32 i = 0; i < 4; ++i) + { + for (u32 j = 0; j < 4; ++j) + { + b3Mat33 Jij = -m_kd * b3Outer(dCdx[i], dCdx[j]); + + J[i][j] = Jij; + } + } + + dfdv(i1, i1) += J[0][0]; + dfdv(i1, i2) += J[0][1]; + dfdv(i1, i3) += J[0][2]; + dfdv(i1, i4) += J[0][3]; + + dfdv(i2, i1) += J[1][0]; + dfdv(i2, i2) += J[1][1]; + dfdv(i2, i3) += J[1][2]; + dfdv(i2, i4) += J[1][3]; + + dfdv(i3, i1) += J[2][0]; + dfdv(i3, i2) += J[2][1]; + dfdv(i3, i3) += J[2][2]; + dfdv(i3, i4) += J[2][3]; + + dfdv(i4, i1) += J[3][0]; + dfdv(i4, i2) += J[3][1]; + dfdv(i4, i3) += J[3][2]; + dfdv(i4, i4) += J[3][3]; + } + } + + f[i1] += m_f1; + f[i2] += m_f2; + f[i3] += m_f3; + f[i4] += m_f4; +} \ No newline at end of file diff --git a/src/bounce/cloth/shear_force.cpp b/src/bounce/cloth/forces/shear_force.cpp similarity index 99% rename from src/bounce/cloth/shear_force.cpp rename to src/bounce/cloth/forces/shear_force.cpp index a6c28a7..a94f835 100644 --- a/src/bounce/cloth/shear_force.cpp +++ b/src/bounce/cloth/forces/shear_force.cpp @@ -16,7 +16,7 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include +#include #include #include #include diff --git a/src/bounce/cloth/spring_force.cpp b/src/bounce/cloth/forces/spring_force.cpp similarity index 98% rename from src/bounce/cloth/spring_force.cpp rename to src/bounce/cloth/forces/spring_force.cpp index 8248c6d..4c365a1 100644 --- a/src/bounce/cloth/spring_force.cpp +++ b/src/bounce/cloth/forces/spring_force.cpp @@ -16,7 +16,7 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include +#include #include #include #include diff --git a/src/bounce/cloth/strech_force.cpp b/src/bounce/cloth/forces/strech_force.cpp similarity index 99% rename from src/bounce/cloth/strech_force.cpp rename to src/bounce/cloth/forces/strech_force.cpp index e918809..0b87bdc 100644 --- a/src/bounce/cloth/strech_force.cpp +++ b/src/bounce/cloth/forces/strech_force.cpp @@ -16,7 +16,7 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include +#include #include #include #include From ab6ef6d685e572c6c424a06f75d5b02965bf1420 Mon Sep 17 00:00:00 2001 From: Irlan Robson Date: Sat, 29 Jun 2019 18:59:15 -0300 Subject: [PATCH 193/198] Update readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 5e3717e..c90f4ab 100644 --- a/readme.md +++ b/readme.md @@ -126,7 +126,7 @@ Below are the external dependencies for testbed. If you don't care about testbed * Cloth * Vertex contact, friction -* Strech, shear, spring force types +* Strech, shear, spring, mouse force types * Linear time solver * Unconditional simulation stability * Ray-casting From d6e2105aa23bc25f2e497c92a7ca0dcb0eb05ba7 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 7 Jul 2019 09:33:20 -0300 Subject: [PATCH 194/198] Small refactor --- include/bounce/bounce.h | 1 + include/bounce/cloth/cloth_mesh.h | 13 ------- include/bounce/cloth/forces/strech_force.h | 4 +-- include/bounce/cloth/garment_cloth_mesh.h | 36 +++++++++++++++++++ ...{cloth_mesh.cpp => garment_cloth_mesh.cpp} | 2 +- 5 files changed, 40 insertions(+), 16 deletions(-) create mode 100644 include/bounce/cloth/garment_cloth_mesh.h rename src/bounce/cloth/{cloth_mesh.cpp => garment_cloth_mesh.cpp} (98%) diff --git a/include/bounce/bounce.h b/include/bounce/bounce.h index 58e0faa..3826bb5 100644 --- a/include/bounce/bounce.h +++ b/include/bounce/bounce.h @@ -65,6 +65,7 @@ #include #include +#include #include #include #include diff --git a/include/bounce/cloth/cloth_mesh.h b/include/bounce/cloth/cloth_mesh.h index 391f88e..98c9205 100644 --- a/include/bounce/cloth/cloth_mesh.h +++ b/include/bounce/cloth/cloth_mesh.h @@ -19,7 +19,6 @@ #ifndef B3_CLOTH_MESH_H #define B3_CLOTH_MESH_H -#include #include struct b3ClothMeshTriangle @@ -53,16 +52,4 @@ struct b3ClothMesh b3ClothMeshSewingLine* sewingLines; }; -struct b3GarmentMesh; - -// Convenience structure. -struct b3GarmentClothMesh : public b3ClothMesh -{ - b3GarmentClothMesh(); - ~b3GarmentClothMesh(); - - // Set this mesh from a 2D garment mesh. - void Set(const b3GarmentMesh* garment); -}; - #endif \ No newline at end of file diff --git a/include/bounce/cloth/forces/strech_force.h b/include/bounce/cloth/forces/strech_force.h index 7d8dde9..ecbb5a2 100644 --- a/include/bounce/cloth/forces/strech_force.h +++ b/include/bounce/cloth/forces/strech_force.h @@ -83,10 +83,10 @@ private: // Damping stiffness float32 m_kd; - // bu + // Desired strechiness in u direction float32 m_bu; - // bv + // Desired strechiness in v direction float32 m_bv; // Action forces diff --git a/include/bounce/cloth/garment_cloth_mesh.h b/include/bounce/cloth/garment_cloth_mesh.h new file mode 100644 index 0000000..b33d810 --- /dev/null +++ b/include/bounce/cloth/garment_cloth_mesh.h @@ -0,0 +1,36 @@ +/* +* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef B3_GARMENT_CLOTH_MESH_H +#define B3_GARMENT_CLOTH_MESH_H + +#include + +struct b3GarmentMesh; + +// Convenience structure. +struct b3GarmentClothMesh : public b3ClothMesh +{ + b3GarmentClothMesh(); + ~b3GarmentClothMesh(); + + // Set this mesh from a 2D garment mesh. + void Set(const b3GarmentMesh* garment); +}; + +#endif \ No newline at end of file diff --git a/src/bounce/cloth/cloth_mesh.cpp b/src/bounce/cloth/garment_cloth_mesh.cpp similarity index 98% rename from src/bounce/cloth/cloth_mesh.cpp rename to src/bounce/cloth/garment_cloth_mesh.cpp index 59b106e..5f09f49 100644 --- a/src/bounce/cloth/cloth_mesh.cpp +++ b/src/bounce/cloth/garment_cloth_mesh.cpp @@ -16,7 +16,7 @@ * 3. This notice may not be removed or altered from any source distribution. */ -#include +#include #include #include #include From b73b760515f57937754c99e1767903c03302cea4 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 7 Jul 2019 09:42:34 -0300 Subject: [PATCH 195/198] Update comment --- include/bounce/cloth/garment_cloth_mesh.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/bounce/cloth/garment_cloth_mesh.h b/include/bounce/cloth/garment_cloth_mesh.h index b33d810..c608077 100644 --- a/include/bounce/cloth/garment_cloth_mesh.h +++ b/include/bounce/cloth/garment_cloth_mesh.h @@ -23,7 +23,8 @@ struct b3GarmentMesh; -// Convenience structure. +// This mesh structure represents a cloth mesh that can be set from 2D a garment mesh. +// The mesh is set in the xy plane (z = 0). struct b3GarmentClothMesh : public b3ClothMesh { b3GarmentClothMesh(); From 2982bc2bd35a01fa3429b7470c1aa6e684993237 Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 7 Jul 2019 10:00:00 -0300 Subject: [PATCH 196/198] Store material parameters inside elements --- include/bounce/softbody/softbody.h | 26 ++++++++----------- src/bounce/softbody/softbody.cpp | 19 +++++++++----- src/bounce/softbody/softbody_force_solver.cpp | 8 +++--- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/include/bounce/softbody/softbody.h b/include/bounce/softbody/softbody.h index fc28e27..f3394c7 100644 --- a/include/bounce/softbody/softbody.h +++ b/include/bounce/softbody/softbody.h @@ -43,6 +43,13 @@ struct b3SoftBodyRayCastSingleOutput // Soft body tetrahedron element struct b3SoftBodyElement { + float32 E; + float32 nu; + + float32 c_yield; + float32 c_creep; + float32 c_max; + b3Mat33 K[16]; // 12 x 12 b3Mat33 invE; b3Quat q; @@ -60,6 +67,7 @@ struct b3SoftBodyTriangle // Soft body definition // This requires defining a soft body mesh which is typically bound to a render mesh +// and some uniform material parameters. struct b3SoftBodyDef { b3SoftBodyDef() @@ -130,6 +138,9 @@ public: // Return the node associated with the given vertex. b3SoftBodyNode* GetVertexNode(u32 i); + // Return the element associated with the given tetrahedron. + b3SoftBodyElement* GetTetrahedronElement(u32 i); + // Return the kinetic (or dynamic) energy in this system. float32 GetEnergy() const; @@ -162,21 +173,6 @@ private: // Soft body density float32 m_density; - // Material Young's modulus - float32 m_E; - - // Material poisson ratio - float32 m_nu; - - // Material yield - float32 m_c_yield; - - // Material creep rate - float32 m_c_creep; - - // Material maximum plastic strain - float32 m_c_max; - // Soft body nodes b3SoftBodyNode* m_nodes; diff --git a/src/bounce/softbody/softbody.cpp b/src/bounce/softbody/softbody.cpp index 3f78684..1571dc3 100644 --- a/src/bounce/softbody/softbody.cpp +++ b/src/bounce/softbody/softbody.cpp @@ -450,11 +450,6 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) m_mesh = def.mesh; m_density = def.density; - m_E = def.E; - m_nu = def.nu; - m_c_yield = def.c_yield; - m_c_creep = def.c_creep; - m_c_max = def.c_max; m_gravity.SetZero(); m_world = nullptr; m_contactManager.m_body = this; @@ -495,6 +490,12 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) { b3SoftBodyMeshTetrahedron* mt = m->tetrahedrons + ei; b3SoftBodyElement* e = m_elements + ei; + + e->E = def.E; + e->nu = def.nu; + e->c_yield = def.c_yield; + e->c_creep = def.c_creep; + e->c_max = def.c_max; u32 v1 = mt->v1; u32 v2 = mt->v2; @@ -520,7 +521,7 @@ b3SoftBody::b3SoftBody(const b3SoftBodyDef& def) // 6 x 6 float32 D[36]; - b3ComputeD(D, m_E, m_nu); + b3ComputeD(D, e->E, e->nu); // 6 x 12 float32* B = e->B; @@ -658,6 +659,12 @@ b3SoftBodyNode* b3SoftBody::GetVertexNode(u32 i) return m_nodes + i; } +b3SoftBodyElement* b3SoftBody::GetTetrahedronElement(u32 i) +{ + B3_ASSERT(i < m_mesh->tetrahedronCount); + return m_elements + i; +} + float32 b3SoftBody::GetEnergy() const { float32 E = 0.0f; diff --git a/src/bounce/softbody/softbody_force_solver.cpp b/src/bounce/softbody/softbody_force_solver.cpp index 3d7adbf..a9eade5 100644 --- a/src/bounce/softbody/softbody_force_solver.cpp +++ b/src/bounce/softbody/softbody_force_solver.cpp @@ -345,9 +345,9 @@ void b3SoftBodyForceSolver::Solve(float32 dt, const b3Vec3& gravity) } float32 len_epsilon_elastic = b3Length(epsilon_elastic, 6); - if (len_epsilon_elastic > m_body->m_c_yield) + if (len_epsilon_elastic > e->c_yield) { - float32 amount = h * b3Min(m_body->m_c_creep, inv_h); + float32 amount = h * b3Min(e->c_creep, inv_h); for (u32 i = 0; i < 6; ++i) { epsilon_plastic[i] += amount * epsilon_elastic[i]; @@ -355,9 +355,9 @@ void b3SoftBodyForceSolver::Solve(float32 dt, const b3Vec3& gravity) } float32 len_epsilon_plastic = b3Length(epsilon_plastic, 6); - if (len_epsilon_plastic > m_body->m_c_max) + if (len_epsilon_plastic > e->c_max) { - float32 scale = m_body->m_c_max / len_epsilon_plastic; + float32 scale = e->c_max / len_epsilon_plastic; for (u32 i = 0; i < 6; ++i) { epsilon_plastic[i] *= scale; From e9714885e45b6bc642ad30c096d4877e3640819c Mon Sep 17 00:00:00 2001 From: Irlan Date: Sun, 7 Jul 2019 23:09:13 -0300 Subject: [PATCH 197/198] Simplification --- include/bounce/cloth/cloth_force_solver.h | 12 ---- src/bounce/cloth/cloth_force_solver.cpp | 78 ++--------------------- 2 files changed, 6 insertions(+), 84 deletions(-) diff --git a/include/bounce/cloth/cloth_force_solver.h b/include/bounce/cloth/cloth_force_solver.h index d91786c..2762c7f 100644 --- a/include/bounce/cloth/cloth_force_solver.h +++ b/include/bounce/cloth/cloth_force_solver.h @@ -53,15 +53,6 @@ struct b3ClothForceSolverData b3DenseVec3* z; }; -struct b3AccelerationConstraint -{ - u32 i1; - u32 ndof; - b3Vec3 p, q, z; - - void Apply(const b3ClothForceSolverData* data); -}; - class b3ClothForceSolver { public: @@ -71,7 +62,6 @@ public: void Solve(float32 dt, const b3Vec3& gravity); private: void ApplyForces(); - void ApplyConstraints(); b3StackAllocator* m_allocator; @@ -81,8 +71,6 @@ private: u32 m_forceCount; b3Force** m_forces; - b3AccelerationConstraint* m_constraints; - b3ClothForceSolverData m_solverData; }; diff --git a/src/bounce/cloth/cloth_force_solver.cpp b/src/bounce/cloth/cloth_force_solver.cpp index c31aaab..f801174 100644 --- a/src/bounce/cloth/cloth_force_solver.cpp +++ b/src/bounce/cloth/cloth_force_solver.cpp @@ -43,13 +43,10 @@ b3ClothForceSolver::b3ClothForceSolver(const b3ClothForceSolverDef& def) m_forceCount = def.forceCount; m_forces = def.forces; - - m_constraints = (b3AccelerationConstraint*)m_allocator->Allocate(m_particleCount * sizeof(b3AccelerationConstraint)); } b3ClothForceSolver::~b3ClothForceSolver() { - m_allocator->Free(m_constraints); } void b3ClothForceSolver::ApplyForces() @@ -60,72 +57,6 @@ void b3ClothForceSolver::ApplyForces() } } -void b3AccelerationConstraint::Apply(const b3ClothForceSolverData* data) -{ - b3DiagMat33& sS = *data->S; - b3DenseVec3& sz = *data->z; - - sz[i1] = z; - - b3Mat33 I; I.SetIdentity(); - - switch (ndof) - { - case 3: - { - sS[i1] = I; - break; - } - case 2: - { - sS[i1] = I - b3Outer(p, p); - break; - } - case 1: - { - sS[i1] = I - b3Outer(p, p) - b3Outer(q, q); - break; - } - case 0: - { - sS[i1].SetZero(); - break; - } - default: - { - B3_ASSERT(false); - break; - } - } -} - -void b3ClothForceSolver::ApplyConstraints() -{ - for (u32 i = 0; i < m_particleCount; ++i) - { - b3Particle* p = m_particles[i]; - b3AccelerationConstraint* c = m_constraints + i; - - c->i1 = i; - - if (p->m_type != e_dynamicParticle) - { - c->ndof = 0; - c->z.SetZero(); - } - else - { - c->ndof = 3; - c->z.SetZero(); - } - } - - for (u32 i = 0; i < m_particleCount; ++i) - { - m_constraints[i].Apply(&m_solverData); - } -} - // Solve Ax = b static void b3SolveMPCG(b3DenseVec3& x, const b3SparseMat33View& A, const b3DenseVec3& b, @@ -239,11 +170,17 @@ void b3ClothForceSolver::Solve(float32 dt, const b3Vec3& gravity) sx[i] = p->m_position; sv[i] = p->m_velocity; sf[i] = p->m_force; + sz[i].SetZero(); if (p->m_type == e_dynamicParticle) { // Apply weight sf[i] += p->m_mass * gravity; + S[i].SetIdentity(); + } + else + { + S[i].SetZero(); } sy[i] = p->m_translation; @@ -253,9 +190,6 @@ void b3ClothForceSolver::Solve(float32 dt, const b3Vec3& gravity) // Apply internal forces ApplyForces(); - // Apply constraints - ApplyConstraints(); - // Solve Ax = b, where // A = M - h * dfdv - h * h * dfdx // b = h * (f0 + h * dfdx * v0 + dfdx * y) From f87e2a6378e2af7525d9777362b61558914d08b4 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 8 Jul 2019 10:58:39 -0300 Subject: [PATCH 198/198] Properly variable name and comment --- src/bounce/cloth/forces/mouse_force.cpp | 82 +++++++++--------- src/bounce/cloth/forces/shear_force.cpp | 54 ++++++------ src/bounce/cloth/forces/spring_force.cpp | 44 +++++----- src/bounce/cloth/forces/strech_force.cpp | 104 +++++++++++------------ 4 files changed, 142 insertions(+), 142 deletions(-) diff --git a/src/bounce/cloth/forces/mouse_force.cpp b/src/bounce/cloth/forces/mouse_force.cpp index c15b01b..51810b7 100644 --- a/src/bounce/cloth/forces/mouse_force.cpp +++ b/src/bounce/cloth/forces/mouse_force.cpp @@ -130,39 +130,39 @@ void b3MouseForce::Apply(const b3ClothForceSolverData* data) m_f3 += fs[2]; m_f4 += fs[3]; - // Jacobian - b3Mat33 J[4][4]; + // Force derivative + b3Mat33 K[4][4]; for (u32 i = 0; i < 4; ++i) { for (u32 j = 0; j < 4; ++j) { //b3Mat33 d2Cvxij; - //b3Mat33 Jij = -m_km * (b3Outer(dCdx[i], dCdx[j]) + C * d2Cvxij); - b3Mat33 Jij = -m_km * b3Outer(dCdx[i], dCdx[j]); + //b3Mat33 Kij = -m_km * (b3Outer(dCdx[i], dCdx[j]) + C * d2Cvxij); + b3Mat33 Kij = -m_km * b3Outer(dCdx[i], dCdx[j]); - J[i][j] = Jij; + K[i][j] = Kij; } } - dfdx(i1, i1) += J[0][0]; - dfdx(i1, i2) += J[0][1]; - dfdx(i1, i3) += J[0][2]; - dfdx(i1, i4) += J[0][3]; + dfdx(i1, i1) += K[0][0]; + dfdx(i1, i2) += K[0][1]; + dfdx(i1, i3) += K[0][2]; + dfdx(i1, i4) += K[0][3]; - dfdx(i2, i1) += J[1][0]; - dfdx(i2, i2) += J[1][1]; - dfdx(i2, i3) += J[1][2]; - dfdx(i2, i4) += J[1][3]; + dfdx(i2, i1) += K[1][0]; + dfdx(i2, i2) += K[1][1]; + dfdx(i2, i3) += K[1][2]; + dfdx(i2, i4) += K[1][3]; - dfdx(i3, i1) += J[2][0]; - dfdx(i3, i2) += J[2][1]; - dfdx(i3, i3) += J[2][2]; - dfdx(i3, i4) += J[2][3]; + dfdx(i3, i1) += K[2][0]; + dfdx(i3, i2) += K[2][1]; + dfdx(i3, i3) += K[2][2]; + dfdx(i3, i4) += K[2][3]; - dfdx(i4, i1) += J[3][0]; - dfdx(i4, i2) += J[3][1]; - dfdx(i4, i3) += J[3][2]; - dfdx(i4, i4) += J[3][3]; + dfdx(i4, i1) += K[3][0]; + dfdx(i4, i2) += K[3][1]; + dfdx(i4, i3) += K[3][2]; + dfdx(i4, i4) += K[3][3]; } if (m_kd > 0.0f) @@ -187,37 +187,37 @@ void b3MouseForce::Apply(const b3ClothForceSolverData* data) m_f3 += fs[2]; m_f4 += fs[3]; - // Jacobian - b3Mat33 J[4][4]; + // Force derivative + b3Mat33 K[4][4]; for (u32 i = 0; i < 4; ++i) { for (u32 j = 0; j < 4; ++j) { - b3Mat33 Jij = -m_kd * b3Outer(dCdx[i], dCdx[j]); + b3Mat33 Kij = -m_kd * b3Outer(dCdx[i], dCdx[j]); - J[i][j] = Jij; + K[i][j] = Kij; } } - dfdv(i1, i1) += J[0][0]; - dfdv(i1, i2) += J[0][1]; - dfdv(i1, i3) += J[0][2]; - dfdv(i1, i4) += J[0][3]; + dfdv(i1, i1) += K[0][0]; + dfdv(i1, i2) += K[0][1]; + dfdv(i1, i3) += K[0][2]; + dfdv(i1, i4) += K[0][3]; - dfdv(i2, i1) += J[1][0]; - dfdv(i2, i2) += J[1][1]; - dfdv(i2, i3) += J[1][2]; - dfdv(i2, i4) += J[1][3]; + dfdv(i2, i1) += K[1][0]; + dfdv(i2, i2) += K[1][1]; + dfdv(i2, i3) += K[1][2]; + dfdv(i2, i4) += K[1][3]; - dfdv(i3, i1) += J[2][0]; - dfdv(i3, i2) += J[2][1]; - dfdv(i3, i3) += J[2][2]; - dfdv(i3, i4) += J[2][3]; + dfdv(i3, i1) += K[2][0]; + dfdv(i3, i2) += K[2][1]; + dfdv(i3, i3) += K[2][2]; + dfdv(i3, i4) += K[2][3]; - dfdv(i4, i1) += J[3][0]; - dfdv(i4, i2) += J[3][1]; - dfdv(i4, i3) += J[3][2]; - dfdv(i4, i4) += J[3][3]; + dfdv(i4, i1) += K[3][0]; + dfdv(i4, i2) += K[3][1]; + dfdv(i4, i3) += K[3][2]; + dfdv(i4, i4) += K[3][3]; } } diff --git a/src/bounce/cloth/forces/shear_force.cpp b/src/bounce/cloth/forces/shear_force.cpp index a94f835..07a35f4 100644 --- a/src/bounce/cloth/forces/shear_force.cpp +++ b/src/bounce/cloth/forces/shear_force.cpp @@ -133,31 +133,31 @@ void b3ShearForce::Apply(const b3ClothForceSolverData* data) m_f2 += fs[1]; m_f3 += fs[2]; - // Jacobian - b3Mat33 J[3][3]; + // Force derivative + b3Mat33 K[3][3]; for (u32 i = 0; i < 3; ++i) { for (u32 j = 0; j < 3; ++j) { //b3Mat33 d2Cxij = alpha * (dwudx[i] * dwvdx[j] + dwudx[j] * dwvdx[i]) * I; - //b3Mat33 Jij = -m_ks * (b3Outer(dCdx[i], dCdx[j]) + C * d2Cxij); - b3Mat33 Jij = -m_ks * b3Outer(dCdx[i], dCdx[j]); + //b3Mat33 Kij = -m_ks * (b3Outer(dCdx[i], dCdx[j]) + C * d2Cxij); + b3Mat33 Kij = -m_ks * b3Outer(dCdx[i], dCdx[j]); - J[i][j] = Jij; + K[i][j] = Kij; } } - dfdx(i1, i1) += J[0][0]; - dfdx(i1, i2) += J[0][1]; - dfdx(i1, i3) += J[0][2]; + dfdx(i1, i1) += K[0][0]; + dfdx(i1, i2) += K[0][1]; + dfdx(i1, i3) += K[0][2]; - dfdx(i2, i1) += J[1][0]; - dfdx(i2, i2) += J[1][1]; - dfdx(i2, i3) += J[1][2]; + dfdx(i2, i1) += K[1][0]; + dfdx(i2, i2) += K[1][1]; + dfdx(i2, i3) += K[1][2]; - dfdx(i3, i1) += J[2][0]; - dfdx(i3, i2) += J[2][1]; - dfdx(i3, i3) += J[2][2]; + dfdx(i3, i1) += K[2][0]; + dfdx(i3, i2) += K[2][1]; + dfdx(i3, i3) += K[2][2]; } if (m_kd > 0.0f) @@ -181,29 +181,29 @@ void b3ShearForce::Apply(const b3ClothForceSolverData* data) m_f2 += fs[1]; m_f3 += fs[2]; - // Jacobian - b3Mat33 J[3][3]; + // Force derivative + b3Mat33 K[3][3]; for (u32 i = 0; i < 3; ++i) { for (u32 j = 0; j < 3; ++j) { - b3Mat33 Jij = -m_kd * b3Outer(dCdx[i], dCdx[j]); + b3Mat33 Kij = -m_kd * b3Outer(dCdx[i], dCdx[j]); - J[i][j] = Jij; + K[i][j] = Kij; } } - dfdv(i1, i1) += J[0][0]; - dfdv(i1, i2) += J[0][1]; - dfdv(i1, i3) += J[0][2]; + dfdv(i1, i1) += K[0][0]; + dfdv(i1, i2) += K[0][1]; + dfdv(i1, i3) += K[0][2]; - dfdv(i2, i1) += J[1][0]; - dfdv(i2, i2) += J[1][1]; - dfdv(i2, i3) += J[1][2]; + dfdv(i2, i1) += K[1][0]; + dfdv(i2, i2) += K[1][1]; + dfdv(i2, i3) += K[1][2]; - dfdv(i3, i1) += J[2][0]; - dfdv(i3, i2) += J[2][1]; - dfdv(i3, i3) += J[2][2]; + dfdv(i3, i1) += K[2][0]; + dfdv(i3, i2) += K[2][1]; + dfdv(i3, i3) += K[2][2]; } f[i1] += m_f1; diff --git a/src/bounce/cloth/forces/spring_force.cpp b/src/bounce/cloth/forces/spring_force.cpp index 4c365a1..a037254 100644 --- a/src/bounce/cloth/forces/spring_force.cpp +++ b/src/bounce/cloth/forces/spring_force.cpp @@ -79,40 +79,40 @@ void b3SpringForce::Apply(const b3ClothForceSolverData* data) if (L > m_L0) { - // Apply tension - b3Vec3 n = dx / L; - - m_f += -m_ks * (L - m_L0) * n; - // Jacobian - b3Mat33 Jx11 = -m_ks * (b3Outer(dx, dx) + (1.0f - m_L0 / L) * (I - b3Outer(dx, dx))); - b3Mat33 Jx12 = -Jx11; - b3Mat33 Jx21 = Jx12; - b3Mat33 Jx22 = Jx11; + b3Vec3 dCdx = dx / L; - dfdx(i1, i1) += Jx11; - dfdx(i1, i2) += Jx12; - dfdx(i2, i1) += Jx21; - dfdx(i2, i2) += Jx22; + m_f += -m_ks * (L - m_L0) * dCdx; + + // Force derivative + b3Mat33 K11 = -m_ks * (b3Outer(dx, dx) + (1.0f - m_L0 / L) * (I - b3Outer(dx, dx))); + b3Mat33 K12 = -K11; + b3Mat33 K21 = K12; + b3Mat33 K22 = K11; + + dfdx(i1, i1) += K11; + dfdx(i1, i2) += K12; + dfdx(i2, i1) += K21; + dfdx(i2, i2) += K22; } } if (m_kd > 0.0f) { - // Apply damping + // C * J b3Vec3 dv = v1 - v2; m_f += -m_kd * dv; - b3Mat33 Jv11 = -m_kd * I; - b3Mat33 Jv12 = -Jv11; - b3Mat33 Jv21 = Jv12; - b3Mat33 Jv22 = Jv11; + b3Mat33 K11 = -m_kd * I; + b3Mat33 K12 = -K11; + b3Mat33 K21 = K12; + b3Mat33 K22 = K11; - dfdv(i1, i1) += Jv11; - dfdv(i1, i2) += Jv12; - dfdv(i2, i1) += Jv21; - dfdv(i2, i2) += Jv22; + dfdv(i1, i1) += K11; + dfdv(i1, i2) += K12; + dfdv(i2, i1) += K21; + dfdv(i2, i2) += K22; } f[i1] += m_f; diff --git a/src/bounce/cloth/forces/strech_force.cpp b/src/bounce/cloth/forces/strech_force.cpp index 0b87bdc..65dec70 100644 --- a/src/bounce/cloth/forces/strech_force.cpp +++ b/src/bounce/cloth/forces/strech_force.cpp @@ -145,31 +145,31 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) m_f2 += fs[1]; m_f3 += fs[2]; - // Jacobian - b3Mat33 J[3][3]; + // Force derivative + b3Mat33 K[3][3]; for (u32 i = 0; i < 3; ++i) { for (u32 j = 0; j < 3; ++j) { b3Mat33 d2Cuxij = (alpha * inv_len_wu * dwudx[i] * dwudx[j]) * (I - b3Outer(n_wu, n_wu)); - b3Mat33 Jij = -m_ks * (b3Outer(dCudx[i], dCudx[j]) + Cu * d2Cuxij); + b3Mat33 Kij = -m_ks * (b3Outer(dCudx[i], dCudx[j]) + Cu * d2Cuxij); - J[i][j] = Jij; + K[i][j] = Kij; } } - dfdx(i1, i1) += J[0][0]; - dfdx(i1, i2) += J[0][1]; - dfdx(i1, i3) += J[0][2]; + dfdx(i1, i1) += K[0][0]; + dfdx(i1, i2) += K[0][1]; + dfdx(i1, i3) += K[0][2]; - dfdx(i2, i1) += J[1][0]; - dfdx(i2, i2) += J[1][1]; - dfdx(i2, i3) += J[1][2]; + dfdx(i2, i1) += K[1][0]; + dfdx(i2, i2) += K[1][1]; + dfdx(i2, i3) += K[1][2]; - dfdx(i3, i1) += J[2][0]; - dfdx(i3, i2) += J[2][1]; - dfdx(i3, i3) += J[2][2]; + dfdx(i3, i1) += K[2][0]; + dfdx(i3, i2) += K[2][1]; + dfdx(i3, i3) += K[2][2]; } } @@ -193,29 +193,29 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) m_f2 += fs[1]; m_f3 += fs[2]; - // Jacobian - b3Mat33 J[3][3]; + // Force derivative + b3Mat33 K[3][3]; for (u32 i = 0; i < 3; ++i) { for (u32 j = 0; j < 3; ++j) { - b3Mat33 Jij = -m_kd * b3Outer(dCudx[i], dCudx[j]); + b3Mat33 Kij = -m_kd * b3Outer(dCudx[i], dCudx[j]); - J[i][j] = Jij; + K[i][j] = Kij; } } - dfdv(i1, i1) += J[0][0]; - dfdv(i1, i2) += J[0][1]; - dfdv(i1, i3) += J[0][2]; + dfdv(i1, i1) += K[0][0]; + dfdv(i1, i2) += K[0][1]; + dfdv(i1, i3) += K[0][2]; - dfdv(i2, i1) += J[1][0]; - dfdv(i2, i2) += J[1][1]; - dfdv(i2, i3) += J[1][2]; + dfdv(i2, i1) += K[1][0]; + dfdv(i2, i2) += K[1][1]; + dfdv(i2, i3) += K[1][2]; - dfdv(i3, i1) += J[2][0]; - dfdv(i3, i2) += J[2][1]; - dfdv(i3, i3) += J[2][2]; + dfdv(i3, i1) += K[2][0]; + dfdv(i3, i2) += K[2][1]; + dfdv(i3, i3) += K[2][2]; } } @@ -248,31 +248,31 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) m_f2 += fs[1]; m_f3 += fs[2]; - // Jacobian - b3Mat33 J[3][3]; + // Force derivative + b3Mat33 K[3][3]; for (u32 i = 0; i < 3; ++i) { for (u32 j = 0; j < 3; ++j) { b3Mat33 d2Cvxij = (alpha * inv_len_wv * dwvdx[i] * dwvdx[j]) * (I - b3Outer(n_wv, n_wv)); - b3Mat33 Jij = -m_ks * (b3Outer(dCvdx[i], dCvdx[j]) + Cv * d2Cvxij); + b3Mat33 Kij = -m_ks * (b3Outer(dCvdx[i], dCvdx[j]) + Cv * d2Cvxij); - J[i][j] = Jij; + K[i][j] = Kij; } } - dfdx(i1, i1) += J[0][0]; - dfdx(i1, i2) += J[0][1]; - dfdx(i1, i3) += J[0][2]; + dfdx(i1, i1) += K[0][0]; + dfdx(i1, i2) += K[0][1]; + dfdx(i1, i3) += K[0][2]; - dfdx(i2, i1) += J[1][0]; - dfdx(i2, i2) += J[1][1]; - dfdx(i2, i3) += J[1][2]; + dfdx(i2, i1) += K[1][0]; + dfdx(i2, i2) += K[1][1]; + dfdx(i2, i3) += K[1][2]; - dfdx(i3, i1) += J[2][0]; - dfdx(i3, i2) += J[2][1]; - dfdx(i3, i3) += J[2][2]; + dfdx(i3, i1) += K[2][0]; + dfdx(i3, i2) += K[2][1]; + dfdx(i3, i3) += K[2][2]; } } @@ -297,29 +297,29 @@ void b3StrechForce::Apply(const b3ClothForceSolverData* data) m_f2 += fs[1]; m_f3 += fs[2]; - // Jacobian - b3Mat33 J[3][3]; + // Force derivative + b3Mat33 K[3][3]; for (u32 i = 0; i < 3; ++i) { for (u32 j = 0; j < 3; ++j) { - b3Mat33 Jij = -m_kd * b3Outer(dCvdx[i], dCvdx[j]); + b3Mat33 Kij = -m_kd * b3Outer(dCvdx[i], dCvdx[j]); - J[i][j] = Jij; + K[i][j] = Kij; } } - dfdv(i1, i1) += J[0][0]; - dfdv(i1, i2) += J[0][1]; - dfdv(i1, i3) += J[0][2]; + dfdv(i1, i1) += K[0][0]; + dfdv(i1, i2) += K[0][1]; + dfdv(i1, i3) += K[0][2]; - dfdv(i2, i1) += J[1][0]; - dfdv(i2, i2) += J[1][1]; - dfdv(i2, i3) += J[1][2]; + dfdv(i2, i1) += K[1][0]; + dfdv(i2, i2) += K[1][1]; + dfdv(i2, i3) += K[1][2]; - dfdv(i3, i1) += J[2][0]; - dfdv(i3, i2) += J[2][1]; - dfdv(i3, i3) += J[2][2]; + dfdv(i3, i1) += K[2][0]; + dfdv(i3, i2) += K[2][1]; + dfdv(i3, i3) += K[2][2]; } }