psychopy 2024.2.4__py3-none-any.whl → 2025.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of psychopy might be problematic. Click here for more details.

Files changed (903) hide show
  1. psychopy/CHANGELOG.txt +208 -4
  2. psychopy/LICENSE.txt +1 -1
  3. psychopy/VERSION +1 -1
  4. psychopy/__init__.py +10 -7
  5. psychopy/alerts/__init__.py +1 -1
  6. psychopy/alerts/_alerts.py +53 -17
  7. psychopy/alerts/_errorHandler.py +3 -4
  8. psychopy/alerts/alertsCatalogue/3210.yaml +27 -0
  9. psychopy/alerts/alertsCatalogue/3610.yaml +19 -0
  10. psychopy/alerts/alertsCatalogue/4130.yaml +19 -0
  11. psychopy/alerts/alertsCatalogue/alertCategories.yaml +8 -1
  12. psychopy/alerts/alertsCatalogue/alertmsg.py +1 -1
  13. psychopy/alerts/alerttools.py +0 -16
  14. psychopy/app/Resources/betasplash.png +0 -0
  15. psychopy/app/Resources/betasplash@2x.png +0 -0
  16. psychopy/app/Resources/classic/case.png +0 -0
  17. psychopy/app/Resources/classic/case@2x.png +0 -0
  18. psychopy/app/Resources/classic/fileaudio.png +0 -0
  19. psychopy/app/Resources/classic/fileaudio@2x.png +0 -0
  20. psychopy/app/Resources/classic/filecss.png +0 -0
  21. psychopy/app/Resources/classic/filecss@2x.png +0 -0
  22. psychopy/app/Resources/classic/filecsv.png +0 -0
  23. psychopy/app/Resources/classic/filecsv@2x.png +0 -0
  24. psychopy/app/Resources/classic/filedesign.png +0 -0
  25. psychopy/app/Resources/classic/filedesign@2x.png +0 -0
  26. psychopy/app/Resources/classic/filefont.png +0 -0
  27. psychopy/app/Resources/classic/filefont@2x.png +0 -0
  28. psychopy/app/Resources/classic/filegit.png +0 -0
  29. psychopy/app/Resources/classic/filegit@2x.png +0 -0
  30. psychopy/app/Resources/classic/filehtml.png +0 -0
  31. psychopy/app/Resources/classic/filehtml@2x.png +0 -0
  32. psychopy/app/Resources/classic/fileimage.png +0 -0
  33. psychopy/app/Resources/classic/fileimage@2x.png +0 -0
  34. psychopy/app/Resources/classic/fileinfo.png +0 -0
  35. psychopy/app/Resources/classic/fileinfo@2x.png +0 -0
  36. psychopy/app/Resources/classic/filejs.png +0 -0
  37. psychopy/app/Resources/classic/filejs@2x.png +0 -0
  38. psychopy/app/Resources/classic/filejson.png +0 -0
  39. psychopy/app/Resources/classic/filejson@2x.png +0 -0
  40. psychopy/app/Resources/classic/filepkg.png +0 -0
  41. psychopy/app/Resources/classic/filepkg@2x.png +0 -0
  42. psychopy/app/Resources/classic/filepsyexp.png +0 -0
  43. psychopy/app/Resources/classic/filepsyexp@2x.png +0 -0
  44. psychopy/app/Resources/classic/filepy.png +0 -0
  45. psychopy/app/Resources/classic/filepy@2x.png +0 -0
  46. psychopy/app/Resources/classic/filetxt.png +0 -0
  47. psychopy/app/Resources/classic/filetxt@2x.png +0 -0
  48. psychopy/app/Resources/classic/fileunknown.png +0 -0
  49. psychopy/app/Resources/classic/fileunknown@2x.png +0 -0
  50. psychopy/app/Resources/classic/filevideo.png +0 -0
  51. psychopy/app/Resources/classic/filevideo@2x.png +0 -0
  52. psychopy/app/Resources/classic/find.png +0 -0
  53. psychopy/app/Resources/classic/find@2x.png +0 -0
  54. psychopy/app/Resources/classic/loop.png +0 -0
  55. psychopy/app/Resources/classic/loop@2x.png +0 -0
  56. psychopy/app/Resources/classic/regex.png +0 -0
  57. psychopy/app/Resources/classic/regex@2x.png +0 -0
  58. psychopy/app/Resources/dark/case.png +0 -0
  59. psychopy/app/Resources/dark/case@2x.png +0 -0
  60. psychopy/app/Resources/dark/fileaudio.png +0 -0
  61. psychopy/app/Resources/dark/fileaudio@2x.png +0 -0
  62. psychopy/app/Resources/dark/filecss.png +0 -0
  63. psychopy/app/Resources/dark/filecss@2x.png +0 -0
  64. psychopy/app/Resources/dark/filecsv.png +0 -0
  65. psychopy/app/Resources/dark/filecsv@2x.png +0 -0
  66. psychopy/app/Resources/dark/filedesign.png +0 -0
  67. psychopy/app/Resources/dark/filedesign@2x.png +0 -0
  68. psychopy/app/Resources/dark/filefont.png +0 -0
  69. psychopy/app/Resources/dark/filefont@2x.png +0 -0
  70. psychopy/app/Resources/dark/filegit.png +0 -0
  71. psychopy/app/Resources/dark/filegit@2x.png +0 -0
  72. psychopy/app/Resources/dark/filehtml.png +0 -0
  73. psychopy/app/Resources/dark/filehtml@2x.png +0 -0
  74. psychopy/app/Resources/dark/fileimage.png +0 -0
  75. psychopy/app/Resources/dark/fileimage@2x.png +0 -0
  76. psychopy/app/Resources/dark/fileinfo.png +0 -0
  77. psychopy/app/Resources/dark/fileinfo@2x.png +0 -0
  78. psychopy/app/Resources/dark/filejs.png +0 -0
  79. psychopy/app/Resources/dark/filejs@2x.png +0 -0
  80. psychopy/app/Resources/dark/filejson.png +0 -0
  81. psychopy/app/Resources/dark/filejson@2x.png +0 -0
  82. psychopy/app/Resources/dark/filepkg.png +0 -0
  83. psychopy/app/Resources/dark/filepkg@2x.png +0 -0
  84. psychopy/app/Resources/dark/filepsyexp.png +0 -0
  85. psychopy/app/Resources/dark/filepsyexp@2x.png +0 -0
  86. psychopy/app/Resources/dark/filepy.png +0 -0
  87. psychopy/app/Resources/dark/filepy@2x.png +0 -0
  88. psychopy/app/Resources/dark/filetxt.png +0 -0
  89. psychopy/app/Resources/dark/filetxt@2x.png +0 -0
  90. psychopy/app/Resources/dark/fileunknown.png +0 -0
  91. psychopy/app/Resources/dark/fileunknown@2x.png +0 -0
  92. psychopy/app/Resources/dark/filevideo.png +0 -0
  93. psychopy/app/Resources/dark/filevideo@2x.png +0 -0
  94. psychopy/app/Resources/dark/find.png +0 -0
  95. psychopy/app/Resources/dark/find@2x.png +0 -0
  96. psychopy/app/Resources/dark/loop.png +0 -0
  97. psychopy/app/Resources/dark/loop@2x.png +0 -0
  98. psychopy/app/Resources/dark/regex.png +0 -0
  99. psychopy/app/Resources/dark/regex@2x.png +0 -0
  100. psychopy/app/Resources/light/case.png +0 -0
  101. psychopy/app/Resources/light/case@2x.png +0 -0
  102. psychopy/app/Resources/light/fileaudio.png +0 -0
  103. psychopy/app/Resources/light/fileaudio@2x.png +0 -0
  104. psychopy/app/Resources/light/filecss.png +0 -0
  105. psychopy/app/Resources/light/filecss@2x.png +0 -0
  106. psychopy/app/Resources/light/filecsv.png +0 -0
  107. psychopy/app/Resources/light/filecsv@2x.png +0 -0
  108. psychopy/app/Resources/light/filedesign.png +0 -0
  109. psychopy/app/Resources/light/filedesign@2x.png +0 -0
  110. psychopy/app/Resources/light/filefont.png +0 -0
  111. psychopy/app/Resources/light/filefont@2x.png +0 -0
  112. psychopy/app/Resources/light/filegit.png +0 -0
  113. psychopy/app/Resources/light/filegit@2x.png +0 -0
  114. psychopy/app/Resources/light/filehtml.png +0 -0
  115. psychopy/app/Resources/light/filehtml@2x.png +0 -0
  116. psychopy/app/Resources/light/fileimage.png +0 -0
  117. psychopy/app/Resources/light/fileimage@2x.png +0 -0
  118. psychopy/app/Resources/light/fileinfo.png +0 -0
  119. psychopy/app/Resources/light/fileinfo@2x.png +0 -0
  120. psychopy/app/Resources/light/filejs.png +0 -0
  121. psychopy/app/Resources/light/filejs@2x.png +0 -0
  122. psychopy/app/Resources/light/filejson.png +0 -0
  123. psychopy/app/Resources/light/filejson@2x.png +0 -0
  124. psychopy/app/Resources/light/filepkg.png +0 -0
  125. psychopy/app/Resources/light/filepkg@2x.png +0 -0
  126. psychopy/app/Resources/light/filepsyexp.png +0 -0
  127. psychopy/app/Resources/light/filepsyexp@2x.png +0 -0
  128. psychopy/app/Resources/light/filepy.png +0 -0
  129. psychopy/app/Resources/light/filepy@2x.png +0 -0
  130. psychopy/app/Resources/light/filetxt.png +0 -0
  131. psychopy/app/Resources/light/filetxt@2x.png +0 -0
  132. psychopy/app/Resources/light/fileunknown.png +0 -0
  133. psychopy/app/Resources/light/fileunknown@2x.png +0 -0
  134. psychopy/app/Resources/light/filevideo.png +0 -0
  135. psychopy/app/Resources/light/filevideo@2x.png +0 -0
  136. psychopy/app/Resources/light/find.png +0 -0
  137. psychopy/app/Resources/light/find@2x.png +0 -0
  138. psychopy/app/Resources/light/loop.png +0 -0
  139. psychopy/app/Resources/light/loop@2x.png +0 -0
  140. psychopy/app/Resources/light/regex.png +0 -0
  141. psychopy/app/Resources/light/regex@2x.png +0 -0
  142. psychopy/app/Resources/routine_templates/Basic.psyexp +0 -1
  143. psychopy/app/Resources/routine_templates/Misc.psyexp +0 -1
  144. psychopy/app/Resources/routine_templates/Online.psyexp +0 -2
  145. psychopy/app/Resources/routine_templates/Trials.psyexp +0 -1
  146. psychopy/app/Resources/splash.png +0 -0
  147. psychopy/app/Resources/splash@2x.png +0 -0
  148. psychopy/app/__init__.py +49 -8
  149. psychopy/app/__main__.py +3 -0
  150. psychopy/app/_psychopyApp.py +134 -125
  151. psychopy/app/builder/builder.py +42 -22
  152. psychopy/app/builder/dialogs/__init__.py +44 -11
  153. psychopy/app/builder/dialogs/dlgsCode.py +1 -1
  154. psychopy/app/builder/dialogs/dlgsConditions.py +1 -1
  155. psychopy/app/builder/dialogs/findDlg.py +106 -20
  156. psychopy/app/builder/dialogs/paramCtrls.py +42 -1
  157. psychopy/app/builder/validators.py +1 -1
  158. psychopy/app/coder/codeEditorBase.py +8 -8
  159. psychopy/app/coder/coder.py +32 -29
  160. psychopy/app/coder/fileBrowser.py +68 -22
  161. psychopy/app/coder/folding.py +1 -1
  162. psychopy/app/coder/psychoParser.py +1 -1
  163. psychopy/app/coder/repl.py +1 -1
  164. psychopy/app/coder/sourceTree.py +1 -1
  165. psychopy/app/connections/__init__.py +1 -1
  166. psychopy/app/connections/news.py +1 -1
  167. psychopy/app/connections/sendusage.py +1 -1
  168. psychopy/app/connections/updates.py +1 -1
  169. psychopy/app/console.py +1 -1
  170. psychopy/app/errorDlg.py +1 -1
  171. psychopy/app/frametracker.py +1 -1
  172. psychopy/app/idle.py +1 -1
  173. psychopy/app/jobs.py +5 -2
  174. psychopy/app/linuxconfig/__init__.py +1 -1
  175. psychopy/app/locale/ar_001/LC_MESSAGE/messages.mo +0 -0
  176. psychopy/app/locale/ar_001/LC_MESSAGE/messages.po +1514 -3259
  177. psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.mo +0 -0
  178. psychopy/app/locale/da_DK/LC_MESSAGE/messages.mo +0 -0
  179. psychopy/app/locale/de_DE/LC_MESSAGE/messages.mo +0 -0
  180. psychopy/app/locale/de_DE/LC_MESSAGE/messages.po +3 -3
  181. psychopy/app/locale/el_GR/LC_MESSAGE/messages.mo +0 -0
  182. psychopy/app/locale/en_NZ/LC_MESSAGE/messages.mo +0 -0
  183. psychopy/app/locale/en_US/LC_MESSAGE/messages.mo +0 -0
  184. psychopy/app/locale/es_CO/LC_MESSAGE/messages.mo +0 -0
  185. psychopy/app/locale/es_CO/LC_MESSAGE/messages.po +4 -4
  186. psychopy/app/locale/es_ES/LC_MESSAGE/messages.mo +0 -0
  187. psychopy/app/locale/es_ES/LC_MESSAGE/messages.po +4 -4
  188. psychopy/app/locale/es_US/LC_MESSAGE/messages.mo +0 -0
  189. psychopy/app/locale/es_US/LC_MESSAGE/messages.po +4 -4
  190. psychopy/app/locale/et_EE/LC_MESSAGE/messages.mo +0 -0
  191. psychopy/app/locale/et_EE/LC_MESSAGE/messages.po +2 -2
  192. psychopy/app/locale/fa_IR/LC_MESSAGE/messages.mo +0 -0
  193. psychopy/app/locale/fa_IR/LC_MESSAGE/messages.po +1 -1
  194. psychopy/app/locale/fi_FI/LC_MESSAGE/messages.mo +0 -0
  195. psychopy/app/locale/fr_FR/LC_MESSAGE/messages.mo +0 -0
  196. psychopy/app/locale/fr_FR/LC_MESSAGE/messages.po +2 -2
  197. psychopy/app/locale/he_IL/LC_MESSAGE/messages.mo +0 -0
  198. psychopy/app/locale/he_IL/LC_MESSAGE/messages.po +2 -2
  199. psychopy/app/locale/hi_IN/LC_MESSAGE/messages.mo +0 -0
  200. psychopy/app/locale/hi_IN/LC_MESSAGE/messages.po +2 -2
  201. psychopy/app/locale/hu_HU/LC_MESSAGE/messages.mo +0 -0
  202. psychopy/app/locale/it_IT/LC_MESSAGE/messages.mo +0 -0
  203. psychopy/app/locale/it_IT/LC_MESSAGE/messages.po +2 -2
  204. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
  205. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.po +3421 -2396
  206. psychopy/app/locale/ko_KR/LC_MESSAGE/messages.mo +0 -0
  207. psychopy/app/locale/ms_MY/LC_MESSAGE/messages.mo +0 -0
  208. psychopy/app/locale/ms_MY/LC_MESSAGE/messages.po +2 -2
  209. psychopy/app/locale/nl_NL/LC_MESSAGE/messages.mo +0 -0
  210. psychopy/app/locale/nn_NO/LC_MESSAGE/messages.mo +0 -0
  211. psychopy/app/locale/pl_PL/LC_MESSAGE/messages.mo +0 -0
  212. psychopy/app/locale/pt_PT/LC_MESSAGE/messages.mo +0 -0
  213. psychopy/app/locale/pt_PT/LC_MESSAGE/messages.po +4 -4
  214. psychopy/app/locale/ro_RO/LC_MESSAGE/messages.mo +0 -0
  215. psychopy/app/locale/ru_RU/LC_MESSAGE/messages.mo +0 -0
  216. psychopy/app/locale/sv_SE/LC_MESSAGE/messages.mo +0 -0
  217. psychopy/app/locale/sv_SE/LC_MESSAGE/messages.po +2 -2
  218. psychopy/app/locale/tr_TR/LC_MESSAGE/messages.mo +0 -0
  219. psychopy/app/locale/tr_TR/LC_MESSAGE/messages.po +2 -2
  220. psychopy/app/locale/zh_CN/LC_MESSAGE/messages.mo +0 -0
  221. psychopy/app/locale/zh_CN/LC_MESSAGE/messages.po +3 -3
  222. psychopy/app/locale/zh_TW/LC_MESSAGE/messages.mo +0 -0
  223. psychopy/app/locale/zh_TW/LC_MESSAGE/messages.po +2 -2
  224. psychopy/app/{builder/localizedStrings.py → localizedStrings.py} +173 -26
  225. psychopy/app/pavlovia_ui/__init__.py +1 -1
  226. psychopy/app/pavlovia_ui/_base.py +1 -1
  227. psychopy/app/pavlovia_ui/functions.py +1 -1
  228. psychopy/app/pavlovia_ui/menu.py +1 -1
  229. psychopy/app/pavlovia_ui/project.py +1 -1
  230. psychopy/app/pavlovia_ui/search.py +1 -1
  231. psychopy/app/pavlovia_ui/sync.py +1 -1
  232. psychopy/app/pavlovia_ui/user.py +1 -1
  233. psychopy/app/plugin_manager/dialog.py +34 -96
  234. psychopy/app/plugin_manager/packages.py +10 -14
  235. psychopy/app/plugin_manager/plugins.py +64 -4
  236. psychopy/app/preferencesDlg.py +12 -37
  237. psychopy/app/psychopyApp.py +130 -44
  238. psychopy/app/ribbon.py +1 -0
  239. psychopy/app/runner/runner.py +37 -15
  240. psychopy/app/runner/scriptProcess.py +11 -6
  241. psychopy/app/stdout/stdOutRich.py +9 -2
  242. psychopy/app/themes/fonts.py +1 -1
  243. psychopy/app/themes/icons.py +2 -38
  244. psychopy/app/ui/__init__.py +1 -1
  245. psychopy/app/utils.py +3 -3
  246. psychopy/assets/default.mp3 +0 -0
  247. psychopy/assets/fonts/NotoSans-Bold.ttf +0 -0
  248. psychopy/assets/fonts/NotoSans-BoldItalic.ttf +0 -0
  249. psychopy/assets/fonts/NotoSans-Italic.ttf +0 -0
  250. psychopy/assets/fonts/NotoSans-Regular.ttf +0 -0
  251. psychopy/assets/voicekeyThresholdStim.wav +0 -0
  252. psychopy/clock.py +4 -1
  253. psychopy/colors.py +2 -0
  254. psychopy/core.py +1 -1
  255. psychopy/data/base.py +1 -1
  256. psychopy/data/experiment.py +151 -46
  257. psychopy/data/routine.py +27 -1
  258. psychopy/data/staircase.py +2 -1
  259. psychopy/data/trial.py +62 -14
  260. psychopy/data/utils.py +1 -1
  261. psychopy/demos/builder/Design Templates/branchedExperiment/branchedExperiment.psyexp +333 -218
  262. psychopy/demos/builder/Design Templates/psychophysicsStaircase/psychophysicsStaircase.psyexp +261 -239
  263. psychopy/demos/builder/Design Templates/psychophysicsStairsInterleaved/psychophysicsStaircaseInterleaved.psyexp +319 -180
  264. psychopy/demos/builder/Design Templates/randomisedBlocks/randomisedBlocks.psyexp +204 -116
  265. psychopy/demos/builder/Experiments/BART/assets/background.jpg +0 -0
  266. psychopy/demos/builder/Experiments/BART/assets/blueBalloon.png +0 -0
  267. psychopy/demos/builder/Experiments/BART/assets/greenBalloon.png +0 -0
  268. psychopy/demos/builder/Experiments/BART/assets/redBalloon.png +0 -0
  269. psychopy/demos/builder/Experiments/BART/bart.psyexp +779 -866
  270. psychopy/demos/builder/Experiments/BigFiveInventory/BFI.psyexp +242 -180
  271. psychopy/demos/builder/Experiments/GoNoGo/gng.psyexp +419 -406
  272. psychopy/demos/builder/Experiments/dragAndDrop/README.md +2 -37
  273. psychopy/demos/builder/Experiments/dragAndDrop/drag_and_drop.psyexp +460 -1204
  274. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/blank_grid.png +0 -0
  275. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/make_shapes.psyexp +221 -0
  276. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/readme.md +4 -0
  277. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_1.png +0 -0
  278. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_10.png +0 -0
  279. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_2.png +0 -0
  280. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_3.png +0 -0
  281. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_4.png +0 -0
  282. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_5.png +0 -0
  283. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_6.png +0 -0
  284. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_7.png +0 -0
  285. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_8.png +0 -0
  286. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_9.png +0 -0
  287. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solutions.xlsx +0 -0
  288. psychopy/demos/builder/Experiments/mentalRotation/MentalRotation.psyexp +583 -542
  289. psychopy/demos/builder/Experiments/navon/NavonTask.psyexp +458 -427
  290. psychopy/demos/builder/Experiments/sternberg/sternberg.psyexp +588 -550
  291. psychopy/demos/builder/Experiments/stroop/stroop.psyexp +303 -207
  292. psychopy/demos/builder/Experiments/stroopExtended/stroop.psyexp +390 -215
  293. psychopy/demos/builder/Experiments/stroopExtended/stroopReverse.psyexp +390 -215
  294. psychopy/demos/builder/Experiments/stroopVoice/stroopVoice.psyexp +357 -331
  295. psychopy/demos/builder/Feature Demos/buttonBox/buttonBoxDemo.psyexp +3 -2
  296. psychopy/demos/builder/Feature Demos/counterbalance/counterbalance.psyexp +287 -277
  297. psychopy/demos/builder/Feature Demos/gratings/gratings.psyexp +370 -320
  298. psychopy/demos/builder/Feature Demos/noise/noise.psyexp +452 -399
  299. psychopy/demos/builder/Feature Demos/panorama/panorama.psyexp +168 -133
  300. psychopy/demos/builder/Feature Demos/pilotMode/pilotMode.psyexp +4 -3
  301. psychopy/demos/builder/Feature Demos/progress/progressBar.psyexp +420 -392
  302. psychopy/demos/builder/Feature Demos/sliders/sliders.psyexp +917 -871
  303. psychopy/demos/builder/Feature Demos/visualValidator/readme.md +7 -0
  304. psychopy/demos/builder/Feature Demos/visualValidator/visualValidator.psyexp +200 -0
  305. psychopy/demos/builder/Hardware/EEG_parallel_component/EEG_triggers_parallel_comp.psyexp +25 -9
  306. psychopy/demos/builder/Hardware/EEG_serial_code/EEG_triggers_serial_code.psyexp +372 -361
  307. psychopy/demos/builder/Hardware/EEG_serial_component/EEG_triggers_serial_comp.psyexp +25 -9
  308. psychopy/demos/builder/Hardware/EGI_netstation/stroop.psyexp +320 -235
  309. psychopy/demos/builder/Hardware/Eyetracking_visual_search/visualSearch.psyexp +790 -651
  310. psychopy/demos/builder/Hardware/camera/camera.psyexp +326 -246
  311. psychopy/demos/builder/Hardware/eyetracking/eyetracking.psyexp +432 -327
  312. psychopy/demos/builder/Hardware/eyetracking_custom_cal/eyetracking_custom_cal.psyexp +440 -321
  313. psychopy/demos/builder/Hardware/fMRI/fMRI_demo.psyexp +190 -181
  314. psychopy/demos/builder/Hardware/lab_streaming_layer/lsl_triggers_demo.psyexp +323 -312
  315. psychopy/demos/builder/Hardware/lab_streaming_layer/lsl_triggers_demo_legacy.psyexp +360 -0
  316. psychopy/demos/builder/{Feature Demos/eyetracking/eyetracking.xml → Hardware/lab_streaming_layer/lsl_triggers_demo_legacy_legacy.psyexp} +154 -140
  317. psychopy/demos/builder/Hardware/lab_streaming_layer_legacy/lsl_triggers_demo_legacy.psyexp +329 -273
  318. psychopy/demos/builder/Hardware/lab_streaming_layer_legacy/lsl_triggers_demo_legacy_legacy.psyexp +360 -0
  319. psychopy/demos/builder/Hardware/lab_streaming_layer_legacy/lsl_triggers_demo_legacy_legacy_legacy.psyexp +312 -0
  320. psychopy/demos/builder/Hardware/microphone/microphone.psyexp +450 -404
  321. psychopy/demos/builder/Hardware/pump/pump.psyexp +593 -329
  322. psychopy/demos/builder/Helper Tools/achorVSalignment/FlowCircular-Regular.ttf +0 -0
  323. psychopy/demos/builder/Helper Tools/achorVSalignment/anchorAlignment.psyexp +336 -274
  324. psychopy/demos/builder/Helper Tools/clockFace/clockFace.psyexp +200 -149
  325. psychopy/demos/builder/Helper Tools/colors/colors.psyexp +300 -279
  326. psychopy/demos/builder/Helper Tools/drawPolygon/drawPolygon.psyexp +677 -564
  327. psychopy/demos/builder/Helper Tools/keyNameFinder/keyNameFinder.psyexp +214 -158
  328. psychopy/demos/builder/Helper Tools/spatialUnits/unitDemo.psyexp +195 -146
  329. psychopy/demos/coder/experiment control/runtimeInfo.py +1 -1
  330. psychopy/devices/__init__.py +1 -1
  331. psychopy/event.py +1 -1
  332. psychopy/exceptions.py +1 -1
  333. psychopy/experiment/__init__.py +1 -1
  334. psychopy/experiment/_experiment.py +62 -16
  335. psychopy/experiment/components/__init__.py +1 -6
  336. psychopy/experiment/components/_base.py +44 -19
  337. psychopy/experiment/components/aperture/__init__.py +1 -1
  338. psychopy/experiment/components/brush/__init__.py +2 -2
  339. psychopy/experiment/components/button/__init__.py +24 -28
  340. psychopy/experiment/components/camera/__init__.py +38 -39
  341. psychopy/experiment/components/code/__init__.py +1 -1
  342. psychopy/experiment/components/dots/__init__.py +1 -1
  343. psychopy/experiment/components/eyetracker_record/__init__.py +7 -3
  344. psychopy/experiment/components/form/__init__.py +3 -3
  345. psychopy/experiment/components/grating/__init__.py +1 -1
  346. psychopy/experiment/components/image/__init__.py +1 -1
  347. psychopy/experiment/components/joyButtons/__init__.py +2 -2
  348. psychopy/experiment/components/joyButtons/virtualJoyButtons.py +1 -1
  349. psychopy/experiment/components/joystick/__init__.py +3 -3
  350. psychopy/experiment/components/joystick/virtualJoystick.py +1 -1
  351. psychopy/experiment/components/keyboard/__init__.py +98 -122
  352. psychopy/experiment/components/microphone/__init__.py +54 -98
  353. psychopy/experiment/components/mouse/__init__.py +92 -93
  354. psychopy/experiment/components/movie/__init__.py +28 -50
  355. psychopy/experiment/components/parallelOut/__init__.py +3 -3
  356. psychopy/experiment/components/polygon/__init__.py +2 -3
  357. psychopy/experiment/components/roi/__init__.py +2 -2
  358. psychopy/experiment/components/routineSettings/__init__.py +2 -0
  359. psychopy/experiment/components/serialOut/__init__.py +7 -7
  360. psychopy/experiment/components/settings/__init__.py +317 -313
  361. psychopy/experiment/components/settings/eyetracking.py +108 -0
  362. psychopy/experiment/components/slider/__init__.py +5 -5
  363. psychopy/experiment/components/sound/__init__.py +168 -78
  364. psychopy/experiment/components/soundsensor/__init__.py +361 -0
  365. psychopy/experiment/components/soundsensor/classic/soundsensor.png +0 -0
  366. psychopy/experiment/components/soundsensor/classic/soundsensor@2x.png +0 -0
  367. psychopy/experiment/components/soundsensor/dark/soundsensor.png +0 -0
  368. psychopy/experiment/components/soundsensor/dark/soundsensor@2x.png +0 -0
  369. psychopy/experiment/components/soundsensor/light/soundsensor.png +0 -0
  370. psychopy/experiment/components/soundsensor/light/soundsensor@2x.png +0 -0
  371. psychopy/experiment/components/static/__init__.py +59 -33
  372. psychopy/experiment/components/text/__init__.py +2 -6
  373. psychopy/experiment/components/textbox/__init__.py +3 -7
  374. psychopy/experiment/components/unknown/__init__.py +2 -0
  375. psychopy/experiment/components/unknownPlugin/__init__.py +2 -0
  376. psychopy/experiment/exports.py +1 -1
  377. psychopy/experiment/flow.py +2 -2
  378. psychopy/experiment/localization.py +1 -1
  379. psychopy/experiment/loops.py +43 -10
  380. psychopy/experiment/params.py +6 -4
  381. psychopy/experiment/plugins.py +8 -1
  382. psychopy/experiment/py2js.py +1 -1
  383. psychopy/experiment/py2js_transpiler.py +1 -1
  384. psychopy/experiment/routines/__init__.py +1 -1
  385. psychopy/experiment/routines/_base.py +23 -24
  386. psychopy/experiment/routines/audioValidator/__init__.py +343 -0
  387. psychopy/experiment/routines/audioValidator/classic/audio_validator.png +0 -0
  388. psychopy/experiment/routines/audioValidator/classic/audio_validator@2x.png +0 -0
  389. psychopy/experiment/routines/audioValidator/dark/audio_validator.png +0 -0
  390. psychopy/experiment/routines/audioValidator/dark/audio_validator@2x.png +0 -0
  391. psychopy/experiment/routines/audioValidator/light/audio_validator.png +0 -0
  392. psychopy/experiment/routines/audioValidator/light/audio_validator@2x.png +0 -0
  393. psychopy/experiment/routines/eyetracker_calibrate/__init__.py +76 -17
  394. psychopy/experiment/routines/eyetracker_validate/__init__.py +1 -1
  395. psychopy/experiment/routines/unknown/__init__.py +2 -0
  396. psychopy/experiment/routines/{photodiodeValidator → visualValidator}/__init__.py +89 -120
  397. psychopy/experiment/routines/visualValidator/classic/visual_validator.png +0 -0
  398. psychopy/experiment/routines/visualValidator/classic/visual_validator@2x.png +0 -0
  399. psychopy/experiment/routines/visualValidator/dark/visual_validator.png +0 -0
  400. psychopy/experiment/routines/visualValidator/dark/visual_validator@2x.png +0 -0
  401. psychopy/experiment/routines/visualValidator/light/visual_validator.png +0 -0
  402. psychopy/experiment/routines/visualValidator/light/visual_validator@2x.png +0 -0
  403. psychopy/experiment/utils.py +1 -1
  404. psychopy/gui/__init__.py +1 -1
  405. psychopy/gui/qtgui.py +15 -6
  406. psychopy/gui/wxgui.py +1 -1
  407. psychopy/hardware/__init__.py +0 -1
  408. psychopy/hardware/base.py +147 -16
  409. psychopy/hardware/bbtk/__init__.py +10 -16
  410. psychopy/hardware/brainproducts.py +7 -13
  411. psychopy/hardware/button.py +21 -2
  412. psychopy/hardware/buttonbox/__init__.py +1 -1
  413. psychopy/hardware/camera/__init__.py +16 -3
  414. psychopy/hardware/cedrus.py +5 -16
  415. psychopy/hardware/crs/__init__.py +1 -1
  416. psychopy/hardware/crs/bits.py +36 -33
  417. psychopy/hardware/crs/colorcal.py +8 -11
  418. psychopy/hardware/crs/optical.py +7 -10
  419. psychopy/hardware/crs/shaders.py +18 -5
  420. psychopy/hardware/emotiv.py +1 -1
  421. psychopy/hardware/emulator.py +11 -5
  422. psychopy/hardware/exceptions.py +86 -0
  423. psychopy/hardware/forp.py +18 -23
  424. psychopy/hardware/gammasci.py +8 -3
  425. psychopy/hardware/iolab.py +8 -19
  426. psychopy/hardware/joystick/__init__.py +865 -266
  427. psychopy/hardware/joystick/_base.py +251 -0
  428. psychopy/hardware/joystick/backend_glfw.py +306 -0
  429. psychopy/hardware/joystick/backend_pyglet.py +309 -0
  430. psychopy/hardware/joystick/mappings.py +287 -0
  431. psychopy/hardware/keyboard.py +4 -2
  432. psychopy/hardware/labhackers.py +1 -1
  433. psychopy/hardware/labjacks.py +9 -13
  434. psychopy/hardware/{photodiode.py → lightsensor.py} +146 -203
  435. psychopy/hardware/listener.py +9 -8
  436. psychopy/hardware/manager.py +24 -35
  437. psychopy/hardware/microphone.py +534 -154
  438. psychopy/hardware/minolta.py +14 -4
  439. psychopy/hardware/mouse/__init__.py +1 -1
  440. psychopy/hardware/photometer/__init__.py +2 -2
  441. psychopy/hardware/pr.py +14 -4
  442. psychopy/hardware/qmix.py +18 -27
  443. psychopy/hardware/serialdevice.py +43 -12
  444. psychopy/hardware/soundsensor.py +473 -0
  445. psychopy/hardware/spatial/__init__.py +231 -0
  446. psychopy/hardware/speaker.py +298 -36
  447. psychopy/hardware/triggerbox/__init__.py +1 -1
  448. psychopy/hardware/triggerbox/base.py +1 -1
  449. psychopy/hardware/triggerbox/parallel.py +1 -1
  450. psychopy/info.py +1 -1
  451. psychopy/iohub/client/__init__.py +17 -0
  452. psychopy/iohub/client/keyboard.py +5 -0
  453. psychopy/iohub/devices/eyetracker/__init__.py +10 -18
  454. psychopy/iohub/devices/eyetracker/calibration/procedure.py +15 -33
  455. psychopy/iohub/devices/eyetracker/hw/pupil_labs/neon/__init__.py +1 -0
  456. psychopy/iohub/devices/mouse/linux2.py +6 -1
  457. psychopy/iohub/devices/mouse/win32.py +5 -0
  458. psychopy/layout.py +1 -1
  459. psychopy/liaison.py +91 -39
  460. psychopy/locale_setup.py +11 -1
  461. psychopy/localization/__init__.py +1 -1
  462. psychopy/localization/_localization.py +1 -1
  463. psychopy/localization/messages.pot +2 -2
  464. psychopy/logging.py +14 -12
  465. psychopy/microphone.py +4 -3
  466. psychopy/misc.py +1 -1
  467. psychopy/monitors/MonitorCenter.py +3 -3
  468. psychopy/monitors/__init__.py +1 -1
  469. psychopy/monitors/calibData.py +1 -1
  470. psychopy/monitors/calibTools.py +3 -2
  471. psychopy/platform_specific/__init__.py +1 -1
  472. psychopy/platform_specific/darwin.py +1 -1
  473. psychopy/platform_specific/linux.py +1 -1
  474. psychopy/platform_specific/win32.py +1 -1
  475. psychopy/plugins/__init__.py +26 -17
  476. psychopy/plugins/util.py +65 -0
  477. psychopy/preferences/Darwin.spec +11 -3
  478. psychopy/preferences/FreeBSD.spec +11 -3
  479. psychopy/preferences/Linux.spec +11 -3
  480. psychopy/preferences/Windows.spec +11 -3
  481. psychopy/preferences/__init__.py +1 -1
  482. psychopy/preferences/baseNoArch.spec +11 -3
  483. psychopy/preferences/hints.py +78 -61
  484. psychopy/preferences/preferences.py +82 -13
  485. psychopy/projects/__init__.py +1 -1
  486. psychopy/projects/pavlovia.py +29 -13
  487. psychopy/scripts/psyexpCompile.py +1 -1
  488. psychopy/session.py +81 -8
  489. psychopy/sound/__init__.py +25 -10
  490. psychopy/sound/_base.py +134 -34
  491. psychopy/sound/audioclip.py +38 -15
  492. psychopy/sound/audiodevice.py +1 -1
  493. psychopy/sound/backend_ptb.py +53 -321
  494. psychopy/sound/backend_pygame.py +7 -3
  495. psychopy/sound/backend_pyo.py +53 -22
  496. psychopy/sound/backend_pysound.py +10 -27
  497. psychopy/sound/backend_sounddevice.py +33 -21
  498. psychopy/sound/exceptions.py +1 -1
  499. psychopy/sound/microphone.py +83 -5
  500. psychopy/sound/transcribe.py +3 -3
  501. psychopy/tests/data/test_basic_run.py +1 -0
  502. psychopy/tests/data/test_loaded_namespace/test_counterbalance.psyexp +142 -0
  503. psychopy/tests/data/test_loaded_namespace/test_custom_missing.psyexp +129 -0
  504. psychopy/tests/data/test_loaded_namespace/test_missing_counterbalance.psyexp +116 -0
  505. psychopy/tests/data/test_loaded_namespace/test_mix_exp.psyexp +181 -0
  506. psychopy/tests/data/test_loaded_namespace/test_mix_missing.psyexp +140 -0
  507. psychopy/tests/data/test_loaded_namespace/test_mix_name_calibration.psyexp +164 -0
  508. psychopy/tests/data/test_sounds/default_16000.wav +0 -0
  509. psychopy/tests/data/test_sounds/default_192000.wav +0 -0
  510. psychopy/tests/data/test_sounds/default_22050.wav +0 -0
  511. psychopy/tests/data/test_sounds/default_32000.wav +0 -0
  512. psychopy/tests/data/test_sounds/default_44100.wav +0 -0
  513. psychopy/tests/data/test_sounds/default_48000.wav +0 -0
  514. psychopy/tests/data/test_sounds/default_8000.wav +0 -0
  515. psychopy/tests/data/test_sounds/default_96000.wav +0 -0
  516. psychopy/tests/test_alerts/test_alerttools.py +2 -1
  517. psychopy/tests/test_app/test_command_line.py +65 -0
  518. psychopy/tests/test_data/test_TrialHandler2.py +18 -7
  519. psychopy/tests/test_demos/test_builder_demos.py +1 -1
  520. psychopy/tests/test_experiment/needs_wx/componsTemplate.txt +5238 -4188
  521. psychopy/tests/test_experiment/needs_wx/test_components.py +1 -1
  522. psychopy/tests/test_experiment/test_components/test_RoutineSettingsComponent.py +16 -0
  523. psychopy/tests/test_experiment/test_components/test_all_components.py +1 -1
  524. psychopy/tests/test_experiment/test_components/test_base_components.py +26 -17
  525. psychopy/tests/test_experiment/test_experiment.py +96 -0
  526. psychopy/tests/test_experiment/test_loops.py +12 -4
  527. psychopy/tests/test_experiment/test_params.py +17 -4
  528. psychopy/tests/test_experiment/test_routines/test_PhotodiodeValidationRoutine.py +2 -2
  529. psychopy/tests/test_hardware/test_photodiode.py +66 -0
  530. psychopy/tests/test_liaison/test_Liaison.py +4 -4
  531. psychopy/tests/test_misc/test_clock.py +2 -0
  532. psychopy/tests/test_misc/test_color.py +2 -0
  533. psychopy/tests/test_misc/test_event.py +1 -0
  534. psychopy/tests/test_plugins/test_plugin_stubs.py +125 -0
  535. psychopy/tests/test_sound/test_sound.py +111 -40
  536. psychopy/tests/test_tools/test_colorspacetools.py +24 -23
  537. psychopy/tests/test_tools/test_mathtools.py +9 -9
  538. psychopy/tests/test_tools/test_viewtools.py +101 -0
  539. psychopy/tests/test_validators/__init__.py +0 -0
  540. psychopy/tests/test_validators/test_voicekeyValidator.py +95 -0
  541. psychopy/tests/test_visual/test_all_stimuli.py +2 -2
  542. psychopy/tests/test_visual/test_basevisual.py +37 -7
  543. psychopy/tests/test_visual/test_button.py +2 -2
  544. psychopy/tests/test_visual/test_circle.py +10 -2
  545. psychopy/tests/test_visual/test_dots.py +1 -1
  546. psychopy/tests/test_visual/test_form.py +13 -13
  547. psychopy/tests/test_visual/test_gamma.py +3 -3
  548. psychopy/tests/test_visual/test_image.py +2 -2
  549. psychopy/tests/test_visual/test_progress.py +2 -2
  550. psychopy/tests/test_visual/test_roi.py +8 -2
  551. psychopy/tests/test_visual/test_shape.py +2 -2
  552. psychopy/tests/test_visual/test_slider.py +2 -2
  553. psychopy/tests/test_visual/test_target.py +2 -2
  554. psychopy/tests/test_visual/test_textbox.py +6 -8
  555. psychopy/tests/test_visual/test_winScalePos.py +6 -5
  556. psychopy/tests/test_visual/test_window.py +17 -0
  557. psychopy/tests/utils.py +51 -1
  558. psychopy/tools/__init__.py +1 -1
  559. psychopy/tools/arraytools.py +2 -2
  560. psychopy/tools/attributetools.py +52 -1
  561. psychopy/tools/audiotools.py +4 -1
  562. psychopy/tools/colorspacetools.py +6 -4
  563. psychopy/tools/coordinatetools.py +1 -1
  564. psychopy/tools/fileerrortools.py +21 -9
  565. psychopy/tools/filetools.py +2 -2
  566. psychopy/tools/fontmanager.py +47 -28
  567. psychopy/tools/gltools.py +2964 -558
  568. psychopy/tools/imagetools.py +1 -1
  569. psychopy/tools/mathtools.py +997 -127
  570. psychopy/tools/monitorunittools.py +7 -1
  571. psychopy/tools/movietools.py +1 -2
  572. psychopy/tools/pkgtools.py +157 -127
  573. psychopy/tools/plottools.py +1 -1
  574. psychopy/tools/rifttools.py +1 -1
  575. psychopy/tools/stereotools.py +1 -1
  576. psychopy/tools/stimulustools.py +172 -2
  577. psychopy/tools/stringtools.py +22 -2
  578. psychopy/tools/systemtools.py +1 -1
  579. psychopy/tools/typetools.py +1 -1
  580. psychopy/tools/unittools.py +1 -1
  581. psychopy/tools/versionchooser.py +3 -1
  582. psychopy/tools/viewtools.py +54 -70
  583. psychopy/tools/wizard.py +2 -2
  584. psychopy/validation/__init__.py +6 -0
  585. psychopy/validation/audio.py +74 -0
  586. psychopy/validation/visual.py +115 -0
  587. psychopy/visual/__init__.py +1 -4
  588. psychopy/visual/aperture.py +9 -6
  589. psychopy/visual/backends/__init__.py +1 -1
  590. psychopy/visual/backends/_base.py +1 -1
  591. psychopy/visual/backends/gamma.py +1 -1
  592. psychopy/visual/backends/glfwbackend.py +8 -12
  593. psychopy/visual/backends/pygamebackend.py +1 -1
  594. psychopy/visual/backends/pygletbackend.py +32 -11
  595. psychopy/visual/basevisual.py +93 -13
  596. psychopy/visual/brush.py +1 -1
  597. psychopy/visual/bufferimage.py +90 -10
  598. psychopy/visual/button.py +1 -1
  599. psychopy/visual/circle.py +12 -20
  600. psychopy/visual/custommouse.py +1 -1
  601. psychopy/visual/dot.py +80 -14
  602. psychopy/visual/elementarray.py +84 -10
  603. psychopy/visual/filters.py +1 -1
  604. psychopy/visual/form.py +43 -28
  605. psychopy/visual/grating.py +105 -25
  606. psychopy/visual/helpers.py +1 -1
  607. psychopy/visual/image.py +98 -20
  608. psychopy/visual/line.py +15 -25
  609. psychopy/visual/movie.py +1 -1
  610. psychopy/visual/movie2.py +4 -3
  611. psychopy/visual/movie3.py +4 -3
  612. psychopy/visual/movies/__init__.py +1 -1
  613. psychopy/visual/movies/frame.py +1 -1
  614. psychopy/visual/movies/metadata.py +1 -1
  615. psychopy/visual/movies/players/__init__.py +1 -1
  616. psychopy/visual/movies/players/_base.py +1 -1
  617. psychopy/visual/movies/players/ffpyplayer_player.py +2 -3
  618. psychopy/visual/nnlvs.py +9 -6
  619. psychopy/visual/noise.py +4 -16
  620. psychopy/visual/patch.py +4 -3
  621. psychopy/visual/pie.py +3 -14
  622. psychopy/visual/polygon.py +21 -28
  623. psychopy/visual/radial.py +4 -18
  624. psychopy/visual/ratingscale.py +4 -3
  625. psychopy/visual/rect.py +16 -25
  626. psychopy/visual/rift.py +8 -2
  627. psychopy/visual/secondorder.py +4 -16
  628. psychopy/visual/shaders.py +620 -286
  629. psychopy/visual/shape.py +281 -90
  630. psychopy/visual/simpleimage.py +1 -1
  631. psychopy/visual/slider.py +78 -25
  632. psychopy/visual/stim3d.py +5 -608
  633. psychopy/visual/text.py +13 -3
  634. psychopy/visual/textbox2/textbox2.py +188 -56
  635. psychopy/visual/vlcmoviestim.py +1 -1
  636. psychopy/visual/window.py +373 -200
  637. psychopy/web.py +1 -1
  638. {psychopy-2024.2.4.dist-info → psychopy-2025.1.0.dist-info}/METADATA +9 -9
  639. {psychopy-2024.2.4.dist-info → psychopy-2025.1.0.dist-info}/RECORD +653 -660
  640. psychopy/.DS_Store +0 -0
  641. psychopy/__init__.py.orig +0 -65
  642. psychopy/app/.DS_Store +0 -0
  643. psychopy/app/Resources/.DS_Store +0 -0
  644. psychopy/app/Resources/classic/filecsv16.png +0 -0
  645. psychopy/app/Resources/classic/fileimage16.png +0 -0
  646. psychopy/app/Resources/classic/fileunknown16.png +0 -0
  647. psychopy/app/Resources/dark/filecsv16.png +0 -0
  648. psychopy/app/Resources/dark/filecsv16@2x.png +0 -0
  649. psychopy/app/Resources/dark/fileimage16.png +0 -0
  650. psychopy/app/Resources/dark/fileimage16@2x.png +0 -0
  651. psychopy/app/Resources/dark/fileunknown16.png +0 -0
  652. psychopy/app/Resources/dark/fileunknown16@2x.png +0 -0
  653. psychopy/app/Resources/fonts/OpenSans-Bold.ttf +0 -0
  654. psychopy/app/Resources/fonts/OpenSans-BoldItalic.ttf +0 -0
  655. psychopy/app/Resources/fonts/OpenSans-ExtraBold.ttf +0 -0
  656. psychopy/app/Resources/fonts/OpenSans-ExtraBoldItalic.ttf +0 -0
  657. psychopy/app/Resources/fonts/OpenSans-Italic.ttf +0 -0
  658. psychopy/app/Resources/fonts/OpenSans-Light.ttf +0 -0
  659. psychopy/app/Resources/fonts/OpenSans-LightItalic.ttf +0 -0
  660. psychopy/app/Resources/fonts/OpenSans-Regular.ttf +0 -0
  661. psychopy/app/Resources/fonts/OpenSans-SemiBold.ttf +0 -0
  662. psychopy/app/Resources/fonts/OpenSans-SemiBoldItalic.ttf +0 -0
  663. psychopy/app/Resources/light/filecsv16.png +0 -0
  664. psychopy/app/Resources/light/filecsv16@2x.png +0 -0
  665. psychopy/app/Resources/light/fileimage16.png +0 -0
  666. psychopy/app/Resources/light/fileimage16@2x.png +0 -0
  667. psychopy/app/Resources/light/fileunknown16.png +0 -0
  668. psychopy/app/Resources/light/fileunknown16@2x.png +0 -0
  669. psychopy/app/Resources/psychopySplash.png +0 -0
  670. psychopy/app/Resources/psychopySplash@2x.png +0 -0
  671. psychopy/app/builder/builder.py.orig +0 -3932
  672. psychopy/app/builder/dialogs/__init__.py.orig +0 -1679
  673. psychopy/app/builder/dialogs/paramCtrls.py.orig +0 -713
  674. psychopy/app/colorpicker/__init__.py.orig +0 -411
  675. psychopy/app/locale/zh_CN/LC_MESSAGE/zh_CN.mo +0 -0
  676. psychopy/app/locale/zh_CN/LC_MESSAGE/zh_CN.po +0 -6127
  677. psychopy/app/locale/zh_CN/LC_MESSAGE/zh_CN_allFlagged.mo +0 -0
  678. psychopy/app/locale/zh_CN/LC_MESSAGE/zh_CN_allFlagged.po +0 -7366
  679. psychopy/core.py.orig +0 -169
  680. psychopy/demos/builder/.DS_Store +0 -0
  681. psychopy/demos/builder/Design Templates/randomisedBlocks/html/index.html +0 -23
  682. psychopy/demos/builder/Design Templates/randomisedBlocks/html/randomisedBlocks-legacy-browsers.js +0 -423
  683. psychopy/demos/builder/Design Templates/randomisedBlocks/html/randomisedBlocks.js +0 -427
  684. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/chooseBlock.xlsx +0 -0
  685. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/facesBlock.xlsx +0 -0
  686. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/housesBlock.xlsx +0 -0
  687. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/face01.jpg +0 -0
  688. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/face02.jpg +0 -0
  689. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/face03.jpg +0 -0
  690. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/house01.jpg +0 -0
  691. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/house02.jpg +0 -0
  692. psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/house03.jpg +0 -0
  693. psychopy/demos/builder/Design Templates/randomisedBlocks/randomisedBlocks.py +0 -330
  694. psychopy/demos/builder/Design Templates/randomisedBlocks/randomisedBlocks_lastrun.py +0 -330
  695. psychopy/demos/builder/Experiments/.DS_Store +0 -0
  696. psychopy/demos/builder/Experiments/dragAndDrop/archived_conditions.xlsx +0 -0
  697. psychopy/demos/builder/Experiments/dragAndDrop/draw grid stim.py +0 -61
  698. psychopy/demos/builder/Experiments/dragAndDrop/shapeMaker.psyexp +0 -91
  699. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_1.png +0 -0
  700. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_10.png +0 -0
  701. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_2.png +0 -0
  702. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_3.png +0 -0
  703. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_4.png +0 -0
  704. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_5.png +0 -0
  705. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_6.png +0 -0
  706. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_7.png +0 -0
  707. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_8.png +0 -0
  708. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_9.png +0 -0
  709. psychopy/demos/builder/Experiments/dragAndDrop/updated_conditions.xlsx +0 -0
  710. psychopy/demos/builder/Feature Demos/eyetracking/eyetracking.xsd +0 -120
  711. psychopy/demos/builder/Tools/.DS_Store +0 -0
  712. psychopy/demos/builder/Tools/gammaCalibration/.DS_Store +0 -0
  713. psychopy/demos/builder/Tools/gammaCalibration/data/.DS_Store +0 -0
  714. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.csv +0 -38
  715. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.log +0 -3418
  716. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.psydat +0 -0
  717. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.csv +0 -2
  718. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.log +0 -15
  719. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.psydat +0 -0
  720. psychopy/demos/builder/Tools/gammaCalibration/gamma_correction_visual.psyexp +0 -323
  721. psychopy/demos/builder/Tools/gammaCalibration/gamma_correction_visual.py +0 -562
  722. psychopy/demos/builder/Tools/gammaCalibration/gamma_correction_visual_lastrun.py +0 -562
  723. psychopy/demos/builder/Tools/gammaCalibration/questStairs.xlsx +0 -0
  724. psychopy/demos/builder/Tools/gammaCalibration/resources/low_contrast.png +0 -0
  725. psychopy/demos/builder/Tools/gammaCalibration/resources/make_2nd_order_tex.py +0 -59
  726. psychopy/demos/builder/Tools/gammaCalibration/resources/second_order_tex.png +0 -0
  727. psychopy/demos/coder/.DS_Store +0 -0
  728. psychopy/demos/coder/experiment control/info_gamma.pickle +0 -0
  729. psychopy/demos/coder/iohub/.iohpid +0 -1
  730. psychopy/demos/coder/iohub/eyetracking/.iohpid +0 -1
  731. psychopy/demos/coder/iohub/wintab/.DS_Store +0 -0
  732. psychopy/demos/coder/stimuli/.DS_Store +0 -0
  733. psychopy/demos/coder/stimuli/radialGratingContracting.py +0 -29
  734. psychopy/experiment/_experiment.py.orig +0 -1032
  735. psychopy/experiment/components/.DS_Store +0 -0
  736. psychopy/experiment/components/_base.py.orig +0 -823
  737. psychopy/experiment/components/form/.DS_Store +0 -0
  738. psychopy/experiment/components/microphone/__init__.py.orig +0 -490
  739. psychopy/experiment/components/settings/__init__.py.orig +0 -1337
  740. psychopy/experiment/components/textbox/__init__.py.orig +0 -310
  741. psychopy/experiment/components/webcam/.DS_Store +0 -0
  742. psychopy/experiment/components/webcam/light/.DS_Store +0 -0
  743. psychopy/experiment/loops.py.orig +0 -829
  744. psychopy/experiment/params.py.orig +0 -408
  745. psychopy/experiment/routine.py.orig +0 -503
  746. psychopy/experiment/routines/photodiodeValidator/classic/photodiode_validator.png +0 -0
  747. psychopy/experiment/routines/photodiodeValidator/classic/photodiode_validator@2x.png +0 -0
  748. psychopy/experiment/routines/photodiodeValidator/dark/photodiode_validator.png +0 -0
  749. psychopy/experiment/routines/photodiodeValidator/dark/photodiode_validator@2x.png +0 -0
  750. psychopy/experiment/routines/photodiodeValidator/light/photodiode_validator.png +0 -0
  751. psychopy/experiment/routines/photodiodeValidator/light/photodiode_validator@2x.png +0 -0
  752. psychopy/hardware/.DS_Store +0 -0
  753. psychopy/hardware/brainproducts.py.orig +0 -680
  754. psychopy/hardware/iolab.py.orig +0 -238
  755. psychopy/hardware/serialport.py +0 -51
  756. psychopy/iohub/datastore/__init__.py.orig +0 -443
  757. psychopy/iohub/datastore/util.py.orig +0 -692
  758. psychopy/iohub/devices/mouse/darwin.py.orig +0 -427
  759. psychopy/iohub/devices/mouse/linux2.py.orig +0 -198
  760. psychopy/preferences/.DS_Store +0 -0
  761. psychopy/projects/pavlovia.py.orig +0 -1295
  762. psychopy/tests/.DS_Store +0 -0
  763. psychopy/tests/data/.DS_Store +0 -0
  764. psychopy/tests/data/TestCircle_fill_local.png +0 -0
  765. psychopy/tests/data/__test.png +0 -0
  766. psychopy/tests/data/aperture1_normHexbackground_local.png +0 -0
  767. psychopy/tests/data/aperture1_norm_local.png +0 -0
  768. psychopy/tests/data/aperture2_normHexbackground_local.png +0 -0
  769. psychopy/tests/data/beatandrcos_height_local.png +0 -0
  770. psychopy/tests/data/beatandrcos_normAddBlend_local.png +0 -0
  771. psychopy/tests/data/beatandrcos_normHexbackground_local.png +0 -0
  772. psychopy/tests/data/beatandrcos_norm_local.png +0 -0
  773. psychopy/tests/data/beatandrcos_stencil_local.png +0 -0
  774. psychopy/tests/data/blend_add_height_local.png +0 -0
  775. psychopy/tests/data/blend_add_normAddBlend_local.png +0 -0
  776. psychopy/tests/data/blend_add_normHexbackground_local.png +0 -0
  777. psychopy/tests/data/blend_add_normNoShade_local.png +0 -0
  778. psychopy/tests/data/blend_add_norm_local.png +0 -0
  779. psychopy/tests/data/blend_add_stencil_local.png +0 -0
  780. psychopy/tests/data/bufferimg_gabor_height_local.png +0 -0
  781. psychopy/tests/data/bufferimg_gabor_normAddBlend_local.png +0 -0
  782. psychopy/tests/data/bufferimg_gabor_normHexbackground_local.png +0 -0
  783. psychopy/tests/data/bufferimg_gabor_normNoShade_local.png +0 -0
  784. psychopy/tests/data/bufferimg_gabor_norm_local.png +0 -0
  785. psychopy/tests/data/bufferimg_gabor_stencil_local.png +0 -0
  786. psychopy/tests/data/circleHex_height_local.png +0 -0
  787. psychopy/tests/data/circleHex_normAddBlend_local.png +0 -0
  788. psychopy/tests/data/circleHex_normHexbackground_local.png +0 -0
  789. psychopy/tests/data/circleHex_normNoShade_local.png +0 -0
  790. psychopy/tests/data/circleHex_norm_local.png +0 -0
  791. psychopy/tests/data/circleHex_stencil_local.png +0 -0
  792. psychopy/tests/data/color_comparison_local.png +0 -0
  793. psychopy/tests/data/corrFullRandom_local.csv +0 -16
  794. psychopy/tests/data/corrFullRandom_local.tsv +0 -6
  795. psychopy/tests/data/correctScript/.DS_Store +0 -0
  796. psychopy/tests/data/dots_height_local.png +0 -0
  797. psychopy/tests/data/dots_normAddBlend_local.png +0 -0
  798. psychopy/tests/data/dots_normHexbackground_local.png +0 -0
  799. psychopy/tests/data/dots_normNoShade_local.png +0 -0
  800. psychopy/tests/data/dots_norm_local.png +0 -0
  801. psychopy/tests/data/dots_stencil_local.png +0 -0
  802. psychopy/tests/data/elarray1_height_local.png +0 -0
  803. psychopy/tests/data/elarray1_normAddBlend_local.png +0 -0
  804. psychopy/tests/data/elarray1_normHexbackground_local.png +0 -0
  805. psychopy/tests/data/elarray1_norm_local.png +0 -0
  806. psychopy/tests/data/elarray1_stencil_local.png +0 -0
  807. psychopy/tests/data/envelopeandrcos_height_local.png +0 -0
  808. psychopy/tests/data/envelopeandrcos_normAddBlend_local.png +0 -0
  809. psychopy/tests/data/envelopeandrcos_normHexbackground_local.png +0 -0
  810. psychopy/tests/data/envelopeandrcos_norm_local.png +0 -0
  811. psychopy/tests/data/envelopeandrcos_stencil_local.png +0 -0
  812. psychopy/tests/data/envelopepowerandrcos_height_local.png +0 -0
  813. psychopy/tests/data/envelopepowerandrcos_normAddBlend_local.png +0 -0
  814. psychopy/tests/data/envelopepowerandrcos_normHexbackground_local.png +0 -0
  815. psychopy/tests/data/envelopepowerandrcos_norm_local.png +0 -0
  816. psychopy/tests/data/envelopepowerandrcos_stencil_local.png +0 -0
  817. psychopy/tests/data/gabor1_height_local.png +0 -0
  818. psychopy/tests/data/gabor1_normAddBlend_local.png +0 -0
  819. psychopy/tests/data/gabor1_normHexbackground_local.png +0 -0
  820. psychopy/tests/data/gabor1_normNoShade_local.png +0 -0
  821. psychopy/tests/data/gabor1_norm_local.png +0 -0
  822. psychopy/tests/data/gabor1_stencil_local.png +0 -0
  823. psychopy/tests/data/greyscale_normHexbackground_local.png +0 -0
  824. psychopy/tests/data/imageAndGauss_height_local.png +0 -0
  825. psychopy/tests/data/imageAndGauss_normAddBlend_local.png +0 -0
  826. psychopy/tests/data/imageAndGauss_normHexbackground_local.png +0 -0
  827. psychopy/tests/data/imageAndGauss_normNoShade_local.png +0 -0
  828. psychopy/tests/data/imageAndGauss_norm_local.png +0 -0
  829. psychopy/tests/data/imageAndGauss_stencil_local.png +0 -0
  830. psychopy/tests/data/movFrame1_stencil_local.png +0 -0
  831. psychopy/tests/data/noiseAndRcos_height_local.png +0 -0
  832. psychopy/tests/data/noiseAndRcos_normAddBlend_local.png +0 -0
  833. psychopy/tests/data/noiseAndRcos_normHexbackground_local.png +0 -0
  834. psychopy/tests/data/noiseAndRcos_normNoShade_local.png +0 -0
  835. psychopy/tests/data/noiseAndRcos_norm_local.png +0 -0
  836. psychopy/tests/data/noiseAndRcos_stencil_local.png +0 -0
  837. psychopy/tests/data/noiseFiltersAndRcos_height_local.png +0 -0
  838. psychopy/tests/data/noiseFiltersAndRcos_normAddBlend_local.png +0 -0
  839. psychopy/tests/data/noiseFiltersAndRcos_normHexbackground_local.png +0 -0
  840. psychopy/tests/data/noiseFiltersAndRcos_normNoShade_local.png +0 -0
  841. psychopy/tests/data/noiseFiltersAndRcos_norm_local.png +0 -0
  842. psychopy/tests/data/noiseFiltersAndRcos_stencil_local.png +0 -0
  843. psychopy/tests/data/numpyImage_height_local.png +0 -0
  844. psychopy/tests/data/numpyImage_normAddBlend_local.png +0 -0
  845. psychopy/tests/data/numpyImage_normHexbackground_local.png +0 -0
  846. psychopy/tests/data/numpyImage_normNoShade_local.png +0 -0
  847. psychopy/tests/data/numpyImage_norm_local.png +0 -0
  848. psychopy/tests/data/numpyImage_stencil_local.png +0 -0
  849. psychopy/tests/data/shape2_1_normAddBlend_local.png +0 -0
  850. psychopy/tests/data/shape2_1_normHexbackground_local.png +0 -0
  851. psychopy/tests/data/shape2_1_normNoShade_local.png +0 -0
  852. psychopy/tests/data/shape2_1_norm_local.png +0 -0
  853. psychopy/tests/data/shape2_1_stencil_local.png +0 -0
  854. psychopy/tests/data/testLoopsBlocks.psyexp_local.py +0 -328
  855. psychopy/tests/data/text1_height_local.png +0 -0
  856. psychopy/tests/data/text1_normAddBlend_local.png +0 -0
  857. psychopy/tests/data/text1_normHexbackground_local.png +0 -0
  858. psychopy/tests/data/text1_norm_local.png +0 -0
  859. psychopy/tests/data/text1_stencil_local.png +0 -0
  860. psychopy/tests/data/text2_height.png +0 -0
  861. psychopy/tests/data/text2_normAddBlend.png +0 -0
  862. psychopy/tests/data/text2_normHexbackground.png +0 -0
  863. psychopy/tests/data/text2_stencil.png +0 -0
  864. psychopy/tests/data/wedge1_height_local.png +0 -0
  865. psychopy/tests/data/wedge1_normAddBlend_local.png +0 -0
  866. psychopy/tests/data/wedge1_normHexbackground_local.png +0 -0
  867. psychopy/tests/data/wedge1_normNoShade_local.png +0 -0
  868. psychopy/tests/data/wedge1_norm_local.png +0 -0
  869. psychopy/tests/data/wedge1_stencil_local.png +0 -0
  870. psychopy/tests/test_app/.DS_Store +0 -0
  871. psychopy/tests/test_app/test_builder/.DS_Store +0 -0
  872. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.csv +0 -9
  873. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.log +0 -177
  874. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.psydat +0 -0
  875. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.xlsx +0 -0
  876. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.csv +0 -9
  877. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.log +0 -168
  878. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.psydat +0 -0
  879. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.xlsx +0 -0
  880. psychopy/tests/test_data/.DS_Store +0 -0
  881. psychopy/tests/test_hardware/test_CRS_BitsSharp.py +0 -67
  882. psychopy/tests/test_hardware/test_CRS_BitsSharp.py.orig +0 -68
  883. psychopy/tests/test_hardware/test_CRS_bitsShaders.py +0 -110
  884. psychopy/tests/test_visual/test_image.py.orig +0 -219
  885. psychopy/visual/basevisual.py.orig +0 -1723
  886. psychopy/visual/form.py.orig +0 -1181
  887. psychopy/visual/text.py.orig +0 -752
  888. psychopy/visual/textbox2/textbox2.py.orig +0 -1315
  889. psychopy/visual/windowwarp.py.orig +0 -463
  890. /psychopy/{app/cortex.log → alerts/alertsCatalogue/3600.yaml} +0 -0
  891. /psychopy/{app/Resources → assets}/fonts/Arvo-Bold.ttf +0 -0
  892. /psychopy/{app/Resources → assets}/fonts/Arvo-BoldItalic.ttf +0 -0
  893. /psychopy/{app/Resources → assets}/fonts/Arvo-Italic.ttf +0 -0
  894. /psychopy/{app/Resources → assets}/fonts/Arvo-Regular.ttf +0 -0
  895. /psychopy/{app/Resources → assets}/fonts/DejaVuSerif.ttf +0 -0
  896. /psychopy/{app/Resources → assets}/fonts/IndieFlower-Regular.ttf +0 -0
  897. /psychopy/{app/Resources → assets}/fonts/JetBrainsMono-Italic-VariableFont_wght.ttf +0 -0
  898. /psychopy/{app/Resources → assets}/fonts/JetBrainsMono-VariableFont_wght.ttf +0 -0
  899. /psychopy/demos/builder/Experiments/{GoNoGo → goNoGo}/readme.md +0 -0
  900. /psychopy/{demos/builder/Tools/gammaCalibration/readme.md → tests/test_plugins/__init__.py} +0 -0
  901. {psychopy-2024.2.4.dist-info → psychopy-2025.1.0.dist-info}/WHEEL +0 -0
  902. {psychopy-2024.2.4.dist-info → psychopy-2025.1.0.dist-info}/entry_points.txt +0 -0
  903. {psychopy-2024.2.4.dist-info → psychopy-2025.1.0.dist-info}/licenses/LICENSE +0 -0
psychopy/tools/gltools.py CHANGED
@@ -5,15 +5,31 @@
5
5
  """
6
6
 
7
7
  # Part of the PsychoPy library
8
- # Copyright (C) 2002-2018 Jonathan Peirce (C) 2019-2024 Open Science Tools Ltd.
8
+ # Copyright (C) 2002-2018 Jonathan Peirce (C) 2019-2025 Open Science Tools Ltd.
9
9
  # Distributed under the terms of the GNU General Public License (GPL).
10
10
 
11
11
  __all__ = [
12
+ 'VERTEX_ATTRIB_POSITION',
13
+ 'VERTEX_ATTRIB_NORMAL',
14
+ 'VERTEX_ATTRIB_COLOR',
15
+ 'VERTEX_ATTRIB_SECONADRY_COLOR',
16
+ 'VERTEX_ATTRIB_COLOR2',
17
+ 'VERTEX_ATTRIB_FOGCOORD',
18
+ 'VERTEX_ATTRIB_MULTITEXCOORD0',
19
+ 'VERTEX_ATTRIB_MULTITEXCOORD1',
20
+ 'VERTEX_ATTRIB_MULTITEXCOORD2',
21
+ 'VERTEX_ATTRIB_MULTITEXCOORD3',
22
+ 'VERTEX_ATTRIB_MULTITEXCOORD4',
23
+ 'VERTEX_ATTRIB_MULTITEXCOORD5',
24
+ 'VERTEX_ATTRIB_MULTITEXCOORD6',
25
+ 'VERTEX_ATTRIB_MULTITEXCOORD7',
12
26
  'createProgram',
13
27
  'createProgramObjectARB',
14
28
  'compileShader',
15
29
  'compileShaderObjectARB',
16
30
  'embedShaderSourceDefs',
31
+ 'deleteShader',
32
+ 'deleteProgram',
17
33
  'deleteObject',
18
34
  'deleteObjectARB',
19
35
  'attachShader',
@@ -27,6 +43,9 @@ __all__ = [
27
43
  'useProgram',
28
44
  'useProgramObjectARB',
29
45
  'getInfoLog',
46
+ 'setUniformValue',
47
+ 'setUniformMatrix',
48
+ 'getUniformLocation',
30
49
  'getUniformLocations',
31
50
  'getAttribLocations',
32
51
  'createQueryObject',
@@ -35,12 +54,20 @@ __all__ = [
35
54
  'endQuery',
36
55
  'getQuery',
37
56
  'getAbsTimeGPU',
57
+ 'FramebufferInfo',
38
58
  'createFBO',
39
- 'attach',
40
- 'isComplete',
59
+ 'attachImage',
60
+ 'isFramebufferComplete',
41
61
  'deleteFBO',
42
62
  'blitFBO',
43
63
  'useFBO',
64
+ 'bindFBO',
65
+ 'unbindFBO',
66
+ 'deleteFBO',
67
+ 'clearFramebuffer',
68
+ 'setDrawBuffer',
69
+ 'setReadBuffer',
70
+ 'RenderbufferInfo',
44
71
  'createRenderbuffer',
45
72
  'deleteRenderbuffer',
46
73
  'createTexImage2D',
@@ -50,12 +77,15 @@ __all__ = [
50
77
  'createVAO',
51
78
  'drawVAO',
52
79
  'deleteVAO',
80
+ 'drawClientArrays',
53
81
  'VertexBufferInfo',
54
82
  'createVBO',
55
83
  'bindVBO',
56
84
  'unbindVBO',
57
85
  'mapBuffer',
58
86
  'unmapBuffer',
87
+ 'mappedBuffer',
88
+ 'updateVBO',
59
89
  'deleteVBO',
60
90
  'setVertexAttribPointer',
61
91
  'enableVertexAttribArray',
@@ -73,8 +103,17 @@ __all__ = [
73
103
  'createMeshGridFromArrays',
74
104
  'createMeshGrid',
75
105
  'createBox',
106
+ 'createDisc',
107
+ 'createAnnulus',
108
+ 'createCylinder',
76
109
  'transformMeshPosOri',
77
110
  'calculateVertexNormals',
111
+ 'mergeVerticies',
112
+ 'smoothCreases',
113
+ 'flipFaces',
114
+ 'interleaveAttributes',
115
+ 'generateTexCoords',
116
+ 'tesselate',
78
117
  'getIntegerv',
79
118
  'getFloatv',
80
119
  'getString',
@@ -106,33 +145,269 @@ from psychopy.visual.helpers import setColor, findImageFile
106
145
  _thisPlatform = platform.system()
107
146
 
108
147
  # create a query counter to get absolute GPU time
109
-
110
148
  QUERY_COUNTER = None # prevent genQueries from being called
111
149
 
150
+ # vertex attribute locations
151
+ VERTEX_ATTRIB_POSITION = 0 # gl_Vertex
152
+ VERTEX_ATTRIB_NORMAL = 2 # gl_Normal
153
+ VERTEX_ATTRIB_COLOR = 3 # gl_Color
154
+ VERTEX_ATTRIB_SECONADRY_COLOR = VERTEX_ATTRIB_COLOR2 = 4 # gl_SecondaryColor
155
+ VERTEX_ATTRIB_FOGCOORD = 5 # gl_FogCoord
156
+ VERTEX_ATTRIB_MULTITEXCOORD0 = 8 # gl_MultiTexCoord0
157
+ VERTEX_ATTRIB_MULTITEXCOORD1 = 9 # gl_MultiTexCoord1
158
+ VERTEX_ATTRIB_MULTITEXCOORD2 = 10 # gl_MultiTexCoord2
159
+ VERTEX_ATTRIB_MULTITEXCOORD3 = 11 # gl_MultiTexCoord3
160
+ VERTEX_ATTRIB_MULTITEXCOORD4 = 12 # gl_MultiTexCoord4
161
+ VERTEX_ATTRIB_MULTITEXCOORD5 = 13 # gl_MultiTexCoord5
162
+ VERTEX_ATTRIB_MULTITEXCOORD6 = 14 # gl_MultiTexCoord6
163
+ VERTEX_ATTRIB_MULTITEXCOORD7 = 15 # gl_MultiTexCoord7
164
+
165
+ # mapping human-readable names to the typical attribute locations
166
+ VERTEX_ATTRIBS = {
167
+ 'gl_Vertex': VERTEX_ATTRIB_POSITION,
168
+ 'gl_Normal': VERTEX_ATTRIB_NORMAL,
169
+ 'gl_Color': VERTEX_ATTRIB_COLOR,
170
+ 'gl_SecondaryColor': VERTEX_ATTRIB_COLOR2,
171
+ 'gl_FogCoord': VERTEX_ATTRIB_FOGCOORD,
172
+ 'gl_MultiTexCoord0': VERTEX_ATTRIB_MULTITEXCOORD0,
173
+ 'gl_MultiTexCoord1': VERTEX_ATTRIB_MULTITEXCOORD1,
174
+ 'gl_MultiTexCoord2': VERTEX_ATTRIB_MULTITEXCOORD2,
175
+ 'gl_MultiTexCoord3': VERTEX_ATTRIB_MULTITEXCOORD3,
176
+ 'gl_MultiTexCoord4': VERTEX_ATTRIB_MULTITEXCOORD4,
177
+ 'gl_MultiTexCoord5': VERTEX_ATTRIB_MULTITEXCOORD5,
178
+ 'gl_MultiTexCoord6': VERTEX_ATTRIB_MULTITEXCOORD6,
179
+ 'gl_MultiTexCoord7': VERTEX_ATTRIB_MULTITEXCOORD7
180
+ }
112
181
 
113
- # compatible Numpy and OpenGL types for common GL type enums
114
- GL_COMPAT_TYPES = {
115
- GL.GL_FLOAT: (np.float32, GL.GLfloat),
116
- GL.GL_DOUBLE: (np.float64, GL.GLdouble),
117
- GL.GL_UNSIGNED_SHORT: (np.uint16, GL.GLushort),
118
- GL.GL_UNSIGNED_INT: (np.uint32, GL.GLuint),
119
- GL.GL_INT: (np.int32, GL.GLint),
120
- GL.GL_SHORT: (np.int16, GL.GLshort),
121
- GL.GL_HALF_FLOAT: (np.float16, GL.GLhalfARB),
122
- GL.GL_UNSIGNED_BYTE: (np.uint8, GL.GLubyte),
123
- GL.GL_BYTE: (np.int8, GL.GLbyte),
124
- np.float32: (GL.GL_FLOAT, GL.GLfloat),
125
- np.float64: (GL.GL_DOUBLE, GL.GLdouble),
126
- np.uint16: (GL.GL_UNSIGNED_SHORT, GL.GLushort),
127
- np.uint32: (GL.GL_UNSIGNED_INT, GL.GLuint),
128
- np.int32: (GL.GL_INT, GL.GLint),
129
- np.int16: (GL.GL_SHORT, GL.GLshort),
130
- np.float16: (GL.GL_HALF_FLOAT, GL.GLhalfARB),
131
- np.uint8: (GL.GL_UNSIGNED_BYTE, GL.GLubyte),
132
- np.int8: (GL.GL_BYTE, GL.GLbyte)
182
+ # Mappings between Python/Numpy and OpenGL data types for arrays. Duplication
183
+ # simplifies the lookup process when used in functions. Some types are not
184
+ # supported by OpenGL, so they are remapped to the closest compatible type.
185
+ ARRAY_TYPES = {
186
+ 'float32': (GL.GL_FLOAT, GL.GLfloat, np.float32),
187
+ 'float': (GL.GL_FLOAT, GL.GLfloat, np.float32),
188
+ 'double': (GL.GL_DOUBLE, GL.GLdouble, float),
189
+ 'float64': (GL.GL_DOUBLE, GL.GLdouble, float),
190
+ 'uint16': (GL.GL_UNSIGNED_SHORT, GL.GLushort, np.uint16),
191
+ 'unsigned_short': (GL.GL_UNSIGNED_SHORT, GL.GLushort, np.uint16),
192
+ 'uint32': (GL.GL_UNSIGNED_INT, GL.GLuint, np.uint32),
193
+ 'unsigned_int': (GL.GL_UNSIGNED_INT, GL.GLuint, np.uint32),
194
+ 'int': (GL.GL_INT, GL.GLint, np.int32), # remapped to int32 from int64
195
+ 'int32': (GL.GL_INT, GL.GLint, np.int32),
196
+ 'int16': (GL.GL_SHORT, GL.GLshort, np.int16),
197
+ 'short': (GL.GL_SHORT, GL.GLshort, np.int16),
198
+ 'uint8': (GL.GL_UNSIGNED_BYTE, GL.GLubyte, np.uint8),
199
+ 'unsigned_byte': (GL.GL_UNSIGNED_BYTE, GL.GLubyte, np.uint8),
200
+ 'int8': (GL.GL_BYTE, GL.GLbyte, np.int8),
201
+ 'byte': (GL.GL_BYTE, GL.GLbyte, np.int8),
202
+ np.float32: (GL.GL_FLOAT, GL.GLfloat, np.float32),
203
+ float: (GL.GL_DOUBLE, GL.GLdouble, float),
204
+ np.float64: (GL.GL_DOUBLE, GL.GLdouble, np.float64),
205
+ np.uint16: (GL.GL_UNSIGNED_SHORT, GL.GLushort, np.uint16),
206
+ np.uint32: (GL.GL_UNSIGNED_INT, GL.GLuint, np.uint32),
207
+ int: (GL.GL_INT, GL.GLint, np.int32), # remapped to int32
208
+ np.int32: (GL.GL_INT, GL.GLint, np.int32),
209
+ np.int16: (GL.GL_SHORT, GL.GLshort, np.int16),
210
+ np.uint8: (GL.GL_UNSIGNED_BYTE, GL.GLubyte, np.uint8),
211
+ np.int8: (GL.GL_BYTE, GL.GLbyte, np.int8),
212
+ GL.GL_FLOAT: (GL.GL_FLOAT, GL.GLfloat, np.float32),
213
+ GL.GL_DOUBLE: (GL.GL_DOUBLE, GL.GLdouble, float), # python float is 64-bit
214
+ GL.GL_UNSIGNED_SHORT: (GL.GL_UNSIGNED_SHORT, GL.GLushort, np.uint16),
215
+ GL.GL_UNSIGNED_INT: (GL.GL_UNSIGNED_INT, GL.GLuint, np.uint32),
216
+ GL.GL_INT: (GL.GL_INT, GL.GLint, np.int32),
217
+ GL.GL_SHORT: (GL.GL_SHORT, GL.GLshort, np.int16),
218
+ GL.GL_UNSIGNED_BYTE: (GL.GL_UNSIGNED_BYTE, GL.GLubyte, np.uint8),
219
+ GL.GL_BYTE: (GL.GL_BYTE, GL.GLbyte, np.int8),
133
220
  }
134
221
 
135
222
 
223
+ def _getGLEnum(*args):
224
+ """Get the OpenGL enum value from a string or GLEnum.
225
+
226
+ Parameters
227
+ ----------
228
+ args : str, GLEnum, int or None
229
+ OpenGL enum value(s) to retrieve. If `None`, `None` is returned.
230
+
231
+ Returns
232
+ -------
233
+ int or list
234
+ OpenGL enum value(s) in the order they were passed.
235
+
236
+ Examples
237
+ --------
238
+ Get the OpenGL enum value for a single string::
239
+
240
+ _getGLEnum('points') # returns GL.GL_POINTS
241
+
242
+ """
243
+ if args is None:
244
+ return None
245
+ elif len(args) == 1:
246
+ return getattr(GL, args[0]) if isinstance(args[0], str) else args[0]
247
+ else:
248
+ return [getattr(GL, i) if isinstance(i, str) else i for i in args]
249
+
250
+
251
+ def getIntegerv(parName):
252
+ """Get a single integer parameter value, return it as a Python integer.
253
+
254
+ Parameters
255
+ ----------
256
+ pName : int
257
+ OpenGL property enum to query (e.g. GL_MAJOR_VERSION).
258
+
259
+ Returns
260
+ -------
261
+ int
262
+
263
+ """
264
+ val = GL.GLint()
265
+ GL.glGetIntegerv(parName, val)
266
+
267
+ return int(val.value)
268
+
269
+
270
+ def getFloatv(parName):
271
+ """Get a single float parameter value, return it as a Python float.
272
+
273
+ Parameters
274
+ ----------
275
+ pName : float
276
+ OpenGL property enum to query.
277
+
278
+ Returns
279
+ -------
280
+ float
281
+
282
+ """
283
+ val = GL.GLfloat()
284
+ GL.glGetFloatv(parName, val)
285
+
286
+ return float(val.value)
287
+
288
+
289
+ def getString(parName):
290
+ """Get a single string parameter value, return it as a Python UTF-8 string.
291
+
292
+ Parameters
293
+ ----------
294
+ pName : int
295
+ OpenGL property enum to query (e.g. GL_VENDOR).
296
+
297
+ Returns
298
+ -------
299
+ str
300
+
301
+ """
302
+ val = ctypes.cast(GL.glGetString(parName), ctypes.c_char_p).value
303
+ return val.decode('UTF-8')
304
+
305
+
306
+ class OpenGLInfo:
307
+ """OpenGL information class.
308
+
309
+ This class is used to store information about the OpenGL implementation on
310
+ the current machine. It provides a consistent means of querying the OpenGL
311
+ implementation regardless of the OpenGL interface being used.
312
+
313
+ Attributes
314
+ ----------
315
+ vendor : str
316
+ The name of the company responsible for the OpenGL implementation.
317
+ renderer : str
318
+ The name of the renderer.
319
+ version : str
320
+ The version of the OpenGL implementation.
321
+ majorVersion : int
322
+ The major version number of the OpenGL implementation.
323
+ minorVersion : int
324
+ The minor version number of the OpenGL implementation.
325
+ shaderVersion : str
326
+ The version of the GLSL implementation.
327
+ doubleBuffer : int
328
+ Indicates if the OpenGL implementation is double buffered.
329
+ maxTextureSize : int
330
+ The maximum texture size supported by the OpenGL implementation.
331
+ stereo : int
332
+ Indicates if the OpenGL implementation supports stereo rendering.
333
+ maxSamples : int
334
+ The maximum number of samples supported by the OpenGL implementation.
335
+ extensions : list
336
+ A list of supported OpenGL extensions.
337
+
338
+ """
339
+ __slots__ = [
340
+ 'vendor', 'renderer', 'version', 'shaderVersion', 'doubleBuffer',
341
+ 'maxTextureSize', 'maxTextureUnits', 'stereo', 'maxSamples',
342
+ 'extensions']
343
+
344
+ # singleton
345
+ _instance = None
346
+
347
+ def __init__(self):
348
+ self.vendor = getString(GL.GL_VENDOR)
349
+ self.renderer = getString(GL.GL_RENDERER)
350
+ self.version = getString(GL.GL_VERSION)
351
+ self.shaderVersion = getString(GL.GL_SHADING_LANGUAGE_VERSION)
352
+ # self.majorVersion = getIntegerv(GL.GL_MAJOR_VERSION)
353
+ # self.minorVersion = getIntegerv(GL.GL_MINOR_VERSION)
354
+ self.doubleBuffer = getIntegerv(GL.GL_DOUBLEBUFFER)
355
+ self.maxTextureSize = getIntegerv(GL.GL_MAX_TEXTURE_SIZE)
356
+ self.maxTextureUnits = getIntegerv(GL.GL_MAX_TEXTURE_IMAGE_UNITS)
357
+ self.stereo = getIntegerv(GL.GL_STEREO)
358
+ self.maxSamples = getIntegerv(GL.GL_MAX_SAMPLES)
359
+ self.extensions = \
360
+ [i for i in getString(GL.GL_EXTENSIONS).split(' ') if i not in ('', ' ')]
361
+
362
+ def __new__(cls):
363
+ if cls._instance is None:
364
+ cls._instance = super(OpenGLInfo, cls).__new__(cls)
365
+
366
+ return cls._instance
367
+
368
+ def hasExtension(self, extName):
369
+ """Check if the OpenGL implementation supports an extension.
370
+
371
+ Parameters
372
+ ----------
373
+ extName : str
374
+ The name of the extension to check for.
375
+
376
+ Returns
377
+ -------
378
+ bool
379
+ True if the extension is supported, False otherwise.
380
+
381
+ """
382
+ return extName in self.extensions
383
+
384
+
385
+ def getOpenGLInfo():
386
+ """Get general information about the OpenGL implementation on this machine.
387
+ This should provide a consistent means of doing so regardless of the OpenGL
388
+ interface we are using.
389
+
390
+ Returns are dictionary with the following fields::
391
+
392
+ vendor, renderer, version, majorVersion, minorVersion, doubleBuffer,
393
+ maxTextureSize, stereo, maxSamples, extensions
394
+
395
+ Supported extensions are returned as a list in the 'extensions' field. You
396
+ can check if a platform supports an extension by checking the membership of
397
+ the extension name in that list.
398
+
399
+ Returns
400
+ -------
401
+ OpenGLInfo
402
+
403
+ """
404
+ return OpenGLInfo()
405
+
406
+
407
+ # OpenGL limits for this system
408
+ MAX_TEXTURE_UNITS = getOpenGLInfo().maxTextureUnits
409
+
410
+
136
411
  # -------------------------------
137
412
  # Shader Program Helper Functions
138
413
  # -------------------------------
@@ -302,7 +577,7 @@ def compileShader(shaderSrc, shaderType):
302
577
  shaderId, GL.GL_COMPILE_STATUS, ctypes.byref(result))
303
578
 
304
579
  if result.value == GL.GL_FALSE: # failed to compile for whatever reason
305
- sys.stderr.write(getInfoLog(shaderId) + '\n')
580
+ print(getInfoLog(shaderId) + '\n')
306
581
  deleteObject(shaderId)
307
582
  raise RuntimeError("Shader compilation failed, check log output.")
308
583
 
@@ -477,6 +752,32 @@ def embedShaderSourceDefs(shaderSrc, defs):
477
752
  return srcOut
478
753
 
479
754
 
755
+ def deleteShader(shader):
756
+ """Delete a shader object.
757
+
758
+ Parameters
759
+ ----------
760
+ shader : int
761
+ Shader object handle to delete. Must have originated from a
762
+ :func:`compileShader` or `glCreateShader` call.
763
+
764
+ """
765
+ GL.glDeleteShader(shader)
766
+
767
+
768
+ def deleteProgram(program):
769
+ """Delete a shader program object.
770
+
771
+ Parameters
772
+ ----------
773
+ program : int
774
+ Program object handle to delete. Must have originated from a
775
+ :func:`createProgram` or `glCreateProgram` call.
776
+
777
+ """
778
+ GL.glDeleteProgram(program)
779
+
780
+
480
781
  def deleteObject(obj):
481
782
  """Delete a shader or program object.
482
783
 
@@ -828,6 +1129,322 @@ def getInfoLog(obj):
828
1129
  return logBuffer.value.decode('UTF-8')
829
1130
 
830
1131
 
1132
+ def getUniformLocation(program, name, error=True):
1133
+ """Get the location of a uniform variable in a shader program.
1134
+
1135
+ Parameters
1136
+ ----------
1137
+ program : int
1138
+ Handle of program to retrieve uniform location. Must have originated
1139
+ from a :func:`createProgram`, :func:`createProgramObjectARB`,
1140
+ `glCreateProgram` or `glCreateProgramObjectARB` call.
1141
+ name : str
1142
+ Name of the uniform variable to retrieve the location of.
1143
+ error : bool, optional
1144
+ Raise an error if the uniform is not found. Default is `True`.
1145
+
1146
+ Returns
1147
+ -------
1148
+ int
1149
+ Location of the uniform variable in the program. If the uniform is not
1150
+ found, `-1` is returned.
1151
+
1152
+ """
1153
+ if not GL.glIsProgram(program):
1154
+ raise ValueError(
1155
+ "Specified value of `program` is not a program object handle.")
1156
+
1157
+ if type(name) is not bytes:
1158
+ name = bytes(name, 'utf-8')
1159
+
1160
+ loc = GL.glGetUniformLocation(program, name)
1161
+
1162
+ if error:
1163
+ if loc == -1:
1164
+ raise ValueError(
1165
+ "Uniform not found in program, it may not be defined or has "
1166
+ "been optimized out by the GLSL compiler.")
1167
+ raise ValueError("Uniform '{}' not found in program.".format(name))
1168
+
1169
+ return loc
1170
+
1171
+
1172
+ # functions for setting uniform values
1173
+ _unifValueFuncs = {
1174
+ 'float': {
1175
+ 1: GL.glUniform1f,
1176
+ 2: GL.glUniform2f,
1177
+ 3: GL.glUniform3f,
1178
+ 4: GL.glUniform4f
1179
+ },
1180
+ 'int': {
1181
+ 1: GL.glUniform1i,
1182
+ 2: GL.glUniform2i,
1183
+ 3: GL.glUniform3i,
1184
+ 4: GL.glUniform4i
1185
+ },
1186
+ 'uint': {
1187
+ 1: GL.glUniform1ui,
1188
+ 2: GL.glUniform2ui,
1189
+ 3: GL.glUniform3ui,
1190
+ 4: GL.glUniform4ui
1191
+ }
1192
+ }
1193
+
1194
+
1195
+ def setUniformValue(program, loc, value, unifType='float',
1196
+ ignoreNotDefined=False):
1197
+ """Set a uniform variable value in a shader program.
1198
+
1199
+ This function sets the value of a uniform variable in a shader program to
1200
+ the specified value. The type of the value must match the type of the
1201
+ uniform variable in the shader program. The location of the uniform variable
1202
+ can be specified as an integer or string name. If the uniform variable is
1203
+ not found in the program, a `ValueError` is raised.
1204
+
1205
+ Parameters
1206
+ ----------
1207
+ program : int
1208
+ Handle of program to set the uniform value. Must have originated from a
1209
+ :func:`createProgram`, :func:`createProgramObjectARB`, `glCreateProgram`
1210
+ or `glCreateProgramObjectARB` call.
1211
+ loc : str or int
1212
+ Location of the uniform variable in the program obtained from a
1213
+ :func:`getUniformLocation` call. You may also specify the name of the
1214
+ uniform variable as a string to look-up the location before setting.
1215
+ value : int, float, list, tuple, numpy.ndarray
1216
+ Value to set the uniform to. The type of the value must match the type
1217
+ of the uniform variable in the shader program.
1218
+ unifType : str, optional
1219
+ Type of the uniform value elements. This is used to determine the
1220
+ appropritate setter function. Must be one of 'float', 'int' or 'uint'.
1221
+ ignoreNotDefined : bool, optional
1222
+ Do not raise an error if the uniform is not found in the program.
1223
+ Default is `False`.
1224
+
1225
+ Notes
1226
+ -----
1227
+ * If you get a "Invalid operation. The specified operation is not allowed in
1228
+ the current state" error, it is likely that the shader program is not
1229
+ currently in use or the data is not the correct type or length. Make sure
1230
+ the `unifType` matches the type of the uniform variable in the shader and
1231
+ the length of the data matches the dimensions of the uniform variable.
1232
+
1233
+ Examples
1234
+ --------
1235
+ Set a single float uniform value::
1236
+
1237
+ # define uniform in shader as `uniform float myFloat;`
1238
+ setUniformValue(myProgram, 'myFloat', 0.5)
1239
+
1240
+ Set a 3-element float vector uniform value::
1241
+
1242
+ # define uniform in shader as `uniform vec3 myVec3;`
1243
+ setUniformValue(myProgram, 'myVec3', [0.5, 0.2, 0.8])
1244
+
1245
+ Set an integer uniform value (eg. texture unit index)::
1246
+
1247
+ # define uniform in shader as `uniform sampler2d colorTexture;`
1248
+ setUniformValue(myProgram, 'colorTexture', 0, unifType='int')
1249
+
1250
+ """
1251
+ if not GL.glIsProgram(program):
1252
+ raise ValueError(
1253
+ "Specified value of `program` is not a program object handle.")
1254
+
1255
+ if isinstance(loc, bytes):
1256
+ loc = GL.glGetUniformLocation(program, loc)
1257
+ elif isinstance(loc, str):
1258
+ loc = GL.glGetUniformLocation(program, bytes(loc, 'utf-8'))
1259
+ else:
1260
+ if not isinstance(loc, int):
1261
+ raise ValueError("Invalid type for uniform location.")
1262
+
1263
+ if loc == -1:
1264
+ if ignoreNotDefined:
1265
+ return # ignore if not found
1266
+ raise ValueError("Uniform '{}' not found in program.".format(loc))
1267
+
1268
+ # handle scalar values
1269
+ if isinstance(value, (float, int)):
1270
+ if unifType == 'float':
1271
+ GL.glUniform1f(loc, float(value))
1272
+ elif unifType == 'int':
1273
+ GL.glUniform1i(loc, int(value))
1274
+ elif unifType == 'uint':
1275
+ if value < 0:
1276
+ raise ValueError(
1277
+ "Invalid unsigned integer value, must be >= 0.")
1278
+ GL.glUniform1ui(loc, int(value))
1279
+ else:
1280
+ raise ValueError("Invalid uniform value type.")
1281
+ return
1282
+
1283
+ # passed as list, tuple or numpy array
1284
+ unifLen = len(value)
1285
+ if unifLen > 4:
1286
+ raise ValueError("Invalid uniform data length, must be 1-4 elements.")
1287
+
1288
+ unifSetFunc = _unifValueFuncs[unifType].get(unifLen, None)
1289
+ if unifSetFunc is None:
1290
+ raise ValueError("Invalid uniform data length.")
1291
+
1292
+ unifSetFunc(loc, *value)
1293
+
1294
+
1295
+ def setUniformSampler2D(program, loc, unit, ignoreNotDefined=False):
1296
+ """Set a 2D texture sampler uniform value in a shader program.
1297
+
1298
+ Parameters
1299
+ ----------
1300
+ program : int
1301
+ Handle of program to set the uniform value. Must have originated from a
1302
+ :func:`createProgram`, :func:`createProgramObjectARB`, `glCreateProgram`
1303
+ or `glCreateProgramObjectARB` call.
1304
+ loc : str or int
1305
+ Location of the uniform variable in the program obtained from a
1306
+ :func:`getUniformLocation` call. You may also specify the name of the
1307
+ uniform variable as a string to look-up the location before setting.
1308
+ unit : int
1309
+ Texture unit index to bind to the sampler uniform.
1310
+ ignoreNotDefined : bool, optional
1311
+ Do not raise an error if the uniform is not found in the program.
1312
+ Default is `False`.
1313
+
1314
+ Examples
1315
+ --------
1316
+ Set a 2D texture sampler uniform value::
1317
+
1318
+ # define uniform in shader as `uniform sampler2D colorTexture;`
1319
+ setUniformSampler2D(myProgram, 'colorTexture', 0)
1320
+
1321
+ Use the enum value for the texture unit::
1322
+
1323
+ setUniformSampler2D(myProgram, 'colorTexture', GL.GL_TEXTURE0)
1324
+ # or ...
1325
+ setUniformSampler2D(myProgram, 'colorTexture', 'GL_TEXTURE0')
1326
+
1327
+ """
1328
+ if not GL.glIsProgram(program):
1329
+ raise ValueError(
1330
+ "Specified value of `program` is not a program object handle.")
1331
+
1332
+ if isinstance(loc, bytes):
1333
+ loc = GL.glGetUniformLocation(program, loc)
1334
+ elif isinstance(loc, str):
1335
+ loc = GL.glGetUniformLocation(program, bytes(loc, 'utf-8'))
1336
+ else:
1337
+ if not isinstance(loc, int):
1338
+ raise ValueError("Invalid type for uniform location.")
1339
+
1340
+ if loc == -1:
1341
+ if ignoreNotDefined:
1342
+ return # ignore if not found
1343
+ raise ValueError("Uniform '{}' not found in program.".format(loc))
1344
+
1345
+ if isinstance(unit, str):
1346
+ unit = getattr(GL, unit)
1347
+
1348
+ if unit >= GL.GL_TEXTURE0: # got enum value
1349
+ unit = unit - GL.GL_TEXTURE0
1350
+
1351
+ GL.glUniform1i(loc, unit)
1352
+
1353
+
1354
+ # lookup table for matrix uniform setter functions, the keys are hashable
1355
+ # tuples of the matrix dimensions
1356
+ _unifMatrixFuncs = {
1357
+ (2, 2): GL.glUniformMatrix2fv,
1358
+ (3, 3): GL.glUniformMatrix3fv,
1359
+ (4, 4): GL.glUniformMatrix4fv,
1360
+ (2, 3): GL.glUniformMatrix2x3fv,
1361
+ (3, 2): GL.glUniformMatrix3x2fv,
1362
+ (2, 4): GL.glUniformMatrix2x4fv,
1363
+ (4, 2): GL.glUniformMatrix4x2fv,
1364
+ (3, 4): GL.glUniformMatrix3x4fv,
1365
+ (4, 3): GL.glUniformMatrix4x3fv
1366
+ }
1367
+
1368
+
1369
+ def setUniformMatrix(program, loc, value, transpose=False,
1370
+ ignoreNotDefined=False):
1371
+ """Set the value of a matrix uniform variable in a shader program.
1372
+
1373
+ Arrays are converted to contiguous arrays with 'float32' data type before
1374
+ setting.
1375
+
1376
+ Parameters
1377
+ ----------
1378
+ program : int
1379
+ Handle of program to set the uniform value. Must have originated from a
1380
+ :func:`createProgram`, :func:`createProgramObjectARB`, `glCreateProgram`
1381
+ or `glCreateProgramObjectARB` call.
1382
+ loc : str or int
1383
+ Location of the uniform variable in the program obtained from a
1384
+ :func:`getUniformLocation` call. You may also specify the name of the
1385
+ uniform variable as a string to look-up the location before setting.
1386
+ value : numpy.ndarray
1387
+ Matrix value to set the uniform to. The shape of the matrix must match
1388
+ the dimensions of the uniform variable in the shader program. The data
1389
+ type of the matrix must be `float32` or else it will be cast to
1390
+ `float32`.
1391
+ transpose : bool, optional
1392
+ Transpose the matrix before setting. Default is `False`.
1393
+ ignoreNotDefined : bool, optional
1394
+ Do not raise an error if the uniform is not found in the program.
1395
+ Default is `False`.
1396
+
1397
+ Notes
1398
+ -----
1399
+ * If you get a "Invalid operation. The specified operation is not allowed in
1400
+ the current state" error, it is likely that the shader program is not
1401
+ currently in use or the data is not the correct type or length. Make sure
1402
+ the `unifType` matches the type of the uniform variable in the shader and
1403
+ the length of the data matches the dimensions of the uniform variable.
1404
+
1405
+ Examples
1406
+ --------
1407
+ Set a 4x4 matrix uniform value::
1408
+
1409
+ # define uniform in shader as `uniform mat4 projectionMatrix;`
1410
+ setUniformMatrix(myProgram, 'projectionMatrix', np.eye(4))
1411
+
1412
+ """
1413
+ if not GL.glIsProgram(program):
1414
+ raise ValueError(
1415
+ "Specified value of `program` is not a program object handle.")
1416
+
1417
+ locInput = loc # for error message
1418
+ if isinstance(loc, bytes):
1419
+ loc = GL.glGetUniformLocation(program, loc)
1420
+ elif isinstance(loc, str):
1421
+ loc = GL.glGetUniformLocation(program, bytes(loc, 'utf-8'))
1422
+ else:
1423
+ if not isinstance(loc, int):
1424
+ raise ValueError("Invalid type for uniform location.")
1425
+
1426
+ if loc == -1:
1427
+ if ignoreNotDefined:
1428
+ return # ignore if not found
1429
+ raise ValueError((
1430
+ "Uniform '{}' not found in program. It is either not defined "
1431
+ "or it is unused.").format(locInput))
1432
+
1433
+ # convert to contiguous array
1434
+ value = np.ascontiguousarray(value, dtype=np.float32)
1435
+
1436
+ # handle scalar values
1437
+ matrixFunc = _unifMatrixFuncs.get(value.shape, None)
1438
+ if matrixFunc is None:
1439
+ raise ValueError("Invalid matrix dimensions")
1440
+
1441
+ transpose = GL.GL_TRUE if transpose else GL.GL_FALSE
1442
+
1443
+ # recast as pointer
1444
+ matrixFunc(loc, 1, transpose, value.ctypes.data_as(
1445
+ ctypes.POINTER(GL.GLfloat)))
1446
+
1447
+
831
1448
  def getUniformLocations(program, builtins=False):
832
1449
  """Get uniform names and locations from a given shader program object.
833
1450
 
@@ -1139,28 +1756,92 @@ Framebuffer = namedtuple(
1139
1756
  )
1140
1757
 
1141
1758
 
1142
- def createFBO(attachments=()):
1143
- """Create a Framebuffer Object.
1759
+ class FramebufferInfo:
1760
+ """Framebuffer object (VBO) descriptor.
1761
+
1762
+ This class only stores information about the FBO it refers to, it does not
1763
+ contain any actual array data associated with the FBO. Calling
1764
+ :func:`createFBO` returns instances of this class.
1765
+
1766
+ It is recommended to use `gltools` functions :func:`bindFBO`,
1767
+ :func:`unbindFBO` and :func:`deleteFBO` to manage FBOs.
1144
1768
 
1145
1769
  Parameters
1146
1770
  ----------
1147
- attachments : :obj:`list` or :obj:`tuple` of :obj:`tuple`
1148
- Optional attachments to initialize the Framebuffer with. Attachments are
1149
- specified as a list of tuples. Each tuple must contain an attachment
1150
- point (e.g. GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, etc.) and a
1151
- buffer descriptor type (Renderbuffer or TexImage2D). If using a combined
1152
- depth/stencil format such as GL_DEPTH24_STENCIL8, GL_DEPTH_ATTACHMENT
1153
- and GL_STENCIL_ATTACHMENT must be passed the same buffer. Alternatively,
1154
- one can use GL_DEPTH_STENCIL_ATTACHMENT instead. If using multisample
1155
- buffers, all attachment images must use the same number of samples!. As
1156
- an example, one may specify attachments as 'attachments=((
1157
- GL.GL_COLOR_ATTACHMENT0, frameTexture), (GL.GL_DEPTH_STENCIL_ATTACHMENT,
1158
- depthRenderBuffer))'.
1159
-
1160
- Returns
1161
- -------
1162
- Framebuffer
1163
- Framebuffer descriptor.
1771
+ name : int
1772
+ Handle of the FBO.
1773
+ target : int
1774
+ Target of the FBO.
1775
+ attachments : dict, optional
1776
+ Dictionary of attachments associated with the FBO. The keys are
1777
+ attachment points (e.g. GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, etc.)
1778
+ and the values are buffer descriptors (`RenderbufferInfo` or
1779
+ `TexImage2DInfo`).
1780
+ sizeHint : tuple, optional
1781
+ Size hint for the FBO. This is used to specify the dimensions of logical
1782
+ buffers attached to the FBO. The size hint is a tuple of two integers
1783
+ (width, height).
1784
+ userData : dict, optional
1785
+ User-defined data associated with the FBO.
1786
+
1787
+ """
1788
+ __slots__ = [
1789
+ 'name', 'target', '_attachments', 'sizeHint', 'userData', '_lastBound']
1790
+
1791
+ def __init__(self, name, target=GL.GL_FRAMEBUFFER, attachments=None,
1792
+ sizeHint=None, userData=None):
1793
+ self.name = name
1794
+ self.target = target
1795
+ self._attachments = {} if attachments is None else attachments
1796
+ self.sizeHint = sizeHint
1797
+ self.userData = userData
1798
+
1799
+ self._lastBound = GL.GLint()
1800
+
1801
+ def __enter__(self):
1802
+ GL.glGetIntegerv(GL.GL_FRAMEBUFFER_BINDING, ctypes.byref(self.lastBound))
1803
+ GL.glBindFramebuffer(self.fbo.target, self.fbo.id)
1804
+
1805
+ def __exit__(self, exc_type, exc_val, exc_tb):
1806
+ GL.glBindFramebuffer(self.fbo.target, self.lastBound.value)
1807
+
1808
+ @property
1809
+ def attachments(self):
1810
+ """Image buffer attachments associated with the FBO (`dict`).
1811
+
1812
+ Do not modify this dictionary directly. Use :func:`attachImage` to
1813
+ attach images to the FBO.
1814
+
1815
+ """
1816
+ return self._attachments
1817
+
1818
+
1819
+ def createFBO(attachments=(), sizeHint=None):
1820
+ """Create a Framebuffer Object.
1821
+
1822
+ Parameters
1823
+ ----------
1824
+ attachments : :obj:`list` or :obj:`tuple` of :obj:`tuple`
1825
+ Optional attachments to initialize the Framebuffer with. Attachments are
1826
+ specified as a list of tuples. Each tuple must contain an attachment
1827
+ point (e.g. GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, etc.) and a
1828
+ buffer descriptor type (RenderbufferInfo or TexImage2DInfo). If using a
1829
+ combined depth/stencil format such as GL_DEPTH24_STENCIL8,
1830
+ GL_DEPTH_ATTACHMENT and GL_STENCIL_ATTACHMENT must be passed the same
1831
+ buffer. Alternatively, one can use GL_DEPTH_STENCIL_ATTACHMENT instead.
1832
+ If using multisample buffers, all attachment images must use the same
1833
+ number of samples!. As an example, one may specify attachments as
1834
+ 'attachments=((GL.GL_COLOR_ATTACHMENT0, frameTexture),
1835
+ (GL.GL_DEPTH_STENCIL_ATTACHMENT, depthRenderBuffer))'.
1836
+ sizeHint : :obj:`tuple`, optional
1837
+ Size hint for the FBO. This is used to specify the dimensions of logical
1838
+ buffers attached to the FBO. The size hint is a tuple of two integers
1839
+ (width, height).
1840
+
1841
+ Returns
1842
+ -------
1843
+ FramebufferInfo
1844
+ Framebuffer descriptor.
1164
1845
 
1165
1846
  Notes
1166
1847
  -----
@@ -1177,22 +1858,18 @@ def createFBO(attachments=()):
1177
1858
 
1178
1859
  Create a render target with multiple color texture attachments::
1179
1860
 
1180
- colorTex = createTexImage2D(1024,1024) # empty texture
1181
- depthRb = createRenderbuffer(800,600,internalFormat=GL.GL_DEPTH24_STENCIL8)
1182
-
1183
- # attach images
1184
- GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, fbo.id)
1185
- attach(GL.GL_COLOR_ATTACHMENT0, colorTex)
1186
- attach(GL.GL_DEPTH_ATTACHMENT, depthRb)
1187
- attach(GL.GL_STENCIL_ATTACHMENT, depthRb)
1188
- # or attach(GL.GL_DEPTH_STENCIL_ATTACHMENT, depthRb)
1189
- GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0)
1190
-
1191
- # above is the same as
1192
- with useFBO(fbo):
1193
- attach(GL.GL_COLOR_ATTACHMENT0, colorTex)
1194
- attach(GL.GL_DEPTH_ATTACHMENT, depthRb)
1195
- attach(GL.GL_STENCIL_ATTACHMENT, depthRb)
1861
+ fbo = gt.createFBO(sizeHint=(512, 512))
1862
+ gt.bindFBO(fbo)
1863
+ gt.attachImage( # color
1864
+ fbo, GL.GL_COLOR_ATTACHMENT0, gt.createTexImage2D(512, 512))
1865
+ gt.attachImage( # normal map
1866
+ fbo, GL.GL_COLOR_ATTACHMENT1, gt.createTexImage2D(512, 512))
1867
+ gt.attachImage( # depth/stencil
1868
+ fbo,
1869
+ GL.GL_DEPTH_STENCIL_ATTACHMENT,
1870
+ gt.createRenderbuffer(512, 512, GL.GL_DEPTH24_STENCIL8))
1871
+ print(gt.isFramebufferComplete(fbo)) # True
1872
+ gt.unbindFBO(None)
1196
1873
 
1197
1874
  Examples of userData some custom function might access::
1198
1875
 
@@ -1213,26 +1890,34 @@ def createFBO(attachments=()):
1213
1890
  GL.glGenFramebuffers(1, ctypes.byref(fboId))
1214
1891
 
1215
1892
  # create a framebuffer descriptor
1216
- fboDesc = Framebuffer(fboId, GL.GL_FRAMEBUFFER, dict())
1893
+ fboDesc = FramebufferInfo(
1894
+ fboId,
1895
+ GL.GL_FRAMEBUFFER,
1896
+ attachments=None, # set later
1897
+ sizeHint=sizeHint,
1898
+ userData=dict())
1217
1899
 
1218
1900
  # initial attachments for this framebuffer
1219
1901
  if attachments:
1220
- with useFBO(fboDesc):
1221
- for attachPoint, imageBuffer in attachments:
1222
- attach(attachPoint, imageBuffer)
1902
+ bindFBO(fboDesc)
1903
+ for attachPoint, imageBuffer in attachments:
1904
+ attachImage(fboDesc, attachPoint, imageBuffer)
1905
+ unbindFBO()
1223
1906
 
1224
1907
  return fboDesc
1225
1908
 
1226
1909
 
1227
- def attach(attachPoint, imageBuffer):
1910
+ def attachImage(fbo, attachPoint, imageBuffer):
1228
1911
  """Attach an image to a specified attachment point on the presently bound
1229
1912
  FBO.
1230
1913
 
1231
1914
  Parameters
1232
1915
  ----------
1916
+ fbo : :obj:`FramebufferInfo`
1917
+ Framebuffer descriptor to attach buffer to.
1233
1918
  attachPoint :obj:`int`
1234
1919
  Attachment point for 'imageBuffer' (e.g. GL.GL_COLOR_ATTACHMENT0).
1235
- imageBuffer : :obj:`TexImage2D` or :obj:`Renderbuffer`
1920
+ imageBuffer : :obj:`TexImage2D` or :obj:`RenderbufferInfo`
1236
1921
  Framebuffer-attachable buffer descriptor.
1237
1922
 
1238
1923
  Examples
@@ -1240,55 +1925,131 @@ def attach(attachPoint, imageBuffer):
1240
1925
  Attach an image to attachment points on the framebuffer::
1241
1926
 
1242
1927
  GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, fbo)
1243
- attach(GL.GL_COLOR_ATTACHMENT0, colorTex)
1244
- attach(GL.GL_DEPTH_STENCIL_ATTACHMENT, depthRb)
1928
+ attachImage(GL.GL_COLOR_ATTACHMENT0, colorTex)
1929
+ attachImage(GL.GL_DEPTH_STENCIL_ATTACHMENT, depthRb)
1245
1930
  GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, lastBoundFbo)
1246
1931
 
1247
1932
  # same as above, but using a context manager
1248
1933
  with useFBO(fbo):
1249
- attach(GL.GL_COLOR_ATTACHMENT0, colorTex)
1250
- attach(GL.GL_DEPTH_STENCIL_ATTACHMENT, depthRb)
1934
+ attachImage(GL.GL_COLOR_ATTACHMENT0, colorTex)
1935
+ attachImage(GL.GL_DEPTH_STENCIL_ATTACHMENT, depthRb)
1251
1936
 
1252
1937
  """
1938
+ if not isinstance(fbo, FramebufferInfo):
1939
+ raise ValueError("Invalid type for `fbo`, must be `FramebufferInfo`.")
1940
+
1941
+ if isinstance(attachPoint, str):
1942
+ attachPoint = getattr(GL, attachPoint)
1943
+
1944
+ # get the last framebuffer bound
1945
+ lastBound = GL.GLint()
1946
+ GL.glGetIntegerv(GL.GL_FRAMEBUFFER_BINDING, ctypes.byref(lastBound))
1947
+
1948
+ # bind the framebuffer
1949
+ changeBinding = fbo.name != lastBound.value
1950
+ if changeBinding:
1951
+ bindFBO(fbo)
1952
+
1953
+ if fbo.sizeHint is not None:
1954
+ if (fbo.sizeHint[0] != imageBuffer.width or
1955
+ fbo.sizeHint[1] != imageBuffer.height):
1956
+ raise ValueError(
1957
+ "Imagebuffer dimensions do not match FBO size hint. Expected "
1958
+ "({}, {}), got ({}, {}).".format(
1959
+ fbo.sizeHint[0], fbo.sizeHint[1],
1960
+ imageBuffer.width, imageBuffer.height))
1961
+
1253
1962
  # We should also support binding GL names specified as integers. Right now
1254
1963
  # you need as descriptor which contains the target and name for the buffer.
1255
1964
  #
1256
- if isinstance(imageBuffer, (TexImage2D, TexImage2DMultisample)):
1965
+ if isinstance(imageBuffer, (TexImage2DInfo, TexImage2DMultisampleInfo)):
1257
1966
  GL.glFramebufferTexture2D(
1258
- GL.GL_FRAMEBUFFER,
1967
+ fbo.target,
1259
1968
  attachPoint,
1260
1969
  imageBuffer.target,
1261
- imageBuffer.id, 0)
1262
- elif isinstance(imageBuffer, Renderbuffer):
1970
+ imageBuffer.name, 0)
1971
+ elif isinstance(imageBuffer, RenderbufferInfo):
1263
1972
  GL.glFramebufferRenderbuffer(
1264
- GL.GL_FRAMEBUFFER,
1973
+ fbo.target,
1265
1974
  attachPoint,
1266
1975
  imageBuffer.target,
1267
- imageBuffer.id)
1976
+ imageBuffer.name)
1977
+
1978
+ fbo.attachments[attachPoint] = imageBuffer
1979
+
1980
+ # restore the last framebuffer
1981
+ if changeBinding:
1982
+ GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, lastBound.value)
1983
+
1984
+
1985
+ # legacy
1986
+ attach = attachImage
1268
1987
 
1269
1988
 
1270
- def isComplete():
1989
+ def isFramebufferComplete(fbo):
1271
1990
  """Check if the currently bound framebuffer is complete.
1272
1991
 
1992
+ Parameters
1993
+ ----------
1994
+ fbo : :obj:`FramebufferInfo`
1995
+ Framebuffer descriptor to check for completeness.
1996
+
1273
1997
  Returns
1274
1998
  -------
1275
1999
  bool
1276
2000
  `True` if the presently bound FBO is complete.
1277
2001
 
1278
2002
  """
1279
- return GL.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER) == \
2003
+ # get the last framebuffer bound
2004
+ lastBound = GL.GLint()
2005
+ GL.glGetIntegerv(GL.GL_FRAMEBUFFER_BINDING, ctypes.byref(lastBound))
2006
+
2007
+ # bind the framebuffer
2008
+ changeBinding = fbo.name != lastBound.value
2009
+ if changeBinding:
2010
+ bindFBO(fbo)
2011
+
2012
+ status = GL.glCheckFramebufferStatus(fbo.target) == \
1280
2013
  GL.GL_FRAMEBUFFER_COMPLETE
2014
+
2015
+ # restore the last framebuffer
2016
+ if changeBinding:
2017
+ GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, lastBound.value)
2018
+
2019
+ return status
1281
2020
 
1282
2021
 
1283
- def deleteFBO(fbo):
2022
+ def deleteFBO(fbo, deleteAttachments=True):
1284
2023
  """Delete a framebuffer.
1285
2024
 
2025
+ Parameters
2026
+ ----------
2027
+ fbo : :obj:`FramebufferInfo` or :obj:`int`
2028
+ Framebuffer descriptor or name to delete.
2029
+ deleteAttachments : bool, optional
2030
+ Delete attachments associated with the framebuffer. Default is `True`.
2031
+
1286
2032
  """
1287
- GL.glDeleteFramebuffers(
1288
- 1, fbo.id if isinstance(fbo, Framebuffer) else int(fbo))
2033
+ if not isinstance(fbo, FramebufferInfo):
2034
+ raise ValueError("Invalid type for `fbo`, must be `FramebufferInfo`.")
2035
+
2036
+ if deleteAttachments:
2037
+ for attachment in fbo.attachments.values():
2038
+ if isinstance(attachment, RenderbufferInfo):
2039
+ deleteRenderbuffer(attachment)
2040
+ elif isinstance(attachment,
2041
+ (TexImage2DInfo, TexImage2DMultisampleInfo)):
2042
+ deleteTexture(attachment)
2043
+
2044
+ GL.glDeleteFramebuffers(1, fbo.name)
1289
2045
 
2046
+ # invalidate the descriptor
2047
+ fbo.name = 0
2048
+ fbo.target = 0
2049
+ fbo.attachments.clear()
1290
2050
 
1291
- def blitFBO(srcRect, dstRect=None, filter=GL.GL_LINEAR):
2051
+
2052
+ def blitFBO(srcRect, dstRect=None, filter=GL.GL_LINEAR, mask=GL.GL_COLOR_BUFFER_BIT):
1292
2053
  """Copy a block of pixels between framebuffers via blitting. Read and draw
1293
2054
  framebuffers must be bound prior to calling this function. Beware, the
1294
2055
  scissor box and viewport are changed when this is called to dstRect.
@@ -1302,43 +2063,181 @@ def blitFBO(srcRect, dstRect=None, filter=GL.GL_LINEAR):
1302
2063
  List specifying the top-left and bottom-right coordinates of the region
1303
2064
  to copy to (<X0>, <Y0>, <X1>, <Y1>). If None, srcRect is used for
1304
2065
  dstRect.
1305
- filter : :obj:`int`
2066
+ filter : :obj:`int` or :obj:`str`
1306
2067
  Interpolation method to use if the image is stretched, default is
1307
2068
  GL_LINEAR, but can also be GL_NEAREST.
1308
-
1309
- Returns
1310
- -------
1311
- None
2069
+ mask : :obj:`int` or :obj:`str`
2070
+ Bitmask specifying which buffers to copy. Default is GL_COLOR_BUFFER_BIT.
1312
2071
 
1313
2072
  Examples
1314
2073
  --------
1315
2074
  Blitting pixels from on FBO to another::
1316
2075
 
1317
- # bind framebuffer to read pixels from
1318
- GL.glBindFramebuffer(GL.GL_READ_FRAMEBUFFER, srcFbo)
1319
-
1320
- # bind framebuffer to draw pixels to
1321
- GL.glBindFramebuffer(GL.GL_DRAW_FRAMEBUFFER, dstFbo)
1322
-
1323
- gltools.blitFBO((0,0,800,600), (0,0,800,600))
2076
+ # set buffers for reading and drawing
2077
+ gt.setReadBuffer(fbo, 'GL_COLOR_ATTACHMENT0')
2078
+ gt.setDrawBuffer(None, 'GL_BACK') # default back buffer for window
2079
+ gt.blitFBO((0 ,0, 512, 512), (0, 0, 512, 512))
2080
+
2081
+ """
2082
+ if isinstance(mask, str):
2083
+ mask = getattr(GL, mask)
1324
2084
 
1325
- # unbind both read and draw buffers
1326
- GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0)
2085
+ if isinstance(filter, str):
2086
+ filter = getattr(GL, filter)
1327
2087
 
1328
- """
1329
2088
  # in most cases srcRect and dstRect will be the same.
1330
2089
  if dstRect is None:
1331
2090
  dstRect = srcRect
1332
2091
 
1333
- # GL.glViewport(*dstRect)
1334
- # GL.glEnable(GL.GL_SCISSOR_TEST)
1335
- # GL.glScissor(*dstRect)
1336
2092
  GL.glBlitFramebuffer(srcRect[0], srcRect[1], srcRect[2], srcRect[3],
1337
2093
  dstRect[0], dstRect[1], dstRect[2], dstRect[3],
1338
- GL.GL_COLOR_BUFFER_BIT, # colors only for now
2094
+ mask, # colors only for now
1339
2095
  filter)
2096
+
2097
+
2098
+ def setReadBuffer(fbo, mode):
2099
+ """Set the read buffer for the framebuffer.
1340
2100
 
1341
- # GL.glDisable(GL.GL_SCISSOR_TEST)
2101
+ Parameters
2102
+ ----------
2103
+ fbo : :obj:`FramebufferInfo` or `None`
2104
+ Framebuffer descriptor. If `None`, the framebuffer with target
2105
+ `GL_FRAMEBUFFER` is bound to `0` (default framebuffer).
2106
+ mode : :obj:`int`, :obj:`GLenum` or :obj:`str`
2107
+ Buffer mode to set as the read buffer. If `fbo` is not `None`, this
2108
+ value may be `GL_COLOR_ATTACHMENT(i)` where `i` is the color attachment
2109
+ index. If `fbo` is `None`, this value may be one of `GL_FRONT_LEFT`,
2110
+ `GL_FRONT_RIGHT`, `GL_BACK_LEFT`, `GL_BACK_RIGHT`, `GL_FRONT`,
2111
+ `GL_BACK`, `GL_LEFT`, `GL_RIGHT`.
2112
+
2113
+ Examples
2114
+ --------
2115
+ Set the read buffer for a framebuffer::
2116
+
2117
+ setReadBuffer(fbo, GL_COLOR_ATTACHMENT0)
2118
+
2119
+ """
2120
+ if isinstance(mode, str):
2121
+ mode = getattr(GL, mode)
2122
+
2123
+ if fbo is None:
2124
+ GL.glBindFramebuffer(GL.GL_READ_FRAMEBUFFER, 0)
2125
+ GL.glReadBuffer(mode)
2126
+ return
2127
+
2128
+ if not isinstance(fbo, FramebufferInfo):
2129
+ raise ValueError(
2130
+ "Invalid type for `fbo`, must be `FramebufferInfo`.")
2131
+
2132
+ GL.glBindFramebuffer(GL.GL_READ_FRAMEBUFFER, fbo.name)
2133
+ GL.glReadBuffer(mode)
2134
+
2135
+
2136
+ def setDrawBuffer(fbo, mode):
2137
+ """Set the draw buffer for the framebuffer.
2138
+
2139
+ Parameters
2140
+ ----------
2141
+ fbo : :obj:`FramebufferInfo` or `None`
2142
+ Framebuffer descriptor. If `None`, the framebuffer with target
2143
+ `GL_FRAMEBUFFER` is bound to `0` (default framebuffer).
2144
+ mode : :obj:`int`, :obj:`GLenum` or :obj:`str`
2145
+ Buffer mode to set as the draw buffer. If `fbo` is not `None`, this
2146
+ value may be `GL_COLOR_ATTACHMENT(i)` where `i` is the color attachment
2147
+ index. If `fbo` is `None`, this value may be one of `GL_FRONT_LEFT`,
2148
+ `GL_FRONT_RIGHT`, `GL_BACK_LEFT`, `GL_BACK_RIGHT`, `GL_FRONT`,
2149
+ `GL_BACK`, `GL_LEFT`, `GL_RIGHT`.
2150
+
2151
+ """
2152
+ if isinstance(mode, str):
2153
+ mode = getattr(GL, mode)
2154
+
2155
+ if fbo is None:
2156
+ GL.glBindFramebuffer(GL.GL_DRAW_FRAMEBUFFER, 0)
2157
+ GL.glDrawBuffer(mode)
2158
+ return
2159
+
2160
+ if not isinstance(fbo, FramebufferInfo):
2161
+ raise ValueError(
2162
+ "Invalid type for `fbo`, must be `FramebufferInfo`.")
2163
+
2164
+ GL.glBindFramebuffer(GL.GL_DRAW_FRAMEBUFFER, fbo.name)
2165
+ GL.glDrawBuffer(mode)
2166
+
2167
+
2168
+ def clearFramebuffer(color=(0.0, 0.0, 0.0, 1.0), depth=None, stencil=None):
2169
+ """Clear the presently bound draw buffer.
2170
+
2171
+ Parameters
2172
+ ----------
2173
+ color : :obj:`tuple`, optional
2174
+ Color to clear the framebuffer to. Default is (0.0, 0.0, 0.0, 1.0).
2175
+ depth : :obj:`float`, optional
2176
+ Depth value to clear the framebuffer to. Default is 1.0.
2177
+ stencil : :obj:`int`, optional
2178
+ Stencil value to clear the framebuffer to. Default is 0.
2179
+
2180
+ Examples
2181
+ --------
2182
+ Clear the framebuffer to green::
2183
+
2184
+ setDrawBuffer(fbo, GL_COLOR_ATTACHMENT0)
2185
+ clearFramebuffer(color=(0.0, 1.0, 0.0, 1.0))
2186
+
2187
+ Clear the window back buffer to black::
2188
+
2189
+ setDrawBuffer(None, GL_BACK)
2190
+ clearFramebuffer()
2191
+
2192
+ """
2193
+ clearFlags = 0
2194
+ if color is not None:
2195
+ GL.glClearColor(*color)
2196
+ clearFlags |= GL.GL_COLOR_BUFFER_BIT
2197
+ if depth is not None:
2198
+ GL.glClearDepth(depth)
2199
+ clearFlags |= GL.GL_DEPTH_BUFFER_BIT
2200
+ if stencil is not None:
2201
+ GL.glClearStencil(stencil)
2202
+ clearFlags |= GL.GL_STENCIL_BUFFER_BIT
2203
+
2204
+ GL.glClear(clearFlags)
2205
+
2206
+
2207
+ def setViewport(x, y, width, height):
2208
+ """Set the viewport for the current render target.
2209
+
2210
+ Parameters
2211
+ ----------
2212
+ x : :obj:`int`
2213
+ X-coordinate of the lower-left corner of the viewport.
2214
+ y : :obj:`int`
2215
+ Y-coordinate of the lower-left corner of the viewport.
2216
+ width : :obj:`int`
2217
+ Width of the viewport.
2218
+ height : :obj:`int`
2219
+ Height of the viewport.
2220
+
2221
+ """
2222
+ GL.glViewport(int(x), int(y), int(width), int(height))
2223
+
2224
+
2225
+ def setScissor(x, y, width, height):
2226
+ """Set the scissor box for the current render target.
2227
+
2228
+ Parameters
2229
+ ----------
2230
+ x : :obj:`int`
2231
+ X-coordinate of the lower-left corner of the scissor box.
2232
+ y : :obj:`int`
2233
+ Y-coordinate of the lower-left corner of the scissor box.
2234
+ width : :obj:`int`
2235
+ Width of the scissor box.
2236
+ height : :obj:`int`
2237
+ Height of the scissor box.
2238
+
2239
+ """
2240
+ GL.glScissor(int(x), int(y), int(width), int(height))
1342
2241
 
1343
2242
 
1344
2243
  @contextmanager
@@ -1380,7 +2279,7 @@ def useFBO(fbo):
1380
2279
  """
1381
2280
  prevFBO = GL.GLint()
1382
2281
  GL.glGetIntegerv(GL.GL_FRAMEBUFFER_BINDING, ctypes.byref(prevFBO))
1383
- toBind = fbo.id if isinstance(fbo, Framebuffer) else int(fbo)
2282
+ toBind = fbo.id if isinstance(fbo, FramebufferInfo) else int(fbo)
1384
2283
  GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, toBind)
1385
2284
  try:
1386
2285
  yield toBind
@@ -1388,6 +2287,80 @@ def useFBO(fbo):
1388
2287
  GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, prevFBO.value)
1389
2288
 
1390
2289
 
2290
+ def bindFBO(fbo, target=None):
2291
+ """Bind a Framebuffer Object (FBO).
2292
+
2293
+ Parameters
2294
+ ----------
2295
+ fbo :obj:`FramebufferInfo`
2296
+ OpenGL Framebuffer Object name/ID or descriptor.
2297
+ target :obj:`int`, :obj:`GLenum`, :obj:`str` or `None`
2298
+ Target to bind the framebuffer to. If `None`, the target of the
2299
+ framebuffer descriptor is used. Valid targets are `GL_FRAMEBUFFER`,
2300
+ `GL_DRAW_FRAMEBUFFER` and `GL_READ_FRAMEBUFFER`.
2301
+
2302
+ Examples
2303
+ --------
2304
+ Bind a framebuffer::
2305
+
2306
+ bindFBO(fbo)
2307
+
2308
+ """
2309
+ if target is None:
2310
+ target = fbo.target if isinstance(fbo, FramebufferInfo) else GL.GL_FRAMEBUFFER
2311
+
2312
+ if isinstance(target, str):
2313
+ target = getattr(GL, target)
2314
+
2315
+ if fbo is None:
2316
+ GL.glBindFramebuffer(target, 0)
2317
+ return
2318
+
2319
+ if not isinstance(fbo, FramebufferInfo):
2320
+ raise ValueError("Invalid type for `fbo`, must be `FramebufferInfo`.")
2321
+
2322
+ GL.glBindFramebuffer(fbo.target, fbo.name)
2323
+
2324
+
2325
+ def unbindFBO(fbo, target=None):
2326
+ """Unbind a Framebuffer Object (FBO).
2327
+
2328
+ While the last FBO does not have to be unbound explicitly, passing the
2329
+ descriptor of the FBO to this function will ensure the target is unbound.
2330
+
2331
+ Parameters
2332
+ ----------
2333
+ fbo :obj:`FramebufferInfo` or `None`
2334
+ OpenGL Framebuffer Object name/ID or descriptor. If `None`, the
2335
+ frambuffer with target `GL_FRAMEBUFFER` is bound to `0`. Values are
2336
+ `GL_FRAMEBUFFER`, `GL_DRAW_FRAMEBUFFER` and `GL_READ_FRAMEBUFFER`.
2337
+ target :obj:`int`, :obj:`GLenum`, :obj:`str` or `None`
2338
+ Target to unbind the framebuffer from. If `None`, the target of the
2339
+ framebuffer descriptor is used.
2340
+
2341
+ Examples
2342
+ --------
2343
+ Unbind a framebuffer::
2344
+
2345
+ unbindFBO(fbo)
2346
+
2347
+ """
2348
+ if target is None:
2349
+ target = fbo.target if isinstance(fbo, FramebufferInfo) else GL.GL_FRAMEBUFFER
2350
+
2351
+ if isinstance(target, str):
2352
+ target = getattr(GL, target)
2353
+
2354
+ if fbo is None:
2355
+ GL.glBindFramebuffer(target, 0)
2356
+ return
2357
+
2358
+ if not isinstance(fbo, FramebufferInfo):
2359
+ raise ValueError("Invalid type for `fbo`, must be `FramebufferInfo`.")
2360
+
2361
+ GL.glBindFramebuffer(fbo.target, 0)
2362
+
2363
+
1391
2364
  # ------------------------------
1392
2365
  # Renderbuffer Objects Functions
1393
2366
  # ------------------------------
@@ -1410,6 +2383,52 @@ Renderbuffer = namedtuple(
1410
2383
  )
1411
2384
 
1412
2385
 
2386
+ class RenderbufferInfo:
2387
+ """Renderbuffer object descriptor.
2388
+
2389
+ This class only stores information about the Renderbuffer it refers to, it
2390
+ does not contain any actual array data associated with the Renderbuffer.
2391
+ Calling :func:`createRenderbuffer` returns instances of this class.
2392
+
2393
+ It is recommended to use `gltools` functions :func:`bindRenderbuffer`,
2394
+ :func:`unbindRenderbuffer` and :func:`deleteRenderbuffer` to manage
2395
+ Renderbuffers.
2396
+
2397
+ Parameters
2398
+ ----------
2399
+ name : int
2400
+ Handle of the Renderbuffer.
2401
+ target : int
2402
+ Target of the Renderbuffer.
2403
+ width : int
2404
+ Width of the Renderbuffer.
2405
+ height : int
2406
+ Height of the Renderbuffer.
2407
+ internalFormat : int
2408
+ Internal format of the Renderbuffer.
2409
+ samples : int
2410
+ Number of samples for multi-sampling.
2411
+ multiSample : bool
2412
+ True if the Renderbuffer is multi-sampled.
2413
+ userData : dict, optional
2414
+ User-defined data associated with the Renderbuffer.
2415
+
2416
+ """
2417
+ __slots__ = ['name', 'target', 'width', 'height', 'internalFormat',
2418
+ 'samples', 'multiSample', 'userData']
2419
+
2420
+ def __init__(self, name, target, width, height, internalFormat, samples,
2421
+ multiSample, userData=None):
2422
+ self.name = name
2423
+ self.target = target
2424
+ self.width = width
2425
+ self.height = height
2426
+ self.internalFormat = internalFormat
2427
+ self.samples = samples
2428
+ self.multiSample = multiSample
2429
+ self.userData = userData
2430
+
2431
+
1413
2432
  def createRenderbuffer(width, height, internalFormat=GL.GL_RGBA8, samples=1):
1414
2433
  """Create a new Renderbuffer Object with a specified internal format. A
1415
2434
  multisample storage buffer is created if samples > 1.
@@ -1428,7 +2447,7 @@ def createRenderbuffer(width, height, internalFormat=GL.GL_RGBA8, samples=1):
1428
2447
  Format for renderbuffer data (e.g. GL_RGBA8, GL_DEPTH24_STENCIL8).
1429
2448
  samples : :obj:`int`
1430
2449
  Number of samples for multi-sampling, should be >1 and power-of-two.
1431
- Work with one sample, but will raise a warning.
2450
+ If samples == 1, a single sample buffer is created.
1432
2451
 
1433
2452
  Returns
1434
2453
  -------
@@ -1441,6 +2460,8 @@ def createRenderbuffer(width, height, internalFormat=GL.GL_RGBA8, samples=1):
1441
2460
  be used to store arbitrary data associated with the buffer.
1442
2461
 
1443
2462
  """
2463
+ internalFormat = _getGLEnum(internalFormat)
2464
+
1444
2465
  width = int(width)
1445
2466
  height = int(height)
1446
2467
 
@@ -1476,14 +2497,14 @@ def createRenderbuffer(width, height, internalFormat=GL.GL_RGBA8, samples=1):
1476
2497
  # done, unbind it
1477
2498
  GL.glBindRenderbuffer(GL.GL_RENDERBUFFER, 0)
1478
2499
 
1479
- return Renderbuffer(rbId,
1480
- GL.GL_RENDERBUFFER,
1481
- width,
1482
- height,
1483
- internalFormat,
1484
- samples,
1485
- samples > 1,
1486
- dict())
2500
+ return RenderbufferInfo(rbId,
2501
+ GL.GL_RENDERBUFFER,
2502
+ width,
2503
+ height,
2504
+ internalFormat,
2505
+ samples,
2506
+ samples > 1,
2507
+ dict())
1487
2508
 
1488
2509
 
1489
2510
  def deleteRenderbuffer(renderBuffer):
@@ -1491,7 +2512,10 @@ def deleteRenderbuffer(renderBuffer):
1491
2512
  renderbuffer's ID.
1492
2513
 
1493
2514
  """
1494
- GL.glDeleteRenderbuffers(1, renderBuffer.id)
2515
+ GL.glDeleteRenderbuffers(1, renderBuffer.name)
2516
+
2517
+ # invalidate the descriptor
2518
+ renderBuffer.name = 0
1495
2519
 
1496
2520
 
1497
2521
  # -----------------
@@ -1502,7 +2526,7 @@ def deleteRenderbuffer(renderBuffer):
1502
2526
  # use them with functions that require that type as input.
1503
2527
  #
1504
2528
 
1505
- class TexImage2D:
2529
+ class TexImage2DInfo:
1506
2530
  """Descriptor for a 2D texture.
1507
2531
 
1508
2532
  This class is used for bookkeeping 2D textures stored in video memory.
@@ -1656,8 +2680,8 @@ def createTexImage2D(width, height, target=GL.GL_TEXTURE_2D, level=0,
1656
2680
 
1657
2681
  Returns
1658
2682
  -------
1659
- TexImage2D
1660
- A `TexImage2D` descriptor.
2683
+ TexImage2DInfo
2684
+ A `TexImage2DInfo` descriptor.
1661
2685
 
1662
2686
  Notes
1663
2687
  -----
@@ -1699,6 +2723,9 @@ def createTexImage2D(width, height, target=GL.GL_TEXTURE_2D, level=0,
1699
2723
  GL.glBindTexture(GL.GL_TEXTURE_2D, textureDesc.id)
1700
2724
 
1701
2725
  """
2726
+ target, internalFormat, pixelFormat, dataType = _getGLEnum(
2727
+ target, internalFormat, pixelFormat, dataType)
2728
+
1702
2729
  width = int(width)
1703
2730
  height = int(height)
1704
2731
 
@@ -1715,11 +2742,12 @@ def createTexImage2D(width, height, target=GL.GL_TEXTURE_2D, level=0,
1715
2742
  texId = GL.GLuint()
1716
2743
  GL.glGenTextures(1, ctypes.byref(texId))
1717
2744
 
1718
- GL.glBindTexture(target, texId)
2745
+ GL.glBindTexture(target, texId.value)
1719
2746
  GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, int(unpackAlignment))
1720
2747
  GL.glTexImage2D(target, level, internalFormat,
1721
2748
  width, height, 0,
1722
2749
  pixelFormat, dataType, data)
2750
+ GL.glGenerateMipmap(target)
1723
2751
 
1724
2752
  # apply texture parameters
1725
2753
  if texParams is not None:
@@ -1727,16 +2755,17 @@ def createTexImage2D(width, height, target=GL.GL_TEXTURE_2D, level=0,
1727
2755
  GL.glTexParameteri(target, pname, param)
1728
2756
 
1729
2757
  # new texture descriptor
1730
- tex = TexImage2D(name=texId,
1731
- target=target,
1732
- width=width,
1733
- height=height,
1734
- internalFormat=internalFormat,
1735
- level=level,
1736
- pixelFormat=pixelFormat,
1737
- dataType=dataType,
1738
- unpackAlignment=unpackAlignment,
1739
- texParams=texParams)
2758
+ tex = TexImage2DInfo(
2759
+ name=texId.value,
2760
+ target=target,
2761
+ width=width,
2762
+ height=height,
2763
+ internalFormat=internalFormat,
2764
+ level=level,
2765
+ pixelFormat=pixelFormat,
2766
+ dataType=dataType,
2767
+ unpackAlignment=unpackAlignment,
2768
+ texParams=texParams)
1740
2769
 
1741
2770
  tex._texParamsNeedUpdate = False
1742
2771
 
@@ -1762,7 +2791,7 @@ def createTexImage2dFromFile(imgFile, transpose=True):
1762
2791
 
1763
2792
  Returns
1764
2793
  -------
1765
- TexImage2D
2794
+ TexImage2DInfo
1766
2795
  Texture descriptor.
1767
2796
 
1768
2797
  """
@@ -1978,62 +3007,62 @@ def createCubeMap(width, height, target=GL.GL_TEXTURE_CUBE_MAP, level=0,
1978
3007
  return tex
1979
3008
 
1980
3009
 
1981
- def bindTexture(texture, unit=None, enable=True):
1982
- """Bind a texture.
3010
+ # def bindTexture(texture, unit=None, enable=True):
3011
+ # """Bind a texture.
1983
3012
 
1984
- Function binds `texture` to `unit` (if specified). If `unit` is `None`, the
1985
- texture will be bound but not assigned to a texture unit.
3013
+ # Function binds `texture` to `unit` (if specified). If `unit` is `None`, the
3014
+ # texture will be bound but not assigned to a texture unit.
1986
3015
 
1987
- Parameters
1988
- ----------
1989
- texture : TexImage2D
1990
- Texture descriptor to bind.
1991
- unit : int, optional
1992
- Texture unit to associated the texture with.
1993
- enable : bool
1994
- Enable textures upon binding.
3016
+ # Parameters
3017
+ # ----------
3018
+ # texture : TexImage2DInfo
3019
+ # Texture descriptor to bind.
3020
+ # unit : int, optional
3021
+ # Texture unit to associated the texture with.
3022
+ # enable : bool
3023
+ # Enable textures upon binding.
1995
3024
 
1996
- """
1997
- if not texture._isBound:
1998
- if enable:
1999
- GL.glEnable(texture.target)
3025
+ # """
3026
+ # if not texture._isBound:
3027
+ # if enable:
3028
+ # GL.glEnable(texture.target)
2000
3029
 
2001
- GL.glBindTexture(texture.target, texture.name)
2002
- texture._isBound = True
3030
+ # GL.glBindTexture(texture.target, texture.name)
3031
+ # texture._isBound = True
2003
3032
 
2004
- if unit is not None:
2005
- texture._unit = unit
2006
- GL.glActiveTexture(GL.GL_TEXTURE0 + unit)
3033
+ # if unit is not None:
3034
+ # texture._unit = unit
3035
+ # GL.glActiveTexture(GL.GL_TEXTURE0 + unit)
2007
3036
 
2008
- # update texture parameters if they have been accessed (changed?)
2009
- if texture._texParamsNeedUpdate:
2010
- for pname, param in texture._texParams.items():
2011
- GL.glTexParameteri(texture.target, pname, param)
2012
- texture._texParamsNeedUpdate = False
3037
+ # # update texture parameters if they have been accessed (changed?)
3038
+ # if texture._texParamsNeedUpdate:
3039
+ # for pname, param in texture._texParams.items():
3040
+ # GL.glTexParameteri(texture.target, pname, param)
3041
+ # texture._texParamsNeedUpdate = False
2013
3042
 
2014
3043
 
2015
- def unbindTexture(texture=None):
2016
- """Unbind a texture.
3044
+ # def unbindTexture(texture=None):
3045
+ # """Unbind a texture.
2017
3046
 
2018
- Parameters
2019
- ----------
2020
- texture : TexImage2D
2021
- Texture descriptor to unbind.
3047
+ # Parameters
3048
+ # ----------
3049
+ # texture : TexImage2DInfo
3050
+ # Texture descriptor to unbind.
2022
3051
 
2023
- """
2024
- if texture._isBound:
2025
- # set the texture unit
2026
- if texture._unit is not None:
2027
- GL.glActiveTexture(GL.GL_TEXTURE0 + texture._unit)
2028
- texture._unit = None
3052
+ # """
3053
+ # if texture._isBound:
3054
+ # # set the texture unit
3055
+ # if texture._unit is not None:
3056
+ # GL.glActiveTexture(GL.GL_TEXTURE0 + texture._unit)
3057
+ # texture._unit = None
2029
3058
 
2030
- GL.glBindTexture(texture.target, 0)
2031
- texture._isBound = False
3059
+ # GL.glBindTexture(texture.target, 0)
3060
+ # texture._isBound = False
2032
3061
 
2033
- GL.glDisable(texture.target)
2034
- else:
2035
- raise RuntimeError('Trying to unbind a texture that was not previously'
2036
- 'bound.')
3062
+ # GL.glDisable(texture.target)
3063
+ # else:
3064
+ # raise RuntimeError('Trying to unbind a texture that was not previously'
3065
+ # 'bound.')
2037
3066
 
2038
3067
 
2039
3068
  # Descriptor for 2D mutlisampled texture
@@ -2049,6 +3078,72 @@ TexImage2DMultisample = namedtuple(
2049
3078
  'userData'])
2050
3079
 
2051
3080
 
3081
+ class TexImage2DMultisampleInfo:
3082
+ """Descriptor for a 2D multisampled texture.
3083
+
3084
+ This class is used for bookkeeping 2D multisampled textures stored in video
3085
+ memory. Information about the texture (eg. `width` and `height`) is
3086
+ available via class attributes. Attributes should never be modified
3087
+ directly.
3088
+
3089
+ """
3090
+ __slots__ = ['width',
3091
+ 'height',
3092
+ 'target',
3093
+ '_name',
3094
+ 'internalFormat',
3095
+ 'samples',
3096
+ 'multisample',
3097
+ 'userData']
3098
+
3099
+ def __init__(self,
3100
+ name=0,
3101
+ target=GL.GL_TEXTURE_2D_MULTISAMPLE,
3102
+ width=64,
3103
+ height=64,
3104
+ internalFormat=GL.GL_RGBA8,
3105
+ samples=1,
3106
+ multisample=True,
3107
+ userData=None):
3108
+ """
3109
+ Parameters
3110
+ ----------
3111
+ name : `int` or `GLuint`
3112
+ OpenGL handle for texture. Is `0` if uninitialized.
3113
+ target : :obj:`int`
3114
+ The target texture should only be `GL_TEXTURE_2D_MULTISAMPLE`.
3115
+ width : :obj:`int`
3116
+ Texture width in pixels.
3117
+ height : :obj:`int`
3118
+ Texture height in pixels.
3119
+ internalFormat : :obj:`int`
3120
+ Internal format for texture data (e.g. GL_RGBA8, GL_R11F_G11F_B10F).
3121
+ samples : :obj:`int`
3122
+ Number of samples for multi-sampling, should be >1 and power-of-two.
3123
+ Work with one sample, but will raise a warning.
3124
+ multisample : :obj:`bool`
3125
+ True if the texture is multi-sampled.
3126
+ userData : :obj:`dict`
3127
+ User-defined data associated with the texture.
3128
+
3129
+ """
3130
+ self.name = name
3131
+ self.width = width
3132
+ self.height = height
3133
+ self.target = target
3134
+ self.internalFormat = internalFormat
3135
+ self.samples = samples
3136
+ self.multisample = multisample
3137
+
3138
+ if userData is None:
3139
+ self.userData = {}
3140
+ elif isinstance(userData, dict):
3141
+ self.userData = userData
3142
+ else:
3143
+ raise TypeError('Invalid type for `userData`.')
3144
+
3145
+
3146
+
2052
3147
  def createTexImage2DMultisample(width, height,
2053
3148
  target=GL.GL_TEXTURE_2D_MULTISAMPLE, samples=1,
2054
3149
  internalFormat=GL.GL_RGBA8, texParameters=()):
@@ -2075,8 +3170,8 @@ def createTexImage2DMultisample(width, height,
2075
3170
 
2076
3171
  Returns
2077
3172
  -------
2078
- TexImage2DMultisample
2079
- A TexImage2DMultisample descriptor.
3173
+ TexImage2DMultisampleInfo
3174
+ A TexImage2DMultisampleInfo descriptor.
2080
3175
 
2081
3176
  """
2082
3177
  width = int(width)
@@ -2107,14 +3202,15 @@ def createTexImage2DMultisample(width, height,
2107
3202
 
2108
3203
  GL.glBindTexture(target, 0)
2109
3204
 
2110
- return TexImage2DMultisample(colorTexId,
2111
- target,
2112
- width,
2113
- height,
2114
- internalFormat,
2115
- samples,
2116
- True,
2117
- dict())
3205
+ return TexImage2DMultisampleInfo(
3206
+ colorTexId,
3207
+ target,
3208
+ width,
3209
+ height,
3210
+ internalFormat,
3211
+ samples,
3212
+ True,
3213
+ dict())
2118
3214
 
2119
3215
 
2120
3216
  def deleteTexture(texture):
@@ -2300,6 +3396,11 @@ def createVAO(attribBuffers, indexBuffer=None, attribDivisors=None, legacy=False
2300
3396
  activeAttribs = {}
2301
3397
  bufferIndices = []
2302
3398
  for i, buffer in attribBuffers.items():
3399
+ if isinstance(i, str):
3400
+ i = VERTEX_ATTRIBS.get(i, None)
3401
+ if i is None:
3402
+ raise ValueError('Invalid attribute name specified.')
3403
+
2303
3404
  if isinstance(buffer, (list, tuple,)):
2304
3405
  if len(buffer) == 1:
2305
3406
  buffer = buffer[0] # size 1 tuple or list eg. (buffer,)
@@ -2370,6 +3471,13 @@ def createVAO(attribBuffers, indexBuffer=None, attribDivisors=None, legacy=False
2370
3471
  legacy)
2371
3472
 
2372
3473
 
3474
+ # use the appropriate VAO binding function for the platform
3475
+ if _thisPlatform != 'Darwin':
3476
+ _glBindVertexArray = GL.glBindVertexArray
3477
+ else:
3478
+ _glBindVertexArray = GL.glBindVertexArrayAPPLE
3479
+
3480
+
2373
3481
  def drawVAO(vao, mode=GL.GL_TRIANGLES, start=0, count=None, instanceCount=None,
2374
3482
  flush=False):
2375
3483
  """Draw a vertex array object. Uses `glDrawArrays` or `glDrawElements` if
@@ -2381,7 +3489,9 @@ def drawVAO(vao, mode=GL.GL_TRIANGLES, start=0, count=None, instanceCount=None,
2381
3489
  vao : VertexArrayObject
2382
3490
  Vertex Array Object (VAO) to draw.
2383
3491
  mode : int, optional
2384
- Drawing mode to use (e.g. GL_TRIANGLES, GL_QUADS, GL_POINTS, etc.)
3492
+ Drawing mode to use (e.g. GL_TRIANGLES, GL_QUADS, GL_POINTS, etc.) for
3493
+ rasterization. Default is `GL_TRIANGLES`. Strings can be used for
3494
+ convenience (e.g. 'GL_TRIANGLES', 'GL_QUADS', 'GL_POINTS').
2385
3495
  start : int, optional
2386
3496
  Starting index for array elements. Default is `0` which is the beginning
2387
3497
  of the array.
@@ -2402,11 +3512,13 @@ def drawVAO(vao, mode=GL.GL_TRIANGLES, start=0, count=None, instanceCount=None,
2402
3512
  drawVAO(vaoDesc, GL.GL_TRIANGLES)
2403
3513
 
2404
3514
  """
3515
+ if isinstance(mode, str):
3516
+ mode = getattr(GL, mode, None)
3517
+ if mode is None:
3518
+ raise ValueError('Invalid drawing mode specified.')
3519
+
2405
3520
  # draw the array
2406
- if _thisPlatform != 'Darwin':
2407
- GL.glBindVertexArray(vao.name)
2408
- else:
2409
- GL.glBindVertexArrayAPPLE(vao.name)
3521
+ _glBindVertexArray(vao.name)
2410
3522
 
2411
3523
  if count is None:
2412
3524
  count = vao.count
@@ -2432,10 +3544,7 @@ def drawVAO(vao, mode=GL.GL_TRIANGLES, start=0, count=None, instanceCount=None,
2432
3544
  GL.glFlush()
2433
3545
 
2434
3546
  # reset
2435
- if _thisPlatform != 'Darwin':
2436
- GL.glBindVertexArray(0)
2437
- else:
2438
- GL.glBindVertexArrayAPPLE(0)
3547
+ _glBindVertexArray(0)
2439
3548
 
2440
3549
 
2441
3550
  def deleteVAO(vao):
@@ -2449,25 +3558,185 @@ def deleteVAO(vao):
2449
3558
  reset.
2450
3559
 
2451
3560
  """
2452
- if isinstance(vao, VertexArrayInfo):
2453
- if vao.name:
2454
- GL.glDeleteVertexArrays(1, GL.GLuint(vao.name))
2455
- vao.name = 0
2456
- vao.isLegacy = False
2457
- vao.indexBuffer = None
2458
- vao.activeAttribs = {}
2459
- vao.count = 0
2460
-
3561
+ if not isinstance(vao, VertexArrayInfo):
3562
+ raise TypeError('Invalid type for `vao`, must be `VertexArrayInfo`.')
3563
+
3564
+ if vao.name:
3565
+ GL.glDeleteVertexArrays(1, GL.GLuint(vao.name))
3566
+ vao.name = 0
3567
+ vao.isLegacy = False
3568
+ vao.indexBuffer = None
3569
+ vao.activeAttribs = {}
3570
+ vao.count = 0
2461
3571
 
2462
- # ---------------------------
2463
- # Vertex Buffer Objects (VBO)
2464
- #
2465
3572
 
3573
+ def drawClientArrays(attribBuffers, mode=GL.GL_TRIANGLES, indexBuffer=None):
3574
+ """Draw vertex arrays using client-side arrays.
3575
+
3576
+ This is a convenience function for drawing vertex arrays by passing
3577
+ client-side (resident in CPU memory space) arrays to the driver directly.
3578
+ Performance may be suboptimal compared to using VBOs and VAOs, as data
3579
+ must be transferred to the GPU each time this function is called. This may
3580
+ also stall the rendering pipeline, preventing the GPU from processing
3581
+ commands in parallel.
2466
3582
 
2467
- class VertexBufferInfo:
2468
- """Vertex buffer object (VBO) descriptor.
3583
+ For best performance, use interleaved arrays for vertex attributes and an
3584
+ index buffer. Using an index buffer is optional, but it greatly reduces
3585
+ the amount of data that must be transferred to the GPU.
2469
3586
 
2470
- This class only stores information about the VBO it refers to, it does not
3587
+ Parameters
3588
+ ----------
3589
+ attribBuffers : dict
3590
+ Attributes and associated buffers to draw. Keys are vertex attribute
3591
+ pointer indices, values are buffer arrays. Values can be `tuples` where
3592
+ the first value is the buffer array, the second is the number of
3593
+ attribute components (`int`, either 2, 3 or 4), the third is the offset
3594
+ (`int`), and the last is whether to normalize the array (`bool`). Buffer
3595
+ arrays may be `numpy` arrays or lists. If lists, the arrays will be
3596
+ converted to `numpy` arrays with `float` (`GL_DOUBLE`) data type.
3597
+ mode : int
3598
+ Drawing mode to use (e.g. GL_TRIANGLES, GL_QUADS, GL_POINTS, etc.) for
3599
+ rasterization.
3600
+ indexBuffer : VertexBufferInfo
3601
+ Optional index buffer. If `None`, `glDrawArrays` is used, else
3602
+ `glDrawElements` is used. The index buffer must be a 2D array that is
3603
+ appropriately sized for the drawing mode. For example, if `mode` is
3604
+ `GL_TRIANGLES`, the index buffer must be a 2D array with 3 columns. The
3605
+ index array is always assumed to be of type `GL_UNSIGNED_INT`, it will
3606
+ be converted if necessary.
3607
+
3608
+ Examples
3609
+ --------
3610
+ Drawing vertex arrays using client-side arrays::
3611
+
3612
+ attribArrays = {
3613
+ 'gl_Vertex': vertexPos, # vertex positions
3614
+ 'gl_Color': vertexColors} # per vertex colors
3615
+
3616
+ drawClientArrays('triangles', attribArrays)
3617
+
3618
+ Drawing using index buffers::
3619
+
3620
+ vertex, normal, texCoord, faces = createAnnulus() # ring geometry
3621
+
3622
+ # bind and setup shader uniforms here...
3623
+
3624
+ attribs = {
3625
+ 'gl_Vertex': vertex,
3626
+ 'gl_Normal': normal,
3627
+ 'gl_MultiTexCoord0': texCoord}
3628
+
3629
+ drawClientArrays('triangles', attribs, indexBuffer=faces)
3630
+
3631
+ Using interleaved arrays is recommended for greater performance, all
3632
+ attributes are stored in a single array which reduces the number of
3633
+ binding calls::
3634
+
3635
+ vertex, normal, texCoord, faces = createPlane() # square
3636
+ interleaved, sizes, offsets = interleaveArrays(
3637
+ [vertex, normal, texCoord])
3638
+
3639
+ # interleaved array layout: 000111222
3640
+ attribs = {}
3641
+ for i, attrib in enumerate('gl_Vertex', 'gl_Normal', 'gl_TexCoord'):
3642
+ attribs[attrib] = (interleaved, sizes[i], offsets[i])
3643
+
3644
+ drawClientArrays('triangles', attribs, indexBuffer=faces)
3645
+
3646
+ """
3647
+ mode = _getGLEnum(mode)
3648
+ if mode is None:
3649
+ raise ValueError('Invalid drawing mode specified.')
3650
+
3651
+ GL.glEnable(GL.GL_VERTEX_ARRAY)
3652
+ GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0)
3653
+ GL.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0)
3654
+ GL.glBindVertexArray(0)
3655
+
3656
+ useIndexBuffer = indexBuffer is not None
3657
+ if useIndexBuffer:
3658
+ # always use unsigned int for index buffer
3659
+ indexBuffer = np.ascontiguousarray(indexBuffer, dtype=np.uint32)
3660
+ if indexBuffer.ndim != 2:
3661
+ raise ValueError('Index buffer must be 2D array.')
3662
+
3663
+ boundArrays = []
3664
+ for arrIdx, buffer in attribBuffers.items():
3665
+ if isinstance(arrIdx, str):
3666
+ arrIdx= VERTEX_ATTRIBS.get(arrIdx, None)
3667
+ if arrIdx is None:
3668
+ raise ValueError('Invalid attribute name specified.')
3669
+
3670
+ if isinstance(buffer, (list, tuple,)):
3671
+ normalize = False
3672
+ nVals = len(buffer)
3673
+ if nVals == 1:
3674
+ buffer = buffer[0] # size 1 tuple or list eg. (buffer,)
3675
+ size = buffer.shape[1]
3676
+ offset = 0
3677
+ elif nVals == 2:
3678
+ buffer, size = buffer
3679
+ offset = 0
3680
+ elif nVals == 3:
3681
+ buffer, size, offset = buffer
3682
+ elif nVals == 4:
3683
+ buffer, size, offset, normalize = buffer
3684
+ else:
3685
+ raise ValueError('Invalid attribute values.')
3686
+ else:
3687
+ size = buffer.shape[1]
3688
+ offset = 0
3689
+ normalize = False
3690
+
3691
+ # make sure the buffer is contiguous array
3692
+ if not isinstance(buffer, np.ndarray):
3693
+ buffer = np.ascontiguousarray(buffer, dtype=float)
3694
+
3695
+ if buffer.ndim != 2:
3696
+ raise ValueError(
3697
+ 'Buffer {} must be 2D array.'.format(arrIdx))
3698
+
3699
+ numVertices = buffer.shape[0]
3700
+
3701
+ # enable and set attribute pointers
3702
+ GL.glEnableVertexAttribArray(arrIdx)
3703
+
3704
+ arrayTypes = ARRAY_TYPES.get(buffer.dtype.type, None)
3705
+ if arrayTypes is None:
3706
+ raise ValueError('Unable to determine data type from buffer.')
3707
+
3708
+ GL.glVertexAttribPointer(
3709
+ arrIdx,
3710
+ size,
3711
+ arrayTypes[0],
3712
+ GL.GL_TRUE if normalize else GL.GL_FALSE,
3713
+ offset,
3714
+ buffer.ctypes)
3715
+
3716
+ boundArrays.append(arrIdx)
3717
+
3718
+ # use the appropriate draw function
3719
+ if useIndexBuffer:
3720
+ GL.glDrawElements(
3721
+ mode, indexBuffer.size, GL.GL_UNSIGNED_INT, indexBuffer.ctypes)
3722
+ else:
3723
+ GL.glDrawArrays(mode, 0, numVertices)
3724
+
3725
+ for arrIdx in boundArrays: # unbind arrays
3726
+ GL.glDisableVertexAttribArray(arrIdx)
3727
+
3728
+ GL.glDisable(GL.GL_VERTEX_ARRAY)
3729
+
3730
+
3731
+ # ---------------------------
3732
+ # Vertex Buffer Objects (VBO)
3733
+ #
3734
+
3735
+
3736
+ class VertexBufferInfo:
3737
+ """Vertex buffer object (VBO) descriptor.
3738
+
3739
+ This class only stores information about the VBO it refers to, it does not
2471
3740
  contain any actual array data associated with the VBO. Calling
2472
3741
  :func:`createVBO` returns instances of this class.
2473
3742
 
@@ -2573,7 +3842,7 @@ class VertexBufferInfo:
2573
3842
  return False
2574
3843
 
2575
3844
  if self.target == GL.GL_ARRAY_BUFFER:
2576
- bindTarget = GL.GL_VERTEX_ARRAY_BUFFER_BINDING
3845
+ bindTarget = GL.GL_VERTEX_ARRAY_BINDING
2577
3846
  elif self.target == GL.GL_ELEMENT_ARRAY_BUFFER:
2578
3847
  bindTarget = GL.GL_ELEMENT_ARRAY_BUFFER_BINDING
2579
3848
  else:
@@ -2609,7 +3878,7 @@ class VertexBufferInfo:
2609
3878
 
2610
3879
  def createVBO(data,
2611
3880
  target=GL.GL_ARRAY_BUFFER,
2612
- dataType=GL.GL_FLOAT,
3881
+ dataType=None,
2613
3882
  usage=GL.GL_STATIC_DRAW):
2614
3883
  """Create an array buffer object (VBO).
2615
3884
 
@@ -2621,15 +3890,21 @@ def createVBO(data,
2621
3890
  data : array_like
2622
3891
  A 2D array of values to write to the array buffer. The data type of the
2623
3892
  VBO is inferred by the type of the array. If the input is a Python
2624
- `list` or `tuple` type, the data type of the array will be `GL_FLOAT`.
2625
- target : :obj:`int`
3893
+ `list` or `tuple` type, the data type of the array will be `GL_DOUBLE`.
3894
+ target : :obj:`int` or :obj:`str`, optional
2626
3895
  Target used when binding the buffer (e.g. `GL_VERTEX_ARRAY` or
2627
- `GL_ELEMENT_ARRAY_BUFFER`). Default is `GL_VERTEX_ARRAY`.
2628
- dataType : Glenum, optional
3896
+ `GL_ELEMENT_ARRAY_BUFFER`). Default is `GL_VERTEX_ARRAY`. Strings may
3897
+ also be used to specify the target, where the following are valid:
3898
+ 'array' (for `GL_VERTEX_ARRAY`) or 'element_array' (for
3899
+ `GL_ELEMENT_ARRAY_BUFFER`).
3900
+ dataType : Glenum or None, optional
2629
3901
  Data type of array. Input data will be recast to an appropriate type if
2630
- necessary. Default is `GL_FLOAT`.
3902
+ necessary. Default is `None`. If `None`, the data type will be
3903
+ inferred from the input data.
2631
3904
  usage : GLenum or int, optional
2632
- Usage type for the array (i.e. `GL_STATIC_DRAW`).
3905
+ Usage hint for the array (i.e. `GL_STATIC_DRAW`). This will hint to the
3906
+ GL driver how the buffer will be used so it can optimize memory
3907
+ allocation and access. Default is `GL_STATIC_DRAW`.
2633
3908
 
2634
3909
  Returns
2635
3910
  -------
@@ -2682,10 +3957,33 @@ def createVBO(data,
2682
3957
  glFlush()
2683
3958
 
2684
3959
  """
2685
- # build input array
2686
- npType, glType = GL_COMPAT_TYPES[dataType]
2687
- data = np.asarray(data, dtype=npType)
2688
-
3960
+ target, dataType, usage = _getGLEnum(target, dataType, usage)
3961
+
3962
+ # try and infer the data type if not specified
3963
+ if dataType is None: # get data type from input
3964
+ if isinstance(data, (list, tuple)): # default for Python array types
3965
+ dataType = GL.GL_DOUBLE
3966
+ elif isinstance(data, np.ndarray): # numpy arrays
3967
+ dataType = data.dtype.type
3968
+ else:
3969
+ raise ValueError('Could not infer data type from input.')
3970
+
3971
+ # get the OpenGL data type and numpy type
3972
+ typeVals = ARRAY_TYPES.get(dataType, None)
3973
+ if typeVals is None:
3974
+ raise ValueError('Invalid data type specified.')
3975
+
3976
+ glEnum, glType, npType = typeVals
3977
+
3978
+ # get the usage hint if a string was passed
3979
+ if isinstance(usage, str):
3980
+ usage = GL_ENUMS.get(usage, None)
3981
+ if usage is None:
3982
+ raise ValueError('Invalid `usage` hint string.')
3983
+
3984
+ # create the input data array
3985
+ data = np.ascontiguousarray(data, dtype=npType)
3986
+
2689
3987
  # get buffer size and pointer
2690
3988
  bufferSize = data.size * ctypes.sizeof(glType)
2691
3989
  if data.ndim > 1:
@@ -2708,7 +4006,7 @@ def createVBO(data,
2708
4006
  bufferName,
2709
4007
  target,
2710
4008
  usage,
2711
- dataType,
4009
+ glEnum,
2712
4010
  bufferSize,
2713
4011
  bufferStride,
2714
4012
  data.shape) # leave userData empty
@@ -2734,7 +4032,7 @@ def bindVBO(vbo):
2734
4032
  if isinstance(vbo, VertexBufferInfo):
2735
4033
  GL.glBindBuffer(vbo.target, vbo.name)
2736
4034
  else:
2737
- raise TypeError('Specified `vbo` is not at `VertexBufferInfo`.')
4035
+ raise TypeError('Specified `vbo` is not a `VertexBufferInfo`.')
2738
4036
 
2739
4037
 
2740
4038
  def unbindVBO(vbo):
@@ -2749,7 +4047,7 @@ def unbindVBO(vbo):
2749
4047
  if isinstance(vbo, VertexBufferInfo):
2750
4048
  GL.glBindBuffer(vbo.target, 0)
2751
4049
  else:
2752
- raise TypeError('Specified `vbo` is not at `VertexBufferInfo`.')
4050
+ raise TypeError('Specified `vbo` is not a `VertexBufferInfo`.')
2753
4051
 
2754
4052
 
2755
4053
  def mapBuffer(vbo, start=0, length=None, read=True, write=True, noSync=False):
@@ -2813,7 +4111,7 @@ def mapBuffer(vbo, start=0, length=None, read=True, write=True, noSync=False):
2813
4111
  unmapBuffer(vbo)
2814
4112
 
2815
4113
  """
2816
- npType, glType = GL_COMPAT_TYPES[vbo.dataType]
4114
+ _, glType, npType = ARRAY_TYPES[vbo.dataType]
2817
4115
  start *= ctypes.sizeof(glType)
2818
4116
 
2819
4117
  if length is None:
@@ -2867,6 +4165,103 @@ def unmapBuffer(vbo):
2867
4165
  return GL.glUnmapBuffer(vbo.target) == GL.GL_TRUE
2868
4166
 
2869
4167
 
4168
+ @contextmanager
4169
+ def mappedBuffer(vbo, start=0, length=None, read=True, write=True, noSync=False):
4170
+ """Context manager for mapping and unmapping a buffer. This is a convenience
4171
+ function for using :func:`mapBuffer` and :func:`unmapBuffer` together.
4172
+
4173
+ Parameters
4174
+ ----------
4175
+ vbo : VertexBufferInfo
4176
+ Vertex buffer to map to client memory.
4177
+ start : int
4178
+ Initial index of the sub-range of the buffer to modify.
4179
+ length : int or None
4180
+ Number of elements of the sub-array to map from `offset`. If `None`, all
4181
+ elements to from `offset` to the end of the array are mapped.
4182
+ read : bool, optional
4183
+ Allow data to be read from the buffer (sets `GL_MAP_READ_BIT`). This is
4184
+ ignored if `noSync` is `True`.
4185
+ write : bool, optional
4186
+ Allow data to be written to the buffer (sets `GL_MAP_WRITE_BIT`).
4187
+ noSync : bool, optional
4188
+ If `True`, GL will not wait until the buffer is free (i.e. not being
4189
+ processed by the GPU) to map it (sets `GL_MAP_UNSYNCHRONIZED_BIT`). The
4190
+ contents of the previous storage buffer are discarded and the driver
4191
+ returns a new one. This prevents the CPU from stalling until the buffer
4192
+ is available
4193
+
4194
+ Yields
4195
+ ------
4196
+ ndarray
4197
+ View of the data. The type of the returned array is one which best
4198
+ matches the data type of the buffer.
4199
+
4200
+ Examples
4201
+ --------
4202
+ Using the context manager to map and unmap a buffer::
4203
+
4204
+ with mappedBuffer(vbo) as arr:
4205
+ arr[:, :] += 2.0
4206
+
4207
+ """
4208
+ arr = mapBuffer(vbo, start, length, read, write, noSync)
4209
+ yield arr
4210
+ unmapBuffer(vbo)
4211
+
4212
+
4213
+ def updateVBO(vbo, data, noSync=False):
4214
+ """Update the contents of a VBO with new data.
4215
+
4216
+ This is a convenience function for mapping a buffer, updating the data, and
4217
+ then unmapping it if the data shape matches the buffer shape.
4218
+
4219
+ Parameters
4220
+ ----------
4221
+ vbo : VertexBufferInfo
4222
+ Vertex buffer to update.
4223
+ data : array_like
4224
+ New data to write to the buffer. The shape of the data must match the
4225
+ shape of the buffer.
4226
+ noSync : bool, optional
4227
+ If `True`, GL will not wait until the buffer is free (i.e. not being
4228
+ processed by the GPU) to map it (sets `GL_MAP_UNSYNCHRONIZED_BIT`). The
4229
+ contents of the previous storage buffer are discarded and the driver
4230
+ returns a new one. This prevents the CPU from stalling until the buffer
4231
+ is available.
4232
+
4233
+ Returns
4234
+ -------
4235
+ bool
4236
+ `True` if the buffer has been successfully modified. If `False`, the
4237
+ data was corrupted for some reason and needs to be resubmitted.
4238
+
4239
+ Examples
4240
+ --------
4241
+ Update a VBO with new data::
4242
+
4243
+ # new vertices
4244
+ verts = [[ 1.0, 1.0, 0.0], # v0
4245
+ [ 0.0, -1.0, 0.0], # v1
4246
+ [-1.0, 1.0, 0.0]] # v2
4247
+
4248
+ # update the VBO
4249
+ updateVBO(vboDesc, verts)
4250
+
4251
+ """
4252
+ if not isinstance(data, np.ndarray): # allow lists, tuples, etc.
4253
+ data = np.ascontiguousarray(data)
4254
+
4255
+ if data.shape != vbo.shape:
4256
+ raise ValueError('Data shape does not match VBO shape, expected {} '
4257
+ 'but got {}.'.format(vbo.shape, data.shape))
4258
+
4259
+ mappedArray = mapBuffer(vbo, noSync=noSync)
4260
+ mappedArray[:, :] = data[:, :] # transfer data to GPU buffer array
4261
+
4262
+ return unmapBuffer(vbo)
4263
+
4264
+
2870
4265
  def deleteVBO(vbo):
2871
4266
  """Delete a vertex buffer object (VBO).
2872
4267
 
@@ -2876,9 +4271,12 @@ def deleteVBO(vbo):
2876
4271
  Descriptor of VBO to delete.
2877
4272
 
2878
4273
  """
4274
+ if not isinstance(vbo, VertexBufferInfo):
4275
+ raise TypeError('Invalid type for `vbo`, must be `VertexBufferInfo`.')
4276
+
2879
4277
  if GL.glIsBuffer(vbo.name):
2880
4278
  GL.glDeleteBuffers(1, vbo.name)
2881
- vbo.name = GL.GLuint(0)
4279
+ vbo.name = GL.GLuint(0) # reset the object to invalidate it
2882
4280
 
2883
4281
 
2884
4282
  def setVertexAttribPointer(index,
@@ -2911,7 +4309,7 @@ def setVertexAttribPointer(index,
2911
4309
  versions.
2912
4310
 
2913
4311
  On nVidia graphics drivers (and maybe others), the following attribute
2914
- pointers indices are aliased with reserved GLSL names:
4312
+ pointer indices are aliased with reserved GLSL names:
2915
4313
 
2916
4314
  * gl_Vertex - 0
2917
4315
  * gl_Normal - 2
@@ -3011,7 +4409,7 @@ def setVertexAttribPointer(index,
3011
4409
  if vbo.target != GL.GL_ARRAY_BUFFER:
3012
4410
  raise ValueError('VBO must have `target` type `GL_ARRAY_BUFFER`.')
3013
4411
 
3014
- _, glType = GL_COMPAT_TYPES[vbo.dataType]
4412
+ _, glType, _ = ARRAY_TYPES[vbo.dataType]
3015
4413
 
3016
4414
  if size is None:
3017
4415
  size = vbo.shape[1]
@@ -3094,120 +4492,506 @@ def disableVertexAttribArray(index, legacy=False):
3094
4492
  GL.glDisableClientState(index)
3095
4493
 
3096
4494
 
3097
- # -------------------------
3098
- # Material Helper Functions
3099
- # -------------------------
3100
- #
3101
- # Materials affect the appearance of rendered faces. These helper functions and
3102
- # datatypes simplify the creation of materials for rendering stimuli.
4495
+ # ---------------------------
4496
+ # Draw settings
3103
4497
  #
3104
4498
 
3105
- Material = namedtuple('Material', ['face', 'params', 'textures', 'userData'])
3106
-
3107
4499
 
3108
- def createMaterial(params=(), textures=(), face=GL.GL_FRONT_AND_BACK):
3109
- """Create a new material.
4500
+ def activeTexture(unit):
4501
+ """Set the active texture unit.
3110
4502
 
3111
4503
  Parameters
3112
4504
  ----------
4505
+ unit : GLenum, int or str
4506
+ Texture unit to activate. Values can be `GL_TEXTURE0`, `GL_TEXTURE1`,
4507
+ `GL_TEXTURE2`, etc.
3113
4508
 
3114
- params : :obj:`list` of :obj:`tuple`, optional
3115
- List of material modes and values. Each mode is assigned a value as
3116
- (mode, color). Modes can be GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR,
3117
- GL_EMISSION, GL_SHININESS or GL_AMBIENT_AND_DIFFUSE. Colors must be
3118
- a tuple of 4 floats which specify reflectance values for each RGBA
3119
- component. The value of GL_SHININESS should be a single float. If no
3120
- values are specified, an empty material will be created.
3121
- textures : :obj:`list` of :obj:`tuple`, optional
3122
- List of texture units and TexImage2D descriptors. These will be written
3123
- to the 'textures' field of the returned descriptor. For example,
3124
- [(GL.GL_TEXTURE0, texDesc0), (GL.GL_TEXTURE1, texDesc1)]. The number of
3125
- texture units per-material is GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS.
3126
- face : :obj:`int`, optional
3127
- Faces to apply material to. Values can be GL_FRONT_AND_BACK, GL_FRONT
3128
- and GL_BACK. The default is GL_FRONT_AND_BACK.
4509
+ """
4510
+ if isinstance(unit, str):
4511
+ unit = _getGLEnum(unit)
4512
+ else:
4513
+ if unit < MAX_TEXTURE_UNITS:
4514
+ unit = GL.GL_TEXTURE0 + unit
3129
4515
 
3130
- Returns
3131
- -------
3132
- Material :
3133
- A descriptor with material properties.
4516
+ GL.glActiveTexture(unit)
3134
4517
 
3135
- Examples
3136
- --------
3137
- Creating a new material with given properties::
3138
4518
 
3139
- # The values for the material below can be found at
3140
- # http://devernay.free.fr/cours/opengl/materials.html
4519
+ def bindTexture(texture, target=None, unit=None):
4520
+ """Bind a texture to a target.
3141
4521
 
3142
- # create a gold material
3143
- gold = createMaterial([
3144
- (GL.GL_AMBIENT, (0.24725, 0.19950, 0.07450, 1.0)),
3145
- (GL.GL_DIFFUSE, (0.75164, 0.60648, 0.22648, 1.0)),
3146
- (GL.GL_SPECULAR, (0.628281, 0.555802, 0.366065, 1.0)),
3147
- (GL.GL_SHININESS, 0.4 * 128.0)])
4522
+ Parameters
4523
+ ----------
4524
+ texture : GLuint, int, None, TexImage2DInfo or TexImage2DMultisampleInfo
4525
+ Texture to bind. If `None`, the texture will be unbound.
4526
+ target : GLenum, int or str, optional
4527
+ Target to bind the texture to. Default is `None`. If `None`, and the
4528
+ texture is a `TexImage2DInfo` or `TexImage2DMultisampleInfo` object,
4529
+ the target will be inferred from the texture object. If specified, the
4530
+ value will override the target inferred from the texture. If `None` and
4531
+ the texture is an integer, the target will default to `GL_TEXTURE_2D`.
4532
+ unit : GLenum, int or None, optional
4533
+ Texture unit to bind the texture to, this will also set the active
4534
+ texture unit. Default is `None`. If `None`, the texture will be bound to
4535
+ the currently active texture unit.
3148
4536
 
3149
- Use the material when drawing::
4537
+ """
4538
+ if isinstance(target, str):
4539
+ target = getattr(GL, target, None)
4540
+ if target is None:
4541
+ raise ValueError('Invalid target string specified.')
4542
+
4543
+ if texture is None:
4544
+ texture = 0
4545
+ else:
4546
+ if isinstance(texture, (TexImage2DInfo, TexImage2DMultisampleInfo)):
4547
+ texture = texture.name
4548
+ if target is None:
4549
+ target = texture.target
4550
+ else:
4551
+ texture = int(texture)
4552
+ if target is None:
4553
+ target = GL.GL_TEXTURE_2D
3150
4554
 
3151
- useMaterial(gold)
3152
- drawVAO( ... ) # all meshes will be gold
3153
- useMaterial(None) # turn off material when done
4555
+ if unit is not None: # bind the texture to a specific unit
4556
+ activeTexture(unit)
4557
+
4558
+ GL.glBindTexture(target, texture)
3154
4559
 
3155
- Create a red plastic material, but define reflectance and shine later::
3156
4560
 
3157
- red_plastic = createMaterial()
4561
+ def setPolygonMode(face, mode):
4562
+ """Set the polygon rasterization mode for a face.
3158
4563
 
3159
- # you need to convert values to ctypes!
3160
- red_plastic.values[GL_AMBIENT] = (GLfloat * 4)(0.0, 0.0, 0.0, 1.0)
3161
- red_plastic.values[GL_DIFFUSE] = (GLfloat * 4)(0.5, 0.0, 0.0, 1.0)
3162
- red_plastic.values[GL_SPECULAR] = (GLfloat * 4)(0.7, 0.6, 0.6, 1.0)
3163
- red_plastic.values[GL_SHININESS] = 0.25 * 128.0
4564
+ Parameters
4565
+ ----------
4566
+ face : GLenum or str
4567
+ Face to set the polygon mode for. Values can be `GL_FRONT`, `GL_BACK`,
4568
+ or `GL_FRONT_AND_BACK`. Strings may also be used to specify the face,
4569
+ where the following are valid: 'front' (for `GL_FRONT`), 'back' (for
4570
+ `GL_BACK`), and 'front_and_back' (for `GL_FRONT_AND_BACK`).
4571
+ mode : GLenum or str
4572
+ Polygon rasterization mode. Values can be `GL_POINT`, `GL_LINE`, or
4573
+ `GL_FILL`. Strings may also be used to specify the mode, where the
4574
+ following are valid: 'point' (for `GL_POINT`), 'line' (for `GL_LINE`),
4575
+ and 'fill' (for `GL_FILL`).
3164
4576
 
3165
- # set and draw
3166
- useMaterial(red_plastic)
3167
- drawVertexbuffers( ... ) # all meshes will be red plastic
3168
- useMaterial(None)
4577
+ """
4578
+ if isinstance(face, str):
4579
+ face = getattr(GL, face, None)
4580
+ if face is None:
4581
+ raise ValueError(
4582
+ 'Invalid face string specified, got {}.'.format(face))
4583
+ if isinstance(mode, str):
4584
+ mode = getattr(GL, mode, None)
4585
+ if mode is None:
4586
+ raise ValueError(
4587
+ 'Invalid mode string specified, got {}.'.format(mode))
4588
+
4589
+ GL.glPolygonMode(face, mode)
4590
+
4591
+
4592
+ def setWireframeDraw(face=GL.GL_FRONT_AND_BACK):
4593
+ """Set the rasterization mode to wireframe.
4594
+
4595
+ Successive draw operations will render wireframe polygons (i.e. outlines).
4596
+
4597
+ Parameters
4598
+ ----------
4599
+ face : GLenum or int, optional
4600
+ Faces to apply wireframe to. Values can be `GL_FRONT_AND_BACK`,
4601
+ `GL_FRONT` and `GL_BACK`. The default is `GL_FRONT_AND_BACK`. Strings
4602
+ may also be used to specify the face, where the following are valid:
4603
+ 'front' (for `GL_FRONT`), 'back' (for `GL_BACK`), and 'front_and_back'
4604
+ (for `GL_FRONT_AND_BACK`).
3169
4605
 
3170
4606
  """
3171
- # setup material mode/value slots
3172
- matDesc = Material(
3173
- face,
3174
- {mode: None for mode in (
3175
- GL.GL_AMBIENT,
3176
- GL.GL_DIFFUSE,
3177
- GL.GL_SPECULAR,
3178
- GL.GL_EMISSION,
3179
- GL.GL_SHININESS)},
3180
- dict(),
3181
- dict())
3182
- if params:
3183
- for mode, param in params:
3184
- matDesc.params[mode] = \
3185
- (GL.GLfloat * 4)(*param) \
3186
- if mode != GL.GL_SHININESS else GL.GLfloat(param)
3187
- if textures:
3188
- maxTexUnits = getIntegerv(GL.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)
3189
- for unit, texDesc in textures:
3190
- if unit <= GL.GL_TEXTURE0 + (maxTexUnits - 1):
3191
- matDesc.textures[unit] = texDesc
3192
- else:
3193
- raise ValueError("Invalid texture unit enum.")
4607
+ if isinstance(face, str):
4608
+ face = getattr(GL, face, None)
4609
+ if face is None:
4610
+ raise ValueError(
4611
+ 'Invalid face string specified, got {}.'.format(face))
4612
+
4613
+ setPolygonMode(face, GL.GL_LINE)
3194
4614
 
3195
- return matDesc
3196
4615
 
4616
+ def setFillDraw(face=GL.GL_FRONT_AND_BACK):
4617
+ """Set the rasterization mode to fill polygons.
3197
4618
 
3198
- class SimpleMaterial:
3199
- """Class representing a simple material.
4619
+ Successive draw operations will render filled polygons.
4620
+
4621
+ Parameters
4622
+ ----------
4623
+ face : GLenum or int, optional
4624
+ Faces to apply fill to. Values can be `GL_FRONT_AND_BACK`, `GL_FRONT`
4625
+ and `GL_BACK`. The default is `GL_FRONT_AND_BACK`. Strings may also be
4626
+ used to specify the face, where the following are valid: 'front' (for
4627
+ `GL_FRONT`), 'back' (for `GL_BACK`), and 'front_and_back' (for
4628
+ `GL_FRONT_AND_BACK`).
3200
4629
 
3201
- This class stores material information to modify the appearance of drawn
3202
- primitives with respect to lighting, such as color (diffuse, specular,
3203
- ambient, and emission), shininess, and textures. Simple materials are
3204
- intended to work with features supported by the fixed-function OpenGL
3205
- pipeline.
4630
+ """
4631
+ if isinstance(face, str):
4632
+ face = getattr(GL, face, None)
4633
+ if face is None:
4634
+ raise ValueError(
4635
+ 'Invalid face string specified, got {}.'.format(face))
4636
+
4637
+ setPolygonMode(face, GL.GL_FILL)
4638
+
4639
+
4640
+ def setDepthTest(enable=True):
4641
+ """Enable or disable depth testing.
4642
+
4643
+ Parameters
4644
+ ----------
4645
+ enable : bool, optional
4646
+ Enable or disable depth testing. Default is `True`.
3206
4647
 
3207
4648
  """
3208
- def __init__(self,
3209
- win=None,
3210
- diffuseColor=(.5, .5, .5),
4649
+ if enable:
4650
+ GL.glEnable(GL.GL_DEPTH_TEST)
4651
+ else:
4652
+ GL.glDisable(GL.GL_DEPTH_TEST)
4653
+
4654
+
4655
+ def setDepthFunc(func):
4656
+ """Set the depth comparison function.
4657
+
4658
+ Parameters
4659
+ ----------
4660
+ func : GLenum or str
4661
+ Depth comparison function. Values can be `GL_NEVER`, `GL_LESS`,
4662
+ `GL_EQUAL`, `GL_LEQUAL`, `GL_GREATER`, `GL_NOTEQUAL`, `GL_GEQUAL`, or
4663
+ `GL_ALWAYS`. Strings may also be used to specify the function, where
4664
+ the following are valid: 'never' (for `GL_NEVER`), 'less' (for `GL_LESS`),
4665
+ 'equal' (for `GL_EQUAL`), 'lequal' (for `GL_LEQUAL`), 'greater' (for
4666
+ `GL_GREATER`), 'notequal' (for `GL_NOTEQUAL`), 'gequal' (for `GL_GEQUAL`),
4667
+ and 'always' (for `GL_ALWAYS`).
4668
+
4669
+ """
4670
+ func = _getGLEnum(func)
4671
+ GL.glDepthFunc(func)
4672
+
4673
+
4674
+ def setDepthMask(enable=True):
4675
+ """Enable or disable writing to the depth buffer.
4676
+
4677
+ Parameters
4678
+ ----------
4679
+ enable : bool, optional
4680
+ Enable or disable writing to the depth buffer. Default is `True`.
4681
+
4682
+ """
4683
+ if enable:
4684
+ GL.glDepthMask(GL.GL_TRUE)
4685
+ else:
4686
+ GL.glDepthMask(GL.GL_FALSE)
4687
+
4688
+
4689
+ def setDepthRange(near, far):
4690
+ """Set the range of depth values.
4691
+
4692
+ Parameters
4693
+ ----------
4694
+ near : float
4695
+ Near clipping plane.
4696
+ far : float
4697
+ Far clipping plane.
4698
+
4699
+ """
4700
+ GL.glDepthRange(near, far)
4701
+
4702
+
4703
+ def setClearColor(color):
4704
+ """Set the clear color for the color buffer.
4705
+
4706
+ Parameters
4707
+ ----------
4708
+ color : array_like
4709
+ Color to clear the color buffer with. The color should be in RGBA
4710
+ format, where each component is in the range [0, 1].
4711
+
4712
+ """
4713
+ GL.glClearColor(*color)
4714
+
4715
+
4716
+ def setClearDepth(depth):
4717
+ """Set the clear value for the depth buffer.
4718
+
4719
+ Parameters
4720
+ ----------
4721
+ depth : float
4722
+ Value to clear the depth buffer with.
4723
+
4724
+ """
4725
+ GL.glClearDepth(depth)
4726
+
4727
+
4728
+ def enable(enum):
4729
+ """Enable a GL capability.
4730
+
4731
+ Parameters
4732
+ ----------
4733
+ enum : GLenum, int or str
4734
+ Capability to enable.
4735
+
4736
+ """
4737
+ if isinstance(enum, str):
4738
+ enum = getattr(GL, enum, None)
4739
+
4740
+ if enum is None:
4741
+ raise ValueError('Invalid capability string specified.')
4742
+
4743
+ GL.glEnable(enum)
4744
+
4745
+
4746
+ def disable(enum):
4747
+ """Disable a GL capability.
4748
+
4749
+ Parameters
4750
+ ----------
4751
+ enum : GLenum, int or str
4752
+ Capability to disable.
4753
+
4754
+ """
4755
+ if isinstance(enum, str):
4756
+ enum = getattr(GL, enum, None)
4757
+
4758
+ if enum is None:
4759
+ raise ValueError('Invalid capability string specified.')
4760
+
4761
+ GL.glDisable(enum)
4762
+
4763
+
4764
+ def setLineWidth(width):
4765
+ """Set the width of rasterized lines.
4766
+
4767
+ Parameters
4768
+ ----------
4769
+ width : float
4770
+ Width of rasterized lines.
4771
+
4772
+ """
4773
+ GL.glLineWidth(width)
4774
+
4775
+
4776
+ def setLineSmooth(enable=True):
4777
+ """Enable or disable line antialiasing.
4778
+
4779
+ Parameters
4780
+ ----------
4781
+ enable : bool, optional
4782
+ Enable or disable line antialiasing. Default is `True`.
4783
+
4784
+ """
4785
+ if enable:
4786
+ GL.glEnable(GL.GL_LINE_SMOOTH)
4787
+ else:
4788
+ GL.glDisable(GL.GL_LINE_SMOOTH)
4789
+
4790
+
4791
+ def setPointSize(size):
4792
+ """Set the size of rasterized points.
4793
+
4794
+ Parameters
4795
+ ----------
4796
+ size : float
4797
+ Size of rasterized points.
4798
+
4799
+ """
4800
+ GL.glPointSize(size)
4801
+
4802
+
4803
+ def setPointSmooth(enable=True):
4804
+ """Enable or disable point antialiasing.
4805
+
4806
+ Parameters
4807
+ ----------
4808
+ enable : bool, optional
4809
+ Enable or disable point antialiasing. Default is `True`.
4810
+
4811
+ """
4812
+ if enable:
4813
+ GL.glEnable(GL.GL_POINT_SMOOTH)
4814
+ else:
4815
+ GL.glDisable(GL.GL_POINT_SMOOTH)
4816
+
4817
+
4818
+ def setMultiSample(enable=True):
4819
+ """Enable or disable multisample antialiasing.
4820
+
4821
+ Parameters
4822
+ ----------
4823
+ enable : bool, optional
4824
+ Enable or disable multisample antialiasing. Default is `True`.
4825
+
4826
+ """
4827
+ if enable:
4828
+ GL.glEnable(GL.GL_MULTISAMPLE)
4829
+ else:
4830
+ GL.glDisable(GL.GL_MULTISAMPLE)
4831
+
4832
+
4833
+ def setBlend(enable=True):
4834
+ """Enable or disable blending.
4835
+
4836
+ Parameters
4837
+ ----------
4838
+ enable : bool, optional
4839
+ Enable or disable blending. Default is `True`.
4840
+
4841
+ """
4842
+ if enable:
4843
+ GL.glEnable(GL.GL_BLEND)
4844
+ else:
4845
+ GL.glDisable(GL.GL_BLEND)
4846
+
4847
+
4848
+ def setBlendFunc(srcFactor, dstFactor):
4849
+ """Set the blending function for source and destination factors.
4850
+
4851
+ Parameters
4852
+ ----------
4853
+ srcFactor : GLenum or str
4854
+ Source blending factor. Eg. `GL_SRC_ALPHA`.
4855
+ dstFactor : GLenum or str
4856
+ Destination blending factor. Eg. `GL_ONE_MINUS_SRC_ALPHA`.
4857
+
4858
+ Examples
4859
+ --------
4860
+ Set the blending function to use source alpha and one minus source alpha::
4861
+
4862
+ setBlendFunc('GL_SRC_ALPHA', 'GL_ONE_MINUS_SRC_ALPHA')
4863
+
4864
+ """
4865
+ if isinstance(srcFactor, str):
4866
+ srcFactor = getattr(GL, srcFactor, None)
4867
+ if srcFactor is None:
4868
+ raise ValueError(
4869
+ 'Invalid enum specified for source factor. Got {}.'.format(
4870
+ srcFactor))
4871
+ if isinstance(dstFactor, str):
4872
+ dstFactor = getattr(GL, dstFactor, None)
4873
+ if dstFactor is None:
4874
+ raise ValueError(
4875
+ 'Invalid enum specified for destination factor. Got {}.'.format(
4876
+ dstFactor))
4877
+
4878
+ GL.glBlendFunc(srcFactor, dstFactor)
4879
+
4880
+
4881
+ # -------------------------
4882
+ # Material Helper Functions
4883
+ # -------------------------
4884
+ #
4885
+ # Materials affect the appearance of rendered faces. These helper functions and
4886
+ # datatypes simplify the creation of materials for rendering stimuli.
4887
+ #
4888
+
4889
+ Material = namedtuple('Material', ['face', 'params', 'textures', 'userData'])
4890
+
4891
+
4892
+ def createMaterial(params=(), textures=(), face=GL.GL_FRONT_AND_BACK):
4893
+ """Create a new material.
4894
+
4895
+ Parameters
4896
+ ----------
4897
+
4898
+ params : :obj:`list` of :obj:`tuple`, optional
4899
+ List of material modes and values. Each mode is assigned a value as
4900
+ (mode, color). Modes can be GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR,
4901
+ GL_EMISSION, GL_SHININESS or GL_AMBIENT_AND_DIFFUSE. Colors must be
4902
+ a tuple of 4 floats which specify reflectance values for each RGBA
4903
+ component. The value of GL_SHININESS should be a single float. If no
4904
+ values are specified, an empty material will be created.
4905
+ textures : :obj:`list` of :obj:`tuple`, optional
4906
+ List of texture units and TexImage2D descriptors. These will be written
4907
+ to the 'textures' field of the returned descriptor. For example,
4908
+ [(GL.GL_TEXTURE0, texDesc0), (GL.GL_TEXTURE1, texDesc1)]. The number of
4909
+ texture units per-material is GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS.
4910
+ face : :obj:`int`, optional
4911
+ Faces to apply material to. Values can be GL_FRONT_AND_BACK, GL_FRONT
4912
+ and GL_BACK. The default is GL_FRONT_AND_BACK.
4913
+
4914
+ Returns
4915
+ -------
4916
+ Material :
4917
+ A descriptor with material properties.
4918
+
4919
+ Examples
4920
+ --------
4921
+ Creating a new material with given properties::
4922
+
4923
+ # The values for the material below can be found at
4924
+ # http://devernay.free.fr/cours/opengl/materials.html
4925
+
4926
+ # create a gold material
4927
+ gold = createMaterial([
4928
+ (GL.GL_AMBIENT, (0.24725, 0.19950, 0.07450, 1.0)),
4929
+ (GL.GL_DIFFUSE, (0.75164, 0.60648, 0.22648, 1.0)),
4930
+ (GL.GL_SPECULAR, (0.628281, 0.555802, 0.366065, 1.0)),
4931
+ (GL.GL_SHININESS, 0.4 * 128.0)])
4932
+
4933
+ Use the material when drawing::
4934
+
4935
+ useMaterial(gold)
4936
+ drawVAO( ... ) # all meshes will be gold
4937
+ useMaterial(None) # turn off material when done
4938
+
4939
+ Create a red plastic material, but define reflectance and shine later::
4940
+
4941
+ red_plastic = createMaterial()
4942
+
4943
+ # you need to convert values to ctypes!
4944
+ red_plastic.values[GL_AMBIENT] = (GLfloat * 4)(0.0, 0.0, 0.0, 1.0)
4945
+ red_plastic.values[GL_DIFFUSE] = (GLfloat * 4)(0.5, 0.0, 0.0, 1.0)
4946
+ red_plastic.values[GL_SPECULAR] = (GLfloat * 4)(0.7, 0.6, 0.6, 1.0)
4947
+ red_plastic.values[GL_SHININESS] = 0.25 * 128.0
4948
+
4949
+ # set and draw
4950
+ useMaterial(red_plastic)
4951
+ drawVertexbuffers( ... ) # all meshes will be red plastic
4952
+ useMaterial(None)
4953
+
4954
+ """
4955
+ # setup material mode/value slots
4956
+ matDesc = Material(
4957
+ face,
4958
+ {mode: None for mode in (
4959
+ GL.GL_AMBIENT,
4960
+ GL.GL_DIFFUSE,
4961
+ GL.GL_SPECULAR,
4962
+ GL.GL_EMISSION,
4963
+ GL.GL_SHININESS)},
4964
+ dict(),
4965
+ dict())
4966
+ if params:
4967
+ for mode, param in params:
4968
+ matDesc.params[mode] = \
4969
+ (GL.GLfloat * 4)(*param) \
4970
+ if mode != GL.GL_SHININESS else GL.GLfloat(param)
4971
+ if textures:
4972
+ maxTexUnits = getIntegerv(GL.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)
4973
+ for unit, texDesc in textures:
4974
+ if unit <= GL.GL_TEXTURE0 + (maxTexUnits - 1):
4975
+ matDesc.textures[unit] = texDesc
4976
+ else:
4977
+ raise ValueError("Invalid texture unit enum.")
4978
+
4979
+ return matDesc
4980
+
4981
+
4982
+ class SimpleMaterial:
4983
+ """Class representing a simple material.
4984
+
4985
+ This class stores material information to modify the appearance of drawn
4986
+ primitives with respect to lighting, such as color (diffuse, specular,
4987
+ ambient, and emission), shininess, and textures. Simple materials are
4988
+ intended to work with features supported by the fixed-function OpenGL
4989
+ pipeline.
4990
+
4991
+ """
4992
+ def __init__(self,
4993
+ win=None,
4994
+ diffuseColor=(.5, .5, .5),
3211
4995
  specularColor=(-1., -1., -1.),
3212
4996
  ambientColor=(-1., -1., -1.),
3213
4997
  emissionColor=(-1., -1., -1.),
@@ -3239,8 +5023,8 @@ class SimpleMaterial:
3239
5023
  colorSpace : float
3240
5024
  Color space for `diffuseColor`, `specularColor`, `ambientColor`, and
3241
5025
  `emissionColor`.
3242
- diffuseTexture : TexImage2D
3243
- specularTexture : TexImage2D
5026
+ diffuseTexture : TexImage2DInfo
5027
+ specularTexture : TexImage2DInfo
3244
5028
  opacity : float
3245
5029
  Opacity of the material. Ranges from 0.0 to 1.0 where 1.0 is fully
3246
5030
  opaque.
@@ -4533,38 +6317,256 @@ def createBox(size=(1., 1., 1.), flipFaces=False):
4533
6317
  return vertices, texCoords, normals, faces
4534
6318
 
4535
6319
 
4536
- def transformMeshPosOri(vertices, normals, pos=(0., 0., 0.), ori=(0., 0., 0., 1.)):
4537
- """Transform a mesh.
6320
+ def createDisc(radius=1.0, edges=16):
6321
+ """Create a disc (filled circle) mesh.
4538
6322
 
4539
- Transform mesh vertices and normals to a new position and orientation using
4540
- a position coordinate and rotation quaternion. Values `vertices` and
4541
- `normals` must be the same shape. This is intended to be used when editing
4542
- raw vertex data prior to rendering. Do not use this to change the
4543
- configuration of an object while rendering.
6323
+ Generates a flat disc mesh with the specified radius and number of `edges`.
6324
+ The origin of the disc is located at the center. Textures coordinates will
6325
+ be mapped to a square which bounds the circle. Normals are perpendicular to
6326
+ the face of the circle.
4544
6327
 
4545
6328
  Parameters
4546
6329
  ----------
4547
- vertices : array_like
4548
- Nx3 array of vertices.
4549
- normals : array_like
4550
- Nx3 array of normals.
4551
- pos : array_like, optional
4552
- Position vector to transform mesh vertices. If Nx3, `vertices` will be
4553
- transformed by corresponding rows of `pos`.
4554
- ori : array_like, optional
4555
- Orientation quaternion in form [x, y, z, w]. If Nx4, `vertices` and
4556
- `normals` will be transformed by corresponding rows of `ori`.
6330
+ radius : float
6331
+ Radius of the disc in scene units.
6332
+ edges : int
6333
+ Number of segments to use to define the outer rim of the disc. Higher
6334
+ numbers will result in a smoother circle but will use more triangles.
4557
6335
 
4558
6336
  Returns
4559
6337
  -------
4560
6338
  tuple
4561
- Transformed vertices and normals.
6339
+ Vertex attribute arrays (position, texture coordinates, and normals) and
6340
+ triangle indices.
4562
6341
 
4563
6342
  Examples
4564
6343
  --------
4565
- Create and re-orient a plane to face upwards::
6344
+ Create a vertex array object to draw a disc::
4566
6345
 
4567
- vertices, textureCoords, normals, faces = createPlane()
6346
+ vertices, textureCoords, normals, faces = gltools.createDisc(edges=128)
6347
+ vertexVBO = gltools.createVBO(vertices)
6348
+ texCoordVBO = gltools.createVBO(textureCoords)
6349
+ normalsVBO = gltools.createVBO(normals)
6350
+ indexBuffer = gltools.createVBO(
6351
+ faces.flatten(),
6352
+ target=GL.GL_ELEMENT_ARRAY_BUFFER,
6353
+ dataType=GL.GL_UNSIGNED_INT)
6354
+
6355
+ vao = gltools.createVAO(
6356
+ {gltools.gl_Vertex: vertexVBO,
6357
+ gltools.gl_MultiTexCoord0: texCoordVBO,
6358
+ gltools.gl_Normal: normalsVBO},
6359
+ indexBuffer=indexBuffer)
6360
+
6361
+ """
6362
+ # get number of steps for vertices to get the number of edges we want
6363
+ nVerts = edges + 1
6364
+ steps = np.linspace(0, 2 * np.pi, num=nVerts, dtype=np.float32)
6365
+
6366
+ # offset since the first vertex is the centre
6367
+ vertices = np.zeros((nVerts + 1, 3), dtype=np.float32)
6368
+ vertices[1:, 0] = np.sin(steps)
6369
+ vertices[1:, 1] = np.cos(steps)
6370
+
6371
+ # compute the face indices
6372
+ faces = []
6373
+ for i in range(nVerts):
6374
+ faces.append([0, i + 1, i])
6375
+
6376
+ faces = np.ascontiguousarray(faces, dtype=np.uint32)
6377
+
6378
+ # compute the texture coordinates for each vertex
6379
+ normals = np.zeros_like(vertices, dtype=np.float32)
6380
+ normals[:, 2] = 1.
6381
+
6382
+ # compute texture coordinates
6383
+ texCoords = vertices.copy()
6384
+ texCoords[:, :] += 1.0
6385
+ texCoords[:, :] *= 0.5
6386
+
6387
+ # scale to specified radius
6388
+ vertices *= radius
6389
+
6390
+ return vertices, texCoords, normals, faces
6391
+
6392
+
6393
+ def createAnnulus(innerRadius=0.5, outerRadius=1.0, edges=16):
6394
+ """Create an annulus (ring) mesh.
6395
+
6396
+ Generates a flat ring mesh with the specified inner/outer radii and number
6397
+ of `edges`. The origin of the ring is located at the center. Textures
6398
+ coordinates will be mapped to a square which bounds the ring. Normals are
6399
+ perpendicular to the plane of the ring.
6400
+
6401
+ Parameters
6402
+ ----------
6403
+ innerRadius, outerRadius : float
6404
+ Radius of the inner and outer rims of the ring in scene units.
6405
+ edges : int
6406
+ Number of segments to use to define the band of the ring. The higher the
6407
+ value, the rounder the ring will look.
6408
+
6409
+ Returns
6410
+ -------
6411
+ tuple
6412
+ Vertex attribute arrays (position, texture coordinates, and normals) and
6413
+ triangle indices.
6414
+
6415
+ """
6416
+ # error checks
6417
+ if innerRadius >= outerRadius:
6418
+ raise ValueError("Inner radius must be less than outer.")
6419
+ elif outerRadius <= 0.:
6420
+ raise ValueError("Outer radius must be >0.")
6421
+ elif innerRadius < 0.:
6422
+ raise ValueError("Inner radius must be positive.")
6423
+ elif edges <= 2:
6424
+ raise ValueError("Number of edges must be >2.")
6425
+
6426
+ # generate inner and outer vertices
6427
+ nVerts = edges + 1
6428
+ steps = np.linspace(0, 2 * np.pi, num=nVerts, dtype=np.float32)
6429
+
6430
+ innerVerts = np.zeros((nVerts, 3), dtype=np.float32)
6431
+ outerVerts = np.zeros((nVerts, 3), dtype=np.float32)
6432
+ innerVerts[:, 0] = outerVerts[:, 0] = np.sin(steps)
6433
+ innerVerts[:, 1] = outerVerts[:, 1] = np.cos(steps)
6434
+
6435
+ # Keep the ring size between -1 and 1 to simplify computing texture
6436
+ # coordinates. We'll scale the vertices to the correct dimensions
6437
+ # afterwards.
6438
+ frac = innerRadius / float(outerRadius)
6439
+ innerVerts[:, :2] *= frac
6440
+
6441
+ # combine inner and outer vertex rings
6442
+ vertPos = np.vstack((innerVerts, outerVerts))
6443
+
6444
+ # generate faces
6445
+ faces = []
6446
+ for i in range(nVerts):
6447
+ faces.append([i, edges + i + 1, edges + i])
6448
+ faces.append([i, i + 1, edges + i + 1])
6449
+
6450
+ vertPos = np.ascontiguousarray(vertPos, dtype=np.float32)
6451
+ normals = np.zeros_like(vertPos, dtype=np.float32)
6452
+ normals[:, 2] = 1.0
6453
+ faces = np.ascontiguousarray(faces, dtype=np.uint32)
6454
+
6455
+ # compute texture coordinates
6456
+ texCoords = vertPos.copy()
6457
+ texCoords[:, :] += 1.0
6458
+ texCoords[:, :] *= 0.5
6459
+
6460
+ # scale to specified outer radius
6461
+ vertPos[:, :2] *= outerRadius
6462
+
6463
+ return vertPos, texCoords, normals, faces
6464
+
6465
+
6466
+ def createCylinder(radius=1.0, height=1.0, edges=16, stacks=1):
6467
+ """Create a cylinder mesh.
6468
+
6469
+ Generate a cylinder mesh with a given `height` and `radius`. The origin of
6470
+ the mesh will centered on it and offset to the base. Texture coordinates
6471
+ will be generated allowing a texture to wrap around it.
6472
+
6473
+ Parameters
6474
+ ----------
6475
+ radius : float
6476
+ Radius of the cylinder in scene units.
6477
+ height : float
6478
+ Height in scene units.
6479
+ edges : int
6480
+ Number of edges, the greater the number, the smoother the cylinder will
6481
+ appear when drawn.
6482
+ stacks : int
6483
+ Number of subdivisions along the height of cylinder to make. Setting to
6484
+ 1 will result in vertex data only being generated for the base and end
6485
+ of the cylinder.
6486
+
6487
+ Returns
6488
+ -------
6489
+ tuple
6490
+ Vertex attribute arrays (position, texture coordinates, and normals) and
6491
+ triangle indices.
6492
+
6493
+ """
6494
+ # generate vertex positions
6495
+ nEdgeVerts = edges + 1
6496
+ rings = stacks + 1
6497
+ steps = np.linspace(0, 2 * np.pi, num=nEdgeVerts)
6498
+ vertPos = np.zeros((nEdgeVerts, 3))
6499
+ vertPos[:, 0] = np.sin(steps)
6500
+ vertPos[:, 1] = np.cos(steps)
6501
+ vertPos = np.tile(vertPos, (rings, 1))
6502
+
6503
+ # apply offset in height for each stack
6504
+ stackHeight = np.linspace(0, height, num=rings)
6505
+ vertPos[:, 2] = np.repeat(stackHeight, nEdgeVerts)
6506
+
6507
+ # generate texture coordinates to they wrap around the cylinder
6508
+ u = np.linspace(0.0, 1.0, nEdgeVerts)
6509
+ v = np.linspace(1.0, 0.0, rings)
6510
+ uu, vv = np.meshgrid(u, v)
6511
+ texCoords = np.vstack([uu.ravel(), vv.ravel()]).T
6512
+
6513
+ # generate vertex normals, since our vertices all on a unit circle, we can
6514
+ # do a trick here
6515
+ normals = vertPos.copy()
6516
+ normals[:, 2] = 0.0
6517
+
6518
+ # create face indices
6519
+ faces = []
6520
+ for i in range(0, stacks):
6521
+ stackOffset = nEdgeVerts * i
6522
+ for j in range(nEdgeVerts):
6523
+ j = stackOffset + j
6524
+ faces.append([j, edges + j, edges + j + 1])
6525
+ faces.append([j, edges + j + 1, j + 1])
6526
+
6527
+ vertPos, texCoords, normals = [
6528
+ np.ascontiguousarray(i, dtype=np.float32) for i in (
6529
+ vertPos, texCoords, normals)]
6530
+ faces = np.ascontiguousarray(faces, dtype=np.uint32)
6531
+
6532
+ # scale the cylinder's radius and height to what the user specified
6533
+ vertPos[:, :2] *= radius
6534
+
6535
+ return vertPos, texCoords, normals, faces
6536
+
6537
+
6538
+ def transformMeshPosOri(vertices, normals, pos=(0., 0., 0.), ori=(0., 0., 0., 1.)):
6539
+ """Transform a mesh.
6540
+
6541
+ Transform mesh vertices and normals to a new position and orientation using
6542
+ a position coordinate and rotation quaternion. Values `vertices` and
6543
+ `normals` must be the same shape. This is intended to be used when editing
6544
+ raw vertex data prior to rendering. Do not use this to change the
6545
+ configuration of an object while rendering.
6546
+
6547
+ Parameters
6548
+ ----------
6549
+ vertices : array_like
6550
+ Nx3 array of vertices.
6551
+ normals : array_like
6552
+ Nx3 array of normals.
6553
+ pos : array_like, optional
6554
+ Position vector to transform mesh vertices. If Nx3, `vertices` will be
6555
+ transformed by corresponding rows of `pos`.
6556
+ ori : array_like, optional
6557
+ Orientation quaternion in form [x, y, z, w]. If Nx4, `vertices` and
6558
+ `normals` will be transformed by corresponding rows of `ori`.
6559
+
6560
+ Returns
6561
+ -------
6562
+ tuple
6563
+ Transformed vertices and normals.
6564
+
6565
+ Examples
6566
+ --------
6567
+ Create and re-orient a plane to face upwards::
6568
+
6569
+ vertices, textureCoords, normals, faces = createPlane()
4568
6570
 
4569
6571
  # rotation quaternion
4570
6572
  qr = quatFromAxisAngle((1., 0., 0.), -90.0) # -90 degrees about +X axis
@@ -4581,31 +6583,39 @@ def transformMeshPosOri(vertices, normals, pos=(0., 0., 0.), ori=(0., 0., 0., 1.
4581
6583
  normals = np.ascontiguousarray(normals)
4582
6584
 
4583
6585
  if not np.allclose(pos, [0., 0., 0.]):
4584
- vertices = mt.transform(pos, ori, vertices)
6586
+ vertices = mt.transform(pos, ori, vertices, dtype=np.float32)
4585
6587
 
4586
6588
  if not np.allclose(ori, [0., 0., 0., 1.]):
4587
- normals = mt.applyQuat(ori, normals)
6589
+ normals = mt.applyQuat(ori, normals, dtype=np.float32)
4588
6590
 
4589
6591
  return vertices, normals
4590
6592
 
4591
6593
 
6594
+ # ------------------------------
6595
+ # Mesh editing and cleanup tools
6596
+ # ------------------------------
6597
+ #
6598
+
4592
6599
  def calculateVertexNormals(vertices, faces, shading='smooth'):
4593
6600
  """Calculate vertex normals given vertices and triangle faces.
4594
6601
 
4595
6602
  Finds all faces sharing a vertex index and sets its normal to either
4596
6603
  the face normal if `shading='flat'` or the average normals of adjacent
4597
- faces if `shading='smooth'`. Flat shading only works correctly if each
6604
+ faces if `shading='smooth'`. Note, this function does not convert between
6605
+ flat and smooth shading. Flat shading only works correctly if each
4598
6606
  vertex belongs to exactly one face.
4599
6607
 
4600
6608
  The direction of the normals are determined by the winding order of
4601
- triangles, assumed counter clock-wise (OpenGL default). Most model
6609
+ triangles, assumed counter clock-wise (OpenGL default). Most 3D model
4602
6610
  editing software exports using this convention. If not, winding orders
4603
6611
  can be reversed by calling::
4604
6612
 
4605
- faces = np.fliplr(faces)
6613
+ faces = numpy.fliplr(faces)
4606
6614
 
4607
- In some case, creases may appear if vertices are at the same location,
4608
- but do not share the same index.
6615
+ In some case when using 'smooth', creases may appear if vertices are at the
6616
+ same location, but do not share the same index. This may be desired in some
6617
+ cases, however one may use the :func:`smoothCreases` function computing
6618
+ normals to smooth out creases.
4609
6619
 
4610
6620
  Parameters
4611
6621
  ----------
@@ -4615,7 +6625,8 @@ def calculateVertexNormals(vertices, faces, shading='smooth'):
4615
6625
  Nx3 vertex indices.
4616
6626
  shading : str, optional
4617
6627
  Shading mode. Options are 'smooth' and 'flat'. Flat only works with
4618
- meshes where no vertex index is shared across faces.
6628
+ meshes where no vertex index is shared across faces, if not, the
6629
+ returned normals will be invalid.
4619
6630
 
4620
6631
  Returns
4621
6632
  -------
@@ -4635,7 +6646,7 @@ def calculateVertexNormals(vertices, faces, shading='smooth'):
4635
6646
  # compute surface normals for all faces
4636
6647
  faceNormals = mt.surfaceNormal(vertices[faces])
4637
6648
 
4638
- normals = []
6649
+ normals = [] # new list of normals to return
4639
6650
  if shading == 'flat':
4640
6651
  for vertexIdx in np.unique(faces):
4641
6652
  match, _ = np.where(faces == vertexIdx)
@@ -4646,7 +6657,611 @@ def calculateVertexNormals(vertices, faces, shading='smooth'):
4646
6657
  match, _ = np.where(faces == vertexIdx)
4647
6658
  normals.append(mt.vertexNormal(faceNormals[match, :]))
4648
6659
 
4649
- return np.ascontiguousarray(normals) + 0.0
6660
+ return np.ascontiguousarray(np.vstack(normals), np.float32) + 0.0
6661
+
6662
+
6663
+ def mergeVertices(vertices, faces, textureCoords=None, vertDist=0.0001,
6664
+ texDist=0.0001):
6665
+ """Simplify a mesh by removing redundant vertices.
6666
+
6667
+ This function simplifies a mesh by merging overlapping (doubled) vertices,
6668
+ welding together adjacent faces and removing sharp creases that appear when
6669
+ rendering. This is useful in cases where a mesh's triangles do not share
6670
+ vertices and one wishes to have it appear smoothly shaded when rendered. One
6671
+ can also use this function reduce the detail of a mesh, however the quality
6672
+ of the results may vary.
6673
+
6674
+ The position of the new vertex after merging will be the average position
6675
+ of the adjacent vertices. Re-indexed faces and recalculated normals are also
6676
+ returned with the cleaned-up vertex data. If texture coordinates are
6677
+ supplied, adjacent vertices will not be removed if their is a distance in
6678
+ texel space is greater than `texDist`. This avoids discontinuities in the
6679
+ texture of the simplified mesh.
6680
+
6681
+ Parameters
6682
+ ----------
6683
+ vertices : ndarray
6684
+ Nx3 array of vertex positions.
6685
+ faces : ndarray
6686
+ Nx3 integer array of face vertex indices.
6687
+ textureCoords : ndarray
6688
+ Nx2 array of texture coordinates.
6689
+ vertDist : float
6690
+ Maximum distance between two adjacent vertices to merge in scene units.
6691
+ texDist : float
6692
+ Maximum distance between texels to permit merging of vertices. If a
6693
+ vertex is within merging distance, it will be moved instead of merged.
6694
+
6695
+ Returns
6696
+ -------
6697
+ tuple
6698
+ Tuple containing newly computed vertices, normals and face indices. If
6699
+ `textureCoords` was specified, a new array of texture coordinates will
6700
+ be returned too at the second index.
6701
+
6702
+ Notes
6703
+ -----
6704
+ * This function only works on meshes consisting of triangle faces.
6705
+
6706
+ Examples
6707
+ --------
6708
+ Remove redundant vertices from a sphere::
6709
+
6710
+ vertices, textureCoords, normals, faces = gltools.createUVSphere()
6711
+ vertices, textureCoords, normals, faces = gltools.removeDoubles(
6712
+ vertices, faces, textureCoords)
6713
+
6714
+ Same but no texture coordinates are specified::
6715
+
6716
+ vertices, normals, faces = gltools.removeDoubles(vertices, faces)
6717
+
6718
+ """
6719
+ # keep track of vertices that we merged
6720
+ vertsProcessed = np.zeros((vertices.shape[0],), dtype=np.bool)
6721
+
6722
+ faces = faces.flatten() # existing faces but flattened
6723
+ # new array of faces that will get updated
6724
+ newFaces = np.zeros_like(faces, dtype=np.uint32)
6725
+
6726
+ # loop over all vertices in the original mesh
6727
+ newVerts = []
6728
+ newTexCoords = []
6729
+ lastProcIdx = 0 # last index processed, used to reindex
6730
+ for i, vertex in enumerate(vertices):
6731
+ if vertsProcessed[i]: # don't do merge check if already processed
6732
+ continue
6733
+
6734
+ # get the distance to all other vertices in mesh
6735
+ vertDists = mt.distance(vertex, vertices)
6736
+
6737
+ # get vertices that fall with the threshold distance
6738
+ toProcess = np.where(vertDists <= vertDist)[0]
6739
+
6740
+ # if all adjacent vertices were processed, move on to the next
6741
+ if np.all(vertsProcessed[toProcess]):
6742
+ continue
6743
+
6744
+ # if we have close verts and they have not been processed, merge them
6745
+ if len(toProcess) > 1:
6746
+ # If we have texture coords, merge those whose texture coords are
6747
+ # close. Move the vertex to the new location for any that are not.
6748
+ if textureCoords is not None:
6749
+ # create a new vertex by averaging out positions
6750
+ texCoordDists = mt.distance(textureCoords[i, :],
6751
+ textureCoords[toProcess, :])
6752
+
6753
+ # get the vertices to merge or move
6754
+ toMerge = toProcess[texCoordDists <= texDist]
6755
+ toMove = toProcess[texCoordDists > texDist]
6756
+
6757
+ # compute mean positions
6758
+ newPos = np.mean(vertices[toMerge, :], axis=0)
6759
+ newTexCoord = np.mean(textureCoords[toMerge, :], axis=0)
6760
+
6761
+ newVerts.append(newPos)
6762
+ newTexCoords.append(newTexCoord)
6763
+ newFaces[np.in1d(faces, toMerge).nonzero()[0]] = lastProcIdx
6764
+
6765
+ # handle vertices that were moved
6766
+ for j, idx in enumerate(toMove):
6767
+ newVerts.append(newPos)
6768
+ newTexCoords.append(textureCoords[idx, :])
6769
+ newFaces[np.argwhere(faces == idx)] = lastProcIdx + j
6770
+
6771
+ lastProcIdx += len(toMove)
6772
+
6773
+ vertsProcessed[toProcess] = 1 # update verts we processed
6774
+
6775
+ else:
6776
+ newPos = np.mean(vertices[toProcess, :], axis=0)
6777
+ newVerts.append(newPos)
6778
+ newFaces[np.in1d(faces, toProcess).nonzero()[0]] = lastProcIdx
6779
+ vertsProcessed[toProcess] = 1 # update verts we processed
6780
+
6781
+ else:
6782
+ # single vertices need to be added too
6783
+ newVerts.append(vertex)
6784
+ if textureCoords is not None:
6785
+ newTexCoords.append(textureCoords[i, :])
6786
+ vertsProcessed[i] = 1 # update merged list
6787
+ newFaces[np.argwhere(faces == i)] = lastProcIdx
6788
+
6789
+ lastProcIdx += 1
6790
+
6791
+ # all vertices have been processed, exit loop early
6792
+ if np.all(vertsProcessed):
6793
+ break
6794
+
6795
+ # create new output arrays
6796
+ newVerts = np.ascontiguousarray(np.vstack(newVerts), dtype=np.float32)
6797
+ newFaces = np.ascontiguousarray(newFaces.reshape((-1, 3)), dtype=np.uint32)
6798
+ newNormals = calculateVertexNormals(newVerts, newFaces, 'smooth')
6799
+
6800
+ if textureCoords is not None:
6801
+ newTexCoords = np.ascontiguousarray(
6802
+ np.vstack(newTexCoords), dtype=np.float32)
6803
+ toReturn = (newVerts, newTexCoords, newNormals, newFaces)
6804
+ else:
6805
+ toReturn = (newVerts, newNormals, newFaces)
6806
+
6807
+ return toReturn
6808
+
6809
+
6810
+ def smoothCreases(vertices, normals, vertDist=0.0001):
6811
+ """Remove creases caused by misaligned surface normals.
6812
+
6813
+ A problem arises where surface normals are not correctly interpolated across
6814
+ the edge where two faces meet, resulting in a visible 'crease' or sharp
6815
+ discontinuity in shading. This is usually caused by the normals of the
6816
+ overlapping vertices forming the edge being mis-aligned (not pointing in the
6817
+ same direction).
6818
+
6819
+ If you notice these crease artifacts are present in your mesh *after*
6820
+ computing surface normals, you can use this function to smooth them out.
6821
+
6822
+ Parameters
6823
+ ----------
6824
+ vertices : ndarray
6825
+ Nx3 array of vertex coordinates.
6826
+ normals : ndarray
6827
+ Nx3 array of vertex normals.
6828
+ vertDist : float
6829
+ Maximum distance between vertices to average. Avoid using large numbers
6830
+ here, vertices to be smoothed should be overlapping. This distance
6831
+ should be as small as possible, just enough to account for numeric
6832
+ rounding errors between vertices intended which would otherwise be at
6833
+ the exact same location.
6834
+
6835
+ Returns
6836
+ -------
6837
+ ndarray
6838
+ Array of smoothed surface normals with the same shape as `normals`.
6839
+
6840
+ """
6841
+ newNormals = normals.copy()
6842
+
6843
+ # keep track of vertices that we processed
6844
+ vertsProcessed = np.zeros((vertices.shape[0],), dtype=np.bool)
6845
+
6846
+ for i, vertex in enumerate(vertices):
6847
+ if vertsProcessed[i]: # don't do merge check if already processed
6848
+ continue
6849
+
6850
+ # get the distance to all other vertices in mesh
6851
+ dist = mt.distance(vertex, vertices)
6852
+
6853
+ # get vertices that fall with the threshold distance
6854
+ adjacentIdx = list(np.where(dist <= vertDist)[0])
6855
+
6856
+ if np.all(vertsProcessed[adjacentIdx]):
6857
+ continue
6858
+
6859
+ # now get their normals and average them
6860
+ if len(adjacentIdx) > 1:
6861
+ toAverage = vertices[adjacentIdx, :]
6862
+ newNormal = np.mean(toAverage, axis=0)
6863
+
6864
+ # normalize
6865
+ newNormal = mt.normalize(newNormal, out=newNormal)
6866
+
6867
+ # overwrite the normals we used to compute this one
6868
+ newNormals[adjacentIdx, :] = newNormal
6869
+
6870
+ # flag these normals as used
6871
+ vertsProcessed[adjacentIdx] = 1
6872
+
6873
+ return newNormals
6874
+
6875
+
6876
+ def flipFaces(normals, faces):
6877
+ """Change the winding order of face indices.
6878
+
6879
+ OpenGL uses the winding order of face vertices to determine which side of
6880
+ the face is either the front and back. This function reverses the winding
6881
+ order of face indices and flips vertex normals so faces can be correctly
6882
+ shaded.
6883
+
6884
+ Parameters
6885
+ ----------
6886
+ normals : ndarray
6887
+ Nx3 array of surface normals.
6888
+ faces : ndarray
6889
+ Nx3 array of face indices.
6890
+
6891
+ Returns
6892
+ -------
6893
+ ndarray
6894
+ Face indices with winding order reversed.
6895
+
6896
+ Examples
6897
+ --------
6898
+ Flip faces and normals of a box mesh so it can be viewed and lit from the
6899
+ inside::
6900
+
6901
+ vertices, texCoords, normals, faces = createBox((5, 5, 5))
6902
+ faces = flipFaces(faces)
6903
+
6904
+ """
6905
+ normals = -normals # invert normal vectors
6906
+ faces = np.fliplr(faces)
6907
+
6908
+ return normals, faces
6909
+
6910
+
6911
+ def interleaveAttributes(attribArrays):
6912
+ """Interleave vertex attributes into a single array.
6913
+
6914
+ Interleave vertex attributes into a single array for use with OpenGL's
6915
+ vertex array objects. This function is useful when creating a VBO from
6916
+ separate arrays of vertex positions, normals, and texture coordinates.
6917
+
6918
+ Parameters
6919
+ ----------
6920
+ attribArrays : list of array_like
6921
+ List of arrays containing vertex attributes. Each array must have the
6922
+ same number of rows.
6923
+
6924
+ Returns
6925
+ -------
6926
+ tuple
6927
+ A tuple containing the interleaved vertex attribute array, a list of
6928
+ attribute sizes, and a list of attribute offsets.
6929
+
6930
+ Examples
6931
+ --------
6932
+ Interleave vertex attributes for use with a VAO::
6933
+
6934
+ vertices, textureCoords, normals, faces = createBox()
6935
+ interleaved, sizes, offsets = interleaveAttributes(
6936
+ [vertices, textureCoords, normals])
6937
+
6938
+ # create a VBO with interleaved attributes
6939
+ vboInterleaved = createVBO(interleaved)
6940
+
6941
+ # ... before rendering, set the attribute pointers
6942
+ GL.glBindBuffer(vboInterleaved.target, vboInterleaved.name)
6943
+ for i, attrib in enumerate([0, 8, 3]):
6944
+ gltools.setVertexAttribPointer(
6945
+ attrib, vboInterleaved, size=sizes[i], offset=offsets[i])
6946
+
6947
+ """
6948
+ # get the number of rows in the first array
6949
+ nRows = attribArrays[0].shape[0]
6950
+
6951
+ # check if all arrays have the same number of rows
6952
+ if any([i.shape[0] != nRows for i in attribArrays]):
6953
+ raise ValueError("All arrays must have the same number of rows.")
6954
+
6955
+ # get a list of attribute widths
6956
+ sizes = [i.shape[1] for i in attribArrays]
6957
+ offsets = [0] + [sum(sizes[:i]) for i in range(1, len(sizes))]
6958
+
6959
+ # combine all arrays horizontally
6960
+ toReturn = np.hstack(attribArrays)
6961
+
6962
+ return np.ascontiguousarray(toReturn, dtype=np.float32), sizes, offsets
6963
+
6964
+
6965
+ def generateTexCoords(vertices):
6966
+ """Generate texture coordinates for a mesh.
6967
+
6968
+ Generate texture coordinates for a mesh by normalizing the vertex positions
6969
+ to the bounding box of the mesh.
6970
+
6971
+ Parameters
6972
+ ----------
6973
+ vertices : ndarray
6974
+ Nx2 or Nx3 array of vertex positions.
6975
+
6976
+ Returns
6977
+ -------
6978
+ ndarray
6979
+ Nx2 array of normalized texture coordinates.
6980
+
6981
+ Examples
6982
+ --------
6983
+ Generate texture coordinates for a box mesh::
6984
+
6985
+ vertices, textureCoords, normals, faces = createBox()
6986
+ texCoords = generateTexCoords(vertices)
6987
+
6988
+ """
6989
+ # normalize the vertex positions to the bounding box
6990
+ texCoords = (vertices - np.min(vertices, axis=0)) / np.ptp(vertices, axis=0)
6991
+
6992
+ return np.ascontiguousarray(texCoords, dtype=np.float32)
6993
+
6994
+
6995
+ def tesselate(points, mode='triangle', config=None):
6996
+ """Tesselate (or fill) a 2D polygon edge loop.
6997
+
6998
+ Tesselate a 2D polygon defined by a set of vertices. This creates a 'filled'
6999
+ polygon where the vertices are connected by a set of triangles. The function
7000
+ will return a tesselated mesh with vertex positions, surface normals, and
7001
+ face indices. Texture coordinates are generated by normalizing the vertex
7002
+ positions.
7003
+
7004
+ Parameters
7005
+ ----------
7006
+ points : ndarray
7007
+ Nx2 array of vertex positions of the outer boundary.
7008
+ mode : str, optional
7009
+ Tesselation mode (algorithm) to use. Options are 'triangle', 'delaunay',
7010
+ 'fan' or 'simple'. Default is 'triangle' (recommended) which is the most
7011
+ robust method that can handle concave polygons and holes. Use 'fan' for
7012
+ equilateral polygons.
7013
+ config : dict, optional
7014
+ Configuration options for the tesselation. This can be used to specify
7015
+ additional options for the tesselation algorithm.
7016
+
7017
+ Returns
7018
+ -------
7019
+ tuple
7020
+ A tuple containing the vertex positions, surface normals, texture
7021
+ coordinates, and face indices of the tesselated mesh.
7022
+
7023
+ Examples
7024
+ --------
7025
+ Tesselate a simple square::
7026
+
7027
+ vertices = np.array([[0, 0], [1, 0], [1, 1], [0, 1]])
7028
+ vertices, normals, texCoords, faces = tesselate(points)
7029
+
7030
+ Notes
7031
+ -----
7032
+ * The 'triangle' mode uses the Triangle library (via MeshPy) to tesselate
7033
+ the polygon, see: https://www.cs.cmu.edu/~quake/triangle.html
7034
+ * The 'delaunay' mode uses the `scipy.spatial.Delaunay` for tesselation
7035
+ (using the Qhull library). However, it cannot handle holes or concave
7036
+ polygons.
7037
+ * The 'fan' mode creates a fan tesselation where the first vertex is the
7038
+ center of the fan and the rest are the outer boundary. This is useful for
7039
+ creating simple, nonconcave shapes like circles.
7040
+
7041
+ """
7042
+ config = config or dict() # ensure we have a config dictionary
7043
+
7044
+ nPoints = len(points)
7045
+ if nPoints < 3:
7046
+ raise ValueError(
7047
+ "At least 3 points are required to tesselate a polygon.")
7048
+ elif nPoints == 3:
7049
+ # if we only have 3 points, we can't tesselate the polygon
7050
+ vertices = np.array(points, dtype=np.float32)
7051
+ faces = np.array([[0, 1, 2]], dtype=np.uint32)
7052
+ normals = np.ascontiguousarray(
7053
+ np.tile([0., 0., -1.], (faces.shape[0], 1)), dtype=np.float32)
7054
+ texCoords = generateTexCoords(vertices)
7055
+
7056
+ return vertices, normals, texCoords, faces
7057
+
7058
+ # perform tesselation based on the mode
7059
+ if mode == 'triangle' or mode == 'meshpy':
7060
+ # trinagulation using meshpy (triangle)
7061
+ from meshpy.triangle import MeshInfo, build
7062
+
7063
+ # create a mesh info object
7064
+ meshInfo = MeshInfo()
7065
+ meshInfo.set_points(points)
7066
+
7067
+ # compute facets
7068
+ facetEnd = len(points) - 1
7069
+ facets = [(i, i + 1) for i in range(0, facetEnd)] + [(facetEnd, 0)]
7070
+ meshInfo.set_facets(facets)
7071
+
7072
+ # build the mesh
7073
+ mesh = build(meshInfo, **config)
7074
+
7075
+ # get the vertices and faces
7076
+ vertices = mesh.points
7077
+ faces = mesh.elements
7078
+
7079
+ elif mode == 'fan' or mode == 'equilateral':
7080
+ # This mode creates a fan tesselation where the first vertex is the
7081
+ # centroid of the polygon and the rest are the outer boundary. Works
7082
+ # well for equilateral polygons that have not concavities. This is
7083
+ # probably the fastest method to tesselate an equilateral polygon with
7084
+ # good results.
7085
+
7086
+ # find the centroid of the polygon
7087
+ centroid = np.mean(points, axis=0)
7088
+
7089
+ # add the centroid to the list of vertices
7090
+ vertices = np.vstack((centroid, points))
7091
+
7092
+ # generate faces
7093
+ faces = np.zeros((len(points), 3), dtype=np.uint32)
7094
+ faces[:, 0] = 0
7095
+ faces[:, 1] = np.arange(1, len(points) + 1)
7096
+ faces[:, 2] = np.roll(faces[:, 1], 1)
7097
+
7098
+ elif mode == 'delaunay' or mode == 'scipy':
7099
+ # Delaunay triangulation using scipy. This triangulates the polygon
7100
+ # using the Qhull library. This method is reasonably fast however it
7101
+ # outputs the convex hull of the polygon which may not be desired.
7102
+
7103
+ # do a delaunay triangulation
7104
+ from scipy.spatial import Delaunay
7105
+
7106
+ # create a delaunay triangulation
7107
+ result = Delaunay(points, **config)
7108
+
7109
+ # get the face indices
7110
+ vertices = result.points
7111
+ faces = result.simplices
7112
+
7113
+ elif mode == 'simple':
7114
+ # Simple tesselation mode where the vertices are connected in a simple
7115
+ # fan pattern from the first vertex in the provided array. This is
7116
+ # useful for simple shapes like circles or squares.
7117
+ vertices = np.vstack((points, points[0]))
7118
+ faces = np.array(
7119
+ [[0, i + 1, i + 2] for i in range(len(points) - 1)])
7120
+
7121
+ else:
7122
+ raise ValueError("Invalid mode '{}' specified.".format(mode))
7123
+
7124
+ # compute surface normals for all faces
7125
+ vertices = np.ascontiguousarray(vertices, dtype=np.float32)
7126
+ faces = np.ascontiguousarray(faces, dtype=np.uint32)
7127
+
7128
+ # generate normals for each vertex, facing outwards
7129
+ normals = np.ascontiguousarray(
7130
+ np.tile([0., 0., -1.], (faces.shape[0], 1)),
7131
+ dtype=np.float32)
7132
+
7133
+ # generate texture coordinates which are normalized vertex positions
7134
+ texCoords = generateTexCoords(vertices)
7135
+
7136
+ return vertices, normals, texCoords, faces
7137
+
7138
+
7139
+ def mergeMeshes(meshData):
7140
+ """Merge multiple meshes into a single mesh.
7141
+
7142
+ Merge multiple meshes into a single mesh by combining their vertex positions,
7143
+ normals, texture coordinates, and face indices.
7144
+
7145
+ Parameters
7146
+ ----------
7147
+ meshData : list of tuple
7148
+ List of tuples containing vertex positions, normals, texture coordinates,
7149
+ and face indices for each mesh to merge. Each tuple must contain the
7150
+ vertex positions, and may optionally contain the normals and texture
7151
+ coordinates. The face indices must be provided.
7152
+
7153
+ Returns
7154
+ -------
7155
+ tuple
7156
+ A tuple containing the merged vertex positions, normals, texture
7157
+ coordinates, and face indices. If `normals` or `texCoords` are not
7158
+ provided, they will be set to `None`.
7159
+
7160
+ Examples
7161
+ --------
7162
+ Merge two meshes into a single mesh::
7163
+
7164
+ vertices1, normals1, texCoords1, faces1 = createBox()
7165
+ vertices2, normals2, texCoords2, faces2 = createUVSphere()
7166
+
7167
+ # merge the two meshes
7168
+ vertices, normals, texCoords, faces = mergeMeshes([
7169
+ (vertices1, normals1, texCoords1, faces1),
7170
+ (vertices2, normals2, texCoords2, faces2)])
7171
+
7172
+ Create a tube using primatives::
7173
+
7174
+ tubeOuter = createCylinder(1.0, 1.0, 16, 1)
7175
+ tubeInner = createCylinder(0.5, 1.0, 16, 1)
7176
+
7177
+ # merge the tube parts
7178
+ vertices, normals, texCoords, faces = mergeMeshes(
7179
+ tubeOuter, tubeInner])
7180
+
7181
+ """
7182
+ vertices = np.ascontiguousarray(
7183
+ np.vstack([i[0] for i in meshData]), dtype=np.float32)
7184
+
7185
+ newFaces = []
7186
+ offset = 0
7187
+ for i in meshData:
7188
+ newFaces.append(i[3] + offset)
7189
+ offset += np.max(i[3]) + 1
7190
+
7191
+ faces = np.ascontiguousarray(np.vstack(newFaces), dtype=np.uint32)
7192
+
7193
+ # check if normals and texture coordinates are provided
7194
+ if all([i[1] is not None for i in meshData]):
7195
+ normals = np.ascontiguousarray(
7196
+ np.vstack([i[1] for i in meshData]), dtype=np.float32)
7197
+ else:
7198
+ normals = None
7199
+
7200
+ if all([i[2] is not None for i in meshData]):
7201
+ texCoords = np.ascontiguousarray(
7202
+ np.vstack([i[2] for i in meshData]), dtype=np.float32)
7203
+ else:
7204
+ texCoords = None
7205
+
7206
+ return vertices, normals, texCoords, faces
7207
+
7208
+
7209
+ def nextPowerOfTwo(value):
7210
+ """Compute the next power of two for a given value.
7211
+
7212
+ Parameters
7213
+ ----------
7214
+ value : int
7215
+ The value to compute the next power of two for.
7216
+
7217
+ Returns
7218
+ -------
7219
+ int
7220
+ The next power of two for the given value.
7221
+
7222
+ """
7223
+ return 2 ** int(np.ceil(np.log2(value)))
7224
+
7225
+
7226
+ def fitTextureToPowerOfTwo(width, height, square=False):
7227
+ """Determine the horizontal and vertical dimensions of a image buffer that
7228
+ can contain the specified width and height, fitted to the next power of two.
7229
+
7230
+ Parameters
7231
+ ----------
7232
+ width : int
7233
+ The width of the texture in pixels.
7234
+ height : int
7235
+ The height of the texture in pixels.
7236
+ square : bool, optional
7237
+ If True, the texture will be fitted to the next power of two in both
7238
+ dimensions. Default is False.
7239
+
7240
+ Returns
7241
+ -------
7242
+ tuple
7243
+ A tuple containing the width and height of the texture fitted to the
7244
+ next power of two.
7245
+
7246
+ Raises
7247
+ ------
7248
+ ValueError
7249
+ If the texture dimensions exceed the maximum texture size
7250
+ supported by the OpenGL implementation.
7251
+
7252
+ """
7253
+ maxTexSize = getOpenGLInfo().maxTextureSize
7254
+
7255
+ newWidth = nextPowerOfTwo(width)
7256
+ newHeight = nextPowerOfTwo(height)
7257
+
7258
+ if newWidth > maxTexSize or newHeight > maxTexSize:
7259
+ raise ValueError("Texture dimensions exceed maximum texture size.")
7260
+
7261
+ if square:
7262
+ newWidth = newHeight = max(newWidth, newHeight)
7263
+
7264
+ return newWidth, newHeight
4650
7265
 
4651
7266
 
4652
7267
  # -----------------------------
@@ -4718,7 +7333,7 @@ def getModelViewMatrix():
4718
7333
 
4719
7334
  """
4720
7335
  modelview = np.zeros((4, 4), dtype=np.float32)
4721
-
7336
+
4722
7337
  GL.glGetFloatv(GL.GL_MODELVIEW_MATRIX, modelview.ctypes.data_as(
4723
7338
  ctypes.POINTER(ctypes.c_float)))
4724
7339
 
@@ -4746,214 +7361,5 @@ def getProjectionMatrix():
4746
7361
  return proj
4747
7362
 
4748
7363
 
4749
- # OpenGL information type
4750
- OpenGLInfo = namedtuple(
4751
- 'OpenGLInfo',
4752
- ['vendor',
4753
- 'renderer',
4754
- 'version',
4755
- 'majorVersion',
4756
- 'minorVersion',
4757
- 'doubleBuffer',
4758
- 'maxTextureSize',
4759
- 'stereo',
4760
- 'maxSamples',
4761
- 'extensions',
4762
- 'userData'])
4763
-
4764
-
4765
- def getOpenGLInfo():
4766
- """Get general information about the OpenGL implementation on this machine.
4767
- This should provide a consistent means of doing so regardless of the OpenGL
4768
- interface we are using.
4769
-
4770
- Returns are dictionary with the following fields::
4771
-
4772
- vendor, renderer, version, majorVersion, minorVersion, doubleBuffer,
4773
- maxTextureSize, stereo, maxSamples, extensions
4774
-
4775
- Supported extensions are returned as a list in the 'extensions' field. You
4776
- can check if a platform supports an extension by checking the membership of
4777
- the extension name in that list.
4778
-
4779
- Returns
4780
- -------
4781
- OpenGLInfo
4782
-
4783
- """
4784
- return OpenGLInfo(getString(GL.GL_VENDOR),
4785
- getString(GL.GL_RENDERER),
4786
- getString(GL.GL_VERSION),
4787
- getIntegerv(GL.GL_MAJOR_VERSION),
4788
- getIntegerv(GL.GL_MINOR_VERSION),
4789
- getIntegerv(GL.GL_DOUBLEBUFFER),
4790
- getIntegerv(GL.GL_MAX_TEXTURE_SIZE),
4791
- getIntegerv(GL.GL_STEREO),
4792
- getIntegerv(GL.GL_MAX_SAMPLES),
4793
- [i for i in getString(GL.GL_EXTENSIONS).split(' ')],
4794
- dict())
4795
-
4796
-
4797
- # ---------------------
4798
- # OpenGL/VRML Materials
4799
- # ---------------------
4800
- #
4801
- # A collection of pre-defined materials for stimuli. Keep in mind that these
4802
- # materials only approximate real-world equivalents. Values were obtained from
4803
- # http://devernay.free.fr/cours/opengl/materials.html (08/24/18). There are four
4804
- # material libraries to use, where individual material descriptors are accessed
4805
- # via property names.
4806
- #
4807
- # Usage:
4808
- #
4809
- # useMaterial(metalMaterials.gold)
4810
- # drawVAO(myObject)
4811
- # ...
4812
- #
4813
- mineralMaterials = namedtuple(
4814
- 'mineralMaterials',
4815
- ['emerald', 'jade', 'obsidian', 'pearl', 'ruby', 'turquoise'])(
4816
- createMaterial(
4817
- [(GL.GL_AMBIENT, (0.0215, 0.1745, 0.0215, 1.0)),
4818
- (GL.GL_DIFFUSE, (0.07568, 0.61424, 0.07568, 1.0)),
4819
- (GL.GL_SPECULAR, (0.633, 0.727811, 0.633, 1.0)),
4820
- (GL.GL_SHININESS, 0.6 * 128.0)]),
4821
- createMaterial(
4822
- [(GL.GL_AMBIENT, (0.135, 0.2225, 0.1575, 1.0)),
4823
- (GL.GL_DIFFUSE, (0.54, 0.89, 0.63, 1.0)),
4824
- (GL.GL_SPECULAR, (0.316228, 0.316228, 0.316228, 1.0)),
4825
- (GL.GL_SHININESS, 0.1 * 128.0)]),
4826
- createMaterial(
4827
- [(GL.GL_AMBIENT, (0.05375, 0.05, 0.06625, 1.0)),
4828
- (GL.GL_DIFFUSE, (0.18275, 0.17, 0.22525, 1.0)),
4829
- (GL.GL_SPECULAR, (0.332741, 0.328634, 0.346435, 1.0)),
4830
- (GL.GL_SHININESS, 0.3 * 128.0)]),
4831
- createMaterial(
4832
- [(GL.GL_AMBIENT, (0.25, 0.20725, 0.20725, 1.0)),
4833
- (GL.GL_DIFFUSE, (1, 0.829, 0.829, 1.0)),
4834
- (GL.GL_SPECULAR, (0.296648, 0.296648, 0.296648, 1.0)),
4835
- (GL.GL_SHININESS, 0.088 * 128.0)]),
4836
- createMaterial(
4837
- [(GL.GL_AMBIENT, (0.1745, 0.01175, 0.01175, 1.0)),
4838
- (GL.GL_DIFFUSE, (0.61424, 0.04136, 0.04136, 1.0)),
4839
- (GL.GL_SPECULAR, (0.727811, 0.626959, 0.626959, 1.0)),
4840
- (GL.GL_SHININESS, 0.6 * 128.0)]),
4841
- createMaterial(
4842
- [(GL.GL_AMBIENT, (0.1, 0.18725, 0.1745, 1.0)),
4843
- (GL.GL_DIFFUSE, (0.396, 0.74151, 0.69102, 1.0)),
4844
- (GL.GL_SPECULAR, (0.297254, 0.30829, 0.306678, 1.0)),
4845
- (GL.GL_SHININESS, 0.1 * 128.0)])
4846
- )
4847
-
4848
- metalMaterials = namedtuple(
4849
- 'metalMaterials',
4850
- ['brass', 'bronze', 'chrome', 'copper', 'gold', 'silver'])(
4851
- createMaterial(
4852
- [(GL.GL_AMBIENT, (0.329412, 0.223529, 0.027451, 1.0)),
4853
- (GL.GL_DIFFUSE, (0.780392, 0.568627, 0.113725, 1.0)),
4854
- (GL.GL_SPECULAR, (0.992157, 0.941176, 0.807843, 1.0)),
4855
- (GL.GL_SHININESS, 0.21794872 * 128.0)]),
4856
- createMaterial(
4857
- [(GL.GL_AMBIENT, (0.2125, 0.1275, 0.054, 1.0)),
4858
- (GL.GL_DIFFUSE, (0.714, 0.4284, 0.18144, 1.0)),
4859
- (GL.GL_SPECULAR, (0.393548, 0.271906, 0.166721, 1.0)),
4860
- (GL.GL_SHININESS, 0.2 * 128.0)]),
4861
- createMaterial(
4862
- [(GL.GL_AMBIENT, (0.25, 0.25, 0.25, 1.0)),
4863
- (GL.GL_DIFFUSE, (0.4, 0.4, 0.4, 1.0)),
4864
- (GL.GL_SPECULAR, (0.774597, 0.774597, 0.774597, 1.0)),
4865
- (GL.GL_SHININESS, 0.6 * 128.0)]),
4866
- createMaterial(
4867
- [(GL.GL_AMBIENT, (0.19125, 0.0735, 0.0225, 1.0)),
4868
- (GL.GL_DIFFUSE, (0.7038, 0.27048, 0.0828, 1.0)),
4869
- (GL.GL_SPECULAR, (0.256777, 0.137622, 0.086014, 1.0)),
4870
- (GL.GL_SHININESS, 0.1 * 128.0)]),
4871
- createMaterial(
4872
- [(GL.GL_AMBIENT, (0.24725, 0.1995, 0.0745, 1.0)),
4873
- (GL.GL_DIFFUSE, (0.75164, 0.60648, 0.22648, 1.0)),
4874
- (GL.GL_SPECULAR, (0.628281, 0.555802, 0.366065, 1.0)),
4875
- (GL.GL_SHININESS, 0.4 * 128.0)]),
4876
- createMaterial(
4877
- [(GL.GL_AMBIENT, (0.19225, 0.19225, 0.19225, 1.0)),
4878
- (GL.GL_DIFFUSE, (0.50754, 0.50754, 0.50754, 1.0)),
4879
- (GL.GL_SPECULAR, (0.508273, 0.508273, 0.508273, 1.0)),
4880
- (GL.GL_SHININESS, 0.4 * 128.0)])
4881
- )
4882
-
4883
- plasticMaterials = namedtuple(
4884
- 'plasticMaterials',
4885
- ['black', 'cyan', 'green', 'red', 'white', 'yellow'])(
4886
- createMaterial(
4887
- [(GL.GL_AMBIENT, (0, 0, 0, 1.0)),
4888
- (GL.GL_DIFFUSE, (0.01, 0.01, 0.01, 1.0)),
4889
- (GL.GL_SPECULAR, (0.5, 0.5, 0.5, 1.0)),
4890
- (GL.GL_SHININESS, 0.25 * 128.0)]),
4891
- createMaterial(
4892
- [(GL.GL_AMBIENT, (0, 0.1, 0.06, 1.0)),
4893
- (GL.GL_DIFFUSE, (0.06, 0, 0.50980392, 1.0)),
4894
- (GL.GL_SPECULAR, (0.50196078, 0.50196078, 0.50196078, 1.0)),
4895
- (GL.GL_SHININESS, 0.25 * 128.0)]),
4896
- createMaterial(
4897
- [(GL.GL_AMBIENT, (0, 0, 0, 1.0)),
4898
- (GL.GL_DIFFUSE, (0.1, 0.35, 0.1, 1.0)),
4899
- (GL.GL_SPECULAR, (0.45, 0.55, 0.45, 1.0)),
4900
- (GL.GL_SHININESS, 0.25 * 128.0)]),
4901
- createMaterial(
4902
- [(GL.GL_AMBIENT, (0, 0, 0, 1.0)),
4903
- (GL.GL_DIFFUSE, (0.5, 0, 0, 1.0)),
4904
- (GL.GL_SPECULAR, (0.7, 0.6, 0.6, 1.0)),
4905
- (GL.GL_SHININESS, 0.25 * 128.0)]),
4906
- createMaterial(
4907
- [(GL.GL_AMBIENT, (0, 0, 0, 1.0)),
4908
- (GL.GL_DIFFUSE, (0.55, 0.55, 0.55, 1.0)),
4909
- (GL.GL_SPECULAR, (0.7, 0.7, 0.7, 1.0)),
4910
- (GL.GL_SHININESS, 0.25 * 128.0)]),
4911
- createMaterial(
4912
- [(GL.GL_AMBIENT, (0, 0, 0, 1.0)),
4913
- (GL.GL_DIFFUSE, (0.5, 0.5, 0, 1.0)),
4914
- (GL.GL_SPECULAR, (0.6, 0.6, 0.5, 1.0)),
4915
- (GL.GL_SHININESS, 0.25 * 128.0)])
4916
- )
4917
-
4918
- rubberMaterials = namedtuple(
4919
- 'rubberMaterials',
4920
- ['black', 'cyan', 'green', 'red', 'white', 'yellow'])(
4921
- createMaterial(
4922
- [(GL.GL_AMBIENT, (0.02, 0.02, 0.02, 1.0)),
4923
- (GL.GL_DIFFUSE, (0.01, 0.01, 0.01, 1.0)),
4924
- (GL.GL_SPECULAR, (0.4, 0.4, 0.4, 1.0)),
4925
- (GL.GL_SHININESS, 0.078125 * 128.0)]),
4926
- createMaterial(
4927
- [(GL.GL_AMBIENT, (0, 0.05, 0.05, 1.0)),
4928
- (GL.GL_DIFFUSE, (0.4, 0.5, 0.5, 1.0)),
4929
- (GL.GL_SPECULAR, (0.04, 0.7, 0.7, 1.0)),
4930
- (GL.GL_SHININESS, 0.078125 * 128.0)]),
4931
- createMaterial(
4932
- [(GL.GL_AMBIENT, (0, 0.05, 0, 1.0)),
4933
- (GL.GL_DIFFUSE, (0.4, 0.5, 0.4, 1.0)),
4934
- (GL.GL_SPECULAR, (0.04, 0.7, 0.04, 1.0)),
4935
- (GL.GL_SHININESS, 0.078125 * 128.0)]),
4936
- createMaterial(
4937
- [(GL.GL_AMBIENT, (0.05, 0, 0, 1.0)),
4938
- (GL.GL_DIFFUSE, (0.5, 0.4, 0.4, 1.0)),
4939
- (GL.GL_SPECULAR, (0.7, 0.04, 0.04, 1.0)),
4940
- (GL.GL_SHININESS, 0.078125 * 128.0)]),
4941
- createMaterial(
4942
- [(GL.GL_AMBIENT, (0.05, 0.05, 0.05, 1.0)),
4943
- (GL.GL_DIFFUSE, (0.5, 0.5, 0.5, 1.0)),
4944
- (GL.GL_SPECULAR, (0.7, 0.7, 0.7, 1.0)),
4945
- (GL.GL_SHININESS, 0.078125 * 128.0)]),
4946
- createMaterial(
4947
- [(GL.GL_AMBIENT, (0.05, 0.05, 0, 1.0)),
4948
- (GL.GL_DIFFUSE, (0.5, 0.5, 0.4, 1.0)),
4949
- (GL.GL_SPECULAR, (0.7, 0.7, 0.04, 1.0)),
4950
- (GL.GL_SHININESS, 0.078125 * 128.0)])
4951
- )
4952
-
4953
- # default material according to the OpenGL spec.
4954
- defaultMaterial = createMaterial(
4955
- [(GL.GL_AMBIENT, (0.2, 0.2, 0.2, 1.0)),
4956
- (GL.GL_DIFFUSE, (0.8, 0.8, 0.8, 1.0)),
4957
- (GL.GL_SPECULAR, (0.0, 0.0, 0.0, 1.0)),
4958
- (GL.GL_EMISSION, (0.0, 0.0, 0.0, 1.0)),
4959
- (GL.GL_SHININESS, 0)])
7364
+ if __name__ == "__main__":
7365
+ pass