gears.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. { 3-D gear wheels. This program is in the public domain.
  2. Brian Paul
  3. Conversion to GLUT by Mark J. Kilgard
  4. Conversion to GtkGLExt by Naofumi Yasufuku
  5. Conversion to Pascal binding of GtkGLExt by Michalis Kamburelis
  6. }
  7. {$mode delphi}
  8. uses Glib2, Gtk2, Gdk2, GdkGLExt, GtkGLExt, gl;
  9. {*
  10. * Draw a gear wheel. You'll probably want to call this function when
  11. * building a display list since we do a lot of trig here.
  12. *
  13. * Input: inner_radius - radius of hole at center
  14. * outer_radius - radius at center of teeth
  15. * width - width of gear
  16. * teeth - number of teeth
  17. * tooth_depth - depth of tooth
  18. *}
  19. procedure gear(
  20. inner_radius, outer_radius, width: GLfloat;
  21. teeth: GLint;
  22. tooth_depth: GLfloat);
  23. var
  24. i: GLint;
  25. r0, r1, r2: GLfloat;
  26. angle, da: GLfloat;
  27. u, v, len: GLfloat;
  28. begin
  29. r0 := inner_radius;
  30. r1 := outer_radius - tooth_depth / 2.0;
  31. r2 := outer_radius + tooth_depth / 2.0;
  32. da := 2.0 * Pi / teeth / 4.0;
  33. glShadeModel(GL_FLAT);
  34. glNormal3f(0.0, 0.0, 1.0);
  35. {* draw front face *}
  36. glBegin(GL_QUAD_STRIP);
  37. for i := 0 to teeth do
  38. begin
  39. angle := i * 2.0 * Pi / teeth;
  40. glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
  41. glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
  42. if i < teeth then
  43. begin
  44. glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
  45. glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5);
  46. end;
  47. end;
  48. glEnd();
  49. {* draw front sides of teeth *}
  50. glBegin(GL_QUADS);
  51. da := 2.0 * Pi / teeth / 4.0;
  52. for i := 0 to teeth - 1 do
  53. begin
  54. angle := i * 2.0 * Pi / teeth;
  55. glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
  56. glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
  57. glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5);
  58. glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5);
  59. end;
  60. glEnd();
  61. glNormal3f(0.0, 0.0, -1.0);
  62. {* draw back face *}
  63. glBegin(GL_QUAD_STRIP);
  64. for i := 0 to teeth do
  65. begin
  66. angle := i * 2.0 * Pi / teeth;
  67. glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
  68. glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
  69. if i < teeth then
  70. begin
  71. glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5);
  72. glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
  73. end;
  74. end;
  75. glEnd();
  76. {* draw back sides of teeth *}
  77. glBegin(GL_QUADS);
  78. da := 2.0 * Pi / teeth / 4.0;
  79. for i := 0 to teeth - 1 do
  80. begin
  81. angle := i * 2.0 * Pi / teeth;
  82. glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5);
  83. glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5);
  84. glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
  85. glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
  86. end;
  87. glEnd();
  88. {* draw outward faces of teeth *}
  89. glBegin(GL_QUAD_STRIP);
  90. for i := 0 to teeth - 1 do
  91. begin
  92. angle := i * 2.0 * Pi / teeth;
  93. glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
  94. glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
  95. u := r2 * cos(angle + da) - r1 * cos(angle);
  96. v := r2 * sin(angle + da) - r1 * sin(angle);
  97. len := sqrt(u * u + v * v);
  98. u := u / len;
  99. v := v / len;
  100. glNormal3f(v, -u, 0.0);
  101. glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
  102. glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
  103. glNormal3f(cos(angle), sin(angle), 0.0);
  104. glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5);
  105. glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5);
  106. u := r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da);
  107. v := r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da);
  108. glNormal3f(v, -u, 0.0);
  109. glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5);
  110. glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5);
  111. glNormal3f(cos(angle), sin(angle), 0.0);
  112. end;
  113. glVertex3f(r1 * cos(0), r1 * sin(0), width * 0.5);
  114. glVertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5);
  115. glEnd();
  116. glShadeModel(GL_SMOOTH);
  117. {* draw inside radius cylinder *}
  118. glBegin(GL_QUAD_STRIP);
  119. for i := 0 to teeth do
  120. begin
  121. angle := i * 2.0 * Pi / teeth;
  122. glNormal3f(-cos(angle), -sin(angle), 0.0);
  123. glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
  124. glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
  125. end;
  126. glEnd();
  127. end;
  128. var
  129. view_rotx: GLfloat = 20.0;
  130. view_roty: GLfloat = 30.0;
  131. view_rotz: GLfloat = 0.0;
  132. gear1, gear2, gear3: GLint;
  133. angle: GLfloat = 0.0;
  134. timer: PGTimer = nil;
  135. frames: gint = 0;
  136. is_sync: boolean = true;
  137. function draw(
  138. widget: PGtkWidget;
  139. event: PGdkEventExpose;
  140. data: gpointer): gboolean; cdecl;
  141. var
  142. seconds: gdouble;
  143. fps: gdouble;
  144. glcontext: PGdkGLContext;
  145. gldrawable: PGdkGLDrawable;
  146. begin
  147. glcontext := gtk_widget_get_gl_context (widget);
  148. gldrawable := gtk_widget_get_gl_drawable (widget);
  149. {*** OpenGL BEGIN ***}
  150. if not gdk_gl_drawable_gl_begin (gldrawable, glcontext) then
  151. Exit(false);
  152. glClear (GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
  153. glPushMatrix ();
  154. glRotatef (view_rotx, 1.0, 0.0, 0.0);
  155. glRotatef (view_roty, 0.0, 1.0, 0.0);
  156. glRotatef (view_rotz, 0.0, 0.0, 1.0);
  157. glPushMatrix ();
  158. glTranslatef (-3.0, -2.0, 0.0);
  159. glRotatef (angle, 0.0, 0.0, 1.0);
  160. glCallList (gear1);
  161. glPopMatrix ();
  162. glPushMatrix ();
  163. glTranslatef (3.1, -2.0, 0.0);
  164. glRotatef (-2.0 * angle - 9.0, 0.0, 0.0, 1.0);
  165. glCallList (gear2);
  166. glPopMatrix ();
  167. glPushMatrix ();
  168. glTranslatef (-3.1, 4.2, 0.0);
  169. glRotatef (-2.0 * angle - 25.0, 0.0, 0.0, 1.0);
  170. glCallList (gear3);
  171. glPopMatrix ();
  172. glPopMatrix ();
  173. if gdk_gl_drawable_is_double_buffered (gldrawable) then
  174. gdk_gl_drawable_swap_buffers (gldrawable) else
  175. glFlush ();
  176. gdk_gl_drawable_gl_end (gldrawable);
  177. {*** OpenGL END ***}
  178. Inc(frames);
  179. seconds := g_timer_elapsed (timer, NULL);
  180. if seconds >= 5.0 then
  181. begin
  182. fps := frames / seconds;
  183. g_print ('%d frames in %6.3f seconds = %6.3f FPS' + LineEnding, [frames, seconds, fps]);
  184. g_timer_reset (timer);
  185. frames := 0;
  186. end;
  187. Result := true;
  188. end;
  189. {* new window size or exposure *}
  190. function reshape (
  191. widget: PGtkWidget;
  192. event: PGdkEventConfigure;
  193. data: gpointer): gboolean; cdecl;
  194. var
  195. glcontext: PGdkGLContext;
  196. gldrawable: PGdkGLDrawable;
  197. h: GLfloat;
  198. begin
  199. glcontext := gtk_widget_get_gl_context (widget);
  200. gldrawable := gtk_widget_get_gl_drawable (widget);
  201. h := widget.allocation.height / widget.allocation.width;
  202. {*** OpenGL BEGIN ***}
  203. if not gdk_gl_drawable_gl_begin (gldrawable, glcontext) then
  204. Exit(false);
  205. glViewport (0, 0, widget.allocation.width, widget.allocation.height);
  206. glMatrixMode (GL_PROJECTION);
  207. glLoadIdentity ();
  208. glFrustum (-1.0, 1.0, -h, h, 5.0, 60.0);
  209. glMatrixMode (GL_MODELVIEW);
  210. glLoadIdentity ();
  211. glTranslatef (0.0, 0.0, -40.0);
  212. gdk_gl_drawable_gl_end (gldrawable);
  213. {*** OpenGL END ***}
  214. Result := true;
  215. end;
  216. procedure init(
  217. widget: PGtkWidget;
  218. data: gpointer); cdecl;
  219. const
  220. pos: array[0..3] of GLfloat = (5.0, 5.0, 10.0, 0.0);
  221. red: array[0..3] of GLfloat = (0.8, 0.1, 0.0, 1.0);
  222. green: array[0..3] of GLfloat = (0.0, 0.8, 0.2, 1.0);
  223. blue: array[0..3] of GLfloat = (0.2, 0.2, 1.0, 1.0);
  224. var
  225. glcontext: PGdkGLContext;
  226. gldrawable: PGdkGLDrawable;
  227. begin
  228. glcontext := gtk_widget_get_gl_context (widget);
  229. gldrawable := gtk_widget_get_gl_drawable (widget);
  230. {*** OpenGL BEGIN ***}
  231. if not gdk_gl_drawable_gl_begin (gldrawable, glcontext) then
  232. Exit;
  233. glLightfv (GL_LIGHT0, GL_POSITION, pos);
  234. glEnable (GL_CULL_FACE);
  235. glEnable (GL_LIGHTING);
  236. glEnable (GL_LIGHT0);
  237. glEnable (GL_DEPTH_TEST);
  238. {* make the gears *}
  239. gear1 := glGenLists (1);
  240. glNewList (gear1, GL_COMPILE);
  241. glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red);
  242. gear (1.0, 4.0, 1.0, 20, 0.7);
  243. glEndList ();
  244. gear2 := glGenLists (1);
  245. glNewList (gear2, GL_COMPILE);
  246. glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green);
  247. gear (0.5, 2.0, 2.0, 10, 0.7);
  248. glEndList ();
  249. gear3 := glGenLists (1);
  250. glNewList (gear3, GL_COMPILE);
  251. glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue);
  252. gear (1.3, 2.0, 0.5, 10, 0.7);
  253. glEndList ();
  254. glEnable (GL_NORMALIZE);
  255. g_print (LineEnding);
  256. g_print ('GL_RENDERER = %s' + LineEnding, [glGetString (GL_RENDERER)]);
  257. g_print ('GL_VERSION = %s' + LineEnding, [glGetString (GL_VERSION)]);
  258. g_print ('GL_VENDOR = %s' + LineEnding, [glGetString (GL_VENDOR)]);
  259. g_print ('GL_EXTENSIONS = %s' + LineEnding, [glGetString (GL_EXTENSIONS)]);
  260. g_print (LineEnding);
  261. gdk_gl_drawable_gl_end (gldrawable);
  262. {*** OpenGL END ***}
  263. {* create timer *}
  264. if timer = nil then
  265. timer := g_timer_new ();
  266. g_timer_start (timer);
  267. end;
  268. function idle (widget: PGtkWidget): gboolean; cdecl;
  269. begin
  270. angle := angle + 2.0;
  271. {* Invalidate the whole window. *}
  272. gdk_window_invalidate_rect (widget.window, @widget.allocation, false);
  273. {* Update synchronously (fast). *}
  274. if is_sync then
  275. gdk_window_process_updates (widget.window, false);
  276. Result := true;
  277. end;
  278. var
  279. idle_id: guint = 0;
  280. procedure idle_add (widget: PGtkWidget); cdecl;
  281. begin
  282. if idle_id = 0 then
  283. begin
  284. idle_id := g_idle_add_full (GDK_PRIORITY_REDRAW,
  285. TGSourceFunc(@idle),
  286. widget,
  287. NULL);
  288. end;
  289. end;
  290. procedure idle_remove (widget: PGtkWidget); cdecl;
  291. begin
  292. if idle_id <> 0 then
  293. begin
  294. g_source_remove (idle_id);
  295. idle_id := 0;
  296. end;
  297. end;
  298. function map (
  299. widget: PGtkWidget;
  300. event: PGdkEventAny;
  301. data: gpointer): gboolean; cdecl;
  302. begin
  303. idle_add (widget);
  304. Result := true;
  305. end;
  306. function unmap (
  307. widget: PGtkWidget;
  308. event: PGdkEventAny;
  309. data: gpointer): gboolean; cdecl;
  310. begin
  311. idle_remove (widget);
  312. Result := true;
  313. end;
  314. function visible (
  315. widget: PGtkWidget;
  316. event: PGdkEventVisibility;
  317. data: gpointer): gboolean; cdecl;
  318. begin
  319. if event.state = GDK_VISIBILITY_FULLY_OBSCURED then
  320. idle_remove (widget) else
  321. idle_add (widget);
  322. Result := true;
  323. end;
  324. {* change view angle, exit upon ESC *}
  325. function key (
  326. widget: PGtkWidget;
  327. event: PGdkEventKey;
  328. data: gpointer): gboolean; cdecl;
  329. begin
  330. case event.keyval of
  331. GDK_KEY_z : view_rotz := view_rotz + 5.0;
  332. GDK_KEY_Capital_Z : view_rotz := view_rotz - 5.0;
  333. GDK_KEY_Up : view_roty := view_roty + 5.0;
  334. GDK_KEY_Down : view_roty := view_roty - 5.0;
  335. GDK_KEY_Left : view_rotx := view_rotx + 5.0;
  336. GDK_KEY_Right : view_rotx := view_rotx - 5.0;
  337. GDK_KEY_Escape : gtk_main_quit ();
  338. else Exit(false);
  339. end;
  340. gdk_window_invalidate_rect (widget.window, @widget.allocation, FALSE);
  341. Result := true;
  342. end;
  343. var
  344. glconfig: PGdkGLConfig;
  345. window: PGtkWidget;
  346. vbox: PGtkWidget;
  347. drawing_area: PGtkWidget;
  348. button: PGtkWidget;
  349. i: Integer;
  350. begin
  351. {*
  352. * Init GTK.
  353. *}
  354. gtk_init (@argc, @argv);
  355. {*
  356. * Init GtkGLExt.
  357. *}
  358. gtk_gl_init (@argc, @argv);
  359. {*
  360. * Command line options.
  361. *}
  362. for i := 1 to ParamCount do
  363. if ParamStr(i) = '--async' then
  364. is_sync := FALSE;
  365. {*
  366. * Configure OpenGL-capable visual.
  367. *}
  368. {* Try double-buffered visual *}
  369. glconfig := gdk_gl_config_new_by_mode (GDK_GL_MODE_RGB or
  370. GDK_GL_MODE_DEPTH or
  371. GDK_GL_MODE_DOUBLE);
  372. if glconfig = nil then
  373. begin
  374. g_print ('*** Cannot find the double-buffered visual.' +LineEnding);
  375. g_print ('*** Trying single-buffered visual.' +LineEnding);
  376. {* Try single-buffered visual *}
  377. glconfig := gdk_gl_config_new_by_mode (GDK_GL_MODE_RGB or
  378. GDK_GL_MODE_DEPTH);
  379. if glconfig = nil then
  380. begin
  381. g_print ('*** No appropriate OpenGL-capable visual found.' +LineEnding);
  382. Halt(1);
  383. end;
  384. end;
  385. {*
  386. * Top-level window.
  387. *}
  388. window := gtk_window_new (GTK_WINDOW_TOPLEVEL);
  389. gtk_window_set_title (GTK_WINDOW (window), 'gears');
  390. {* Get automatically redrawn if any of their children changed allocation. *}
  391. gtk_container_set_reallocate_redraws (GTK_CONTAINER (window), TRUE);
  392. g_signal_connect (G_OBJECT (window), 'delete_event',
  393. G_CALLBACK (@gtk_main_quit), NULL);
  394. {*
  395. * VBox.
  396. *}
  397. vbox := gtk_vbox_new (FALSE, 0);
  398. gtk_container_add (GTK_CONTAINER (window), vbox);
  399. gtk_widget_show (vbox);
  400. {*
  401. * Drawing area for drawing OpenGL scene.
  402. *}
  403. drawing_area := gtk_drawing_area_new ();
  404. gtk_widget_set_size_request (drawing_area, 300, 300);
  405. {* Set OpenGL-capability to the widget. *}
  406. gtk_widget_set_gl_capability (drawing_area,
  407. glconfig,
  408. NULL,
  409. TRUE,
  410. GDK_GL_RGBA_TYPE);
  411. gtk_widget_add_events (drawing_area,
  412. GDK_VISIBILITY_NOTIFY_MASK);
  413. g_signal_connect_after (G_OBJECT (drawing_area), 'realize',
  414. G_CALLBACK (@init), NULL);
  415. g_signal_connect (G_OBJECT (drawing_area), 'configure_event',
  416. G_CALLBACK (@reshape), NULL);
  417. g_signal_connect (G_OBJECT (drawing_area), 'expose_event',
  418. G_CALLBACK (@draw), NULL);
  419. g_signal_connect (G_OBJECT (drawing_area), 'map_event',
  420. G_CALLBACK (@map), NULL);
  421. g_signal_connect (G_OBJECT (drawing_area), 'unmap_event',
  422. G_CALLBACK (@unmap), NULL);
  423. g_signal_connect (G_OBJECT (drawing_area), 'visibility_notify_event',
  424. G_CALLBACK (@visible), NULL);
  425. g_signal_connect_swapped (G_OBJECT (window), 'key_press_event',
  426. G_CALLBACK (@key), drawing_area);
  427. gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0);
  428. gtk_widget_show (drawing_area);
  429. {*
  430. * Simple quit button.
  431. *}
  432. button := gtk_button_new_with_label ('Quit');
  433. g_signal_connect (G_OBJECT (button), 'clicked',
  434. G_CALLBACK (@gtk_main_quit), NULL);
  435. gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
  436. gtk_widget_show (button);
  437. {*
  438. * Show window.
  439. *}
  440. gtk_widget_show (window);
  441. {*
  442. * Main loop.
  443. *}
  444. gtk_main ();
  445. end.