// <ZZ> This file contains functions for drawing and moving the scope itself...
#define SCOPE_ENTRY_SIZE 60.0f

#define SPRING_TYPE_TOOTHPICK 0
#define SPRING_TYPE_STIFFENER 1

unsigned short num_scope_point = 0;                     // Number of points in the scope
unsigned short num_scope_control = 0;                   // The number of control points
unsigned short num_scope_spring = 0;                 // The number of springs connecting points of the scope
unsigned short num_scope_main = 0;                      // Number of points running up the center of the scope
float scope_point_data[MAX_SCOPE_POINT][7];             // The position and velocity and weight of each point in the endoscope
float scope_control_previous_xyz[MAX_SCOPE_CONTROL][3]; // Previous location of each control point
unsigned short scope_control_list[MAX_SCOPE_CONTROL];   // The index of each control point
unsigned short scope_main_list[MAX_SCOPE_POINT];        // The index of each point in the line that runs up the center of the scope
unsigned short scope_spring_list[MAX_SCOPE_SPRING][2];  // The index of each point in each spring
float scope_control_fore_xyz[MAX_SCOPE_CONTROL][3];          // The forward direction vector for this spline point (for figuring in-between point locations)
float scope_control_normal_xyz[MAX_SCOPE_CONTROL][3];   // The up vector for each point in the scope (primarily for determining camera orientation)
unsigned char scope_control_in_entry_area[MAX_SCOPE_CONTROL];  // Just what it says...
float scope_spring_length[MAX_SCOPE_SPRING];            // The ideal length of each spring in the scope
unsigned char scope_spring_type[MAX_SCOPE_SPRING];      // How this spring behaves...

float global_x_initial = 0.0f;
float global_y_initial = 0.0f;
float global_z_initial = 0.0f;                          // Z coordinate of base control point at 0 insertion...


//------------------------------------------------------------------------------------
void scope_add_spring(unsigned short a, unsigned short b, unsigned char type)
{
    // <ZZ> This function adds a spring to the scope, between the two specified
    //      vertices...
    float x, y, z;

    if(num_scope_spring < MAX_SCOPE_SPRING)
    {
        if(a < num_scope_point && b < num_scope_point)
        {
            // Add the new spring...
            scope_spring_list[num_scope_spring][0] = a;
            scope_spring_list[num_scope_spring][1] = b;
            x = scope_point_data[a][X] - scope_point_data[b][X];
            y = scope_point_data[a][Y] - scope_point_data[b][Y];
            z = scope_point_data[a][Z] - scope_point_data[b][Z];
            scope_spring_length[num_scope_spring] = (float) sqrt(x*x + y*y + z*z);
            scope_spring_type[num_scope_spring] = type;
            num_scope_spring++;
        }
    }
}

//------------------------------------------------------------------------------------
void scope_determine_spline()
{
    // <ZZ> This function builds the spline of in-between points, based on control point positions...
    unsigned short count, i, j, k, a, c;
    float first_xyz[3];
    float second_xyz[3];
    float weight, inverse, weight_add;
    float big_weight, big_inverse;
    float multiplier, temp;


    num_scope_point = num_scope_control;
    num_scope_main = 0;
    if(num_scope_control > 1 && global_scope_between_points > 0)
    {
        // Fill in first tangent vector...
        a = scope_control_list[0];
        c = scope_control_list[1];
        scope_control_fore_xyz[0][X] = (scope_point_data[a][X] - scope_point_data[c][X]);
        scope_control_fore_xyz[0][Y] = (scope_point_data[a][Y] - scope_point_data[c][Y]);
        scope_control_fore_xyz[0][Z] = (scope_point_data[a][Z] - scope_point_data[c][Z]);
        // Fill in the final tangent vector...
        a = scope_control_list[num_scope_control-2];
        c = scope_control_list[num_scope_control-1];
        scope_control_fore_xyz[num_scope_control-1][X] = (scope_point_data[a][X] - scope_point_data[c][X]);
        scope_control_fore_xyz[num_scope_control-1][Y] = (scope_point_data[a][Y] - scope_point_data[c][Y]);
        scope_control_fore_xyz[num_scope_control-1][Z] = (scope_point_data[a][Z] - scope_point_data[c][Z]);
        // Now fill in all the other tangent vectors...
        count = num_scope_control-1;
        i = 1;
        while(i < count)
        {
            a = scope_control_list[i-1];
            c = scope_control_list[i+1];
            scope_control_fore_xyz[i][X] = (scope_point_data[a][X] - scope_point_data[c][X]);
            scope_control_fore_xyz[i][Y] = (scope_point_data[a][Y] - scope_point_data[c][Y]);
            scope_control_fore_xyz[i][Z] = (scope_point_data[a][Z] - scope_point_data[c][Z]);
            i++;
        }
        repeat(i, num_scope_control)
        {
            normalize(scope_control_fore_xyz[i]);
        }
        // Now determine all subpoint positions of every spline segment...
        multiplier = global_scope_segment_length * 0.5f;
        weight_add = 1.0f/((float) (global_scope_between_points+1));
        repeat(i, count)
        {
            a = scope_control_list[i];
            k = i+1;
            c = scope_control_list[k];
            scope_main_list[num_scope_main] = a;
            num_scope_main++;
            weight = 0.0f;
            inverse = 1.0f;
            repeat(j, global_scope_between_points)
            {
                weight += weight_add;
                inverse -= weight_add;

                big_weight = (float) ((sin((weight-0.5f)*PI)+1.0f)*0.5f);
                big_inverse = 1.0f - big_weight;

temp = weight*multiplier;
                first_xyz[X] = scope_point_data[a][X] - scope_control_fore_xyz[i][X]*temp;
                first_xyz[Y] = scope_point_data[a][Y] - scope_control_fore_xyz[i][Y]*temp;
                first_xyz[Z] = scope_point_data[a][Z] - scope_control_fore_xyz[i][Z]*temp;

temp = inverse*multiplier;
                second_xyz[X] = scope_point_data[c][X] + scope_control_fore_xyz[k][X]*temp;
                second_xyz[Y] = scope_point_data[c][Y] + scope_control_fore_xyz[k][Y]*temp;
                second_xyz[Z] = scope_point_data[c][Z] + scope_control_fore_xyz[k][Z]*temp;

                scope_point_data[num_scope_point][X] = (first_xyz[X]*big_inverse) + (second_xyz[X]*big_weight);
                scope_point_data[num_scope_point][Y] = (first_xyz[Y]*big_inverse) + (second_xyz[Y]*big_weight);
                scope_point_data[num_scope_point][Z] = (first_xyz[Z]*big_inverse) + (second_xyz[Z]*big_weight);
                scope_point_data[num_scope_point][VX] = 0.0f;
                scope_point_data[num_scope_point][VY] = 0.0f;
                scope_point_data[num_scope_point][VZ] = 0.0f;
                scope_main_list[num_scope_main] = num_scope_point;
                num_scope_main++;
                num_scope_point++;
            }
        }
        scope_main_list[num_scope_main] = scope_control_list[i];
        num_scope_main++;
    }
}

//-----------------------------------------------------------------------------------------------
void scope_draw()
{
    // <ZZ> This function draws the endoscope...
    unsigned short i, j, k, count;
    float area_xyz[3];
//    float tangent_xyz[3];
    float normal_xyz[3];



    // How do we want to draw the scope?
    if(object_draw_wireframe || object_draw_spring)
    {
        // Draw all of the control points as spheres...
        repeat(i, num_scope_control)
        {
            if(!global_scope_cam || i > 1)
            {
                j = scope_control_list[i];
                particle_draw(scope_point_data[j], global_scope_radius, sphere_texture);
            }
        }
    }
    else
    {
        repeat(i, num_scope_control)
        {
            if(!global_scope_cam || i > 1)
            {
                j = scope_control_list[i];
                particle_draw(scope_point_data[j], global_scope_radius, scope_texture);
            }
        }


        // Draw the sub points too while we're at it...
        repeat(i, num_scope_main)
        {
            if(!global_scope_cam || i > 1)
            {
                j = scope_main_list[i];
                particle_draw(scope_point_data[j], global_scope_radius*0.25f, scope_texture);
            }
        }
    }




    // Line drawing mode...
    display_texture_off();
    display_blend_off();
    display_shade_off();



    // Draw all of the normal lines...
    if(object_draw_normal)
    {
        repeat(i, num_scope_control)
        {
            display_color(green);
            j = scope_control_list[i];
            normal_xyz[X] = scope_point_data[j][X] + scope_control_normal_xyz[i][X]*20.0f;
            normal_xyz[Y] = scope_point_data[j][Y] + scope_control_normal_xyz[i][Y]*20.0f;
            normal_xyz[Z] = scope_point_data[j][Z] + scope_control_normal_xyz[i][Z]*20.0f;
            display_start_line();
                display_vertex(scope_point_data[j]);
                display_vertex(normal_xyz);
            display_end();
        }
    }


    // Draw all of the fore lines...
//    repeat(i, num_scope_control)
//    {
//        display_color(blue);
//        j = scope_control_list[i];
//        normal_xyz[X] = scope_point_data[j][X] + scope_control_fore_xyz[i][X]*20.0f;
//        normal_xyz[Y] = scope_point_data[j][Y] + scope_control_fore_xyz[i][Y]*20.0f;
//        normal_xyz[Z] = scope_point_data[j][Z] + scope_control_fore_xyz[i][Z]*20.0f;
//        display_start_line();
//            display_vertex(scope_point_data[j]);
//            display_vertex(normal_xyz);
//        display_end();
//    }


    // Draw all of the spring lines
    if(object_draw_spring)
    {
        repeat(i, num_scope_spring)
        {
            if(scope_spring_type[i] == SPRING_TYPE_TOOTHPICK)
            {
                display_color(yellow);
            }
            else
            {
                display_color(red);
            }
            j = scope_spring_list[i][0];
            k = scope_spring_list[i][1];
            display_start_line();
                display_vertex(scope_point_data[j]);
                display_vertex(scope_point_data[k]);
            display_end();
        }
    }


    // Draw the line that runs up the center of the scope...
    if(num_scope_main > 1)
    {
        display_color(white);
        count = num_scope_main - 1;
        i = 0;
        if(global_scope_cam)
        {
            i = 2 + global_scope_between_points;
        }
        while(i < count)
        {
            j = scope_main_list[i];
            k = scope_main_list[i+1];
            display_start_line();
                display_vertex(scope_point_data[j]);
                display_vertex(scope_point_data[k]);
            display_end();
            i++;
        }
    }



    // Draw all of the spline tangent lines
/*
    if(FALSE)
    {
        display_color(green);
        repeat(i, num_scope_control)
        {
            j = scope_control_list[i];
            display_color(green);
            display_start_line();
                display_vertex(scope_point_data[j]);
                tangent_xyz[X] = scope_point_data[j][X] + scope_control_fore_xyz[i][X];
                tangent_xyz[Y] = scope_point_data[j][Y] + scope_control_fore_xyz[i][Y];
                tangent_xyz[Z] = scope_point_data[j][Z] + scope_control_fore_xyz[i][Z];
                display_vertex(tangent_xyz);
            display_end();
            display_color(blue);
            display_start_line();
                display_vertex(scope_point_data[j]);
                tangent_xyz[X] = scope_point_data[j][X] - scope_control_fore_xyz[i][X];
                tangent_xyz[Y] = scope_point_data[j][Y] - scope_control_fore_xyz[i][Y];
                tangent_xyz[Z] = scope_point_data[j][Z] - scope_control_fore_xyz[i][Z];
                display_vertex(tangent_xyz);
            display_end();
        }
    }
*/

    // Draw the entry area...
    if(!global_scope_cam)
    {
        display_blend_trans();
        display_color_alpha(entry);
        display_start_fan();
            area_xyz[X] = -SCOPE_ENTRY_SIZE;
            area_xyz[Y] = -SCOPE_ENTRY_SIZE;
            area_xyz[Z] = 0.0f;
            display_vertex(area_xyz);
            area_xyz[X] = SCOPE_ENTRY_SIZE;
            display_vertex(area_xyz);
            area_xyz[Y] = SCOPE_ENTRY_SIZE;
            display_vertex(area_xyz);
            area_xyz[X] = -SCOPE_ENTRY_SIZE;
            display_vertex(area_xyz);
        display_end();
        display_blend_off();
    }
}

//------------------------------------------------------------------------------------
void scope_physics_update()
{
    // <ZZ> This function performs the physics calculations on the scope
    //      spring/toothpick system
    unsigned short a, b, c, i, j;
    float dis_xyz[3];
    float dis, force, temp, angle;
    float normal_xyz[3];
    float fore_xyz[3];
    float side_xyz[3];
    float previous_normal_xyz[3];
//    float fore_component, side_component, normal_component;
//    float previous_side_xyz[3];


    // Apply (cheesy non-inertial) gravity to all control points outside the entry area
    repeat(i, num_scope_control)
    {
        j = scope_control_list[i];
        scope_point_data[j][Z]-=global_gravity;
    }


    // Walk through each control point, starting at the base and working to the tip...
    // Reposition the normal of each, according to the normal of the previous...
    angle = global_dll_zangle;
    angle = angle * 2.0f * PI / 360.0f;
    normal_xyz[X] = (float) sin(angle);
    normal_xyz[Y] = (float) -cos(angle);
    normal_xyz[Z] = 0.0f;
    j = num_scope_control-2;
    while(j > 0)
    {
        a = scope_control_list[j-1];  // The point nearer to the tip for the current segments
        b = scope_control_list[j];  // The point in the center of the current segments
        c = scope_control_list[j+1];// The point nearer to the base for the current segments

        // Determine the new normal for the current point
        fore_xyz[X] = scope_point_data[a][X] - scope_point_data[c][X];
        fore_xyz[Y] = scope_point_data[a][Y] - scope_point_data[c][Y];
        fore_xyz[Z] = scope_point_data[a][Z] - scope_point_data[c][Z];
        cross_product(fore_xyz, normal_xyz, side_xyz);
        cross_product(side_xyz, fore_xyz, normal_xyz);

        // Remember the previous normal direction, for rotating points later...
        previous_normal_xyz[X] = scope_control_normal_xyz[j][X];
        previous_normal_xyz[Y] = scope_control_normal_xyz[j][Y];
        previous_normal_xyz[Z] = scope_control_normal_xyz[j][Z];

        // Save the newly found normal...
        normalize(normal_xyz);
        scope_control_normal_xyz[j][X] = normal_xyz[X];
        scope_control_normal_xyz[j][Y] = normal_xyz[Y];
        scope_control_normal_xyz[j][Z] = normal_xyz[Z];
        j--;



/*
        if(j > 1)
        {
            // We will also want to cause a rotational effect on the scope
            // vertices, according to our rotational stiffness.  This is
            // so a scope bent into an L shape doesn't stay stationary when
            // spun.
            c = scope_control_list[j-2];  // The next point closer to the tip

            // Determine the previous normal and side vectors...
            cross_product(fore_xyz, previous_normal_xyz, previous_side_xyz);
            cross_product(previous_side_xyz, fore_xyz, previous_normal_xyz);
            normalize(previous_normal_xyz);
            normalize(previous_side_xyz);
            normalize(fore_xyz);
            normalize(side_xyz);


            // Convert the position offset of the c point, into three
            // vector components...
            dis_xyz[X] = scope_point_data[c][X] - scope_point_data[a][X];
            dis_xyz[Y] = scope_point_data[c][Y] - scope_point_data[a][Y];
            dis_xyz[Z] = scope_point_data[c][Z] - scope_point_data[a][Z];
            fore_component = dot_product(dis_xyz, fore_xyz);
            normal_component = dot_product(dis_xyz, previous_normal_xyz);
            side_component = dot_product(dis_xyz, previous_side_xyz);

            // Determine the newly desired position of this vertex, given
            // the new side and normal vectors...
            dis_xyz[X] = scope_point_data[a][X] + fore_component*fore_xyz[X] + side_component*side_xyz[X] + normal_component*normal_xyz[X];
            dis_xyz[Y] = scope_point_data[a][Y] + fore_component*fore_xyz[Y] + side_component*side_xyz[Y] + normal_component*normal_xyz[Y];
            dis_xyz[Z] = scope_point_data[a][Z] + fore_component*fore_xyz[Z] + side_component*side_xyz[Z] + normal_component*normal_xyz[Z];

            // Help the vertex reach its desired location...
            dis_xyz[X] -= scope_point_data[c][X];
            dis_xyz[Y] -= scope_point_data[c][Y];
            dis_xyz[Z] -= scope_point_data[c][Z];
            dis_xyz[X]*=global_scope_turn_stiffness;
            dis_xyz[Y]*=global_scope_turn_stiffness;
            dis_xyz[Z]*=global_scope_turn_stiffness;
            scope_point_data[c][X] += dis_xyz[X];
            scope_point_data[c][Y] += dis_xyz[Y];
            scope_point_data[c][Z] += dis_xyz[Z];

            // Apply the same force to all following points
            k = j-2;
            while(k > 0)
            {
                k--;
                c = scope_control_list[k];  // The next point closer to the tip
                scope_point_data[c][X] += dis_xyz[X];
                scope_point_data[c][Y] += dis_xyz[Y];
                scope_point_data[c][Z] += dis_xyz[Z];
            }
        }
*/
    }







    // Do a similar thing for the tip vertex -- but using only two points...
    a = scope_control_list[0];  // The tip vertex
    b = scope_control_list[1];  // The point adjacent to the tip...

    // Determine the new normal for the current point
    fore_xyz[X] = scope_point_data[a][X] - scope_point_data[b][X];
    fore_xyz[Y] = scope_point_data[a][Y] - scope_point_data[b][Y];
    fore_xyz[Z] = scope_point_data[a][Z] - scope_point_data[b][Z];
    cross_product(fore_xyz, normal_xyz, side_xyz);
    cross_product(side_xyz, fore_xyz, normal_xyz);

    // Save the newly found normal...
    normalize(normal_xyz);
    scope_control_normal_xyz[0][X] = normal_xyz[X];
    scope_control_normal_xyz[0][Y] = normal_xyz[Y];
    scope_control_normal_xyz[0][Z] = normal_xyz[Z];











    // Move all of the points based on their current velocity...
//    repeat(i, num_scope_control)
//    {
//        j = scope_control_list[i];
//        scope_point_data[j][X]+=scope_point_data[j][VX];
//        scope_point_data[j][Y]+=scope_point_data[j][VY];
//        scope_point_data[j][Z]+=scope_point_data[j][VZ];
//    }


    // Update each spring in our scope mesh...
    temp = global_scope_stiffness*global_scope_stiffness;
//    repeat(pass, 2)
    {
        repeat(i, num_scope_spring)
        {
            a = scope_spring_list[i][0];
            b = scope_spring_list[i][1];


            // Find the distance between the two points...
            dis_xyz[X] = scope_point_data[b][X] - scope_point_data[a][X];
            dis_xyz[Y] = scope_point_data[b][Y] - scope_point_data[a][Y];
            dis_xyz[Z] = scope_point_data[b][Z] - scope_point_data[a][Z];
            dis = dis_xyz[X]*dis_xyz[X] + dis_xyz[Y]*dis_xyz[Y] + dis_xyz[Z]*dis_xyz[Z];
            dis = (float) sqrt(dis);
            dis += 0.0001f;
            dis_xyz[X]/=dis;
            dis_xyz[Y]/=dis;
            dis_xyz[Z]/=dis;
            force = scope_spring_length[i] - dis;
            force*=0.5f;
            if(scope_spring_type[i] == SPRING_TYPE_TOOTHPICK)
            {
                // Apply forces to point b (point a always stays stationary)...
                scope_point_data[b][X] += dis_xyz[X]*force;
                scope_point_data[b][Y] += dis_xyz[Y]*force;
                scope_point_data[b][Z] += dis_xyz[Z]*force;
                scope_point_data[a][X] -= dis_xyz[X]*force;
                scope_point_data[a][Y] -= dis_xyz[Y]*force;
                scope_point_data[a][Z] -= dis_xyz[Z]*force;
            }
            else if(scope_spring_type[i] == SPRING_TYPE_STIFFENER)
            {
                // Apply forces to point b, but only if we're pushing...
//                if(force > 0.0f)
//                {
                    force*=temp;
                    scope_point_data[b][X] += dis_xyz[X]*force;
                    scope_point_data[b][Y] += dis_xyz[Y]*force;
                    scope_point_data[b][Z] += dis_xyz[Z]*force;
                    scope_point_data[a][X] -= dis_xyz[X]*force;
                    scope_point_data[a][Y] -= dis_xyz[Y]*force;
                    scope_point_data[a][Z] -= dis_xyz[Z]*force;
//                }
            }
        }
    }


    old_dll_zangle = global_dll_zangle;
    return;
}

//------------------------------------------------------------------------------------
void scope_collide()
{
    // <ZZ> This function does all of the collisions for the scope...
    unsigned short i, l, m, object, k, a, b, c;
    float radius, dis, multiplier;
    float center_xyz[3];
    float v_xyz[3];
    float mass_multiplier;
    float temp;
//global_testing = 0.0;



    // Now check every segment of our scope against every triangle of the
    // object mesh...
    i = num_scope_control;
    while(i > 1)
    {
        // Walk from the base of the scope to the tip...
        i--;

        // Get the two points associated with this segment of the scope...
        l = scope_control_list[i];
        m = scope_control_list[i-1];


        // Get the center point of the segment...
        center_xyz[X] = (scope_point_data[l][X] + scope_point_data[m][X])*0.5f;
        center_xyz[Y] = (scope_point_data[l][Y] + scope_point_data[m][Y])*0.5f;
        center_xyz[Z] = (scope_point_data[l][Z] + scope_point_data[m][Z])*0.5f;



        // Don't bother colliding this segment with the mesh if it hasn't been
        // inserted yet
        if(!scope_control_in_entry_area[i] && !scope_control_in_entry_area[i-1])
        {
            // Go through each mesh object in the scene...
            repeat(object, num_object)
            {
                // Is the center point within this object's bounding radius?
                v_xyz[X] = center_xyz[X] - object_center_xyz[object][X];
                v_xyz[Y] = center_xyz[Y] - object_center_xyz[object][Y];
                v_xyz[Z] = center_xyz[Z] - object_center_xyz[object][Z];
                dis = v_xyz[X]*v_xyz[X] + v_xyz[Y]*v_xyz[Y] + v_xyz[Z]*v_xyz[Z];
                radius = (global_scope_radius + global_scope_segment_length*0.5f) + object_bounding_radius[object];
                radius*=radius;
                if(dis < radius)
                {
                    // It is, so let's check the center point against every
                    // triangle of the object's mesh...  We precalculated a
                    // radius for each that should be safe to check in this manner...
                    repeat(k, object_num_triangle[object])
                    {
                        // Is the point within this triangle's bounding radius?
                        v_xyz[X] = center_xyz[X] - object_triangle_center_xyz[object][k][X];
                        v_xyz[Y] = center_xyz[Y] - object_triangle_center_xyz[object][k][Y];
                        v_xyz[Z] = center_xyz[Z] - object_triangle_center_xyz[object][k][Z];
                        dis = v_xyz[X]*v_xyz[X] + v_xyz[Y]*v_xyz[Y] + v_xyz[Z]*v_xyz[Z];
                        radius = (global_scope_radius + global_scope_segment_length*0.5f) + object_triangle_bounding_radius[object][k];
                        radius*=radius;
                        if(dis < radius)
                        {
                            // Okay, it's pretty darn close -- so let's do a more robust
                            // collision check to see if it actually hit...
                            a = object_triangle_list[object][k][0];
                            b = object_triangle_list[object][k][1];
                            c = object_triangle_list[object][k][2];


                            // We'll use our new capped cylinder/triangle collision routine
                            // to find the minimum distance...
//debug_draw = FALSE;
//if(i == 1)
//{
//    debug_draw = TRUE;
//}
                            dis = collide_cylinder_triangle(scope_point_data[l], scope_point_data[m], global_scope_radius, object_vertex_data[object][a], object_vertex_data[object][b], object_vertex_data[object][c], object_triangle_normal[object][k]);
//dis = -1.0f;
//debug_draw = FALSE;

                            // Now, did we actually have a hit?
                            if(dis > 0.0f)
                            {
                                // We had a collision between this cylinder and this triangle...
                                // Let's start by determining our mass multiplier, so a heavy
                                // object isn't affected by the scope as much as a light one...
                                mass_multiplier = global_scope_weight/(global_scope_weight + object_weight[object] + 0.00001f);


// Modify the mass multiplier so as to make vertices more and more massive as they
// move away from their original locations...
temp = (object_vertex_data[object][a][MULTIPLIER] + object_vertex_data[object][b][MULTIPLIER] + object_vertex_data[object][c][MULTIPLIER]) * 0.33333333333333333f;
mass_multiplier *= temp;

//if(debug_space)
//{
//    mass_multiplier = 0.0f;
//}
//else
//{
//    mass_multiplier = 0.5f;
//}

                                // Move the triangle away from the point of collision...
                                multiplier = dis*mass_multiplier;
multiplier*=0.3f;
//                                multiplier = (global_collision_backface) ? -multiplier : multiplier;
                                object_triangle_hit[object][k] = i;

//                                object_vertex_data[object][a][VX] -= multiplier*object_vertex_data[object][a][NX];
//                                object_vertex_data[object][a][VY] -= multiplier*object_vertex_data[object][a][NY];
//                                object_vertex_data[object][a][VZ] -= multiplier*object_vertex_data[object][a][NZ];
//                                object_vertex_data[object][b][VX] -= multiplier*object_vertex_data[object][b][NX];
//                                object_vertex_data[object][b][VY] -= multiplier*object_vertex_data[object][b][NY];
//                                object_vertex_data[object][b][VZ] -= multiplier*object_vertex_data[object][b][NZ];
//                                object_vertex_data[object][c][VX] -= multiplier*object_vertex_data[object][c][NX];
//                                object_vertex_data[object][c][VY] -= multiplier*object_vertex_data[object][c][NY];
//                                object_vertex_data[object][c][VZ] -= multiplier*object_vertex_data[object][c][NZ];

                                object_vertex_data[object][a][X] -= multiplier*object_vertex_data[object][a][NX];
                                object_vertex_data[object][a][Y] -= multiplier*object_vertex_data[object][a][NY];
                                object_vertex_data[object][a][Z] -= multiplier*object_vertex_data[object][a][NZ];
                                object_vertex_data[object][b][X] -= multiplier*object_vertex_data[object][b][NX];
                                object_vertex_data[object][b][Y] -= multiplier*object_vertex_data[object][b][NY];
                                object_vertex_data[object][b][Z] -= multiplier*object_vertex_data[object][b][NZ];
                                object_vertex_data[object][c][X] -= multiplier*object_vertex_data[object][c][NX];
                                object_vertex_data[object][c][Y] -= multiplier*object_vertex_data[object][c][NY];
                                object_vertex_data[object][c][Z] -= multiplier*object_vertex_data[object][c][NZ];

                                // Move the control points away from the triangle...
//if(i != 1)
//{
                                multiplier = dis*(1.0f-mass_multiplier);
multiplier*=0.3f;
//                                multiplier = (global_collision_backface) ? -multiplier : multiplier;
//                                multiplier *= global_collision_percent;
                                scope_point_data[m][X] += multiplier*object_triangle_normal[object][k][X];
                                scope_point_data[m][Y] += multiplier*object_triangle_normal[object][k][Y];
                                scope_point_data[m][Z] += multiplier*object_triangle_normal[object][k][Z];
//}

//                                scope_point_data[m][VX] += multiplier*object_triangle_normal[object][k][X];
//                                scope_point_data[m][VY] += multiplier*object_triangle_normal[object][k][Y];
//                                scope_point_data[m][VZ] += multiplier*object_triangle_normal[object][k][Z];




                                // Do the same with the other control point...
                                multiplier = dis*(1.0f-mass_multiplier);
multiplier*=0.3f;
//                                multiplier = (global_collision_backface) ? -multiplier : multiplier;
//                                multiplier *= (1.0f - global_collision_percent);

                                scope_point_data[l][X] += multiplier*object_triangle_normal[object][k][X];
                                scope_point_data[l][Y] += multiplier*object_triangle_normal[object][k][Y];
                                scope_point_data[l][Z] += multiplier*object_triangle_normal[object][k][Z];

//                                scope_point_data[l][VX] += multiplier*object_triangle_normal[object][k][X];
//                                scope_point_data[l][VY] += multiplier*object_triangle_normal[object][k][Y];
//                                scope_point_data[l][VZ] += multiplier*object_triangle_normal[object][k][Z];

                                if(i == 1 && global_collision_percent > 0.99f)
                                {
                                    // Let's also do a check for tally, since this is our tip...
                                    // Only record a tally if the scope is looking into the triangle it hit, within a
                                    // 90' angle or so...
                                    temp = -dot_product(scope_control_fore_xyz[0], object_triangle_normal[object][k]);
global_testing = global_collision_percent;
//                                    if(temp > 0.866f)
                                    if(temp > 0.707f)
                                    {
                                        if(global_tally_timer == 0)
                                        {
                                            global_tally++;
                                            temp = (rand()&1023)/10.23f;
                                            if(temp < global_percent_chance_of_permanent_whiteout)
                                            {
                                                global_permanent_whiteout = TRUE;
                                            }
                                        }
                                        global_tally_in_contact = TRUE;
                                        global_tally_timer = TALLY_TIMER_RESET;
                                        global_whiteout_amount = 1.0f;
                                        temp = global_whiteout_max_seconds - global_whiteout_min_seconds;
                                        temp = ((rand()&1023)/1023.0f)*temp;
                                        temp += global_whiteout_min_seconds;
                                        // temp is now the number of seconds to keep the whiteout active...
                                        temp = (temp > 0.1f) ? temp : 0.1f;
                                        global_whiteout_drain = 1.0f/(UPDATES_PER_SECOND*temp);
                                    }
                                }


                                // Prevent other collisions from sneaking through if we messed up one of the seams in the mesh
object_vertex_data[object][a][WELDSTOP] = 1.0f;
object_vertex_data[object][b][WELDSTOP] = 1.0f;
object_vertex_data[object][c][WELDSTOP] = 1.0f;
                                object_keep_welded_vertices_together();
object_vertex_data[object][a][WELDSTOP] = 0.0f;
object_vertex_data[object][b][WELDSTOP] = 0.0f;
object_vertex_data[object][c][WELDSTOP] = 0.0f;
                            }

                        }
                    }
                }
            }
        }
    }

    return;
}

//------------------------------------------------------------------------------------
void scope_clear_collision_list(void)
{
    unsigned short object, k;
    repeat(object, num_object)
    {
        // Check every triangle of the object...
        repeat(k, object_num_triangle[object])
        {
            object_triangle_hit[object][k] = MAX_SCOPE_CONTROL;
        }
    }
}

//------------------------------------------------------------------------------------
void scope_move_head(float yangle, int iterations)
{
    // <ZZ> This function moves the head control point without affecting the
    //      rest of the spline...
    float desired, sine, cosine;
    float front_xyz[3], up_xyz[3], dis_xyz[3], side_xyz[3];
float v_xyz[3];
    unsigned short a, b, c;
    float iteration_multiplier;
    float multiplier;
    float inverse;


    iteration_multiplier = 1.0f/iterations;
    multiplier = iteration_multiplier*global_scope_tip_stiffness;
    inverse = 1.0f - multiplier;
    if(num_scope_control > 2)
    {
//        desired = scope_spring_length[0];
        a = scope_control_list[0];
        b = scope_control_list[1];
        c = scope_control_list[2];
        desired = global_scope_segment_length;

        // Find our front and up vectors...
        front_xyz[X] = scope_point_data[b][X] - scope_point_data[c][X];
        front_xyz[Y] = scope_point_data[b][Y] - scope_point_data[c][Y];
        front_xyz[Z] = scope_point_data[b][Z] - scope_point_data[c][Z];
        normalize(front_xyz);
//        cross_product(scope_control_normal_xyz[1], front_xyz, side_xyz);
        cross_product(scope_control_normal_xyz[2], front_xyz, side_xyz);
        cross_product(side_xyz, front_xyz, up_xyz);


        // Find the desired vector for the head point...
        yangle = -yangle * 2.0f * PI / 360.0f;
        sine = (float) sin(yangle);
        cosine = (float) cos(yangle);
        dis_xyz[X] = (cosine*front_xyz[X] + sine*up_xyz[X]);
        dis_xyz[Y] = (cosine*front_xyz[Y] + sine*up_xyz[Y]);
        dis_xyz[Z] = (cosine*front_xyz[Z] + sine*up_xyz[Z]);
        normalize(dis_xyz);
        dis_xyz[X]*=desired;
        dis_xyz[Y]*=desired;
        dis_xyz[Z]*=desired;

        // Set the final desired head position...
        dis_xyz[X] += scope_point_data[b][X];
        dis_xyz[Y] += scope_point_data[b][Y];
        dis_xyz[Z] += scope_point_data[b][Z];
v_xyz[X] = scope_point_data[a][X];
v_xyz[Y] = scope_point_data[a][Y];
v_xyz[Z] = scope_point_data[a][Z];
        scope_point_data[a][X] = (dis_xyz[X]*multiplier) + (scope_point_data[a][X]*inverse);
        scope_point_data[a][Y] = (dis_xyz[Y]*multiplier) + (scope_point_data[a][Y]*inverse);
        scope_point_data[a][Z] = (dis_xyz[Z]*multiplier) + (scope_point_data[a][Z]*inverse);
v_xyz[X] -= scope_point_data[a][X];
v_xyz[Y] -= scope_point_data[a][Y];
v_xyz[Z] -= scope_point_data[a][Z];
        scope_point_data[b][X] += v_xyz[X]*0.5f;
        scope_point_data[b][Y] += v_xyz[Y]*0.5f;
        scope_point_data[b][Z] += v_xyz[Z]*0.5f;
        scope_point_data[c][X] += v_xyz[X]*0.5f;
        scope_point_data[c][Y] += v_xyz[Y]*0.5f;
        scope_point_data[c][Z] += v_xyz[Z]*0.5f;
    }
    return;
}

//------------------------------------------------------------------------------------
void scope_handle_entry_area()
{
    // Keep all points below z of 0.0 aligned to the central axis...
    unsigned short a, i, j;
    float angle;
    float z;
    float temp;
    float normal_xyz[3];


    // Remember old control point locations...
    repeat(i, num_scope_control)
    {
        j = scope_control_list[i];
        scope_control_previous_xyz[i][X] = scope_point_data[j][X];
        scope_control_previous_xyz[i][Y] = scope_point_data[j][Y];
        scope_control_previous_xyz[i][Z] = scope_point_data[j][Z];
        scope_control_in_entry_area[i] = FALSE;
    }



    if(num_scope_control > 1)
    {
        angle = global_dll_zangle;
        angle = angle * 2.0f * PI / 360.0f;
        normal_xyz[X] = (float) sin(angle);
        normal_xyz[Y] = (float) -cos(angle);
        normal_xyz[Z] = 0.0f;
        i = num_scope_control;
        z = global_z_initial + global_dll_zin;
        z-=global_scope_segment_length;
        while(i > 0 && z < 0.0f)
        {
            i--;
            a = scope_control_list[i];
            scope_point_data[a][X] *= 0.90f;
            scope_point_data[a][Y] *= 0.90f;
//            scope_point_data[a][Z] = (z*0.03f) + (scope_point_data[a][Z]*0.97f);
            scope_point_data[a][Z] = (z*0.20f) + (scope_point_data[a][Z]*0.80f);
            z += global_scope_segment_length;

            scope_control_normal_xyz[i][X] = normal_xyz[X];
            scope_control_normal_xyz[i][Y] = normal_xyz[Y];
            scope_control_normal_xyz[i][Z] = normal_xyz[Z];
            scope_control_in_entry_area[i] = TRUE;
            if(i == 0)
            {
                global_permanent_whiteout = FALSE;
                global_whiteout_amount *= 0.9f;
                global_tally = 0;
                global_tally_contact_time = 0.0f;
                global_tally_in_contact = FALSE;
            }
        }
        if(i > 0 && z < global_scope_segment_length && z >= 0.0f)
        {
            i--;
            temp = z/global_scope_segment_length;
            temp *= 0.10f;
            temp += 0.90f;
            a = scope_control_list[i];
            scope_point_data[a][X] *= temp;
            scope_point_data[a][Y] *= temp;
            scope_control_in_entry_area[i] = TRUE;
        }
    }
    return;
}

//-----------------------------------------------------------------------------------------------
void scope_initialize(unsigned short number_of_control_points, float length, float radius, float x, float y, float z)
{
    // <ZZ> This function sets up the endoscope.  It is positioned with the leading point
    //      (number 0) at the xyz location specified, and all other vertices below that.
    unsigned short i, j, a, b;
    int f;

    log_message("Attempting to setup endoscope with %d control points", number_of_control_points);
    log_message("Overall length of endoscope is to be %f cm", length);
    log_message("Radius of endoscope is to be %f cm", radius);


    global_tally = 0;
    global_tally_contact_time = 0.0f;
    global_tally_in_contact = FALSE;
    global_permanent_whiteout = FALSE;
    global_whiteout_amount = 0.0f;
    global_x_initial = x;
    global_y_initial = y;
    global_z_initial = z;
    global_dll_zin = 0.0f;
    global_dll_zangle = 0.0f;
    global_dll_headangle = 0.0f;


    num_scope_point = 0;
    num_scope_control = 0;
    num_scope_main = 0;
    num_scope_spring = 0;
    global_scope_radius = radius;
    global_scope_segment_length =  0.0f;
    if(number_of_control_points > 0 && number_of_control_points <= MAX_SCOPE_CONTROL)
    {
        // Add each control point
        if(number_of_control_points > 1)
        {
            length = length / ((float) (number_of_control_points-1));
        }
        global_scope_segment_length = length;
        repeat(i, number_of_control_points)
        {
            // Add the point to the scope...
            scope_point_data[num_scope_point][X] = x;
            scope_point_data[num_scope_point][Y] = y;
            scope_point_data[num_scope_point][Z] = z;
            scope_point_data[num_scope_point][VX] = 0.0f;
            scope_point_data[num_scope_point][VY] = 0.0f;
            scope_point_data[num_scope_point][VZ] = 0.0f;
            scope_point_data[num_scope_point][MASS] = 5.0f;
            scope_control_list[num_scope_control] = num_scope_point;
            scope_control_normal_xyz[num_scope_control][X] = 0.0f;
            scope_control_normal_xyz[num_scope_control][Y] = -1.0f;
            scope_control_normal_xyz[num_scope_control][Z] = 0.0f;
            num_scope_control++;
            num_scope_point++;
            z-=length;
        }
        

        // Set the mass for the main control point...
        a = scope_control_list[num_scope_control-1];
        scope_point_data[a][MASS] = 100.0f;
        global_z_initial = scope_point_data[a][Z];


        // Add a bunch of stiffener springs to help the scope
        // keep a rigid curve...
        repeat(j, 3)
        {
            i = num_scope_control-1;
            while(i > 0)
            {
                f = i-(j)-2;
//                if(f > -1)
                if(f > 0)
                {
                    a = scope_control_list[i];
                    b = scope_control_list[f];
                    scope_add_spring(a, b, SPRING_TYPE_STIFFENER);
                }
                i--;
            }
        }


        // Now add toothpick springs between control points to maintain
        // a constant distance...
        i = num_scope_control-1;
        while(i > 0)
        {
            a = scope_control_list[i];
            b = scope_control_list[i-1];
            scope_add_spring(a, b, SPRING_TYPE_TOOTHPICK);
            i--;
        }
    }
    else
    {
        log_message("ERROR:  Can't setup endoscope with those parameters");
    }
    log_message("Scope initialization complete");
    scope_determine_spline();
    return;
}

//------------------------------------------------------------------------------------
void scope_create_object(unsigned short divisions)
{
    // <ZZ> This function creates a mesh tube object based on the scope's current location...
    float angle, add, y, yadd;
    unsigned short i, a, b, c, j, v;
    float t_xyz[3], p_xyz[3], side_xyz[3], up_xyz[3];
    float radius, cosine, sine;
    float temp_scaling;

    up_xyz[X] = 0.0f;
    up_xyz[Y] = 1.0f;
    up_xyz[Z] = 0.0f;


    log_message("Creating scope object...");

    object_start();
    temp_scaling = global_object_scaling;
    global_object_scaling = 1.0f;
    add = 2 * PI / divisions;
    radius = 25.0f;
    y = 0.0f;
    yadd = 0.33f / (global_scope_between_points + 1);
    divisions++;
    v = 0;
    repeat(i, (num_scope_main-1))
    {
        a = scope_main_list[i];
        b = scope_main_list[i+1];


        t_xyz[X] = scope_point_data[b][X] - scope_point_data[a][X];
        t_xyz[Y] = scope_point_data[b][Y] - scope_point_data[a][Y];
        t_xyz[Z] = scope_point_data[b][Z] - scope_point_data[a][Z];
        normalize(t_xyz);
        cross_product(t_xyz, up_xyz, side_xyz);
        cross_product(t_xyz, side_xyz, up_xyz);
        up_xyz[X] = -up_xyz[X];
        up_xyz[Y] = -up_xyz[Y];
        up_xyz[Z] = -up_xyz[Z];
        normalize(side_xyz);
        normalize(up_xyz);



        angle = 0.0f;
        repeat(j, divisions)
        {

            cosine = (float) cos(angle);
            sine = (float) sin(angle);
            p_xyz[X] = scope_point_data[a][X] + cosine*side_xyz[X]*radius + sine*up_xyz[X]*radius;
            p_xyz[Y] = scope_point_data[a][Y] + cosine*side_xyz[Y]*radius + sine*up_xyz[Y]*radius;
            p_xyz[Z] = scope_point_data[a][Z] + cosine*side_xyz[Z]*radius + sine*up_xyz[Z]*radius;

            object_add_vertex(p_xyz[X], p_xyz[Y], p_xyz[Z]);
            object_set_texpos(v, (j*1.0f/(divisions-1)), y);
            v++;
            angle+=add;
        }
        y+=yadd;
    }

    // Now add all triangles...
    repeat(i, (num_scope_main-2))
    {
        repeat(j, (divisions-1))
        {
            a = (divisions*i)+j;
            b = (divisions*i)+j+1;
            c = (divisions*(i+1))+j+1;
            object_add_triangle(a, b, c);
            b = (divisions*(i+1))+j;
            object_add_triangle(a, c, b);
        }
    }

    // Set the texture...
    object_set_texture(display_load_texture_file_rgba("MODELS\\COMMON\\DEFAULT.TGA"));


    object_end();
    object_fix_z();
    global_object_scaling = temp_scaling;
    scope_initialize(global_scope_control_points, global_scope_length, global_scope_radius, 0.0f, 0.0f, 0.0f);
}

//-----------------------------------------------------------------------------------------------
void scope_camera(unsigned char right_facing)
{
    // <ZZ> A little wrapper function for the camera setup stuff that gets called every display
    //      update...
    if(num_scope_control > 2)
    {
        // Position the camera inside the scope...  Between the first and second control points...
        display_arbitrary_camera(scope_point_data[0], scope_control_fore_xyz[0], scope_control_normal_xyz[0]);
    }
    scope_fore_xyz[X] = camera_fore_xyz[X];
    scope_fore_xyz[Y] = camera_fore_xyz[Y];
    scope_fore_xyz[Z] = camera_fore_xyz[Z];
    scope_up_xyz[X] = camera_up_xyz[X];
    scope_up_xyz[Y] = camera_up_xyz[Y];
    scope_up_xyz[Z] = camera_up_xyz[Z];
    scope_side_xyz[X] = camera_side_xyz[X];
    scope_side_xyz[Y] = camera_side_xyz[Y];
    scope_side_xyz[Z] = camera_side_xyz[Z];
    scope_xyz[X] = camera_xyz[X];
    scope_xyz[Y] = camera_xyz[Y];
    scope_xyz[Z] = camera_xyz[Z];


    if(global_scope_cam == FALSE || num_scope_control <= 2)
    {
        if(camera_distance < MIN_CAMERA_DISTANCE)
        {
            camera_distance = MIN_CAMERA_DISTANCE;
        }

        // Center the external camera on the scope head...
        target_xyz[X]=scope_point_data[0][X];
        target_xyz[Y]=scope_point_data[0][Y];
        target_xyz[Z]=scope_point_data[0][Z];
        if(right_facing)
        {
            camera_rotation_xy[X]+=16384;
            display_camera_position();
            display_look_at(camera_xyz, target_xyz);
            camera_rotation_xy[X]-=16384;
        }
        else
        {
            display_camera_position();
            display_look_at(camera_xyz, target_xyz);
        }
    }
    return;
}

//------------------------------------------------------------------------------------
