Subversion Repositories Games.Carmageddon

Rev

Rev 18 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #include "raycast.h"
  2. #include "brender/brender.h"
  3. #include "brucetrk.h"
  4. #include "globvars.h"
  5. #include "harness/trace.h"
  6. #include <float.h>
  7. #include <math.h>
  8. #include <stdlib.h>
  9.  
  10. br_matrix34 gPick_model_to_view__raycast; // suffix added to avoid duplicate symbol
  11. int gBelow_face_index;
  12. br_scalar gCurrent_y;
  13. int gAbove_face_index;
  14. br_model* gAbove_model;
  15. br_model* gBelow_model;
  16. br_scalar gHighest_y_below;
  17. br_actor* gY_picking_camera;
  18. br_scalar gLowest_y_above;
  19.  
  20. // Added, probably can be replaced with NULL
  21. br_model* model_unk1;
  22. br_material* material_unk1;
  23.  
  24. // IDA: int __usercall DRActorToRoot@<EAX>(br_actor *a@<EAX>, br_actor *world@<EDX>, br_matrix34 *m@<EBX>)
  25. int DRActorToRoot(br_actor* a, br_actor* world, br_matrix34* m) {
  26.     LOG_TRACE("(%p, %p, %p)", a, world, m);
  27.  
  28.     if (world == a) {
  29.         BrMatrix34Identity(m);
  30.         return 1;
  31.     } else {
  32.         BrTransformToMatrix34(m, &a->t);
  33.         for (a = a->parent; a && world != a; a = a->parent) {
  34.             if (a->t.type != BR_TRANSFORM_IDENTITY) {
  35.                 BrMatrix34PostTransform(m, &a->t);
  36.             }
  37.         }
  38.         return world == a;
  39.     }
  40. }
  41.  
  42. // IDA: void __cdecl InitRayCasting()
  43. void InitRayCasting(void) {
  44.     br_camera* camera_ptr;
  45.     LOG_TRACE("()");
  46.  
  47.     gY_picking_camera = BrActorAllocate(BR_ACTOR_CAMERA, NULL);
  48.     camera_ptr = gY_picking_camera->type_data;
  49.     camera_ptr->type = BR_CAMERA_PERSPECTIVE_FOV;
  50.     camera_ptr->field_of_view = BrDegreeToAngle(70.0f);
  51.     camera_ptr->hither_z = 0.001f;
  52.     camera_ptr->yon_z = 1000.0f;
  53.     camera_ptr->aspect = 1.0f;
  54.     gY_picking_camera->t.t.mat.m[0][0] = 1.0;
  55.     gY_picking_camera->t.t.mat.m[0][1] = 0.0;
  56.     gY_picking_camera->t.t.mat.m[0][2] = 0.0;
  57.     gY_picking_camera->t.t.mat.m[1][0] = 0.0;
  58.     gY_picking_camera->t.t.mat.m[1][1] = 0.0;
  59.     gY_picking_camera->t.t.mat.m[1][2] = -1.0;
  60.     gY_picking_camera->t.t.mat.m[2][0] = 0.0;
  61.     gY_picking_camera->t.t.mat.m[2][1] = 1.0;
  62.     gY_picking_camera->t.t.mat.m[2][2] = 0.0;
  63. }
  64.  
  65. // IDA: int __cdecl BadDiv(br_scalar a, br_scalar b)
  66. // Suffix added to avoid duplicate symbol
  67. int BadDiv__raycast(br_scalar a, br_scalar b) {
  68.     // LOG_TRACE("(%f, %f)", a, b);
  69.  
  70.     return fabs(b) < 1.0 && fabs(a) > fabs(b) * BR_SCALAR_MAX;
  71. }
  72.  
  73. // IDA: void __usercall DRVector2AccumulateScale(br_vector2 *a@<EAX>, br_vector2 *b@<EDX>, br_scalar s)
  74. //  Suffix added to avoid duplicate symbol
  75. void DRVector2AccumulateScale__raycast(br_vector2* a, br_vector2* b, br_scalar s) {
  76.     LOG_TRACE("(%p, %p, %f)", a, b, s);
  77.  
  78.     a->v[0] = b->v[0] * s + a->v[0];
  79.     a->v[1] = b->v[1] * s + a->v[1];
  80. }
  81.  
  82. // IDA: int __usercall PickBoundsTestRay@<EAX>(br_bounds *b@<EAX>, br_vector3 *rp@<EDX>, br_vector3 *rd@<EBX>, br_scalar t_near, br_scalar t_far, br_scalar *new_t_near, br_scalar *new_t_far)
  83. // Suffix added to avoid duplicate symbol
  84. int PickBoundsTestRay__raycast(br_bounds* b, br_vector3* rp, br_vector3* rd, br_scalar t_near, br_scalar t_far, br_scalar* new_t_near, br_scalar* new_t_far) {
  85.     int i;
  86.     float s;
  87.     float t;
  88.     LOG_TRACE("(%p, %p, %p, %f, %f, %p, %p)", b, rp, rd, t_near, t_far, new_t_near, new_t_far);
  89.  
  90.     for (i = 0; i < 3; i++) {
  91.         if (rd->v[i] > 0.00000023841858) {
  92.             s = (1.0f / rd->v[i]) * (rp->v[i] - b->max.v[i]);
  93.             if (s > BR_SCALAR_MAX) {
  94.                 t_near = BR_SCALAR_MAX;
  95.             } else if (s > t_near) {
  96.                 t_near = s;
  97.             }
  98.             t = (1.0f / rd->v[i]) * (rp->v[i] - b->min.v[i]);
  99.             if (t < BR_SCALAR_MIN) {
  100.                 t_far = BR_SCALAR_MIN;
  101.             } else if (t < t_far) {
  102.                 t_far = t;
  103.             }
  104.         } else if (rd->v[i] < -0.00000023841858) {
  105.             s = (1.0f / rd->v[i]) * (rp->v[i] - b->max.v[i]);
  106.             if (s < BR_SCALAR_MIN) {
  107.                 t_far = BR_SCALAR_MIN;
  108.             } else if (s < t_far) {
  109.                 t_far = s;
  110.             }
  111.             t = (1.0f / rd->v[i]) * (rp->v[i] - b->min.v[i]);
  112.             if (t > BR_SCALAR_MAX) {
  113.                 t_near = BR_SCALAR_MAX;
  114.             } else if (t > t_near) {
  115.                 t_near = t;
  116.             }
  117.         } else if (rp->v[i] > b->max.v[i] || rp->v[i] < b->min.v[i]) {
  118.             return 0;
  119.         }
  120.     }
  121.     if (t_far < t_near) {
  122.         return 0;
  123.     }
  124.     *new_t_near = t_near;
  125.     *new_t_far = t_far;
  126.     return 1;
  127. }
  128.  
  129. // IDA: int __usercall ActorPick2D@<EAX>(br_actor *ap@<EAX>, br_model *model@<EDX>, br_material *material@<EBX>, dr_pick2d_cbfn *callback@<ECX>, void *arg)
  130. int ActorPick2D(br_actor* ap, br_model* model, br_material* material, dr_pick2d_cbfn* callback, void* arg) {
  131.     br_actor* a;
  132.     br_model* this_model;
  133.     br_material* this_material;
  134.     br_matrix34 m_to_v;
  135.     br_matrix34 v_to_m;
  136.     br_scalar t_near;
  137.     br_scalar t_far;
  138.     int r;
  139.     br_vector3 dir;
  140.     LOG_TRACE("(%p, %p, %p, %p, %p)", ap, model, material, callback, arg);
  141.  
  142.     r = 0;
  143.     if (ap->model != NULL) {
  144.         this_model = ap->model;
  145.     } else {
  146.         this_model = model;
  147.     }
  148.     if (ap->material != NULL) {
  149.         this_material = ap->material;
  150.     } else {
  151.         this_material = material;
  152.     }
  153.     if (ap->render_style == BR_RSTYLE_NONE) {
  154.         return 0;
  155.     }
  156.     m_to_v = gPick_model_to_view__raycast;
  157.  
  158.     BrMatrix34PreTransform(&gPick_model_to_view__raycast, &ap->t);
  159.     if (ap->type == BR_ACTOR_MODEL) {
  160.         BrMatrix34Inverse(&v_to_m, &gPick_model_to_view__raycast);
  161.         if (PickBoundsTestRay__raycast(
  162.                 &this_model->bounds,
  163.                 (br_vector3*)v_to_m.m[3],
  164.                 (br_vector3*)v_to_m.m[2],
  165.                 0.0,
  166.                 BR_SCALAR_MAX,
  167.                 &t_near,
  168.                 &t_far)) {
  169.             dir.v[0] = -v_to_m.m[2][0];
  170.             dir.v[1] = -v_to_m.m[2][1];
  171.             dir.v[2] = -v_to_m.m[2][2];
  172.  
  173.             r = callback(
  174.                 ap,
  175.                 this_model,
  176.                 this_material,
  177.                 (br_vector3*)v_to_m.m[3],
  178.                 &dir,
  179.                 t_near,
  180.                 t_far,
  181.                 arg);
  182.             if (r) {
  183.                 gPick_model_to_view__raycast = m_to_v;
  184.                 return r;
  185.             }
  186.         }
  187.         if (r) {
  188.             gPick_model_to_view__raycast = m_to_v;
  189.             return r;
  190.         }
  191.     } else if (ap->type == BR_ACTOR_BOUNDS || ap->type == BR_ACTOR_BOUNDS_CORRECT) {
  192.         BrMatrix34Inverse(&v_to_m, &gPick_model_to_view__raycast);
  193.         if (PickBoundsTestRay__raycast(
  194.                 (br_bounds*)ap->type_data,
  195.                 (br_vector3*)v_to_m.m[3],
  196.                 (br_vector3*)v_to_m.m[2],
  197.                 0.0,
  198.                 BR_SCALAR_MAX,
  199.                 &t_near,
  200.                 &t_far)) {
  201.             for (a = ap->children; a != NULL; a = a->next) {
  202.                 r = ActorPick2D(a, this_model, this_material, callback, arg);
  203.                 if (r) {
  204.                     break;
  205.                 }
  206.             }
  207.         }
  208.         gPick_model_to_view__raycast = m_to_v;
  209.         return r;
  210.     }
  211.     for (a = ap->children; a != NULL; a = a->next) {
  212.         r = ActorPick2D(a, this_model, this_material, callback, arg);
  213.         if (r) {
  214.             break;
  215.         }
  216.     }
  217.     gPick_model_to_view__raycast = m_to_v;
  218.     return r;
  219. }
  220.  
  221. // IDA: int __usercall DRScenePick2DXY@<EAX>(br_actor *world@<EAX>, br_actor *camera@<EDX>, br_pixelmap *viewport@<EBX>, int pick_x@<ECX>, int pick_y, dr_pick2d_cbfn *callback, void *arg)
  222. int DRScenePick2DXY(br_actor* world, br_actor* camera, br_pixelmap* viewport, int pick_x, int pick_y, dr_pick2d_cbfn* callback, void* arg) {
  223.     br_matrix34 camera_tfm;
  224.     br_scalar scale;
  225.     br_scalar cos_angle;
  226.     br_scalar sin_angle;
  227.     br_camera* camera_data;
  228.     br_angle view_over_2;
  229.     LOG_TRACE("(%p, %p, %p, %d, %d, %p, %p)", world, camera, viewport, pick_x, pick_y, callback, arg);
  230.  
  231.     camera_data = camera->type_data;
  232.     DRActorToRoot(camera, world, &camera_tfm);
  233.     BrMatrix34Inverse(&gPick_model_to_view__raycast, &camera_tfm);
  234.     view_over_2 = camera_data->field_of_view / 2;
  235.     cos_angle = BR_COS(view_over_2);
  236.     sin_angle = BR_SIN(view_over_2);
  237.     scale = cos_angle / sin_angle;
  238.     BrMatrix34PostScale(&gPick_model_to_view__raycast, scale / camera_data->aspect, scale, 1.f);
  239.     BrMatrix34PostShearZ(&gPick_model_to_view__raycast,
  240.         2 * pick_x / (float)viewport->width,
  241.         -2 * pick_y / (float)viewport->height);
  242.     return ActorPick2D(world, model_unk1, material_unk1, callback, arg);
  243. }
  244.  
  245. // IDA: int __usercall DRScenePick2D@<EAX>(br_actor *world@<EAX>, br_actor *camera@<EDX>, dr_pick2d_cbfn *callback@<EBX>, void *arg@<ECX>)
  246. int DRScenePick2D(br_actor* world, br_actor* camera, dr_pick2d_cbfn* callback, void* arg) {
  247.     br_matrix34 camera_tfm;
  248.     br_scalar scale;
  249.     br_camera* camera_data;
  250.     LOG_TRACE("(%p, %p, %p, %p)", world, camera, callback, arg);
  251.  
  252.     camera_data = (br_camera*)camera->type_data;
  253.     DRActorToRoot(camera, world, &camera_tfm);
  254.     BrMatrix34Inverse(&gPick_model_to_view__raycast, &camera_tfm);
  255.     scale = cosf(BrAngleToRadian(camera_data->field_of_view / 2)) / sinf(BrAngleToRadian(camera_data->field_of_view / 2));
  256.  
  257.     BrMatrix34PostScale(&gPick_model_to_view__raycast, scale / camera_data->aspect, scale, 1.0f);
  258.     return ActorPick2D(world, model_unk1, material_unk1, callback, arg);
  259. }
  260.  
  261. // IDA: int __usercall DRModelPick2D@<EAX>(br_model *model@<EAX>, br_material *material@<EDX>, br_vector3 *ray_pos@<EBX>, br_vector3 *ray_dir@<ECX>, br_scalar t_near, br_scalar t_far, dr_modelpick2d_cbfn *callback, void *arg)
  262. // Suffix added to avoid duplicate symbol
  263. int DRModelPick2D__raycast(br_model* model, br_material* material, br_vector3* ray_pos, br_vector3* ray_dir, br_scalar t_near, br_scalar t_far, dr_modelpick2d_cbfn* callback, void* arg) {
  264.     DR_FACE* fp;
  265.     int f;
  266.     int axis_m;
  267.     int axis_0;
  268.     int axis_1;
  269.     int group;
  270.     br_scalar t;
  271.     //br_scalar n; // Pierre-Marie Baty -- unused variable
  272.     br_scalar d;
  273.     br_vector3 p;
  274.     double u0;
  275.     double u1;
  276.     double u2;
  277.     double v0;
  278.     double v1;
  279.     double v2;
  280.     br_scalar v0i1;
  281.     br_scalar v0i2;
  282.     double alpha;
  283.     double beta;
  284.     double f_d;
  285.     double f_n;
  286.     br_scalar s_alpha;
  287.     br_scalar s_beta;
  288.     br_vector2 map;
  289.     int v;
  290.     int e;
  291.     int r;
  292.     br_material* this_material;
  293.     br_scalar numerator;
  294.     //double f_numerator; // Pierre-Marie Baty -- unused variable
  295.     LOG_TRACE("(%p, %p, %p, %p, %f, %f, %p, %p)", model, material, ray_pos, ray_dir, t_near, t_far, callback, arg);
  296.  
  297.     t_near -= 0.001f;
  298.     t_far += 0.001f;
  299.     for (group = 0; group < V11MODEL(model)->ngroups; group++) {
  300.         for (f = 0; f < V11MODEL(model)->groups[group].nfaces; f++) {
  301.             fp = &V11MODEL(model)->groups[group].faces[f];
  302.             if (V11MODEL(model)->groups[group].face_colours_material != NULL) {
  303.                 this_material = V11MODEL(model)->groups[group].face_colours_material;
  304.             } else {
  305.                 this_material = material;
  306.             }
  307.             d = BrVector3Dot(&fp->eqn, ray_dir);
  308.             if (fabsf(d) >= 0.00000023841858f && ((this_material->flags & (BR_MATF_TWO_SIDED | BR_MATF_ALWAYS_VISIBLE)) != 0 || d <= 0.0)) //
  309.             {
  310.                 numerator = BrVector3Dot(&fp->eqn, ray_pos) - fp->eqn.v[3];
  311.                 if (!BadDiv__raycast(numerator, d)) {
  312.                     t = -(numerator / d);
  313.                     if (t >= t_near && t <= t_far) {
  314.                         BrVector3Scale(&p, ray_dir, t);
  315.                         BrVector3Accumulate(&p, ray_pos);
  316.                         axis_m = (fabsf(fp->eqn.v[1]) > fabsf(fp->eqn.v[0])) ? 1 : 0;
  317.                         if (fabsf(fp->eqn.v[2]) > fabsf(fp->eqn.v[axis_m])) {
  318.                             axis_m = 2;
  319.                         }
  320.                         if (axis_m == 0) {
  321.                             axis_0 = 1;
  322.                             axis_1 = 2;
  323.                         } else if (axis_m == 1) {
  324.                             axis_0 = 0;
  325.                             axis_1 = 2;
  326.                         } else if (axis_m == 2) {
  327.                             axis_0 = 0;
  328.                             axis_1 = 1;
  329.                         }
  330.  
  331.                         v0 = V11MODEL(model)->groups[group].vertices[fp->vertices[0]].p.v[axis_0];
  332.                         u0 = V11MODEL(model)->groups[group].vertices[fp->vertices[0]].p.v[axis_1];
  333.                         v1 = V11MODEL(model)->groups[group].vertices[fp->vertices[1]].p.v[axis_0] - v0;
  334.                         u1 = V11MODEL(model)->groups[group].vertices[fp->vertices[1]].p.v[axis_1] - u0;
  335.                         v2 = V11MODEL(model)->groups[group].vertices[fp->vertices[2]].p.v[axis_0] - v0;
  336.                         u2 = V11MODEL(model)->groups[group].vertices[fp->vertices[2]].p.v[axis_1] - u0;
  337.  
  338.                         v0i1 = p.v[axis_0] - v0;
  339.                         v0i2 = p.v[axis_1] - u0;
  340.                         if (fabs(v1) > 0.0000002384185791015625) {
  341.                             f_d = v0i2 * v1 - u1 * v0i1;
  342.                             f_n = u2 * v1 - u1 * v2;
  343.                             if (f_n == 0.) {
  344.                                 continue;
  345.                             }
  346.                             beta = f_d / f_n;
  347.                             alpha = (v0i1 - beta * v2) / v1;
  348.                         } else {
  349.                             beta = v0i1 / v2;
  350.                             alpha = (v0i2 - beta * u2) / u1;
  351.                         }
  352.  
  353.                         if (alpha >= 0.0 && beta >= 0.0 && beta + alpha <= 1.0) {
  354.                             s_alpha = alpha;
  355.                             s_beta = beta;
  356.                             map.v[0] = V11MODEL(model)->groups[group].vertices[fp->vertices[1]].map.v[0] * s_alpha;
  357.                             map.v[1] = V11MODEL(model)->groups[group].vertices[fp->vertices[1]].map.v[1] * s_alpha;
  358.                             DRVector2AccumulateScale__raycast(
  359.                                 &map,
  360.                                 &V11MODEL(model)->groups[group].vertices[fp->vertices[2]].map,
  361.                                 s_beta);
  362.                             DRVector2AccumulateScale__raycast(
  363.                                 &map,
  364.                                 &V11MODEL(model)->groups[group].vertices[fp->vertices[0]].map,
  365.                                 1.0f - (s_alpha + s_beta));
  366.                             v = 0;
  367.                             e = 1;
  368.                             if (s_alpha <= s_beta) {
  369.                                 if (0.5f - s_beta / 2.0f > s_alpha) {
  370.                                     e = 0;
  371.                                 }
  372.                                 if (1.0f - s_beta * 2.0f < s_alpha) {
  373.                                     v = 1;
  374.                                 }
  375.                             } else {
  376.                                 if (1.0f - s_beta * 2.0f > s_alpha) {
  377.                                     e = 2;
  378.                                 }
  379.                                 if (0.5f - s_beta / 2.0f < s_alpha) {
  380.                                     v = 2;
  381.                                 }
  382.                             }
  383.                             r = callback(model, this_material, ray_pos, ray_dir, t, f, e, v, &p, &map, arg);
  384.                             if (r != 0) {
  385.                                 return r;
  386.                             }
  387.                         }
  388.                     }
  389.                 }
  390.             }
  391.         }
  392.     }
  393.     return 0;
  394. }
  395.  
  396. // IDA: int __cdecl FindHighestPolyCallBack(br_model *pModel, br_material *pMaterial, br_vector3 *pRay_pos, br_vector3 *pRay_dir, br_scalar pT, int pF, int pE, int pV, br_vector3 *pPoint, br_vector2 *pMap, void *pArg)
  397. //  Suffix added to avoid duplicate symbol
  398. int FindHighestPolyCallBack__raycast(br_model* pModel, br_material* pMaterial, br_vector3* pRay_pos, br_vector3* pRay_dir, br_scalar pT, int pF, int pE, int pV, br_vector3* pPoint, br_vector2* pMap, void* pArg) {
  399.     //br_scalar the_y; // Pierre-Marie Baty -- unused variable
  400.     LOG_TRACE("(%p, %p, %p, %p, %f, %d, %d, %d, %p, %p, %p)", pModel, pMaterial, pRay_pos, pRay_dir, pT, pF, pE, pV, pPoint, pMap, pArg);
  401.  
  402.     if (pPoint->v[1] > gCurrent_y) {
  403.         if (gLowest_y_above > pPoint->v[1]) {
  404.             gLowest_y_above = pPoint->v[1];
  405.             gAbove_face_index = pF;
  406.             gAbove_model = pModel;
  407.         }
  408.     } else if (pPoint->v[1] > gHighest_y_below) {
  409.         gHighest_y_below = pPoint->v[1];
  410.         gBelow_face_index = pF;
  411.         gBelow_model = pModel;
  412.     }
  413.     return 0;
  414. }
  415.  
  416. // IDA: int __cdecl FindHighestCallBack(br_actor *pActor, br_model *pModel, br_material *pMaterial, br_vector3 *pRay_pos, br_vector3 *pRay_dir, br_scalar pT_near, br_scalar pT_far, void *pArg)
  417. //  Suffix added to avoid duplicate symbol
  418. int FindHighestCallBack__raycast(br_actor* pActor, br_model* pModel, br_material* pMaterial, br_vector3* pRay_pos, br_vector3* pRay_dir, br_scalar pT_near, br_scalar pT_far, void* pArg) {
  419.     LOG_TRACE("(%p, %p, %p, %p, %p, %f, %f, %p)", pActor, pModel, pMaterial, pRay_pos, pRay_dir, pT_near, pT_far, pArg);
  420.  
  421.     if (gProgram_state.current_car.current_car_actor < 0
  422.         || gProgram_state.current_car.car_model_actors[gProgram_state.current_car.current_car_actor].actor != pActor) {
  423.         DRModelPick2D__raycast(pModel, pMaterial, pRay_pos, pRay_dir, pT_near, pT_far, FindHighestPolyCallBack__raycast, pArg);
  424.     }
  425.     return 0;
  426. }
  427.  
  428. // IDA: void __usercall FindBestY(br_vector3 *pPosition@<EAX>, br_actor *gWorld@<EDX>, br_scalar pStarting_height, br_scalar *pNearest_y_above, br_scalar *pNearest_y_below, br_model **pNearest_above_model, br_model **pNearest_below_model, int *pNearest_above_face_index, int *pNearest_below_face_index)
  429. void FindBestY(br_vector3* pPosition, br_actor* gWorld, br_scalar pStarting_height, br_scalar* pNearest_y_above, br_scalar* pNearest_y_below, br_model** pNearest_above_model, br_model** pNearest_below_model, int* pNearest_above_face_index, int* pNearest_below_face_index) {
  430.     LOG_TRACE("(%p, %p, %f, %p, %p, %p, %p, %p, %p)", pPosition, gWorld, pStarting_height, pNearest_y_above, pNearest_y_below, pNearest_above_model, pNearest_below_model, pNearest_above_face_index, pNearest_below_face_index);
  431.  
  432.     gLowest_y_above = 30000.0;
  433.     gHighest_y_below = -30000.0;
  434.     gCurrent_y = pPosition->v[1] + 0.000011920929;
  435.     gY_picking_camera->t.t.euler.t = *pPosition;
  436.     gY_picking_camera->t.t.mat.m[3][1] = gY_picking_camera->t.t.mat.m[3][1] + pStarting_height;
  437.     DRScenePick2D(gWorld, gY_picking_camera, FindHighestCallBack__raycast, 0);
  438.     *pNearest_y_above = gLowest_y_above;
  439.     *pNearest_y_below = gHighest_y_below;
  440.     *pNearest_above_model = gAbove_model;
  441.     *pNearest_below_model = gBelow_model;
  442.     *pNearest_above_face_index = gAbove_face_index;
  443.     *pNearest_below_face_index = gBelow_face_index;
  444.     // LOG_DEBUG("FindBestY %f %f '%s' '%s' %d %d", gLowest_y_above, gHighest_y_below, gAbove_model->identifier, gBelow_model->identifier, gAbove_face_index, gBelow_face_index);
  445. }
  446.  
  447. // IDA: int __cdecl FindYVerticallyBelowPolyCallBack(br_model *pModel, br_material *pMaterial, br_vector3 *pRay_pos, br_vector3 *pRay_dir, br_scalar pT, int pF, int pE, int pV, br_vector3 *pPoint, br_vector2 *pMap, void *pArg)
  448. int FindYVerticallyBelowPolyCallBack(br_model* pModel, br_material* pMaterial, br_vector3* pRay_pos, br_vector3* pRay_dir, br_scalar pT, int pF, int pE, int pV, br_vector3* pPoint, br_vector2* pMap, void* pArg) {
  449.     br_scalar the_y;
  450.     LOG_TRACE("(%p, %p, %p, %p, %f, %d, %d, %d, %p, %p, %p)", pModel, pMaterial, pRay_pos, pRay_dir, pT, pF, pE, pV, pPoint, pMap, pArg);
  451.  
  452.     if (pMaterial->identifier == NULL || pMaterial->identifier[0] != '!') {
  453.         the_y = pPoint->v[V_Y];
  454.         if (the_y > gHighest_y_below) {
  455.             gHighest_y_below = the_y;
  456.         }
  457.     }
  458.     return 0;
  459. }
  460.  
  461. // IDA: int __cdecl FindYVerticallyBelowCallBack(br_actor *pActor, br_model *pModel, br_material *pMaterial, br_vector3 *pRay_pos, br_vector3 *pRay_dir, br_scalar pT_near, br_scalar pT_far, void *pArg)
  462. int FindYVerticallyBelowCallBack(br_actor* pActor, br_model* pModel, br_material* pMaterial, br_vector3* pRay_pos, br_vector3* pRay_dir, br_scalar pT_near, br_scalar pT_far, void* pArg) {
  463.     LOG_TRACE("(%p, %p, %p, %p, %p, %f, %f, %p)", pActor, pModel, pMaterial, pRay_pos, pRay_dir, pT_near, pT_far, pArg);
  464.  
  465.     if (gProgram_state.current_car.current_car_actor < 0
  466.         || gProgram_state.current_car.car_model_actors[gProgram_state.current_car.current_car_actor].actor != pActor) {
  467.         DRModelPick2D__raycast(pModel, pMaterial, pRay_pos, pRay_dir, pT_near, pT_far, (dr_modelpick2d_cbfn*)FindYVerticallyBelowPolyCallBack, pArg);
  468.     }
  469.     return 0;
  470. }
  471.  
  472. // IDA: br_scalar __usercall FindYVerticallyBelow@<ST0>(br_vector3 *pPosition@<EAX>)
  473. br_scalar FindYVerticallyBelow(br_vector3* pPosition) {
  474.     tU8 cx;
  475.     tU8 cz;
  476.     tU8 x;
  477.     tU8 z;
  478.     tTrack_spec* track_spec;
  479.     LOG_TRACE("(%p)", pPosition);
  480.  
  481.     track_spec = &gProgram_state.track_spec;
  482.     XZToColumnXZ(&cx, &cz, pPosition->v[V_X], pPosition->v[V_Z], track_spec);
  483.     gHighest_y_below = BR_SCALAR_MIN;
  484.     BrVector3Copy(&gY_picking_camera->t.t.translate.t, pPosition);
  485.     for (x = MAX(cx - 1, 0); x < MIN(cx + 2, track_spec->ncolumns_x); x++) {
  486.         for (z = MAX(cz - 1, 0); z < MIN(cz + 2, track_spec->ncolumns_z); z++) {
  487.             if (track_spec->columns[z][x] != NULL) {
  488.                 if (track_spec->blends[z][x] != NULL) {
  489.                     track_spec->blends[z][x]->render_style = BR_RSTYLE_FACES;
  490.                 }
  491.                 DRScenePick2D(track_spec->columns[z][x], gY_picking_camera, FindYVerticallyBelowCallBack, NULL);
  492.                 if (track_spec->blends[z][x] != NULL) {
  493.                     track_spec->blends[z][x]->render_style = BR_RSTYLE_NONE;
  494.                 }
  495.             }
  496.         }
  497.     }
  498.     return gHighest_y_below;
  499. }
  500.  
  501. // IDA: br_scalar __usercall FindYVerticallyBelow2@<ST0>(br_vector3 *pCast_point@<EAX>)
  502. br_scalar FindYVerticallyBelow2(br_vector3* pCast_point) {
  503.     br_scalar result;
  504.     int number_of_attempts;
  505.     br_vector3 cast_point;
  506.     LOG_TRACE("(%p)", pCast_point);
  507.  
  508.     BrVector3Copy(&cast_point, pCast_point);
  509.     for (number_of_attempts = 0; number_of_attempts <= 10; number_of_attempts++) {
  510.         result = FindYVerticallyBelow(&cast_point);
  511.         cast_point.v[V_Y] += .2f;
  512.         if (result >= -100.f) {
  513.             return result;
  514.         }
  515.     }
  516.     return result;
  517. }
  518.