gim_tri_collision.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. /*! \file gim_tri_collision.h
  2. \author Francisco Leon Najera
  3. */
  4. /*
  5. -----------------------------------------------------------------------------
  6. This source file is part of GIMPACT Library.
  7. For the latest info, see http://gimpact.sourceforge.net/
  8. Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371.
  9. email: [email protected]
  10. This library is free software; you can redistribute it and/or
  11. modify it under the terms of EITHER:
  12. (1) The GNU Lesser General Public License as published by the Free
  13. Software Foundation; either version 2.1 of the License, or (at
  14. your option) any later version. The text of the GNU Lesser
  15. General Public License is included with this library in the
  16. file GIMPACT-LICENSE-LGPL.TXT.
  17. (2) The BSD-style license that is included with this library in
  18. the file GIMPACT-LICENSE-BSD.TXT.
  19. (3) The zlib/libpng license that is included with this library in
  20. the file GIMPACT-LICENSE-ZLIB.TXT.
  21. This library is distributed in the hope that it will be useful,
  22. but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
  24. GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details.
  25. -----------------------------------------------------------------------------
  26. */
  27. #include "gim_tri_collision.h"
  28. #define TRI_LOCAL_EPSILON 0.000001f
  29. #define MIN_EDGE_EDGE_DIS 0.00001f
  30. class GIM_TRIANGLE_CALCULATION_CACHE
  31. {
  32. public:
  33. GREAL margin;
  34. btVector3 tu_vertices[3];
  35. btVector3 tv_vertices[3];
  36. btVector4 tu_plane;
  37. btVector4 tv_plane;
  38. btVector3 closest_point_u;
  39. btVector3 closest_point_v;
  40. btVector3 edge_edge_dir;
  41. btVector3 distances;
  42. GREAL du[4];
  43. GREAL du0du1;
  44. GREAL du0du2;
  45. GREAL dv[4];
  46. GREAL dv0dv1;
  47. GREAL dv0dv2;
  48. btVector3 temp_points[MAX_TRI_CLIPPING];
  49. btVector3 temp_points1[MAX_TRI_CLIPPING];
  50. btVector3 contact_points[MAX_TRI_CLIPPING];
  51. //! if returns false, the faces are paralele
  52. SIMD_FORCE_INLINE bool compute_intervals(
  53. const GREAL &D0,
  54. const GREAL &D1,
  55. const GREAL &D2,
  56. const GREAL &D0D1,
  57. const GREAL &D0D2,
  58. GREAL &scale_edge0,
  59. GREAL &scale_edge1,
  60. GUINT &edge_index0,
  61. GUINT &edge_index1)
  62. {
  63. if (D0D1 > 0.0f)
  64. {
  65. /* here we know that D0D2<=0.0 */
  66. /* that is D0, D1 are on the same side, D2 on the other or on the plane */
  67. scale_edge0 = -D2 / (D0 - D2);
  68. scale_edge1 = -D1 / (D2 - D1);
  69. edge_index0 = 2;
  70. edge_index1 = 1;
  71. }
  72. else if (D0D2 > 0.0f)
  73. {
  74. /* here we know that d0d1<=0.0 */
  75. scale_edge0 = -D0 / (D1 - D0);
  76. scale_edge1 = -D1 / (D2 - D1);
  77. edge_index0 = 0;
  78. edge_index1 = 1;
  79. }
  80. else if (D1 * D2 > 0.0f || D0 != 0.0f)
  81. {
  82. /* here we know that d0d1<=0.0 or that D0!=0.0 */
  83. scale_edge0 = -D0 / (D1 - D0);
  84. scale_edge1 = -D2 / (D0 - D2);
  85. edge_index0 = 0;
  86. edge_index1 = 2;
  87. }
  88. else
  89. {
  90. return false;
  91. }
  92. return true;
  93. }
  94. //! clip triangle
  95. /*!
  96. */
  97. SIMD_FORCE_INLINE GUINT clip_triangle(
  98. const btVector4 &tri_plane,
  99. const btVector3 *tripoints,
  100. const btVector3 *srcpoints,
  101. btVector3 *clip_points)
  102. {
  103. // edge 0
  104. btVector4 edgeplane;
  105. EDGE_PLANE(tripoints[0], tripoints[1], tri_plane, edgeplane);
  106. GUINT clipped_count = PLANE_CLIP_TRIANGLE3D(
  107. edgeplane, srcpoints[0], srcpoints[1], srcpoints[2], temp_points);
  108. if (clipped_count == 0) return 0;
  109. // edge 1
  110. EDGE_PLANE(tripoints[1], tripoints[2], tri_plane, edgeplane);
  111. clipped_count = PLANE_CLIP_POLYGON3D(
  112. edgeplane, temp_points, clipped_count, temp_points1);
  113. if (clipped_count == 0) return 0;
  114. // edge 2
  115. EDGE_PLANE(tripoints[2], tripoints[0], tri_plane, edgeplane);
  116. clipped_count = PLANE_CLIP_POLYGON3D(
  117. edgeplane, temp_points1, clipped_count, clip_points);
  118. return clipped_count;
  119. /*GUINT i0 = (tri_plane.closestAxis()+1)%3;
  120. GUINT i1 = (i0+1)%3;
  121. // edge 0
  122. btVector3 temp_points[MAX_TRI_CLIPPING];
  123. btVector3 temp_points1[MAX_TRI_CLIPPING];
  124. GUINT clipped_count= PLANE_CLIP_TRIANGLE_GENERIC(
  125. 0,srcpoints[0],srcpoints[1],srcpoints[2],temp_points,
  126. DISTANCE_EDGE(tripoints[0],tripoints[1],i0,i1));
  127. if(clipped_count == 0) return 0;
  128. // edge 1
  129. clipped_count = PLANE_CLIP_POLYGON_GENERIC(
  130. 0,temp_points,clipped_count,temp_points1,
  131. DISTANCE_EDGE(tripoints[1],tripoints[2],i0,i1));
  132. if(clipped_count == 0) return 0;
  133. // edge 2
  134. clipped_count = PLANE_CLIP_POLYGON_GENERIC(
  135. 0,temp_points1,clipped_count,clipped_points,
  136. DISTANCE_EDGE(tripoints[2],tripoints[0],i0,i1));
  137. return clipped_count;*/
  138. }
  139. SIMD_FORCE_INLINE void sort_isect(
  140. GREAL &isect0, GREAL &isect1, GUINT &e0, GUINT &e1, btVector3 &vec0, btVector3 &vec1)
  141. {
  142. if (isect1 < isect0)
  143. {
  144. //swap
  145. GIM_SWAP_NUMBERS(isect0, isect1);
  146. GIM_SWAP_NUMBERS(e0, e1);
  147. btVector3 tmp = vec0;
  148. vec0 = vec1;
  149. vec1 = tmp;
  150. }
  151. }
  152. //! Test verifying interval intersection with the direction between planes
  153. /*!
  154. \pre tv_plane and tu_plane must be set
  155. \post
  156. distances[2] is set with the distance
  157. closest_point_u, closest_point_v, edge_edge_dir are set too
  158. \return
  159. - 0: faces are paralele
  160. - 1: face U casts face V
  161. - 2: face V casts face U
  162. - 3: nearest edges
  163. */
  164. SIMD_FORCE_INLINE GUINT cross_line_intersection_test()
  165. {
  166. // Compute direction of intersection line
  167. edge_edge_dir = tu_plane.cross(tv_plane);
  168. GREAL Dlen;
  169. VEC_LENGTH(edge_edge_dir, Dlen);
  170. if (Dlen < 0.0001)
  171. {
  172. return 0; //faces near paralele
  173. }
  174. edge_edge_dir *= 1 / Dlen; //normalize
  175. // Compute interval for triangle 1
  176. GUINT tu_e0, tu_e1; //edge indices
  177. GREAL tu_scale_e0, tu_scale_e1; //edge scale
  178. if (!compute_intervals(du[0], du[1], du[2],
  179. du0du1, du0du2, tu_scale_e0, tu_scale_e1, tu_e0, tu_e1)) return 0;
  180. // Compute interval for triangle 2
  181. GUINT tv_e0, tv_e1; //edge indices
  182. GREAL tv_scale_e0, tv_scale_e1; //edge scale
  183. if (!compute_intervals(dv[0], dv[1], dv[2],
  184. dv0dv1, dv0dv2, tv_scale_e0, tv_scale_e1, tv_e0, tv_e1)) return 0;
  185. //proyected vertices
  186. btVector3 up_e0 = tu_vertices[tu_e0].lerp(tu_vertices[(tu_e0 + 1) % 3], tu_scale_e0);
  187. btVector3 up_e1 = tu_vertices[tu_e1].lerp(tu_vertices[(tu_e1 + 1) % 3], tu_scale_e1);
  188. btVector3 vp_e0 = tv_vertices[tv_e0].lerp(tv_vertices[(tv_e0 + 1) % 3], tv_scale_e0);
  189. btVector3 vp_e1 = tv_vertices[tv_e1].lerp(tv_vertices[(tv_e1 + 1) % 3], tv_scale_e1);
  190. //proyected intervals
  191. GREAL isect_u[] = {up_e0.dot(edge_edge_dir), up_e1.dot(edge_edge_dir)};
  192. GREAL isect_v[] = {vp_e0.dot(edge_edge_dir), vp_e1.dot(edge_edge_dir)};
  193. sort_isect(isect_u[0], isect_u[1], tu_e0, tu_e1, up_e0, up_e1);
  194. sort_isect(isect_v[0], isect_v[1], tv_e0, tv_e1, vp_e0, vp_e1);
  195. const GREAL midpoint_u = 0.5f * (isect_u[0] + isect_u[1]); // midpoint
  196. const GREAL midpoint_v = 0.5f * (isect_v[0] + isect_v[1]); // midpoint
  197. if (midpoint_u < midpoint_v)
  198. {
  199. if (isect_u[1] >= isect_v[1]) // face U casts face V
  200. {
  201. return 1;
  202. }
  203. else if (isect_v[0] <= isect_u[0]) // face V casts face U
  204. {
  205. return 2;
  206. }
  207. // closest points
  208. closest_point_u = up_e1;
  209. closest_point_v = vp_e0;
  210. // calc edges and separation
  211. if (isect_u[1] + MIN_EDGE_EDGE_DIS < isect_v[0]) //calc distance between two lines instead
  212. {
  213. SEGMENT_COLLISION(
  214. tu_vertices[tu_e1], tu_vertices[(tu_e1 + 1) % 3],
  215. tv_vertices[tv_e0], tv_vertices[(tv_e0 + 1) % 3],
  216. closest_point_u,
  217. closest_point_v);
  218. edge_edge_dir = closest_point_u - closest_point_v;
  219. VEC_LENGTH(edge_edge_dir, distances[2]);
  220. edge_edge_dir *= 1.0f / distances[2]; // normalize
  221. }
  222. else
  223. {
  224. distances[2] = isect_v[0] - isect_u[1]; //distance negative
  225. //edge_edge_dir *= -1.0f; //normal pointing from V to U
  226. }
  227. }
  228. else
  229. {
  230. if (isect_v[1] >= isect_u[1]) // face V casts face U
  231. {
  232. return 2;
  233. }
  234. else if (isect_u[0] <= isect_v[0]) // face U casts face V
  235. {
  236. return 1;
  237. }
  238. // closest points
  239. closest_point_u = up_e0;
  240. closest_point_v = vp_e1;
  241. // calc edges and separation
  242. if (isect_v[1] + MIN_EDGE_EDGE_DIS < isect_u[0]) //calc distance between two lines instead
  243. {
  244. SEGMENT_COLLISION(
  245. tu_vertices[tu_e0], tu_vertices[(tu_e0 + 1) % 3],
  246. tv_vertices[tv_e1], tv_vertices[(tv_e1 + 1) % 3],
  247. closest_point_u,
  248. closest_point_v);
  249. edge_edge_dir = closest_point_u - closest_point_v;
  250. VEC_LENGTH(edge_edge_dir, distances[2]);
  251. edge_edge_dir *= 1.0f / distances[2]; // normalize
  252. }
  253. else
  254. {
  255. distances[2] = isect_u[0] - isect_v[1]; //distance negative
  256. //edge_edge_dir *= -1.0f; //normal pointing from V to U
  257. }
  258. }
  259. return 3;
  260. }
  261. //! collides by two sides
  262. SIMD_FORCE_INLINE bool triangle_collision(
  263. const btVector3 &u0,
  264. const btVector3 &u1,
  265. const btVector3 &u2,
  266. GREAL margin_u,
  267. const btVector3 &v0,
  268. const btVector3 &v1,
  269. const btVector3 &v2,
  270. GREAL margin_v,
  271. GIM_TRIANGLE_CONTACT_DATA &contacts)
  272. {
  273. margin = margin_u + margin_v;
  274. tu_vertices[0] = u0;
  275. tu_vertices[1] = u1;
  276. tu_vertices[2] = u2;
  277. tv_vertices[0] = v0;
  278. tv_vertices[1] = v1;
  279. tv_vertices[2] = v2;
  280. //create planes
  281. // plane v vs U points
  282. TRIANGLE_PLANE(tv_vertices[0], tv_vertices[1], tv_vertices[2], tv_plane);
  283. du[0] = DISTANCE_PLANE_POINT(tv_plane, tu_vertices[0]);
  284. du[1] = DISTANCE_PLANE_POINT(tv_plane, tu_vertices[1]);
  285. du[2] = DISTANCE_PLANE_POINT(tv_plane, tu_vertices[2]);
  286. du0du1 = du[0] * du[1];
  287. du0du2 = du[0] * du[2];
  288. if (du0du1 > 0.0f && du0du2 > 0.0f) // same sign on all of them + not equal 0 ?
  289. {
  290. if (du[0] < 0) //we need test behind the triangle plane
  291. {
  292. distances[0] = GIM_MAX3(du[0], du[1], du[2]);
  293. distances[0] = -distances[0];
  294. if (distances[0] > margin) return false; //never intersect
  295. //reorder triangle v
  296. VEC_SWAP(tv_vertices[0], tv_vertices[1]);
  297. VEC_SCALE_4(tv_plane, -1.0f, tv_plane);
  298. }
  299. else
  300. {
  301. distances[0] = GIM_MIN3(du[0], du[1], du[2]);
  302. if (distances[0] > margin) return false; //never intersect
  303. }
  304. }
  305. else
  306. {
  307. //Look if we need to invert the triangle
  308. distances[0] = (du[0] + du[1] + du[2]) / 3.0f; //centroid
  309. if (distances[0] < 0.0f)
  310. {
  311. //reorder triangle v
  312. VEC_SWAP(tv_vertices[0], tv_vertices[1]);
  313. VEC_SCALE_4(tv_plane, -1.0f, tv_plane);
  314. distances[0] = GIM_MAX3(du[0], du[1], du[2]);
  315. distances[0] = -distances[0];
  316. }
  317. else
  318. {
  319. distances[0] = GIM_MIN3(du[0], du[1], du[2]);
  320. }
  321. }
  322. // plane U vs V points
  323. TRIANGLE_PLANE(tu_vertices[0], tu_vertices[1], tu_vertices[2], tu_plane);
  324. dv[0] = DISTANCE_PLANE_POINT(tu_plane, tv_vertices[0]);
  325. dv[1] = DISTANCE_PLANE_POINT(tu_plane, tv_vertices[1]);
  326. dv[2] = DISTANCE_PLANE_POINT(tu_plane, tv_vertices[2]);
  327. dv0dv1 = dv[0] * dv[1];
  328. dv0dv2 = dv[0] * dv[2];
  329. if (dv0dv1 > 0.0f && dv0dv2 > 0.0f) // same sign on all of them + not equal 0 ?
  330. {
  331. if (dv[0] < 0) //we need test behind the triangle plane
  332. {
  333. distances[1] = GIM_MAX3(dv[0], dv[1], dv[2]);
  334. distances[1] = -distances[1];
  335. if (distances[1] > margin) return false; //never intersect
  336. //reorder triangle u
  337. VEC_SWAP(tu_vertices[0], tu_vertices[1]);
  338. VEC_SCALE_4(tu_plane, -1.0f, tu_plane);
  339. }
  340. else
  341. {
  342. distances[1] = GIM_MIN3(dv[0], dv[1], dv[2]);
  343. if (distances[1] > margin) return false; //never intersect
  344. }
  345. }
  346. else
  347. {
  348. //Look if we need to invert the triangle
  349. distances[1] = (dv[0] + dv[1] + dv[2]) / 3.0f; //centroid
  350. if (distances[1] < 0.0f)
  351. {
  352. //reorder triangle v
  353. VEC_SWAP(tu_vertices[0], tu_vertices[1]);
  354. VEC_SCALE_4(tu_plane, -1.0f, tu_plane);
  355. distances[1] = GIM_MAX3(dv[0], dv[1], dv[2]);
  356. distances[1] = -distances[1];
  357. }
  358. else
  359. {
  360. distances[1] = GIM_MIN3(dv[0], dv[1], dv[2]);
  361. }
  362. }
  363. GUINT bl;
  364. /* bl = cross_line_intersection_test();
  365. if(bl==3)
  366. {
  367. //take edge direction too
  368. bl = distances.maxAxis();
  369. }
  370. else
  371. {*/
  372. bl = 0;
  373. if (distances[0] < distances[1]) bl = 1;
  374. //}
  375. if (bl == 2) //edge edge separation
  376. {
  377. if (distances[2] > margin) return false;
  378. contacts.m_penetration_depth = -distances[2] + margin;
  379. contacts.m_points[0] = closest_point_v;
  380. contacts.m_point_count = 1;
  381. VEC_COPY(contacts.m_separating_normal, edge_edge_dir);
  382. return true;
  383. }
  384. //clip face against other
  385. GUINT point_count;
  386. //TODO
  387. if (bl == 0) //clip U points against V
  388. {
  389. point_count = clip_triangle(tv_plane, tv_vertices, tu_vertices, contact_points);
  390. if (point_count == 0) return false;
  391. contacts.merge_points(tv_plane, margin, contact_points, point_count);
  392. }
  393. else //clip V points against U
  394. {
  395. point_count = clip_triangle(tu_plane, tu_vertices, tv_vertices, contact_points);
  396. if (point_count == 0) return false;
  397. contacts.merge_points(tu_plane, margin, contact_points, point_count);
  398. contacts.m_separating_normal *= -1.f;
  399. }
  400. if (contacts.m_point_count == 0) return false;
  401. return true;
  402. }
  403. };
  404. /*class GIM_TRIANGLE_CALCULATION_CACHE
  405. {
  406. public:
  407. GREAL margin;
  408. GUINT clipped_count;
  409. btVector3 tu_vertices[3];
  410. btVector3 tv_vertices[3];
  411. btVector3 temp_points[MAX_TRI_CLIPPING];
  412. btVector3 temp_points1[MAX_TRI_CLIPPING];
  413. btVector3 clipped_points[MAX_TRI_CLIPPING];
  414. GIM_TRIANGLE_CONTACT_DATA contacts1;
  415. GIM_TRIANGLE_CONTACT_DATA contacts2;
  416. //! clip triangle
  417. GUINT clip_triangle(
  418. const btVector4 & tri_plane,
  419. const btVector3 * tripoints,
  420. const btVector3 * srcpoints,
  421. btVector3 * clipped_points)
  422. {
  423. // edge 0
  424. btVector4 edgeplane;
  425. EDGE_PLANE(tripoints[0],tripoints[1],tri_plane,edgeplane);
  426. GUINT clipped_count = PLANE_CLIP_TRIANGLE3D(
  427. edgeplane,srcpoints[0],srcpoints[1],srcpoints[2],temp_points);
  428. if(clipped_count == 0) return 0;
  429. // edge 1
  430. EDGE_PLANE(tripoints[1],tripoints[2],tri_plane,edgeplane);
  431. clipped_count = PLANE_CLIP_POLYGON3D(
  432. edgeplane,temp_points,clipped_count,temp_points1);
  433. if(clipped_count == 0) return 0;
  434. // edge 2
  435. EDGE_PLANE(tripoints[2],tripoints[0],tri_plane,edgeplane);
  436. clipped_count = PLANE_CLIP_POLYGON3D(
  437. edgeplane,temp_points1,clipped_count,clipped_points);
  438. return clipped_count;
  439. }
  440. //! collides only on one side
  441. bool triangle_collision(
  442. const btVector3 & u0,
  443. const btVector3 & u1,
  444. const btVector3 & u2,
  445. GREAL margin_u,
  446. const btVector3 & v0,
  447. const btVector3 & v1,
  448. const btVector3 & v2,
  449. GREAL margin_v,
  450. GIM_TRIANGLE_CONTACT_DATA & contacts)
  451. {
  452. margin = margin_u + margin_v;
  453. tu_vertices[0] = u0;
  454. tu_vertices[1] = u1;
  455. tu_vertices[2] = u2;
  456. tv_vertices[0] = v0;
  457. tv_vertices[1] = v1;
  458. tv_vertices[2] = v2;
  459. //create planes
  460. // plane v vs U points
  461. TRIANGLE_PLANE(tv_vertices[0],tv_vertices[1],tv_vertices[2],contacts1.m_separating_normal);
  462. clipped_count = clip_triangle(
  463. contacts1.m_separating_normal,tv_vertices,tu_vertices,clipped_points);
  464. if(clipped_count == 0 )
  465. {
  466. return false;//Reject
  467. }
  468. //find most deep interval face1
  469. contacts1.merge_points(contacts1.m_separating_normal,margin,clipped_points,clipped_count);
  470. if(contacts1.m_point_count == 0) return false; // too far
  471. //Normal pointing to triangle1
  472. //contacts1.m_separating_normal *= -1.f;
  473. //Clip tri1 by tri2 edges
  474. TRIANGLE_PLANE(tu_vertices[0],tu_vertices[1],tu_vertices[2],contacts2.m_separating_normal);
  475. clipped_count = clip_triangle(
  476. contacts2.m_separating_normal,tu_vertices,tv_vertices,clipped_points);
  477. if(clipped_count == 0 )
  478. {
  479. return false;//Reject
  480. }
  481. //find most deep interval face1
  482. contacts2.merge_points(contacts2.m_separating_normal,margin,clipped_points,clipped_count);
  483. if(contacts2.m_point_count == 0) return false; // too far
  484. contacts2.m_separating_normal *= -1.f;
  485. ////check most dir for contacts
  486. if(contacts2.m_penetration_depth<contacts1.m_penetration_depth)
  487. {
  488. contacts.copy_from(contacts2);
  489. }
  490. else
  491. {
  492. contacts.copy_from(contacts1);
  493. }
  494. return true;
  495. }
  496. };*/
  497. bool GIM_TRIANGLE::collide_triangle_hard_test(
  498. const GIM_TRIANGLE &other,
  499. GIM_TRIANGLE_CONTACT_DATA &contact_data) const
  500. {
  501. GIM_TRIANGLE_CALCULATION_CACHE calc_cache;
  502. return calc_cache.triangle_collision(
  503. m_vertices[0], m_vertices[1], m_vertices[2], m_margin,
  504. other.m_vertices[0], other.m_vertices[1], other.m_vertices[2], other.m_margin,
  505. contact_data);
  506. }