void ResolveInterActorCollisions(const float dt, PointColDetector &interPointCD,
        const int free_collcab, int collcabs[], int cabs[],
        collcab_rate_t inter_collcabrate[], node_t nodes[],
        const float collrange,
        ground_model_t &submesh_ground_model)
{
    Actor** actor_slots = RoR::App::GetSimController()->GetBeamFactory()->GetInternalActorSlots();

    for (int i=0; i<free_collcab; i++)
    {
        if (inter_collcabrate[i].rate > 0)
        {
            inter_collcabrate[i].distance++;
            inter_collcabrate[i].rate--;
            continue;
        }
        inter_collcabrate[i].rate = std::min(inter_collcabrate[i].distance, 12);
        inter_collcabrate[i].distance = 0;

        int tmpv = collcabs[i]*3;
        const auto no = &nodes[cabs[tmpv]];
        const auto na = &nodes[cabs[tmpv+1]];
        const auto nb = &nodes[cabs[tmpv+2]];

        interPointCD.query(no->AbsPosition
                , na->AbsPosition
                , nb->AbsPosition, collrange);

        if (interPointCD.hit_count > 0)
        {
            // setup transformation of points to triangle local coordinates
            const Triangle triangle(na->AbsPosition, nb->AbsPosition, no->AbsPosition);
            const CartesianToTriangleTransform transform(triangle);

            for (int h=0; h<interPointCD.hit_count; h++)
            {
                const auto hitnodeid = interPointCD.hit_list[h]->node_id;
                const auto hit_actor_id = interPointCD.hit_list[h]->actor_id;
                const auto hitnode = &actor_slots[hit_actor_id]->ar_nodes[hitnodeid];
                const Actor* hit_actor = actor_slots[hit_actor_id];

                // transform point to triangle local coordinates
                const auto local_point = transform(hitnode->AbsPosition);

                // collision test
                const bool is_colliding = InsideTriangleTest(local_point, collrange);
                if (is_colliding)
                {
                    inter_collcabrate[i].rate = 0;

                    const auto coord = local_point.barycentric;
                    auto distance   = local_point.distance;
                    auto normal     = triangle.normal();

                    // adapt in case the collision is occuring on the backface of the triangle
                    const auto neighbour_node_ids = hit_actor->ar_node_to_node_connections[hitnodeid];
                    const bool is_backface = BackfaceCollisionTest(distance, normal, *no, neighbour_node_ids, hit_actor->ar_nodes);
                    if (is_backface)
                    {
                        // flip surface normal and distance to triangle plane
                        normal   = -normal;
                        distance = -distance;
                    }

                    const auto penetration_depth = collrange - distance;

                    ResolveCollisionForces(penetration_depth, *hitnode, *na, *nb, *no, coord.alpha,
                            coord.beta, coord.gamma, normal, dt, submesh_ground_model);
                }
            }
        }
        else
        {
            inter_collcabrate[i].rate++;
        }
    }
}
void ResolveIntraActorCollisions(const float dt, PointColDetector &intraPointCD,
        const int free_collcab, int collcabs[], int cabs[],
        collcab_rate_t intra_collcabrate[], node_t nodes[],
        const float collrange,
        ground_model_t &submesh_ground_model)
{
    for (int i=0; i<free_collcab; i++)
    {
        if (intra_collcabrate[i].rate > 0)
        {
            intra_collcabrate[i].distance++;
            intra_collcabrate[i].rate--;
            continue;
        }
        if (intra_collcabrate[i].distance > 0)
        {
            intra_collcabrate[i].rate = std::min(intra_collcabrate[i].distance, 12);
            intra_collcabrate[i].distance = 0;
        }

        int tmpv = collcabs[i]*3;
        const auto no = &nodes[cabs[tmpv]];
        const auto na = &nodes[cabs[tmpv+1]];
        const auto nb = &nodes[cabs[tmpv+2]];

        intraPointCD.query(no->AbsPosition
                , na->AbsPosition
                , nb->AbsPosition, collrange);

        bool collision = false;

        if (intraPointCD.hit_count > 0)
        {
            // setup transformation of points to triangle local coordinates
            const Triangle triangle(na->AbsPosition, nb->AbsPosition, no->AbsPosition);
            const CartesianToTriangleTransform transform(triangle);

            for (int h=0; h<intraPointCD.hit_count; h++)
            {
                const auto hitnodeid = intraPointCD.hit_list[h]->node_id;
                const auto hitnode = &nodes[hitnodeid];

                //ignore wheel/chassis self contact
                if (hitnode->iswheel) continue;
                if (no == hitnode || na == hitnode || nb == hitnode) continue;

                // transform point to triangle local coordinates
                const auto local_point = transform(hitnode->AbsPosition);

                // collision test
                const bool is_colliding = InsideTriangleTest(local_point, collrange);
                if (is_colliding)
                {
                    collision = true;

                    const auto coord = local_point.barycentric;
                    auto distance = local_point.distance;
                    auto normal   = triangle.normal();

                    // adapt in case the collision is occuring on the backface of the triangle
                    if (distance < 0) 
                    {
                        // flip surface normal and distance to triangle plane
                        normal   = -normal;
                        distance = -distance;
                    }

                    const auto penetration_depth = collrange - distance;

                    ResolveCollisionForces(penetration_depth, *hitnode, *na, *nb, *no, coord.alpha,
                            coord.beta, coord.gamma, normal, dt, submesh_ground_model);
                }
            }
        }

        if (collision)
        {
            intra_collcabrate[i].rate = -20000;
        }
        else
        {
            intra_collcabrate[i].rate++;
        }
    }
}
void ResolveInterActorCollisions(const float dt, PointColDetector &interPointCD,
        const int free_collcab, int collcabs[], int cabs[],
        collcab_rate_t inter_collcabrate[], node_t nodes[],
        const float collrange,
        ground_model_t &submesh_ground_model)
{
    for (int i=0; i<free_collcab; i++)
    {
        if (inter_collcabrate[i].rate > 0)
        {
            inter_collcabrate[i].distance++;
            inter_collcabrate[i].rate--;
            continue;
        }
        inter_collcabrate[i].rate = std::min(inter_collcabrate[i].distance, 12);
        inter_collcabrate[i].distance = 0;

        int tmpv = collcabs[i]*3;
        const auto no = &nodes[cabs[tmpv]];
        const auto na = &nodes[cabs[tmpv+1]];
        const auto nb = &nodes[cabs[tmpv+2]];

        interPointCD.query(no->AbsPosition
                , na->AbsPosition
                , nb->AbsPosition, collrange);

        if (!interPointCD.hit_list.empty())
        {
            // setup transformation of points to triangle local coordinates
            const Triangle triangle(na->AbsPosition, nb->AbsPosition, no->AbsPosition);
            const CartesianToTriangleTransform transform(triangle);

            for (auto h : interPointCD.hit_list)
            {
                const auto hit_actor = h->actor;
                const auto hitnode = &hit_actor->ar_nodes[h->node_id];

                // transform point to triangle local coordinates
                const auto local_point = transform(hitnode->AbsPosition);

                // collision test
                const bool is_colliding = InsideTriangleTest(local_point, collrange);
                if (is_colliding)
                {
                    inter_collcabrate[i].rate = 0;

                    const auto coord = local_point.barycentric;
                    auto distance   = local_point.distance;
                    auto normal     = triangle.normal();

                    // adapt in case the collision is occuring on the backface of the triangle
                    const auto neighbour_node_ids = hit_actor->ar_node_to_node_connections[h->node_id];
                    const bool is_backface = BackfaceCollisionTest(distance, normal, *no, neighbour_node_ids, hit_actor->ar_nodes);
                    if (is_backface)
                    {
                        // flip surface normal and distance to triangle plane
                        normal   = -normal;
                        distance = -distance;
                    }

                    const auto penetration_depth = collrange - distance;

                    const bool remote = (hit_actor->ar_sim_state == Actor::SimState::NETWORKED_OK);

                    ResolveCollisionForces(penetration_depth, *hitnode, *na, *nb, *no, coord.alpha,
                            coord.beta, coord.gamma, normal, dt, remote, submesh_ground_model);

                    hitnode->nd_last_collision_gm = &submesh_ground_model;
                    hitnode->nd_has_mesh_contact = true;
                    na->nd_has_mesh_contact = true;
                    nb->nd_has_mesh_contact = true;
                    no->nd_has_mesh_contact = true;
                }
            }
        }
        else
        {
            inter_collcabrate[i].rate++;
        }
    }
}