A3_R3F_LOG_Epoch/fonctions_generales/lib_geometrie_3D.sqf
MusTanG e41f05dc87 Initial Upload
Current Live script. Epoch Lock checks on. working to prevent movable
object without plotpole nearby
2015-07-03 23:54:55 -05:00

1097 lines
42 KiB
Plaintext

/**
* Bibliothèque de fonctions de calculs dans un espace 3D (calculs d'intersection, rotations, etc.).
*
* Fournit une série de fonctions de calculs d'intersection (bounding sphere, bounding box, rayon-bbox)
* Fournit des fonctions de génération de matrices 3x3 de rotations yaw, pitch, roll
* Fournit des fonctions de multiplications vec3 x mat3x3 et mat3x3 x mat3x3
*
* Copyright (C) 2014 Team ~R3F~
*
* This program is free software under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Liste des fonctions :
* ---------------------
* // Calculs d'intersection rayon/bounding box/sphere/mesh
* R3F_LOG_FNCT_3D_ray_intersect_bbox
* R3F_LOG_FNCT_3D_ray_intersect_bbox_obj
* R3F_LOG_FNCT_3D_cam_intersect_bbox_obj
*
* R3F_LOG_FNCT_3D_pos_est_dans_bbox
* R3F_LOG_FNCT_3D_distance_min_pos_bbox
*
* R3F_LOG_FNCT_3D_bounding_sphere_intersect_bounding_sphere
* R3F_LOG_FNCT_3D_intersect_bounding_sphere_objs
* R3F_LOG_FNCT_3D_bounding_sphere_intersect_bounding_box
* R3F_LOG_FNCT_3D_bbox_intersect_bbox
* R3F_LOG_FNCT_3D_bbox_intersect_bbox_objs
* R3F_LOG_FNCT_3D_mesh_collision_objs
*
* // Recherche de position
* R3F_LOG_FNCT_3D_tirer_position_degagee_ciel
* R3F_LOG_FNCT_3D_tirer_position_degagee_sol
*
* // Utilitaires
* R3F_LOG_FNCT_3D_cursorTarget_distance_bbox
* R3F_LOG_FNCT_3D_cursorTarget_virtuel
* R3F_LOG_FNCT_3D_get_huit_coins_bounding_box_model
* R3F_LOG_FNCT_3D_get_huit_coins_bounding_box_world
* R3F_LOG_FNCT_3D_get_objets_genants_rayon
* R3F_LOG_FNCT_3D_get_bounding_box_depuis_classname
* R3F_LOG_FNCT_3D_get_hauteur_terrain_min_max_objet
*
* // Transformation 3D
* R3F_LOG_FNCT_3D_mult_mat3x3
* R3F_LOG_FNCT_3D_mult_vec_mat3x3
* R3F_LOG_FNCT_3D_mat_rot_roll
* R3F_LOG_FNCT_3D_mat_rot_pitch
* R3F_LOG_FNCT_3D_mat_rot_yaw
*
* // Visualisation
* R3F_LOG_FNCT_3D_tracer_bbox
* R3F_LOG_FNCT_3D_tracer_bbox_obj
*/
/**
* Calcule l'intersection d'un rayon avec une bounding box
* @param 0 position du rayon (dans le repère de la bbox)
* @param 1 direction du rayon (dans le repère de la bbox)
* @param 2 position min de la bounding box
* @param 3 position max de la bounding box
* @return la distance entre la position du rayon et la bounding box; 1E39 (infini) si pas d'intersection
* @note le rayon doit être défini dans le repère de la bbox (worldToModel)
*/
R3F_LOG_FNCT_3D_ray_intersect_bbox =
{
private ["_ray_pos", "_ray_dir", "_bbox_min", "_bbox_max", "_inv_ray_x", "_inv_ray_y", "_inv_ray_z"];
private ["_tmin", "_tmax", "_tymin", "_tymax", "_tzmin", "_tzmax"];
_ray_pos = _this select 0;
_ray_dir = _this select 1;
_bbox_min = _this select 2;
_bbox_max = _this select 3;
// Optimisation (1 div + 2 mul au lieu de 2 div) et gestion de la division par zéro
_inv_ray_x = if (_ray_dir select 0 != 0) then {1 / (_ray_dir select 0)} else {1E39};
_inv_ray_y = if (_ray_dir select 1 != 0) then {1 / (_ray_dir select 1)} else {1E39};
_inv_ray_z = if (_ray_dir select 2 != 0) then {1 / (_ray_dir select 2)} else {1E39};
/* Pour chaque axe, on calcule la distance d'intersection du rayon avec les deux plans de la bounding box */
if (_inv_ray_x < 0) then
{
_tmax = ((_bbox_min select 0) - (_ray_pos select 0)) * _inv_ray_x;
_tmin = ((_bbox_max select 0) - (_ray_pos select 0)) * _inv_ray_x;
}
else
{
_tmin = ((_bbox_min select 0) - (_ray_pos select 0)) * _inv_ray_x;
_tmax = ((_bbox_max select 0) - (_ray_pos select 0)) * _inv_ray_x;
};
if (_inv_ray_y < 0) then
{
_tymax = ((_bbox_min select 1) - (_ray_pos select 1)) * _inv_ray_y;
_tymin = ((_bbox_max select 1) - (_ray_pos select 1)) * _inv_ray_y;
}
else
{
_tymin = ((_bbox_min select 1) - (_ray_pos select 1)) * _inv_ray_y;
_tymax = ((_bbox_max select 1) - (_ray_pos select 1)) * _inv_ray_y;
};
if ((_tmin > _tymax) || (_tymin > _tmax)) exitWith {1E39};
_tmin = _tmin max _tymin;
_tmax = _tmax min _tymax;
if (_inv_ray_z < 0) then
{
_tzmax = ((_bbox_min select 2) - (_ray_pos select 2)) * _inv_ray_z;
_tzmin = ((_bbox_max select 2) - (_ray_pos select 2)) * _inv_ray_z;
}
else
{
_tzmin = ((_bbox_min select 2) - (_ray_pos select 2)) * _inv_ray_z;
_tzmax = ((_bbox_max select 2) - (_ray_pos select 2)) * _inv_ray_z;
};
if ((_tmin > _tzmax) || (_tzmin > _tmax)) exitWith {1E39};
_tmin = _tmin max _tzmin;
_tmax = _tmax min _tzmax;
if (_tmax < 0) exitWith {1E39};
_tmin
};
/**
* Calcule l'intersection d'un rayon avec un objet
* @param 0 position du rayon (dans le repère worldATL)
* @param 1 direction du rayon (dans le repère world)
* @param 2 l'objet pour lequel calculer l'intersection de bounding box
* @return la distance entre la position du rayon et la bounding box; 1E39 (infini) si pas d'intersection
*/
R3F_LOG_FNCT_3D_ray_intersect_bbox_obj =
{
private ["_ray_pos", "_ray_dir", "_objet"];
_ray_pos = _this select 0;
_ray_dir = _this select 1;
_objet = _this select 2;
[
_objet worldToModel _ray_pos,
// (_objet worldToModel _ray_dir) vectorDiff (_objet worldToModel [0,0,0]), Manque de précision numérique, d'où l'expression ci-dessous
(_objet worldToModel ASLtoATL (_ray_dir vectorAdd getPosASL _objet)) vectorDiff (_objet worldToModel ASLtoATL (getPosASL _objet)),
boundingBoxReal _objet select 0,
boundingBoxReal _objet select 1
] call R3F_LOG_FNCT_3D_ray_intersect_bbox
};
/**
* Calcule l'intersection du centre de la caméra avec la bounding box d'un objet
* @param 0 l'objet pour lequel on souhaite calculer l'intersection de bounding box
* @return la distance entre la caméra du joueur et la bounding box; 1E39 (infini) si pas d'intersection
*/
R3F_LOG_FNCT_3D_cam_intersect_bbox_obj =
{
private ["_objet", "_pos_cam", "_pos_devant", "_dir_cam"];
_objet = _this select 0;
if (isNull _objet) exitWith {1E39};
_pos_cam = positionCameraToWorld [0, 0, 0];
_pos_devant = positionCameraToWorld [0, 0, 1];
_dir_cam = (ATLtoASL _pos_devant) vectorDiff (ATLtoASL _pos_cam);
[_pos_cam, _dir_cam, _objet] call R3F_LOG_FNCT_3D_ray_intersect_bbox_obj
};
/**
* Indique si une position se trouve à l'intérieur d'une bounding box
* @param 0 position à tester (dans le repère de la bbox)
* @param 1 position min de la bounding box
* @param 2 position max de la bounding box
* @return true si la position se trouve à l'intérieur de la bounding box, false sinon
* @note la position doit être défini dans le repère de la bbox (worldToModel)
*/
R3F_LOG_FNCT_3D_pos_est_dans_bbox =
{
private ["_pos", "_bbox_min", "_bbox_max"];
_pos = _this select 0;
_bbox_min = _this select 1;
_bbox_max = _this select 2;
(_bbox_min select 0 <= _pos select 0) && (_pos select 0 <= _bbox_max select 0) &&
(_bbox_min select 1 <= _pos select 1) && (_pos select 1 <= _bbox_max select 1) &&
(_bbox_min select 2 <= _pos select 2) && (_pos select 2 <= _bbox_max select 2)
};
/**
* Calcule la distance minimale entre une position et une bounding box
* @param 0 la position pour laquelle calculer la distance avec la bbox (dans le repère de la bbox)
* @param 1 position min de la bounding box
* @param 2 position max de la bounding box
* @return distance du segment le plus court reliant la position à la bounding box
*/
R3F_LOG_FNCT_3D_distance_min_pos_bbox =
{
private ["_pos", "_bbox_min", "_bbox_max", "_pos_intersect_min_bbox"];
_pos = _this select 0;
_bbox_min = _this select 1;
_bbox_max = _this select 2;
_pos_intersect_min_bbox =
[
(_bbox_min select 0) max (_pos select 0) min (_bbox_max select 0),
(_bbox_min select 1) max (_pos select 1) min (_bbox_max select 1),
(_bbox_min select 2) max (_pos select 2) min (_bbox_max select 2)
];
_pos_intersect_min_bbox distance _pos
};
/**
* Indique s'il y a intersection entre deux bounding sphere
* @param 0 position centrale de la première bounding sphere
* @param 1 rayon de la première bounding sphere
* @param 2 position centrale de la deuxième bounding sphere
* @param 3 rayon de la deuxième bounding sphere
* @return true s'il y a intersection entre les deux bounding sphere, false sinon
* @note les deux bounding sphere doivent être définies dans le même repère (worldASL ou model)
* @note pour effecteur un test entre un point et une sphere, définir un rayon de 0
*/
R3F_LOG_FNCT_3D_bounding_sphere_intersect_bounding_sphere =
{
private ["_pos1", "_rayon1", "_pos2", "_rayon2"];
_pos1 = _this select 0;
_rayon1 = _this select 1;
_pos2 = _this select 2;
_rayon2 = _this select 3;
(_pos1 distance _pos2) <= (_rayon1 + _rayon2)
};
/**
* Détermine s'il y a intersection entre les bounding spheres de deux objets
* @param 0 le premier objet pour lequel calculer l'intersection de bounding sphere
* @param 1 le deuxième objet pour lequel calculer l'intersection de bounding sphere
* @return true s'il y a intersection entre les bounding sphere des deux objets, false sinon
*/
R3F_LOG_FNCT_3D_intersect_bounding_sphere_objs =
{
private ["_objet1", "_objet2"];
_objet1 = _this select 0;
_objet2 = _this select 1;
// Valeurs selon le formule de R3F_LOG_FNCT_3D_bounding_sphere_intersect_bounding_sphere
//_pos1 = [0,0,0];
//_rayon1 = (vectorMagnitude (boundingBoxReal _objet1 select 0)) max (vectorMagnitude (boundingBoxReal _objet1 select 1));
//_pos2 = _objet1 worldToModel (_objet2 modelToWorld [0,0,0]);
//_rayon2 = (vectorMagnitude (boundingBoxReal _objet2 select 0)) max (vectorMagnitude (boundingBoxReal _objet2 select 1));
// Retour : (_pos1 distance _pos2) <= (_rayon1 + _rayon2)
// Ce qui donne
vectorMagnitude (_objet1 worldToModel (_objet2 modelToWorld [0,0,0])) <= (
((vectorMagnitude (boundingBoxReal _objet1 select 0)) max (vectorMagnitude (boundingBoxReal _objet1 select 1)))
+
((vectorMagnitude (boundingBoxReal _objet2 select 0)) max (vectorMagnitude (boundingBoxReal _objet2 select 1)))
)
};
/**
* Détermine s'il y a intersection entre entre une bounding box et une bounding sphere
* @param 0 la position centrale de la bounding sphere (dans le repère de la bbox)
* @param 1 rayon de la bounding sphere
* @param 2 position min de la bounding box
* @param 3 position max de la bounding box
* @return true s'il y a intersection entre la bounding box et la bounding sphere, false sinon
*/
R3F_LOG_FNCT_3D_bounding_sphere_intersect_bounding_box =
{
private ["_pos_bsphere", "_rayon_bsphere", "_bbox_min", "_bbox_max", "_pos_intersect_min_bbox"];
// Utilisation "inline" de la fonction R3F_LOG_FNCT_3D_distance_min_pos_bbox
_pos_bsphere = _this select 0;
_rayon_bsphere = _this select 1;
_bbox_min = _this select 2;
_bbox_max = _this select 3;
_pos_intersect_min_bbox =
[
(_bbox_min select 0) max (_pos_bsphere select 0) min (_bbox_max select 0),
(_bbox_min select 1) max (_pos_bsphere select 1) min (_bbox_max select 1),
(_bbox_min select 2) max (_pos_bsphere select 2) min (_bbox_max select 2)
];
(_pos_intersect_min_bbox distance _pos_bsphere) <= _rayon_bsphere
};
/**
* Détermine s'il y a intersection entre les bounding box de deux objets
* @param 0 le premier objet pour lequel calculer l'intersection
* @param 1 position min de la bounding box du premier objet
* @param 2 position max de la bounding box du premier objet
* @param 3 le deuxième objet pour lequel calculer l'intersection
* @param 4 position min de la bounding box du deuxième objet
* @param 5 position max de la bounding box du deuxième objet
* @return true s'il y a intersection entre les bounding box des deux objets, false sinon
* @note les objets peuvent être d'un type ne correspondant pas aux bounding box
* @note cela permet par exemple d'utiliser une logique de jeu, pour un calcul à priori
*/
R3F_LOG_FNCT_3D_bbox_intersect_bbox =
{
private ["_objet1", "_objet2", "_bbox1_min", "_bbox1_max", "_bbox2_min", "_bbox2_max", "_intersect", "_coins", "_rayons"];
_objet1 = _this select 0;
_bbox1_min = _this select 1;
_bbox1_max = _this select 2;
_objet2 = _this select 3;
_bbox2_min = _this select 4;
_bbox2_max = _this select 5;
// Quitter dès maintenant s'il est impossible d'avoir une intersection
if !(
[
_objet2 worldToModel (_objet1 modelToWorld [0,0,0]),
(vectorMagnitude _bbox1_min) max (vectorMagnitude _bbox1_max),
_bbox2_min,
_bbox2_max
] call R3F_LOG_FNCT_3D_bounding_sphere_intersect_bounding_box
&&
[
_objet1 worldToModel (_objet2 modelToWorld [0,0,0]),
(vectorMagnitude _bbox2_min) max (vectorMagnitude _bbox2_max),
_bbox1_min,
_bbox1_max
] call R3F_LOG_FNCT_3D_bounding_sphere_intersect_bounding_box
) exitWith {false};
_intersect = false;
_coins = [];
// Composition des coordonnées des 8 coins de la bounding box de l'objet1, dans l'espace du modèle _objet2
_coins set [0, _objet2 worldToModel (_objet1 modelToWorld [_bbox1_min select 0, _bbox1_min select 1, _bbox1_min select 2])];
_coins set [1, _objet2 worldToModel (_objet1 modelToWorld [_bbox1_min select 0, _bbox1_min select 1, _bbox1_max select 2])];
_coins set [2, _objet2 worldToModel (_objet1 modelToWorld [_bbox1_min select 0, _bbox1_max select 1, _bbox1_min select 2])];
_coins set [3, _objet2 worldToModel (_objet1 modelToWorld [_bbox1_min select 0, _bbox1_max select 1, _bbox1_max select 2])];
_coins set [4, _objet2 worldToModel (_objet1 modelToWorld [_bbox1_max select 0, _bbox1_min select 1, _bbox1_min select 2])];
_coins set [5, _objet2 worldToModel (_objet1 modelToWorld [_bbox1_max select 0, _bbox1_min select 1, _bbox1_max select 2])];
_coins set [6, _objet2 worldToModel (_objet1 modelToWorld [_bbox1_max select 0, _bbox1_max select 1, _bbox1_min select 2])];
_coins set [7, _objet2 worldToModel (_objet1 modelToWorld [_bbox1_max select 0, _bbox1_max select 1, _bbox1_max select 2])];
// Test de présence de chacun des coins de la bounding box de l'objet1, dans la bounding box de l'objet2
{
// Utilisation "inline" de la fonction R3F_LOG_FNCT_3D_pos_est_dans_bbox
if (
(_bbox2_min select 0 <= _x select 0) && (_x select 0 <= _bbox2_max select 0) &&
(_bbox2_min select 1 <= _x select 1) && (_x select 1 <= _bbox2_max select 1) &&
(_bbox2_min select 2 <= _x select 2) && (_x select 2 <= _bbox2_max select 2)
) exitWith {_intersect = true;};
} forEach _coins;
if (_intersect) exitWith {true};
// Composition des coordonnées des 8 coins de la bounding box de l'objet2, dans l'espace du modèle _objet1
_coins set [0, _objet1 worldToModel (_objet2 modelToWorld [_bbox2_min select 0, _bbox2_min select 1, _bbox2_min select 2])];
_coins set [1, _objet1 worldToModel (_objet2 modelToWorld [_bbox2_min select 0, _bbox2_min select 1, _bbox2_max select 2])];
_coins set [2, _objet1 worldToModel (_objet2 modelToWorld [_bbox2_min select 0, _bbox2_max select 1, _bbox2_min select 2])];
_coins set [3, _objet1 worldToModel (_objet2 modelToWorld [_bbox2_min select 0, _bbox2_max select 1, _bbox2_max select 2])];
_coins set [4, _objet1 worldToModel (_objet2 modelToWorld [_bbox2_max select 0, _bbox2_min select 1, _bbox2_min select 2])];
_coins set [5, _objet1 worldToModel (_objet2 modelToWorld [_bbox2_max select 0, _bbox2_min select 1, _bbox2_max select 2])];
_coins set [6, _objet1 worldToModel (_objet2 modelToWorld [_bbox2_max select 0, _bbox2_max select 1, _bbox2_min select 2])];
_coins set [7, _objet1 worldToModel (_objet2 modelToWorld [_bbox2_max select 0, _bbox2_max select 1, _bbox2_max select 2])];
// Test de présence de chacun des coins de la bounding box de l'objet2, dans la bounding box de l'objet1
{
// Utilisation "inline" de la fonction R3F_LOG_FNCT_3D_pos_est_dans_bbox
if (
(_bbox1_min select 0 <= _x select 0) && (_x select 0 <= _bbox1_max select 0) &&
(_bbox1_min select 1 <= _x select 1) && (_x select 1 <= _bbox1_max select 1) &&
(_bbox1_min select 2 <= _x select 2) && (_x select 2 <= _bbox1_max select 2)
) exitWith {_intersect = true;};
} forEach _coins;
if (_intersect) exitWith {true};
// Composition des 12 rayons [pos, dir, longueur] correspondant aux segments de la bounding box de l'objet2, dans l'espace du modèle _objet1
_rayons = [];
_rayons set [ 0, [_coins select 1, _coins select 0, vectorMagnitude ((_coins select 1) vectorDiff (_coins select 0))]];
_rayons set [ 1, [_coins select 2, _coins select 0, vectorMagnitude ((_coins select 2) vectorDiff (_coins select 0))]];
_rayons set [ 2, [_coins select 1, _coins select 3, vectorMagnitude ((_coins select 1) vectorDiff (_coins select 3))]];
_rayons set [ 3, [_coins select 2, _coins select 3, vectorMagnitude ((_coins select 2) vectorDiff (_coins select 3))]];
_rayons set [ 4, [_coins select 5, _coins select 4, vectorMagnitude ((_coins select 5) vectorDiff (_coins select 4))]];
_rayons set [ 5, [_coins select 6, _coins select 4, vectorMagnitude ((_coins select 6) vectorDiff (_coins select 4))]];
_rayons set [ 6, [_coins select 5, _coins select 7, vectorMagnitude ((_coins select 5) vectorDiff (_coins select 7))]];
_rayons set [ 7, [_coins select 6, _coins select 7, vectorMagnitude ((_coins select 6) vectorDiff (_coins select 7))]];
_rayons set [ 8, [_coins select 0, _coins select 4, vectorMagnitude ((_coins select 0) vectorDiff (_coins select 4))]];
_rayons set [ 9, [_coins select 1, _coins select 5, vectorMagnitude ((_coins select 1) vectorDiff (_coins select 5))]];
_rayons set [10, [_coins select 2, _coins select 6, vectorMagnitude ((_coins select 2) vectorDiff (_coins select 6))]];
_rayons set [11, [_coins select 3, _coins select 7, vectorMagnitude ((_coins select 3) vectorDiff (_coins select 7))]];
// Test d'intersection de chaque rayon avec la bounding box de l'objet1
{
// Si la dimension de la bbox, dans l'axe concerné, est nulle, on fait un calcul basé sur la position (rayon de longueur nulle)
if (_x select 2 == 0) then
{
if ([_x select 0, _bbox1_min, _bbox1_max] call R3F_LOG_FNCT_3D_pos_est_dans_bbox) exitWith {_intersect = true;};
}
else
{
if ([
_x select 0,
((_x select 1) vectorDiff (_x select 0)) vectorMultiply (1 / (_x select 2)), // Direction rayon
_bbox1_min,
_bbox1_max
] call R3F_LOG_FNCT_3D_ray_intersect_bbox <= (_x select 2)) exitWith {_intersect = true;};
};
} forEach _rayons;
_intersect
};
/**
* Détermine s'il y a intersection entre les bounding box de deux objets
* @param 0 le premier objet pour lequel calculer l'intersection
* @param 1 le deuxième objet pour lequel calculer l'intersection
* @return true s'il y a intersection entre les bounding box des deux objets, false sinon
*/
R3F_LOG_FNCT_3D_bbox_intersect_bbox_objs =
{
private ["_objet1", "_objet2"];
_objet1 = _this select 0;
_objet2 = _this select 1;
[
_objet1,
boundingBoxReal _objet1 select 0,
boundingBoxReal _objet1 select 1,
_objet2,
boundingBoxReal _objet2 select 0,
boundingBoxReal _objet2 select 1
] call R3F_LOG_FNCT_3D_bbox_intersect_bbox
};
/**
* Détermine s'il y a une collision physique réelle (mesh) entre deux objets
* @param 0 le premier objet pour lequel calculer l'intersection
* @param 1 le deuxième objet pour lequel calculer l'intersection
* @param 2 (optionnel) true pour tester directement la collision de mesh sans tester les bbox, false pour d'abord tester les bbox (défaut : false)
* @return true s'il y a une collision physique réelle (mesh) entre deux objets, false sinon
* @note le calcul est basé sur les collisions PhysX, des objets non PhysX ne genère pas de collision
*
* @note WARNING WORK IN PROGRESS FUNCTION, NOT FOR USE !!! TODO FINALIZE IT
*/
R3F_LOG_FNCT_3D_mesh_collision_objs =
{
private ["_objet1", "_objet2", "_objet_test1", "_objet_test2", "_force_test_mesh", "_pos_test", "_num_frame_start", "_collision"];
_objet1 = _this select 0;
_objet2 = _this select 1;
_force_test_mesh = if (count _this > 2) then {_this select 2} else {false};
// Quitter dès maintenant s'il est impossible d'avoir une intersection (sauf test forcé)
if (!_force_test_mesh && {!(
[
_objet1,
boundingBoxReal _objet1 select 0,
boundingBoxReal _objet1 select 1,
_objet2,
boundingBoxReal _objet2 select 0,
boundingBoxReal _objet2 select 1
] call R3F_LOG_FNCT_3D_bbox_intersect_bbox
)}) exitWith {false};
systemChat format ["PROBABLE INTERSECT MESH : %1 @ %2", _objet2, time];//TODO REMOVE
_pos_test = ATLtoASL (player modelToWorld [0,16,20]);// TODO remplacer par R3F_LOG_FNCT_3D_tirer_position_degagee_ciel
_objet_test1 = (typeOf _objet1) createVehicleLocal ([] call R3F_LOG_FNCT_3D_tirer_position_degagee_ciel);
_objet_test1 setVectorDirAndUp [vectorDir _objet1, vectorUp _objet1];
_objet_test1 allowDamage false;
_objet_test1 addEventHandler ["EpeContactStart", {if (!isNull (_this select 1)) then {(_this select 0) setVariable ["R3F_LOG_3D_collision", true, false];};}];
_objet_test1 setVariable ["R3F_LOG_3D_collision", false, false];
_objet_test2 = (typeOf _objet2) createVehicleLocal ([] call R3F_LOG_FNCT_3D_tirer_position_degagee_ciel);
_objet_test2 setVectorDirAndUp [vectorDir _objet2, vectorUp _objet2];
_objet_test2 allowDamage false;
_objet_test2 addEventHandler ["EpeContactStart", {if (!isNull (_this select 1)) then {(_this select 0) setVariable ["R3F_LOG_3D_collision", true, false];};}];
_objet_test2 setVariable ["R3F_LOG_3D_collision", false, false];
_objet_test1 setVelocity [0,0,0];
_objet_test1 setVectorDirAndUp [vectorDir _objet1, vectorUp _objet1];
_objet_test1 setPosASL _pos_test;
_objet_test2 setVelocity [0,0,0];
_objet_test2 setVectorDirAndUp [vectorDir _objet2, vectorUp _objet2];
_objet_test2 setPosASL (_pos_test vectorAdd ((_objet1 worldToModel (_objet2 modelToWorld [0,0,0])) vectorDiff (_objet1 modelToWorld [0,0,0])));
_num_frame_start = diag_frameno;
waitUntil
{
_collision = (_objet_test1 getVariable "R3F_LOG_3D_collision") || (_objet_test2 getVariable "R3F_LOG_3D_collision");
_collision || (diag_frameno - _num_frame_start > 10)
};
if (_collision) then {systemChat format ["RESULTAT COLLISION: %1 @ %2", _objet2, time];};//TODO REMOVE
sleep 0.02;// TODO REMOVE
deleteVehicle _objet_test1;
deleteVehicle _objet_test2;
_collision
};
/**
* Retourne une position dégagée dans le ciel
* @param 0 (optionnel) offset 3D du cube dans lequel chercher une position (défaut [0,0,0])
* @return position dégagée (sphère de 50m de rayon) dans le ciel
*/
R3F_LOG_FNCT_3D_tirer_position_degagee_ciel =
{
private ["_offset", "_nb_tirages", "_position_degagee"];
_offset = if (count _this > 0) then {_this select 0} else {[0,0,0]};
// Trouver une position dégagée (sphère de 50m de rayon) dans le ciel
for [
{
_position_degagee = [random 3000, random 3000, 10000 + (random 20000)] vectorAdd _offset;
_nb_tirages = 1;
},
{
!isNull (nearestObject _position_degagee) && _nb_tirages < 25
},
{
_position_degagee = [random 3000, random 3000, 10000 + (random 20000)] vectorAdd _offset;
_nb_tirages = _nb_tirages+1;
}
] do {};
_position_degagee
};
/**
* Retourne une position suffisamment dégagée au sol pour créer un objet
* @param 0 le rayon de la zone dégagée à trouver au sein de la zone de recherche
* @param 1 la position centrale autour de laquelle chercher
* @param 2 le rayon maximal autour de la position centrale dans lequel chercher la position dégagée
* @param 3 (optionnel) nombre limite de tentatives de sélection d'une position dégagée avant abandon (défaut : 30)
* @param 4 (optionnel) true pour autoriser de retourner une position sur l'eau, false sinon (défaut : false)
* @return position dégagée du rayon indiqué, au sein de la zone de recherche, ou un tableau vide en cas d'échec
* @note cette fonction pallie au manque de fiabilité des commandes findEmptyPosition et isFlatEmpty concernant les collisions
*/
R3F_LOG_FNCT_3D_tirer_position_degagee_sol =
{
private ["_rayon_degage", "_pos_centre", "_rayon_max", "_nb_tirages_max", "_eau_autorise", "_rayon_max_carre"];
private ["_nb_tirages", "_objets_genants", "_position_degagee", "_rayon_curr", "_angle_curr", "_intersect"];
_rayon_degage = 1 max (_this select 0);
_pos_centre = _this select 1;
_rayon_max = _rayon_degage max (_this select 2);
_nb_tirages_max = if (count _this > 3) then {_this select 3} else {30};
_eau_autorise = if (count _this > 4) then {_this select 4} else {false};
_rayon_max_carre = _rayon_max * _rayon_max;
for [
{
_position_degagee = [_pos_centre select 0, _pos_centre select 1, 0];
_nb_tirages = 0;
},
{
if (!_eau_autorise && surfaceIsWater _position_degagee) then {_nb_tirages < _nb_tirages_max}
else
{
_intersect = false;
// Pour chaque objets à proximité de la zone à tester
{
// Test de collision de la bbox de l'objet avec la bounding sphere de la zone à tester
if (
[
_x worldToModel _position_degagee,
_rayon_degage,
boundingBoxReal _x select 0,
boundingBoxReal _x select 1
] call R3F_LOG_FNCT_3D_bounding_sphere_intersect_bounding_box
) exitWith {_intersect = true;};
} forEach ([_position_degagee, _rayon_degage+15] call R3F_LOG_FNCT_3D_get_objets_genants_rayon);
_intersect && _nb_tirages < _nb_tirages_max
}
},
{
// Tirage d'un angle aléatoire, et d'une rayon aléatoirement (distribution surfacique uniforme)
_angle_curr = random 360;
_rayon_curr = sqrt random _rayon_max_carre;
_position_degagee =
[
(_pos_centre select 0) + _rayon_curr * sin _angle_curr,
(_pos_centre select 1) + _rayon_curr * cos _angle_curr,
0
];
_nb_tirages = _nb_tirages+1;
}
] do {};
// Echec, position introuvée
if (_nb_tirages >= _nb_tirages_max) then {_position_degagee = [];};
_position_degagee
};
/**
* Calcule la distance entre le joueur et la bbox de l'objet pointé
* @return tableau avec en premier élément l'objet pointé (ou objNull), et en deuxième élément la distance entre le joueur et la bbox de l'objet pointé
*/
R3F_LOG_FNCT_3D_cursorTarget_distance_bbox =
{
private ["_objet", "_joueur"];
_objet = cursorTarget;
_joueur = player;
if (!isNull _objet && !isNull _joueur && alive _joueur && cameraOn == _joueur) then
{
[
_objet,
[
_objet worldToModel (_joueur modelToWorld (_joueur selectionPosition "head")),
boundingBoxReal _objet select 0,
boundingBoxReal _objet select 1
] call R3F_LOG_FNCT_3D_distance_min_pos_bbox
]
}
else
{
[objNull, 1E39]
};
};
/**
* Retourne l'objet pointé par le joueur à une distance max de la bounding box de l'objet pointé
* @param 0 (optionnel) liste d'objets à ignorer (défaut [])
* @param 1 (optionnel) distance maximale entre l'unité et la bounding box des objets (défaut : 10)
* @return l'objet pointé par le joueur ou objNull
*/
R3F_LOG_FNCT_3D_cursorTarget_virtuel =
{
private ["_liste_ingores", "_distance_max", "_joueur", "_objet_pointe", "_cursorTarget_distance"];
if (isNull player) exitWith {objNull};
_liste_ingores = if (!isNil "_this" && {typeName _this == "ARRAY" && {count _this > 0}}) then {_this select 0} else {[]};
_distance_max = if (!isNil "_this" && {typeName _this == "ARRAY" && {count _this > 1}}) then {_this select 1} else {10};
_joueur = player;
_objet_pointe = objNull;
_cursorTarget_distance = call R3F_LOG_FNCT_3D_cursorTarget_distance_bbox;
if (!isNull (_cursorTarget_distance select 0) &&
{!((_cursorTarget_distance select 0) in _liste_ingores) && (_cursorTarget_distance select 1) <= _distance_max}
) then
{
_objet_pointe = cursorTarget;
}
else
{
private ["_vec_dir_unite_world", "_pos_unite_world", "_liste_objets"];
_vec_dir_unite_world = (ATLtoASL positionCameraToWorld [0, 0, 1]) vectorDiff (ATLtoASL positionCameraToWorld [0,0,0]);
_pos_unite_world = _joueur modelToWorld (_joueur selectionPosition "head");
_liste_objets = lineIntersectsObjs [
(ATLtoASL _pos_unite_world),
(ATLtoASL _pos_unite_world) vectorAdd (_vec_dir_unite_world vectorMultiply _distance_max),
objNull,
player,
true,
16 + 32
];
{
if (!(_x in _liste_ingores) &&
[
_x worldToModel _pos_unite_world,
boundingBoxReal _x select 0,
boundingBoxReal _x select 1
] call R3F_LOG_FNCT_3D_distance_min_pos_bbox <= _distance_max
) exitWith {_objet_pointe = _x;};
} forEach _liste_objets;
};
_objet_pointe
};
/**
* Retourne la position des huit coins d'une bounding box dans le repère du modèle
* @param 0 position min de la bounding box
* @param 1 position max de la bounding box
* @return tableau contenant la position des huit coins d'une bounding box dans le repère du modèle
*/
R3F_LOG_FNCT_3D_get_huit_coins_bounding_box_model =
{
private ["_bbox_min", "_bbox_max"];
_bbox_min = _this select 0;
_bbox_max = _this select 1;
[
[_bbox_min select 0, _bbox_min select 1, _bbox_min select 2],
[_bbox_min select 0, _bbox_min select 1, _bbox_max select 2],
[_bbox_min select 0, _bbox_max select 1, _bbox_min select 2],
[_bbox_min select 0, _bbox_max select 1, _bbox_max select 2],
[_bbox_max select 0, _bbox_min select 1, _bbox_min select 2],
[_bbox_max select 0, _bbox_min select 1, _bbox_max select 2],
[_bbox_max select 0, _bbox_max select 1, _bbox_min select 2],
[_bbox_max select 0, _bbox_max select 1, _bbox_max select 2]
]
};
/**
* Retourne la position des huit coins d'une bounding box dans le repère world
* @param 0 l'objet pour lequel calculer les huit coins de la bbox dans le repère world
* @return tableau contenant la position des huit coins d'une bounding box dans le repère world
*/
R3F_LOG_FNCT_3D_get_huit_coins_bounding_box_world =
{
private ["_objet", "_bbox_min", "_bbox_max"];
_objet = _this select 0;
_bbox_min = boundingBoxReal _objet select 0;
_bbox_max = boundingBoxReal _objet select 1;
[
_objet modelToWorld [_bbox_min select 0, _bbox_min select 1, _bbox_min select 2],
_objet modelToWorld [_bbox_min select 0, _bbox_min select 1, _bbox_max select 2],
_objet modelToWorld [_bbox_min select 0, _bbox_max select 1, _bbox_min select 2],
_objet modelToWorld [_bbox_min select 0, _bbox_max select 1, _bbox_max select 2],
_objet modelToWorld [_bbox_max select 0, _bbox_min select 1, _bbox_min select 2],
_objet modelToWorld [_bbox_max select 0, _bbox_min select 1, _bbox_max select 2],
_objet modelToWorld [_bbox_max select 0, _bbox_max select 1, _bbox_min select 2],
_objet modelToWorld [_bbox_max select 0, _bbox_max select 1, _bbox_max select 2]
]
};
/**
* Retourne la liste des objets présents dans un périmètre et pouvant avoir une collision physique, y compris les éléments de décors propres à la carte
* @param 0 la position centrale de la zone de recherche
* @param 1 le rayon de recherche
* @return la liste des objets présents dans un périmètre et pouvant avoir une collision physique
* @note la liste des objets retournées contient également les éléments de terrain tels que les rochers et les arbres, murs, bâtiments, ...
*/
R3F_LOG_FNCT_3D_get_objets_genants_rayon =
{
private ["_pos_centre", "_rayon", "_obj_proches", "_elements_terrain", "_bbox_dim", "_volume", "_e"];
_pos_centre = _this select 0;
_rayon = _this select 1;
// Récupération des objets et véhicules proches avec bounding suffisamment grande
_obj_proches = [];
{
_bbox_dim = (boundingBoxReal _x select 1) vectorDiff (boundingBoxReal _x select 0);
_volume = (_bbox_dim select 0) * (_bbox_dim select 1) * (_bbox_dim select 2);
// Filtre : volume suffisamment important
if (_volume > 0.08) then
{
// Filtre : insectes et vie ambiante
if !(typeOf _x in ["Snake_random_F", "ButterFly_random", "HouseFly", "HoneyBee", "Mosquito"]) then
{
_obj_proches pushBack _x;
};
};
} forEach (nearestObjects [_pos_centre, ["All"], _rayon]);
// Récupération de TOUS les éléments à proximité (y compris les rochers, végétations, insectes, particules en suspension, ...)
// On ignore les éléments non gênants tels que les traces de pas, insectes, particules en suspension, ...
_elements_terrain = [];
{
_e = _x;
// Filtre : objet immobile
if (vectorMagnitude velocity _e == 0) then
{
_bbox_dim = (boundingBoxReal _e select 1) vectorDiff (boundingBoxReal _e select 0);
_volume = (_bbox_dim select 0) * (_bbox_dim select 1) * (_bbox_dim select 2);
// Filtre : volume suffisamment important
if (_volume > 0.08) then
{
// Filtre : insectes et vie ambiante
if !(typeOf _x in ["Snake_random_F", "ButterFly_random", "HouseFly", "HoneyBee", "Mosquito"]) then
{
// Filtre : ignorer les segments de routes
if ({_x == _e} count (getPos _e nearRoads 1) == 0) then
{
_elements_terrain pushBack _e;
};
};
};
};
} forEach nearestObjects [_pos_centre, [], _rayon];
_elements_terrain - _obj_proches + _obj_proches
};
/**
* Retourne la bounding box d'un objet depuis son nom de classe
* @param 0 le nom de classe de l'objet
* @return la bounding box d'un objet correspondant au nom de classe
*/
R3F_LOG_FNCT_3D_get_bounding_box_depuis_classname =
{
private ["_classe", "_objet_tmp", "_bbox"];
_classe = _this select 0;
// Création du véhicule local temporaire dans le ciel pour connaître la bounding box de l'objet
_objet_tmp = _classe createVehicleLocal ([] call R3F_LOG_FNCT_3D_tirer_position_degagee_ciel);
sleep 0.01;
_bbox = boundingBoxReal _objet_tmp;
deleteVehicle _objet_tmp;
_bbox
};
/**
* Calcule les hauteurs de terrain ASL minimale et maximale des quatre coins inférieurs d'un objet
* @param 0 l'objet pour lequel calculer les hauteur de terrains min et max
* @return tableau contenant respectivement las hauteurs de terrain ASL minimal et maximal
*/
R3F_LOG_FNCT_3D_get_hauteur_terrain_min_max_objet =
{
private ["_objet", "_x1", "_x2", "_y1", "_y2", "_z", "_hauteur_min", "_hauteur_max", "_hauteur"];
_objet = _this select 0;
_x1 = boundingBoxReal _objet select 0 select 0;
_x2 = boundingBoxReal _objet select 1 select 0;
_y1 = boundingBoxReal _objet select 0 select 1;
_y2 = boundingBoxReal _objet select 1 select 1;
_z = boundingBoxReal _objet select 0 select 2;
_hauteur_min = 1E39;
_hauteur_max = -1E39;
// Pour chaque coin de l'objet
{
_hauteur = getTerrainHeightASL (_objet modelToWorld _x);
if (_hauteur < _hauteur_min) then {_hauteur_min = _hauteur};
if (_hauteur > _hauteur_max) then {_hauteur_max = _hauteur};
} forEach [[_x1, _y1, _z], [_x1, _y2, _z], [_x2, _y1, _z], [_x2, _y2, _z]];
[_hauteur_min, _hauteur_max]
};
/**
* Multiplie deux matrices 3x3
* @param 0 la première matrice 3x3 à multiplier
* @param 1 la deuxième matrice 3x3 à multiplier
* @return la matrice 3x3 résultant de la multiplication
*/
R3F_LOG_FNCT_3D_mult_mat3x3 =
{
private ["_a", "_b"];
_a = _this select 0;
_b = _this select 1;
[
[
(_a select 0 select 0) * (_b select 0 select 0) + (_a select 0 select 1) * (_b select 1 select 0) + (_a select 0 select 2) * (_b select 2 select 0),
(_a select 0 select 0) * (_b select 0 select 1) + (_a select 0 select 1) * (_b select 1 select 1) + (_a select 0 select 2) * (_b select 2 select 1),
(_a select 0 select 0) * (_b select 0 select 2) + (_a select 0 select 1) * (_b select 1 select 2) + (_a select 0 select 2) * (_b select 2 select 2)
],
[
(_a select 1 select 0) * (_b select 0 select 0) + (_a select 1 select 1) * (_b select 1 select 0) + (_a select 1 select 2) * (_b select 2 select 0),
(_a select 1 select 0) * (_b select 0 select 1) + (_a select 1 select 1) * (_b select 1 select 1) + (_a select 1 select 2) * (_b select 2 select 1),
(_a select 1 select 0) * (_b select 0 select 2) + (_a select 1 select 1) * (_b select 1 select 2) + (_a select 1 select 2) * (_b select 2 select 2)
],
[
(_a select 2 select 0) * (_b select 0 select 0) + (_a select 2 select 1) * (_b select 1 select 0) + (_a select 2 select 2) * (_b select 2 select 0),
(_a select 2 select 0) * (_b select 0 select 1) + (_a select 2 select 1) * (_b select 1 select 1) + (_a select 2 select 2) * (_b select 2 select 1),
(_a select 2 select 0) * (_b select 0 select 2) + (_a select 2 select 1) * (_b select 1 select 2) + (_a select 2 select 2) * (_b select 2 select 2)
]
]
};
/**
* Multiplie un vecteur 3D avec une matrice 3x3
* @param 0 le vecteur 3D à multiplier
* @param 1 le matrice 3x3 avec laquelle multiplier le vecteur
* @return le vecteur 3D résultant de la multiplication
*/
R3F_LOG_FNCT_3D_mult_vec_mat3x3 =
{
private ["_vec", "_mat"];
_vec = _this select 0;
_mat = _this select 1;
[
(_vec select 0) * (_mat select 0 select 0) + (_vec select 1) * (_mat select 1 select 0) + (_vec select 2) * (_mat select 2 select 0),
(_vec select 0) * (_mat select 0 select 1) + (_vec select 1) * (_mat select 1 select 1) + (_vec select 2) * (_mat select 2 select 1),
(_vec select 0) * (_mat select 0 select 2) + (_vec select 1) * (_mat select 1 select 2) + (_vec select 2) * (_mat select 2 select 2)
]
};
/**
* Retourne la matrice 3x3 de rotation en roulis (roll) pour un angle donné
* @param l'angle de rotation en degrés
* @return la matrice 3x3 de rotation en roulis (roll) pour un angle donné
*/
R3F_LOG_FNCT_3D_mat_rot_roll =
{
[
[cos _this, 0, sin _this],
[0, 1, 0],
[-sin _this, 0, cos _this]
]
};
/**
* Retourne la matrice 3x3 de rotation en tangage (pitch) pour un angle donné
* @param l'angle de rotation en degrés
* @return la matrice 3x3 de rotation en tangage (pitch) pour un angle donné
*/
R3F_LOG_FNCT_3D_mat_rot_pitch =
{
[
[1, 0, 0],
[0, cos _this, -sin _this],
[0, sin _this, cos _this]
]
};
/**
* Retourne la matrice 3x3 de rotation en lacet (yaw) pour un angle donné
* @param l'angle de rotation en degrés
* @return la matrice 3x3 de rotation en lacet (yaw) pour un angle donné
*/
R3F_LOG_FNCT_3D_mat_rot_yaw =
{
[
[cos _this, -sin _this, 0],
[sin _this, cos _this, 0],
[0, 0, 1]
]
};
/**
* Trace dans le jeu une bounding box donnée pour un objet passé en paramètre
* @param 0 l'objet pour lequel tracer la bounding box
* @param 1 position min de la bounding box de l'objet
* @param 2 position max de la bounding box de l'objet
* @note les objets peuvent être d'un type ne correspondant pas aux bounding box
* @note cela permet par exemple d'utiliser une logique de jeu, pour un calcul à priori
*/
R3F_LOG_FNCT_3D_tracer_bbox =
{
private ["_objet", "_bbox_min", "_bbox_max", "_coins", "_couleur"];
_objet = _this select 0;
_bbox_min = _this select 1;
_bbox_max = _this select 2;
if !(isNull _objet) then
{
// Composition des coordonnées des 8 coins, dans l'espace world
_coins = [_objet] call R3F_LOG_FNCT_3D_get_huit_coins_bounding_box_world;
// Faire clignoter en rouge/vert le tracé
_couleur = if (floor (2*diag_tickTime) % 2 == 0) then {[0.95,0,0,1]} else {[0,1,0,1]};
// Tracer les segments de la bounding box
drawLine3D [_coins select 1, _coins select 0, _couleur];
drawLine3D [_coins select 2, _coins select 0, _couleur];
drawLine3D [_coins select 1, _coins select 3, _couleur];
drawLine3D [_coins select 2, _coins select 3, _couleur];
drawLine3D [_coins select 5, _coins select 4, _couleur];
drawLine3D [_coins select 6, _coins select 4, _couleur];
drawLine3D [_coins select 5, _coins select 7, _couleur];
drawLine3D [_coins select 6, _coins select 7, _couleur];
drawLine3D [_coins select 0, _coins select 4, _couleur];
drawLine3D [_coins select 1, _coins select 5, _couleur];
drawLine3D [_coins select 2, _coins select 6, _couleur];
drawLine3D [_coins select 3, _coins select 7, _couleur];
};
};
/**
* Trace dans le jeu la bounding box de l'objet passé en paramètre
* @param 0 l'objet pour lequel tracer la bounding box
*/
R3F_LOG_FNCT_3D_tracer_bbox_obj =
{
private ["_objet"];
_objet = _this select 0;
if !(isNull _objet) then
{
[_objet, boundingBoxReal _objet select 0, boundingBoxReal _objet select 1] call R3F_LOG_FNCT_3D_tracer_bbox;
};
};
// Quelques contrôles et visualisations in-game durant le développement
//#define R3F_LOG_3D_dev_mode // TODO commenter cette ligne lors du release
#ifdef R3F_LOG_3D_dev_mode
if (isNil "R3F_LOG_joueur_deplace_objet") then {R3F_LOG_joueur_deplace_objet = objNull};
addMissionEventHandler ["Draw3D",
{
if !(isNull player) then
{
private ["_objet"];
_objet = cursorTarget;
if (!isNull R3F_LOG_joueur_deplace_objet) then
{
//[R3F_LOG_joueur_deplace_objet] call R3F_LOG_FNCT_3D_tracer_bbox_obj;
{
if ([
R3F_LOG_joueur_deplace_objet,
boundingBoxReal R3F_LOG_joueur_deplace_objet select 0,
boundingBoxReal R3F_LOG_joueur_deplace_objet select 1,
_x,
boundingBoxReal _x select 0,
boundingBoxReal _x select 1
] call R3F_LOG_FNCT_3D_bbox_intersect_bbox) then
{
//systemChat format ["COLLISION BBOX %1 @ %2", typeOf _x, time];
[_x] call R3F_LOG_FNCT_3D_tracer_bbox_obj;
};
} forEach (nearestObjects [R3F_LOG_joueur_deplace_objet, ["All"], 15] - [player, R3F_LOG_joueur_deplace_objet]);
}
else
{
if (false && !isNull _objet) then
{
hintSilent format ["%1 | %2 | %3", typeOf _objet, [_objet] call R3F_LOG_FNCT_3D_cam_intersect_bbox_obj,
[_objet worldToModel (positionCameraToWorld [0, 0, 0]), boundingBoxReal _objet select 0, boundingBoxReal _objet select 1] call R3F_LOG_FNCT_3D_pos_est_dans_bbox];
[_objet] call R3F_LOG_FNCT_3D_tracer_bbox_obj;
}
else
{
_cursorTarget_distance = call R3F_LOG_FNCT_3D_cursorTarget_distance_bbox;
hintSilent format ["%1 | %2", typeOf (_cursorTarget_distance select 0), _cursorTarget_distance select 1];
if !(isNull (_cursorTarget_distance select 0)) then
{
[_cursorTarget_distance select 0] call R3F_LOG_FNCT_3D_tracer_bbox_obj;
};
};
};
//{
// // Calcul de la bbox élargie par rapport au gabarit max d'une unité
// _bbox_min_elargie = (boundingBoxReal _x select 0) vectorDiff [1, 1, 2];
// _bbox_max_elargie = (boundingBoxReal _x select 1) vectorAdd [1, 1, 2];
//
// if ([_x worldToModel (player modelToWorld [0,0,0]), _bbox_min_elargie, _bbox_max_elargie] call R3F_LOG_FNCT_3D_pos_est_dans_bbox) then
// {
// //systemChat format ["JOUEUR PROCHE %1 @ %2", typeOf _x, time];
// };
//} forEach (nearestObjects [player, ["All"], 15] - [player]);
drawIcon3D ["\A3\ui_f\data\map\vehicleicons\iconManMedic_ca.paa", [1,0,0,1], positionCameraToWorld [0, 0, 1], 0.2, 0.2, 0, "", 1, 0, "TahomaB"];
//if !(isNil "R3F_LOG_liste_objets_en_deplacement") then
//{
// {
// [_x] call R3F_LOG_FNCT_3D_tracer_bbox_obj;
// } forEach R3F_LOG_liste_objets_en_deplacement;
//};
//{
// [_x] call R3F_LOG_FNCT_3D_tracer_bbox_obj;
//} forEach [[[R3F_LOG_joueur_deplace_objet]] call R3F_LOG_FNCT_3D_cursorTarget_virtuel];
//{
// [_x] call R3F_LOG_FNCT_3D_tracer_bbox_obj;
//} forEach ([player, 100] call R3F_LOG_FNCT_3D_get_objets_genants_rayon);
};
}];
#endif