You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 11 години
преди 12 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. //
  2. // Lol Engine - EasyMesh tutorial
  3. //
  4. // Copyright: (c) 2011-2013 Sam Hocevar <sam@hocevar.net>
  5. // (c) 2012-2013 Benjamin "Touky" Huet <huet.benjamin@gmail.com>
  6. // This program is free software; you can redistribute it and/or
  7. // modify it under the terms of the Do What The Fuck You Want To
  8. // Public License, Version 2, as published by Sam Hocevar. See
  9. // http://www.wtfpl.net/ for more details.
  10. //
  11. #if defined HAVE_CONFIG_H
  12. # include "config.h"
  13. #endif
  14. #include <cfloat> /* for FLT_MAX */
  15. #include "core.h"
  16. using namespace std;
  17. using namespace lol;
  18. static int const TEXTURE_WIDTH = 256;
  19. #define R_M 2.f
  20. #define WIDTH (770.f * R_M)
  21. #define HEIGHT (200.f * R_M)
  22. #define SCREEN_W (10.f / WIDTH)
  23. #define SCREEN_LIMIT 1.1f
  24. #define RATIO_HW (HEIGHT / WIDTH)
  25. #define RATIO_WH (WIDTH / HEIGHT)
  26. #define ROT_SPEED vec2(50.f)
  27. #define ROT_CLAMP 89.f
  28. #define POS_SPEED vec2(1.2f)
  29. #define POS_CLAMP 1.f
  30. #define FOV_SPEED 20.f
  31. #define FOV_CLAMP 120.f
  32. #define ZOM_SPEED 3.f
  33. #define ZOM_CLAMP 20.f
  34. #define WITH_TEXTURE 0
  35. LOLFX_RESOURCE_DECLARE(shinyfur);
  36. LOLFX_RESOURCE_DECLARE(shinymvtexture);
  37. enum
  38. {
  39. KEY_CAM_RESET,
  40. KEY_CAM_POS,
  41. KEY_CAM_FOV,
  42. KEY_CAM_UP,
  43. KEY_CAM_DOWN,
  44. KEY_CAM_LEFT,
  45. KEY_CAM_RIGHT,
  46. KEY_MESH_NEXT,
  47. KEY_MESH_PREV,
  48. KEY_F1,
  49. KEY_F2,
  50. KEY_F3,
  51. KEY_F4,
  52. KEY_F5,
  53. KEY_ESC,
  54. KEY_MAX,
  55. };
  56. enum MessageType
  57. {
  58. MSG_IN,
  59. MSG_OUT,
  60. MSG_MAX
  61. };
  62. class MeshViewer : public WorldEntity
  63. {
  64. public:
  65. MeshViewer(char const *file_name = "data/mesh-buffer.txt")
  66. : m_file_name(file_name)
  67. {
  68. m_init = false;
  69. }
  70. void Init()
  71. {
  72. m_init = true;
  73. #if !__native_client__
  74. /* Register an input controller for the keyboard */
  75. m_controller = new Controller("Default", KEY_MAX, 0);
  76. //Camera keyboard rotation
  77. m_controller->GetKey(KEY_CAM_UP ).Bind("Keyboard", "Up");
  78. m_controller->GetKey(KEY_CAM_DOWN ).Bind("Keyboard", "Down");
  79. m_controller->GetKey(KEY_CAM_LEFT ).Bind("Keyboard", "Left");
  80. m_controller->GetKey(KEY_CAM_RIGHT).Bind("Keyboard", "Right");
  81. //Camera keyboard position switch
  82. m_controller->GetKey(KEY_CAM_POS ).Bind("Keyboard", "LeftShift");
  83. m_controller->GetKey(KEY_CAM_FOV ).Bind("Keyboard", "LeftCtrl");
  84. //Camera unzoom switch
  85. m_controller->GetKey(KEY_CAM_RESET).Bind("Keyboard", "Space");
  86. //Mesh change
  87. m_controller->GetKey(KEY_MESH_NEXT).Bind("Keyboard", "PageUp");
  88. m_controller->GetKey(KEY_MESH_PREV).Bind("Keyboard", "PageDown");
  89. //Base setup
  90. m_controller->GetKey(KEY_F1).Bind("Keyboard", "F1");
  91. m_controller->GetKey(KEY_F2).Bind("Keyboard", "F2");
  92. m_controller->GetKey(KEY_F3).Bind("Keyboard", "F3");
  93. m_controller->GetKey(KEY_F4).Bind("Keyboard", "F4");
  94. m_controller->GetKey(KEY_F5).Bind("Keyboard", "F5");
  95. m_controller->GetKey(KEY_ESC).Bind("Keyboard", "Escape");
  96. #endif //!__native_client__
  97. // Message Service
  98. MessageService::Setup(MSG_MAX);
  99. // Mesh Setup
  100. m_mesh_id = 0;
  101. m_mesh_id1 = 0.f;
  102. m_default_texture = NULL;
  103. //Camera Setup
  104. m_fov = -100.f;
  105. m_fov_mesh = 0.f;
  106. m_fov_speed = 0.f;
  107. m_zoom = -100.f;
  108. m_zoom_mesh = 0.f;
  109. m_zoom_speed = 0.f;
  110. m_rot = vec2(45.f);
  111. m_rot_mesh = vec2(0.f);
  112. m_rot_speed = vec2(0.f);
  113. m_pos = vec2(0.f);
  114. m_pos_mesh = vec2(0.f);
  115. m_pos_speed = vec2(0.f);
  116. m_screen_offset = vec2(0.f);
  117. m_camera = new Camera();
  118. m_camera->SetView(vec3(0.f, 0.f, 10.f), vec3(0.f, 0.f, 0.f), vec3(0.f, 1.f, 0.f));
  119. m_camera->SetProjection(0.f, .0001f, 2000.f, WIDTH * SCREEN_W, RATIO_HW);
  120. //m_camera->SetScreenScale(vec2(10.f));
  121. m_camera->UseShift(true);
  122. g_scene->PushCamera(m_camera);
  123. //Lights setup
  124. m_lights << new Light();
  125. m_lights.Last()->SetPosition(vec4(4.f, -1.f, -4.f, 1.f));
  126. m_lights.Last()->SetColor(vec4(.0f, .2f, .5f, 1.f));
  127. Ticker::Ref(m_lights.Last());
  128. m_lights << new Light();
  129. m_lights.Last()->SetPosition(vec4(8.f, 2.f, 6.f, 1.f));
  130. m_lights.Last()->SetColor(vec4(1.f, 1.f, 1.f, 1.f));
  131. Ticker::Ref(m_lights.Last());
  132. //stream update
  133. m_stream_update_time = 2.0f;
  134. m_stream_update_timer = 1.0f;
  135. }
  136. ~MeshViewer()
  137. {
  138. if (m_camera)
  139. g_scene->PopCamera(m_camera);
  140. for (int i = 0; i < m_lights.Count(); ++i)
  141. Ticker::Unref(m_lights[i]);
  142. MessageService::Destroy();
  143. }
  144. virtual void TickGame(float seconds)
  145. {
  146. WorldEntity::TickGame(seconds);
  147. if (!m_init && g_scene)
  148. {
  149. Init();
  150. return;
  151. }
  152. if (!m_init)
  153. return;
  154. //TODO : This should probably be "standard LoL behaviour"
  155. #if !__native_client__
  156. {
  157. //Shutdown logic
  158. if (m_controller->GetKey(KEY_ESC).IsReleased())
  159. Ticker::Shutdown();
  160. }
  161. #endif //!__native_client__
  162. //Mesh Change
  163. #if !__native_client__
  164. m_mesh_id = clamp(m_mesh_id + ((int)m_controller->GetKey(KEY_MESH_PREV).IsPressed() - (int)m_controller->GetKey(KEY_MESH_NEXT).IsPressed()), 0, m_meshes.Count() - 1);
  165. #endif //!__native_client__
  166. m_mesh_id1 = damp(m_mesh_id1, (float)m_mesh_id, .2f, seconds);
  167. //Camera update
  168. bool is_pos = false;
  169. bool is_fov = false;
  170. vec2 tmp = vec2(0.f);
  171. #if !__native_client__
  172. is_pos = m_controller->GetKey(KEY_CAM_POS).IsDown();
  173. is_fov = m_controller->GetKey(KEY_CAM_FOV).IsDown();
  174. tmp = vec2((float)m_controller->GetKey(KEY_CAM_UP ).IsDown() - (float)m_controller->GetKey(KEY_CAM_DOWN).IsDown(),
  175. ((float)m_controller->GetKey(KEY_CAM_RIGHT ).IsDown() - (float)m_controller->GetKey(KEY_CAM_LEFT).IsDown()));
  176. #endif //!__native_client__
  177. //Base data
  178. vec2 rot = (!is_pos && !is_fov)?(tmp):(vec2(.0f)); rot = vec2(rot.x, rot.y);
  179. vec2 pos = (is_pos && !is_fov)?(tmp):(vec2(.0f)); pos = -vec2(pos.y, pos.x);
  180. vec2 fov = (!is_pos && is_fov)?(tmp):(vec2(.0f)); fov = vec2(-fov.x, fov.y);
  181. //speed
  182. m_rot_speed = damp(m_rot_speed, rot * ROT_SPEED, .2f, seconds);
  183. float pos_factor = 1.f / (1.f + m_zoom_mesh * .5f);
  184. m_pos_speed = damp(m_pos_speed, pos * POS_SPEED * pos_factor, .2f, seconds);
  185. float fov_factor = 1.f + lol::pow((m_fov_mesh / FOV_CLAMP) * 1.f, 2.f);
  186. m_fov_speed = damp(m_fov_speed, fov.x * FOV_SPEED * fov_factor, .2f, seconds);
  187. float zom_factor = 1.f + lol::pow((m_zoom_mesh / ZOM_CLAMP) * 1.f, 2.f);
  188. m_zoom_speed = damp(m_zoom_speed, fov.y * ZOM_SPEED * zom_factor, .2f, seconds);
  189. m_rot += m_rot_speed * seconds;
  190. #if !__native_client__
  191. //Transform update
  192. if (!m_controller->GetKey(KEY_CAM_RESET).IsDown())
  193. {
  194. m_pos += m_pos_speed * seconds;
  195. m_fov += m_fov_speed * seconds;
  196. m_zoom += m_zoom_speed * seconds;
  197. }
  198. #endif //!__native_client__
  199. //clamp
  200. vec2 rot_mesh = vec2(SmoothClamp(m_rot.x, -ROT_CLAMP, ROT_CLAMP, ROT_CLAMP * .1f), m_rot.y);
  201. vec2 pos_mesh = vec2(SmoothClamp(m_pos.x, -POS_CLAMP, POS_CLAMP, POS_CLAMP * .1f),
  202. SmoothClamp(m_pos.y, -POS_CLAMP, POS_CLAMP, POS_CLAMP * .1f));
  203. float fov_mesh = SmoothClamp(m_fov, 0.f, FOV_CLAMP, FOV_CLAMP * .1f);
  204. float zoom_mesh = SmoothClamp(m_zoom, 0.f, ZOM_CLAMP, ZOM_CLAMP * .1f);
  205. #if !__native_client__
  206. if (m_controller->GetKey(KEY_CAM_RESET).IsDown())
  207. {
  208. pos_mesh = vec2(0.f);
  209. zoom_mesh = 0.f;
  210. }
  211. #endif //!__native_client__
  212. m_rot_mesh = vec2(damp(m_rot_mesh.x, rot_mesh.x, .2f, seconds), damp(m_rot_mesh.y, rot_mesh.y, .2f, seconds));
  213. m_pos_mesh = vec2(damp(m_pos_mesh.x, pos_mesh.x, .2f, seconds), damp(m_pos_mesh.y, pos_mesh.y, .2f, seconds));
  214. m_fov_mesh = damp(m_fov_mesh, fov_mesh, .2f, seconds);
  215. m_zoom_mesh = damp(m_zoom_mesh, zoom_mesh, .2f, seconds);
  216. //Mesh mat calculation
  217. m_mat = mat4(quat::fromeuler_xyz(vec3(m_rot_mesh, .0f)));
  218. //Target List Setup
  219. Array<vec3> target_list;
  220. if (m_meshes.Count() && m_mesh_id >= 0)
  221. for (int i = 0; i < m_meshes[m_mesh_id].GetVertexCount(); i++)
  222. target_list << (m_mat * mat4::translate(m_meshes[m_mesh_id].GetVertexLocation(i))).v3.xyz;
  223. //--
  224. //Update mesh screen location - Get the Min/Max needed
  225. //--
  226. vec2 cam_center(0.f);
  227. float cam_factor = .0f;
  228. vec3 local_min_max[2] = { vec3(FLT_MAX), vec3(-FLT_MAX) };
  229. vec2 screen_min_max[2] = { vec2(FLT_MAX), vec2(-FLT_MAX) };
  230. mat4 world_cam = m_camera->GetView();
  231. mat4 cam_screen = m_camera->GetProjection();
  232. //target on-screen computation
  233. for (int i = 0; i < target_list.Count(); i++)
  234. {
  235. vec3 obj_loc = target_list[i];
  236. {
  237. //Debug::DrawBox(obj_loc - vec3(4.f), obj_loc + vec3(4.f), vec4(1.f, 0.f, 0.f, 1.f));
  238. mat4 target_mx = mat4::translate(obj_loc);
  239. vec3 vpos;
  240. //Get location in cam coordinates
  241. target_mx = world_cam * target_mx;
  242. vpos = target_mx.v3.xyz;
  243. local_min_max[0] = min(vpos.xyz, local_min_max[0]);
  244. local_min_max[1] = max(vpos.xyz, local_min_max[1]);
  245. //Get location in screen coordinates
  246. target_mx = cam_screen * target_mx;
  247. vpos = (target_mx.v3 / target_mx.v3.w).xyz;
  248. screen_min_max[0] = min(screen_min_max[0], vpos.xy * vec2(RATIO_WH, 1.f));
  249. screen_min_max[1] = max(screen_min_max[1], vpos.xy * vec2(RATIO_WH, 1.f));
  250. //Build Barycenter
  251. cam_center += vpos.xy;
  252. cam_factor += 1.f;
  253. }
  254. }
  255. float screen_ratio = max(max(lol::abs(local_min_max[0].x), lol::abs(local_min_max[0].y)),
  256. max(lol::abs(local_min_max[1].x), lol::abs(local_min_max[1].y)));
  257. float scale_ratio = max(max(lol::abs(screen_min_max[0].x), lol::abs(screen_min_max[0].y)),
  258. max(lol::abs(screen_min_max[1].x), lol::abs(screen_min_max[1].y)));
  259. vec2 screen_offset = vec2(0.f, -(screen_min_max[1].y + screen_min_max[0].y) * .5f);
  260. m_screen_offset = damp(m_screen_offset, screen_offset, .9f, seconds);
  261. float z_pos = (inverse(world_cam) * mat4::translate(vec3(0.f, 0.f, max(local_min_max[0].z, local_min_max[1].z)))).v3.z;
  262. if (cam_factor > 0.f)
  263. {
  264. vec2 new_screen_scale = m_camera->GetScreenScale();
  265. m_camera->SetScreenScale(max(vec2(0.001f), new_screen_scale * ((1.0f + m_zoom_mesh) / (scale_ratio * SCREEN_LIMIT))));
  266. m_camera->m_position.z = damp(m_camera->m_position.z, z_pos + screen_ratio * 2.f, .1f, seconds);
  267. m_camera->SetFov(m_fov_mesh);
  268. m_camera->SetScreenInfos(damp(m_camera->GetScreenSize(), max(1.f, screen_ratio), 1.2f, seconds));
  269. }
  270. //--
  271. //Message Service
  272. //--
  273. String mesh("");
  274. int u = 4;
  275. while (u-- > 0 && MessageService::FetchFirst(MSG_IN, mesh))
  276. {
  277. int o = 4;
  278. while (o-- > 0)
  279. {
  280. if (m_mesh_id == m_meshes.Count() - 1)
  281. m_mesh_id++;
  282. //Create a new mesh
  283. EasyMesh tmp;
  284. if (tmp.Compile(mesh.C()))
  285. m_meshes.Push(tmp);
  286. }
  287. }
  288. #if __native_client__
  289. if (m_stream_update_time > .0f)
  290. {
  291. m_stream_update_time = -1.f;
  292. MessageService::Send(MSG_IN, "[sc#f8f afcb 1 1 1 0]");
  293. // MessageService::Send(MSG_IN, "[sc#8ff afcb 1 1 1 0]");
  294. // MessageService::Send(MSG_IN, "[sc#ff8 afcb 1 1 1 0]");
  295. }
  296. #elif WIN32
  297. //--
  298. //File management
  299. //--
  300. m_stream_update_time += seconds;
  301. if (m_stream_update_time > m_stream_update_timer)
  302. {
  303. m_stream_update_time = 0.f;
  304. File f;
  305. f.Open(m_file_name.C(), FileAccess::Read);
  306. String cmd = f.ReadString();
  307. f.Close();
  308. /*
  309. for (int i = 0; i < cmd.Count() - 1; i++)
  310. {
  311. if (cmd[i] == '/' && cmd[i + 1] == '/')
  312. {
  313. int j = i;
  314. for (; j < cmd.Count(); j++)
  315. {
  316. if (cmd[j] == '\r' || cmd[j] == '\n')
  317. break;
  318. }
  319. String new_cmd = cmd.Sub(0, i);
  320. if (j < cmd.Count())
  321. new_cmd += cmd.Sub(j, cmd.Count() - j);
  322. cmd = new_cmd;
  323. i--;
  324. }
  325. }
  326. */
  327. if (cmd.Count()
  328. && (!m_cmdlist.Count() || cmd != m_cmdlist.Last()))
  329. {
  330. m_cmdlist << cmd;
  331. MessageService::Send(MSG_IN, cmd);
  332. }
  333. }
  334. #endif //WINDOWS
  335. }
  336. virtual void TickDraw(float seconds)
  337. {
  338. WorldEntity::TickDraw(seconds);
  339. if (!m_init)
  340. return;
  341. //TODO : This should probably be "standard LoL behaviour"
  342. #if !__native_client__
  343. {
  344. if (m_controller->GetKey(KEY_F1).IsReleased())
  345. Video::SetDebugRenderMode(DebugRenderMode::Default);
  346. if (m_controller->GetKey(KEY_F2).IsReleased())
  347. Video::SetDebugRenderMode(DebugRenderMode::Wireframe);
  348. if (m_controller->GetKey(KEY_F3).IsReleased())
  349. Video::SetDebugRenderMode(DebugRenderMode::Lighting);
  350. if (m_controller->GetKey(KEY_F4).IsReleased())
  351. Video::SetDebugRenderMode(DebugRenderMode::Normal);
  352. if (m_controller->GetKey(KEY_F5).IsReleased())
  353. Video::SetDebugRenderMode(DebugRenderMode::UV);
  354. }
  355. #endif //!__native_client__
  356. #if !__native_client__
  357. if (!m_default_texture)
  358. {
  359. m_texture_shader = Shader::Create(LOLFX_RESOURCE_NAME(shinymvtexture));
  360. m_texture_uni = m_texture_shader->GetUniformLocation("u_Texture");
  361. m_default_texture = Tiler::Register("data/test-texture.png", ivec2(0), ivec2(0,1));
  362. }
  363. else if (m_texture && m_default_texture)
  364. m_texture_shader->SetUniform(m_texture_uni, m_default_texture->GetTexture(), 0);
  365. #endif //!__native_client__
  366. g_renderer->SetClearColor(vec4(0.0f, 0.0f, 0.0f, 1.0f));
  367. vec3 x = vec3(1.f,0.f,0.f);
  368. vec3 y = vec3(0.f,1.f,0.f);
  369. for (int i = 0; i < m_meshes.Count(); i++)
  370. {
  371. {
  372. if (m_meshes[i].GetMeshState() == MeshRender::NeedConvert)
  373. {
  374. #if WITH_TEXTURE
  375. m_meshes[i].MeshConvert(new DefaultShaderData(((1 << VertexUsage::Position) | (1 << VertexUsage::Normal) |
  376. (1 << VertexUsage::Color) | (1 << VertexUsage::TexCoord)),
  377. m_texture_shader, true));
  378. #else
  379. m_meshes[i].MeshConvert();
  380. #endif //WITH_TEXTURE
  381. }
  382. mat4 save_proj = m_camera->GetProjection();
  383. float j = -(float)(m_meshes.Count() - (i + 1)) + (-m_mesh_id1 + (float)(m_meshes.Count() - 1));
  384. if (m_meshes[i].GetMeshState() > MeshRender::NeedConvert)
  385. {
  386. mat4 new_proj =
  387. //Y object Offset
  388. mat4::translate(x * m_screen_offset.x + y * m_screen_offset.y) *
  389. //Mesh Pos Offset
  390. mat4::translate((x * m_pos_mesh.x * RATIO_HW + y * m_pos_mesh.y) * 2.f * (1.f + .5f * m_zoom_mesh / SCREEN_LIMIT)) *
  391. //Mesh count offset
  392. mat4::translate(x * RATIO_HW * 2.f * j) *
  393. //Align right meshes
  394. mat4::translate(x - x * RATIO_HW) *
  395. //Mesh count scale
  396. //mat4::scale(1.f - .2f * j * (1.f / (float)m_meshes.Count())) *
  397. //Camera projection
  398. save_proj;
  399. m_camera->SetProjection(new_proj);
  400. m_meshes[i].Render(m_mat);
  401. g_renderer->Clear(ClearMask::Depth);
  402. }
  403. m_camera->SetProjection(save_proj);
  404. }
  405. }
  406. }
  407. private:
  408. Array<Light *> m_lights;
  409. Controller *m_controller;
  410. mat4 m_mat;
  411. bool m_init;
  412. //Camera Setup
  413. Camera *m_camera;
  414. float m_fov;
  415. float m_fov_mesh;
  416. float m_fov_speed;
  417. float m_zoom;
  418. float m_zoom_mesh;
  419. float m_zoom_speed;
  420. vec2 m_rot;
  421. vec2 m_rot_mesh;
  422. vec2 m_rot_speed;
  423. vec2 m_pos;
  424. vec2 m_pos_mesh;
  425. vec2 m_pos_speed;
  426. vec2 m_screen_offset;
  427. //Mesh infos
  428. int m_mesh_id;
  429. float m_mesh_id1;
  430. Array<EasyMesh> m_meshes;
  431. //File data
  432. String m_file_name;
  433. Array<String> m_cmdlist;
  434. float m_stream_update_time;
  435. float m_stream_update_timer;
  436. //misc datas
  437. Shader * m_texture_shader;
  438. TileSet * m_default_texture;
  439. Texture * m_texture;
  440. ShaderUniform m_texture_uni;
  441. Image * m_image;
  442. };
  443. //The basic main :
  444. int main(int argc, char **argv)
  445. {
  446. System::Init(argc, argv);
  447. Application app("MeshViewer", ivec2((int)WIDTH, (int)HEIGHT), 60.0f);
  448. if (argc > 1)
  449. new MeshViewer(argv[1]);
  450. else
  451. new MeshViewer();
  452. app.Run();
  453. return EXIT_SUCCESS;
  454. }