OpenGLContext 3.0.0a1__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.
Files changed (417) hide show
  1. OpenGLContext/__init__.py +151 -0
  2. OpenGLContext/arrays.py +2 -0
  3. OpenGLContext/atlas.py +300 -0
  4. OpenGLContext/audio/__init__.py +32 -0
  5. OpenGLContext/audio/scene.py +130 -0
  6. OpenGLContext/audio/settings.py +82 -0
  7. OpenGLContext/bin/__init__.py +0 -0
  8. OpenGLContext/bin/choosecontext.py +224 -0
  9. OpenGLContext/bin/choosefonts.py +209 -0
  10. OpenGLContext/bin/gltest.py +121 -0
  11. OpenGLContext/bin/gltf_demo.py +492 -0
  12. OpenGLContext/bin/gltf_regression.py +645 -0
  13. OpenGLContext/bin/gltf_view.py +30 -0
  14. OpenGLContext/bin/keyboardevents.py +93 -0
  15. OpenGLContext/bin/lorentz.py +109 -0
  16. OpenGLContext/bin/physics_cook.py +81 -0
  17. OpenGLContext/bin/profile_view.py +38 -0
  18. OpenGLContext/bin/terrain_view.py +581 -0
  19. OpenGLContext/bin/tiles_view.py +39 -0
  20. OpenGLContext/bin/ui_demo.py +228 -0
  21. OpenGLContext/bin/view.py +341 -0
  22. OpenGLContext/bin/vrml_view.py +42 -0
  23. OpenGLContext/capture.py +125 -0
  24. OpenGLContext/context.py +1253 -0
  25. OpenGLContext/contextconfig.py +177 -0
  26. OpenGLContext/contextdefinition.py +275 -0
  27. OpenGLContext/debug/__init__.py +6 -0
  28. OpenGLContext/debug/bufferimage.py +80 -0
  29. OpenGLContext/debug/leaks.py +13 -0
  30. OpenGLContext/debug/logcontext.py +9 -0
  31. OpenGLContext/debug/logs.py +15 -0
  32. OpenGLContext/debug/state.py +329 -0
  33. OpenGLContext/displaylist.py +69 -0
  34. OpenGLContext/doinchildmatrix.py +34 -0
  35. OpenGLContext/drawcube.py +58 -0
  36. OpenGLContext/events/__init__.py +10 -0
  37. OpenGLContext/events/default_config.py +29 -0
  38. OpenGLContext/events/event.py +65 -0
  39. OpenGLContext/events/eventhandlermixin.py +214 -0
  40. OpenGLContext/events/eventmanager.py +269 -0
  41. OpenGLContext/events/fxevents.py +90 -0
  42. OpenGLContext/events/glfwevents.py +424 -0
  43. OpenGLContext/events/glutevents.py +184 -0
  44. OpenGLContext/events/inputstate.py +129 -0
  45. OpenGLContext/events/internaltime.py +161 -0
  46. OpenGLContext/events/keyboardevents.py +153 -0
  47. OpenGLContext/events/mouseevents.py +473 -0
  48. OpenGLContext/events/pygameevents.py +162 -0
  49. OpenGLContext/events/systemtime.py +12 -0
  50. OpenGLContext/events/timeeventgeneratormanager.py +40 -0
  51. OpenGLContext/events/timeevents.py +143 -0
  52. OpenGLContext/events/timer.py +170 -0
  53. OpenGLContext/events/tkevents.py +172 -0
  54. OpenGLContext/events/wxevents.py +185 -0
  55. OpenGLContext/extensionmanager.py +158 -0
  56. OpenGLContext/framecounter.py +74 -0
  57. OpenGLContext/frustum.py +135 -0
  58. OpenGLContext/glfwcontext.py +395 -0
  59. OpenGLContext/glfwinteractivecontext.py +34 -0
  60. OpenGLContext/glfwvrmlcontext.py +11 -0
  61. OpenGLContext/glutcontext.py +224 -0
  62. OpenGLContext/glutinteractivecontext.py +24 -0
  63. OpenGLContext/gluttestingcontext.py +11 -0
  64. OpenGLContext/glutvrmlcontext.py +25 -0
  65. OpenGLContext/glutvrmltestingcontext.py +48 -0
  66. OpenGLContext/hud.py +404 -0
  67. OpenGLContext/interactivecontext.py +29 -0
  68. OpenGLContext/loaders/__init__.py +1 -0
  69. OpenGLContext/loaders/base.py +84 -0
  70. OpenGLContext/loaders/cc0.py +137 -0
  71. OpenGLContext/loaders/gltf/__init__.py +71 -0
  72. OpenGLContext/loaders/gltf/accessors.py +255 -0
  73. OpenGLContext/loaders/gltf/animation.py +611 -0
  74. OpenGLContext/loaders/gltf/draco.py +166 -0
  75. OpenGLContext/loaders/gltf/environment_sky.py +416 -0
  76. OpenGLContext/loaders/gltf/loader.py +80 -0
  77. OpenGLContext/loaders/gltf/materials.py +298 -0
  78. OpenGLContext/loaders/gltf/meshes.py +292 -0
  79. OpenGLContext/loaders/gltf/samples.py +162 -0
  80. OpenGLContext/loaders/gltf/scene.py +673 -0
  81. OpenGLContext/loaders/gltf/specular_glossiness.py +154 -0
  82. OpenGLContext/loaders/gltf/textures.py +155 -0
  83. OpenGLContext/loaders/gltf/transforms.py +261 -0
  84. OpenGLContext/loaders/gltf_demos.py +511 -0
  85. OpenGLContext/loaders/hdr.py +228 -0
  86. OpenGLContext/loaders/hdri.py +83 -0
  87. OpenGLContext/loaders/loader.py +255 -0
  88. OpenGLContext/loaders/obj.py +299 -0
  89. OpenGLContext/loaders/resolver.py +536 -0
  90. OpenGLContext/loaders/tiles3d/__init__.py +9 -0
  91. OpenGLContext/loaders/tiles3d/boundingvolume.py +120 -0
  92. OpenGLContext/loaders/tiles3d/dem.py +71 -0
  93. OpenGLContext/loaders/tiles3d/fetch.py +102 -0
  94. OpenGLContext/loaders/tiles3d/foliage.py +786 -0
  95. OpenGLContext/loaders/tiles3d/frustum.py +70 -0
  96. OpenGLContext/loaders/tiles3d/geomorph.py +57 -0
  97. OpenGLContext/loaders/tiles3d/gltf_uploader.py +246 -0
  98. OpenGLContext/loaders/tiles3d/loadmanager.py +121 -0
  99. OpenGLContext/loaders/tiles3d/physics_colliders.py +78 -0
  100. OpenGLContext/loaders/tiles3d/procedural.py +355 -0
  101. OpenGLContext/loaders/tiles3d/residency.py +102 -0
  102. OpenGLContext/loaders/tiles3d/runtime.py +168 -0
  103. OpenGLContext/loaders/tiles3d/sample.py +212 -0
  104. OpenGLContext/loaders/tiles3d/scatter.py +77 -0
  105. OpenGLContext/loaders/tiles3d/screenspaceerror.py +31 -0
  106. OpenGLContext/loaders/tiles3d/tileset.py +390 -0
  107. OpenGLContext/loaders/tiles3d/traversal.py +108 -0
  108. OpenGLContext/loaders/tiles3d/vegetation.py +361 -0
  109. OpenGLContext/loaders/vrml97.py +133 -0
  110. OpenGLContext/looptrace.py +307 -0
  111. OpenGLContext/move/__init__.py +0 -0
  112. OpenGLContext/move/bindingstore.py +232 -0
  113. OpenGLContext/move/direct.py +156 -0
  114. OpenGLContext/move/dragwatcher.py +42 -0
  115. OpenGLContext/move/examinemanager.py +100 -0
  116. OpenGLContext/move/followcam.py +121 -0
  117. OpenGLContext/move/modes.py +522 -0
  118. OpenGLContext/move/movementmanager.py +74 -0
  119. OpenGLContext/move/navigation.py +137 -0
  120. OpenGLContext/move/physicsplatform.py +203 -0
  121. OpenGLContext/move/physicswalk.py +469 -0
  122. OpenGLContext/move/smooth.py +68 -0
  123. OpenGLContext/move/terrainwalk.py +451 -0
  124. OpenGLContext/move/trackball.py +92 -0
  125. OpenGLContext/move/viewplatform.py +283 -0
  126. OpenGLContext/move/viewplatformmixin.py +335 -0
  127. OpenGLContext/nav/__init__.py +13 -0
  128. OpenGLContext/nav/navmesh.py +476 -0
  129. OpenGLContext/passes/__init__.py +0 -0
  130. OpenGLContext/passes/_flat.py +1317 -0
  131. OpenGLContext/passes/asyncpick.py +216 -0
  132. OpenGLContext/passes/bloom.py +257 -0
  133. OpenGLContext/passes/flatcompat.py +246 -0
  134. OpenGLContext/passes/flatcore.py +198 -0
  135. OpenGLContext/passes/flateffects.py +372 -0
  136. OpenGLContext/passes/ibl.py +711 -0
  137. OpenGLContext/passes/instancing.py +775 -0
  138. OpenGLContext/passes/pbrpass.py +829 -0
  139. OpenGLContext/passes/renderpass.py +79 -0
  140. OpenGLContext/passes/renderstats.py +55 -0
  141. OpenGLContext/passes/rendervisitor.py +60 -0
  142. OpenGLContext/passes/selection.py +450 -0
  143. OpenGLContext/passes/selectionbuffers.py +442 -0
  144. OpenGLContext/passes/shaderpass.py +1018 -0
  145. OpenGLContext/passes/shaderpass_shadow.py +197 -0
  146. OpenGLContext/passes/shadersource.py +121 -0
  147. OpenGLContext/passes/shadowcaps.py +182 -0
  148. OpenGLContext/passes/shadowmap.py +330 -0
  149. OpenGLContext/passes/shadowmath.py +290 -0
  150. OpenGLContext/passes/shadowmixin.py +906 -0
  151. OpenGLContext/passes/shadowpool.py +186 -0
  152. OpenGLContext/passes/transmission.py +124 -0
  153. OpenGLContext/physics/__init__.py +16 -0
  154. OpenGLContext/physics/debugdraw.py +370 -0
  155. OpenGLContext/physics/demo.py +157 -0
  156. OpenGLContext/physics/gltf_world.py +185 -0
  157. OpenGLContext/physics/manager.py +84 -0
  158. OpenGLContext/physics/threaded.py +93 -0
  159. OpenGLContext/plugins.py +61 -0
  160. OpenGLContext/pygamecontext.py +178 -0
  161. OpenGLContext/pygameinteractivecontext.py +36 -0
  162. OpenGLContext/pygametestingcontext.py +10 -0
  163. OpenGLContext/pygamevrmlcontext.py +13 -0
  164. OpenGLContext/quaternion.py +190 -0
  165. OpenGLContext/renderoptions.py +280 -0
  166. OpenGLContext/resources/__init__.py +36 -0
  167. OpenGLContext/resources/available_contexts.txt +4 -0
  168. OpenGLContext/resources/available_contexts_txt.py +9 -0
  169. OpenGLContext/resources/context_icon_png.py +46 -0
  170. OpenGLContext/resources/context_icon_small_png.py +21 -0
  171. OpenGLContext/resources/disk_icon_hi_png.py +14 -0
  172. OpenGLContext/resources/disk_icon_png.py +15 -0
  173. OpenGLContext/resources/environment/pimbackground_BK.jpg +0 -0
  174. OpenGLContext/resources/environment/pimbackground_DN.jpg +0 -0
  175. OpenGLContext/resources/environment/pimbackground_FR.jpg +0 -0
  176. OpenGLContext/resources/environment/pimbackground_LF.jpg +0 -0
  177. OpenGLContext/resources/environment/pimbackground_RT.jpg +0 -0
  178. OpenGLContext/resources/environment/pimbackground_UP.jpg +0 -0
  179. OpenGLContext/resources/environment/studio_BK.jpg +0 -0
  180. OpenGLContext/resources/environment/studio_DN.jpg +0 -0
  181. OpenGLContext/resources/environment/studio_FR.jpg +0 -0
  182. OpenGLContext/resources/environment/studio_LF.jpg +0 -0
  183. OpenGLContext/resources/environment/studio_RT.jpg +0 -0
  184. OpenGLContext/resources/environment/studio_UP.jpg +0 -0
  185. OpenGLContext/resources/environment/studiobright_BK.jpg +0 -0
  186. OpenGLContext/resources/environment/studiobright_DN.jpg +0 -0
  187. OpenGLContext/resources/environment/studiobright_FR.jpg +0 -0
  188. OpenGLContext/resources/environment/studiobright_LF.jpg +0 -0
  189. OpenGLContext/resources/environment/studiobright_RT.jpg +0 -0
  190. OpenGLContext/resources/environment/studiobright_UP.jpg +0 -0
  191. OpenGLContext/resources/green_arrow_right_png.py +47 -0
  192. OpenGLContext/resources/legacy_lighting.vert.txt +37 -0
  193. OpenGLContext/resources/legacy_lighting_vert_txt.py +18 -0
  194. OpenGLContext/resources/lights.vert.txt +107 -0
  195. OpenGLContext/resources/lights_vert_txt.py +23 -0
  196. OpenGLContext/resources/phongprecalc_vert.py +30 -0
  197. OpenGLContext/resources/phongweights_frag.py +47 -0
  198. OpenGLContext/resources/pygame_icon_png.py +47 -0
  199. OpenGLContext/resources/red_arrow_right_png.py +50 -0
  200. OpenGLContext/resources/simpleshader.frag.txt +3 -0
  201. OpenGLContext/resources/simpleshader.vert.txt +3 -0
  202. OpenGLContext/resources/simpleshader_frag_txt.py +9 -0
  203. OpenGLContext/resources/simpleshader_vert_txt.py +10 -0
  204. OpenGLContext/scenegraph/__init__.py +19 -0
  205. OpenGLContext/scenegraph/appearance.py +107 -0
  206. OpenGLContext/scenegraph/arraygeometry.py +238 -0
  207. OpenGLContext/scenegraph/audio.py +632 -0
  208. OpenGLContext/scenegraph/background.py +108 -0
  209. OpenGLContext/scenegraph/basenodes.py +50 -0
  210. OpenGLContext/scenegraph/billboard.py +14 -0
  211. OpenGLContext/scenegraph/boundingvolume.py +494 -0
  212. OpenGLContext/scenegraph/box.py +180 -0
  213. OpenGLContext/scenegraph/collision.py +14 -0
  214. OpenGLContext/scenegraph/coordinate.py +20 -0
  215. OpenGLContext/scenegraph/coordinatebounded.py +18 -0
  216. OpenGLContext/scenegraph/cubebackground.py +207 -0
  217. OpenGLContext/scenegraph/extrusions.py +275 -0
  218. OpenGLContext/scenegraph/fog.py +123 -0
  219. OpenGLContext/scenegraph/gear.py +323 -0
  220. OpenGLContext/scenegraph/group.py +20 -0
  221. OpenGLContext/scenegraph/grouping.py +140 -0
  222. OpenGLContext/scenegraph/hdrbackground.py +384 -0
  223. OpenGLContext/scenegraph/ifscompiler.py +633 -0
  224. OpenGLContext/scenegraph/imagetexture.py +365 -0
  225. OpenGLContext/scenegraph/indexedfaceset.py +238 -0
  226. OpenGLContext/scenegraph/indexedlineset.py +418 -0
  227. OpenGLContext/scenegraph/indexedpolygons.py +422 -0
  228. OpenGLContext/scenegraph/inline.py +79 -0
  229. OpenGLContext/scenegraph/instancedgl.py +187 -0
  230. OpenGLContext/scenegraph/interpolators.py +122 -0
  231. OpenGLContext/scenegraph/light.py +213 -0
  232. OpenGLContext/scenegraph/lod.py +22 -0
  233. OpenGLContext/scenegraph/material.py +96 -0
  234. OpenGLContext/scenegraph/material_fields.py +50 -0
  235. OpenGLContext/scenegraph/mouseover.py +43 -0
  236. OpenGLContext/scenegraph/nodepath.py +52 -0
  237. OpenGLContext/scenegraph/nurbs.py +637 -0
  238. OpenGLContext/scenegraph/nurbssampling.py +101 -0
  239. OpenGLContext/scenegraph/nurbstess.py +316 -0
  240. OpenGLContext/scenegraph/nurbstrim.py +54 -0
  241. OpenGLContext/scenegraph/particles.py +786 -0
  242. OpenGLContext/scenegraph/pbrmaterial.py +229 -0
  243. OpenGLContext/scenegraph/pbrmesh.py +558 -0
  244. OpenGLContext/scenegraph/physicsbody.py +72 -0
  245. OpenGLContext/scenegraph/pointset.py +257 -0
  246. OpenGLContext/scenegraph/polygon.py +166 -0
  247. OpenGLContext/scenegraph/polygonsort.py +119 -0
  248. OpenGLContext/scenegraph/polygontessellator.py +213 -0
  249. OpenGLContext/scenegraph/quadrics.py +494 -0
  250. OpenGLContext/scenegraph/scenegraph.py +21 -0
  251. OpenGLContext/scenegraph/shadergeometry.py +403 -0
  252. OpenGLContext/scenegraph/shaders.py +812 -0
  253. OpenGLContext/scenegraph/shape.py +243 -0
  254. OpenGLContext/scenegraph/simplebackground.py +34 -0
  255. OpenGLContext/scenegraph/spherebackground.py +462 -0
  256. OpenGLContext/scenegraph/switch.py +75 -0
  257. OpenGLContext/scenegraph/teapot.py +439 -0
  258. OpenGLContext/scenegraph/teapot_nurbs.py +291 -0
  259. OpenGLContext/scenegraph/teapot_nurbs_data.py +384 -0
  260. OpenGLContext/scenegraph/terrain/__init__.py +12 -0
  261. OpenGLContext/scenegraph/terrain/heightfield.py +172 -0
  262. OpenGLContext/scenegraph/terrain/splat.py +224 -0
  263. OpenGLContext/scenegraph/tessellationlod.py +97 -0
  264. OpenGLContext/scenegraph/text/__init__.py +1 -0
  265. OpenGLContext/scenegraph/text/_toolsfont.py +200 -0
  266. OpenGLContext/scenegraph/text/font.py +452 -0
  267. OpenGLContext/scenegraph/text/fontprovider.py +193 -0
  268. OpenGLContext/scenegraph/text/fonts/__init__.py +75 -0
  269. OpenGLContext/scenegraph/text/fonts/font_atlas_10.py +133 -0
  270. OpenGLContext/scenegraph/text/fonts/font_atlas_12.py +158 -0
  271. OpenGLContext/scenegraph/text/fonts/font_atlas_14.py +176 -0
  272. OpenGLContext/scenegraph/text/fonts/font_atlas_16.py +204 -0
  273. OpenGLContext/scenegraph/text/fonts/font_atlas_18.py +222 -0
  274. OpenGLContext/scenegraph/text/fonts/font_atlas_20.py +244 -0
  275. OpenGLContext/scenegraph/text/fonts/font_atlas_24.py +294 -0
  276. OpenGLContext/scenegraph/text/fonts/font_atlas_28.py +339 -0
  277. OpenGLContext/scenegraph/text/fonts/font_atlas_32.py +390 -0
  278. OpenGLContext/scenegraph/text/fontstyle3d.py +62 -0
  279. OpenGLContext/scenegraph/text/glutfont.py +186 -0
  280. OpenGLContext/scenegraph/text/pygamefont.py +213 -0
  281. OpenGLContext/scenegraph/text/shaderfont.py +340 -0
  282. OpenGLContext/scenegraph/text/shadertext.py +433 -0
  283. OpenGLContext/scenegraph/text/text.py +75 -0
  284. OpenGLContext/scenegraph/text/toolsfont.py +819 -0
  285. OpenGLContext/scenegraph/text/ttfregistry.py +135 -0
  286. OpenGLContext/scenegraph/text/wglfont.py +228 -0
  287. OpenGLContext/scenegraph/text/wglfontprovider.py +37 -0
  288. OpenGLContext/scenegraph/text/wxfont.py +314 -0
  289. OpenGLContext/scenegraph/texturetransform.py +66 -0
  290. OpenGLContext/scenegraph/tilesterrain.py +95 -0
  291. OpenGLContext/scenegraph/timesensor.py +51 -0
  292. OpenGLContext/scenegraph/transform.py +180 -0
  293. OpenGLContext/scenegraph/vegetation/__init__.py +20 -0
  294. OpenGLContext/scenegraph/vegetation/base.py +96 -0
  295. OpenGLContext/scenegraph/vegetation/billboards.py +145 -0
  296. OpenGLContext/scenegraph/vegetation/clumps.py +260 -0
  297. OpenGLContext/scenegraph/vegetation/grid.py +112 -0
  298. OpenGLContext/scenegraph/vegetation/nearmesh.py +185 -0
  299. OpenGLContext/scenegraph/vertex.py +54 -0
  300. OpenGLContext/scenegraph/viewpoint.py +42 -0
  301. OpenGLContext/scenegraph/winding.py +49 -0
  302. OpenGLContext/shaders/_brdf_inc.glsl +124 -0
  303. OpenGLContext/shaders/_common_inc.glsl +24 -0
  304. OpenGLContext/shaders/_cubemap_inc.glsl +26 -0
  305. OpenGLContext/shaders/_lights_inc.glsl +32 -0
  306. OpenGLContext/shaders/_objectid_inc.glsl +14 -0
  307. OpenGLContext/shaders/_shadow_inc.glsl +254 -0
  308. OpenGLContext/shaders/_vrml97_lighting_inc.glsl +78 -0
  309. OpenGLContext/shaders/hdr_background.frag +32 -0
  310. OpenGLContext/shaders/hdr_background.vert +15 -0
  311. OpenGLContext/shaders/ibl_brdf.frag +78 -0
  312. OpenGLContext/shaders/ibl_env.frag +53 -0
  313. OpenGLContext/shaders/ibl_equirect.frag +19 -0
  314. OpenGLContext/shaders/ibl_fullscreen.vert +11 -0
  315. OpenGLContext/shaders/ibl_irradiance.frag +40 -0
  316. OpenGLContext/shaders/ibl_prefilter.frag +56 -0
  317. OpenGLContext/shaders/particle.frag +42 -0
  318. OpenGLContext/shaders/particle.vert +52 -0
  319. OpenGLContext/shaders/pbr.frag +906 -0
  320. OpenGLContext/shaders/pbr.vert +70 -0
  321. OpenGLContext/shaders/shadow_depth.frag +4 -0
  322. OpenGLContext/shaders/shadow_depth.vert +18 -0
  323. OpenGLContext/shaders/terrain_splat.frag +80 -0
  324. OpenGLContext/shaders/terrain_splat.vert +16 -0
  325. OpenGLContext/shaders/veg_billboard.frag +35 -0
  326. OpenGLContext/shaders/veg_billboard.vert +17 -0
  327. OpenGLContext/shaders/veg_clump.frag +27 -0
  328. OpenGLContext/shaders/veg_mesh.frag +26 -0
  329. OpenGLContext/shaders/veg_mesh.vert +17 -0
  330. OpenGLContext/shaders/vrml97_background.frag +17 -0
  331. OpenGLContext/shaders/vrml97_background.vert +24 -0
  332. OpenGLContext/shaders/vrml97_lighting.frag +111 -0
  333. OpenGLContext/shaders/vrml97_lighting.vert +50 -0
  334. OpenGLContext/shaders/vrml97_line.frag +23 -0
  335. OpenGLContext/shaders/vrml97_line.vert +24 -0
  336. OpenGLContext/shaders/vrml97_point.frag +36 -0
  337. OpenGLContext/shaders/vrml97_point.vert +21 -0
  338. OpenGLContext/shaders/vrml97_unlit.frag +59 -0
  339. OpenGLContext/shaders/vrml97_unlit.vert +18 -0
  340. OpenGLContext/shaders/vrml97_vertex_color.frag +85 -0
  341. OpenGLContext/shaders/vrml97_vertex_color.vert +34 -0
  342. OpenGLContext/stalltrace.py +637 -0
  343. OpenGLContext/testing/__init__.py +67 -0
  344. OpenGLContext/testing/display.py +30 -0
  345. OpenGLContext/testing/event_injector.py +564 -0
  346. OpenGLContext/testing/framebuffer_comparison.py +842 -0
  347. OpenGLContext/testing/gl_env.py +39 -0
  348. OpenGLContext/testing/paths.py +23 -0
  349. OpenGLContext/testing/process_exit.py +30 -0
  350. OpenGLContext/testing/report_generator.py +594 -0
  351. OpenGLContext/testing/subprocess_runner.py +411 -0
  352. OpenGLContext/testingcontext.py +36 -0
  353. OpenGLContext/tests/test_atlas.py +62 -0
  354. OpenGLContext/tests/test_configs.py +27 -0
  355. OpenGLContext/tests/test_polygonsort.py +45 -0
  356. OpenGLContext/tests/test_utilities.py +84 -0
  357. OpenGLContext/tests/test_viewplatform.py +39 -0
  358. OpenGLContext/texture.py +449 -0
  359. OpenGLContext/texturecache.py +37 -0
  360. OpenGLContext/triangleutilities.py +89 -0
  361. OpenGLContext/ui/__init__.py +39 -0
  362. OpenGLContext/ui/bindings.py +243 -0
  363. OpenGLContext/ui/console.py +339 -0
  364. OpenGLContext/ui/debugoverlay.py +519 -0
  365. OpenGLContext/ui/dialogs.py +124 -0
  366. OpenGLContext/ui/draw.py +617 -0
  367. OpenGLContext/ui/gallery.py +425 -0
  368. OpenGLContext/ui/generate.py +195 -0
  369. OpenGLContext/ui/geometry.py +88 -0
  370. OpenGLContext/ui/hudwidgets.py +1053 -0
  371. OpenGLContext/ui/layout.py +263 -0
  372. OpenGLContext/ui/metrics.py +218 -0
  373. OpenGLContext/ui/overlay.py +412 -0
  374. OpenGLContext/ui/panel.py +459 -0
  375. OpenGLContext/ui/pictures.py +261 -0
  376. OpenGLContext/ui/screen.py +177 -0
  377. OpenGLContext/ui/scroll.py +297 -0
  378. OpenGLContext/ui/session.py +329 -0
  379. OpenGLContext/ui/settings.py +273 -0
  380. OpenGLContext/ui/skin.py +330 -0
  381. OpenGLContext/ui/widgets.py +1199 -0
  382. OpenGLContext/userpaths.py +48 -0
  383. OpenGLContext/utilities.py +94 -0
  384. OpenGLContext/vectorutilities.py +101 -0
  385. OpenGLContext/viewer/__init__.py +60 -0
  386. OpenGLContext/viewer/adapters/__init__.py +150 -0
  387. OpenGLContext/viewer/adapters/base.py +157 -0
  388. OpenGLContext/viewer/adapters/gltf.py +31 -0
  389. OpenGLContext/viewer/adapters/obj.py +20 -0
  390. OpenGLContext/viewer/adapters/scenegraph.py +71 -0
  391. OpenGLContext/viewer/adapters/tiles.py +296 -0
  392. OpenGLContext/viewer/adapters/vrml.py +25 -0
  393. OpenGLContext/viewer/asyncscene.py +117 -0
  394. OpenGLContext/viewer/caption.py +126 -0
  395. OpenGLContext/viewer/capture.py +77 -0
  396. OpenGLContext/viewer/debug.py +78 -0
  397. OpenGLContext/viewer/environment.py +131 -0
  398. OpenGLContext/viewer/framing.py +111 -0
  399. OpenGLContext/viewer/library.py +235 -0
  400. OpenGLContext/viewer/menu.py +218 -0
  401. OpenGLContext/viewer/options.py +150 -0
  402. OpenGLContext/viewer/overlay.py +63 -0
  403. OpenGLContext/viewer/sceneviewer.py +1056 -0
  404. OpenGLContext/viewer/screens.py +172 -0
  405. OpenGLContext/viewer/source.py +56 -0
  406. OpenGLContext/visitor.py +68 -0
  407. OpenGLContext/vrmlcontext.py +72 -0
  408. OpenGLContext/wxcontext.py +424 -0
  409. OpenGLContext/wxinteractivecontext.py +25 -0
  410. OpenGLContext/wxtestingcontext.py +9 -0
  411. OpenGLContext/wxvrmlcontext.py +13 -0
  412. openglcontext-3.0.0a1.dist-info/METADATA +650 -0
  413. openglcontext-3.0.0a1.dist-info/RECORD +417 -0
  414. openglcontext-3.0.0a1.dist-info/WHEEL +5 -0
  415. openglcontext-3.0.0a1.dist-info/entry_points.txt +11 -0
  416. openglcontext-3.0.0a1.dist-info/licenses/license.txt +89 -0
  417. openglcontext-3.0.0a1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,492 @@
1
+ #! /usr/bin/env python
2
+ """PBR glTF sample browser (``oglc-gltf-demo``) -- a thin wrapper over the viewer.
3
+
4
+ Streams every model in the KhronosGroup/glTF-Sample-Models 2.0 catalogue and shows
5
+ it with the shared ``oglc-gltf`` viewer's PBR scene/lighting/framing, adding only what
6
+ is specific to browsing: catalogue navigation and the reference-screenshot thumbnail
7
+ overlaid top-right so a rendering can be compared against the reference.
8
+
9
+ n / Page Down next model
10
+ p / Page Up previous model
11
+ F2 save a screenshot (iso-dated PNG in the current directory)
12
+ Alt + s the engine's own screenshot key, named for the program and
13
+ written to the current directory too
14
+ Alt + f the developer overlay -- frame rate, renderer features and
15
+ what the frame cost (see docs/hud.html)
16
+
17
+ (The arrow keys are left free for camera navigation.)
18
+
19
+ Models are downloaded on a background thread, so switching models never freezes the
20
+ window: the current model keeps rendering (with a "Loading ..." overlay) until the
21
+ next one is decoded and ready to upload to the card.
22
+
23
+ The model name is drawn top-left (white); a download/decode failure is shown there
24
+ in red. Models are centred, auto-framed and slowly turned -- embedded cameras are
25
+ authored for the model's original placement and don't survive centring, so the
26
+ viewer's ``--no-cameras`` framing is used.
27
+
28
+ Run: oglc-gltf-demo
29
+ Start elsewhere: oglc-gltf-demo --model DamagedHelmet (or MODEL=DamagedHelmet)
30
+
31
+ Every ``oglc-gltf`` command-line option applies (``--shadows/--no-shadows``,
32
+ ``--lights``, ``--ibl-intensity``, ...); see ``oglc-gltf-demo -h``.
33
+ """
34
+ import os
35
+ import sys
36
+ from dataclasses import dataclass
37
+ from typing import Any, Sequence
38
+
39
+ os.environ.setdefault('OPENGLCONTEXT_SHADOWS', '1')
40
+
41
+ from OpenGLContext.bin.view import apply_render_env, build_parser
42
+ from OpenGLContext.ui.gallery import Picture
43
+ from OpenGLContext.ui.hudwidgets import HUDGroup, Readout
44
+ from OpenGLContext.viewer.sceneviewer import ViewerContext
45
+ from OpenGLContext.viewer import ViewerOptions
46
+ from OpenGLContext.loaders import gltf
47
+ from OpenGLContext.loaders import gltf_demos
48
+
49
+
50
+ # -- per-model demo profiles -------------------------------------------------
51
+ # The browser can't frame every model the same way: an object (a helmet) wants a
52
+ # slow turntable while *facing* the camera; a building (Sponza) wants to be walked
53
+ # through from the courtyard; a city wants a free-fly camera *inside* it. This
54
+ # table sets, per model, the facing yaw, whether to turntable, and whether to run
55
+ # physics (and start flying). Unlisted models get DEFAULT_PROFILE. Grow it from the
56
+ # conformance harness (plans/GLTF-DEMO-CONFORMANCE.md) as reference poses are dialed
57
+ # in; --no-rotate / --yaw / --physics still override per run.
58
+
59
+ @dataclass(frozen=True)
60
+ class ModelProfile:
61
+ yaw: float = 0.0 # facing rotation about +Y (radians) on first load
62
+ turntable: bool = True # slowly rotate the model
63
+ physics: bool = False # walk/fly the scene with collision
64
+ fly: bool = False # start in free-fly (vs grounded walk) when physics on
65
+ background: str = '' # '' (black), 'sky', or 'cube' (env cubemap skybox)
66
+ bloom: bool = False # HDR bloom (emissive glow) -- for emissive-heavy demos
67
+
68
+
69
+ DEFAULT_PROFILE = ModelProfile()
70
+
71
+ # Sub-demos whose materials need a lit environment to read correctly: transmissive
72
+ # glass refracts the backdrop (a black one renders rough glass opaque), metals
73
+ # reflect it, and a lamp lit only by its own bulb meters an exposure that leaves
74
+ # everything the bulb does not reach black. The roster is derived from the shared
75
+ # per-scene table (gltf_demos), so the demo and the capture harness cannot drift on
76
+ # which models need one; the demo lights them with the cheap analytic 'sky' (see
77
+ # the loop below).
78
+ _ENV_BACKGROUND = tuple(sorted(gltf_demos.ENV_BACKGROUND_MODELS))
79
+
80
+ # Emissive-heavy sub-demos whose reference shows a bloom/glow. Bloom is HDR + a
81
+ # post-process (OPENGLCONTEXT_BLOOM); it is enabled per-model so it does not change
82
+ # the tone-mapping of the (majority) non-emissive demos.
83
+ _BLOOM_MODELS = ('EmissiveStrengthTest', 'IridescenceLamp')
84
+
85
+ MODEL_PROFILES = {
86
+ # Architecture / scenes: walk or fly, no turntable. "Start in the courtyard" /
87
+ # "inside the city" is handled by the character spawn sampling clear floor.
88
+ 'Sponza': ModelProfile(turntable=False, physics=True),
89
+ 'VirtualCity': ModelProfile(turntable=False, physics=True, fly=True),
90
+ # Objects (DamagedHelmet, Duck, ...) are correct at the default yaw 0 (verified:
91
+ # the helmet's visor faces the camera) with a slow turntable, so they need no
92
+ # entry. Add a `yaw=` here only for a model the conformance harness shows facing
93
+ # away at 0 -- do not guess (a wrong yaw turns a front-facing model backwards).
94
+ }
95
+ # Fold the env-background list into the profile table (same mechanism as yaw/physics).
96
+ # 'sky' (the lit gradient) is used rather than 'cube': it reliably gives glass a lit
97
+ # backdrop to refract (a black one renders rough glass opaque) and metals a lit
98
+ # reflection, without the image-cube skybox's per-face load cost in the browser.
99
+ for _name in _ENV_BACKGROUND:
100
+ _base = MODEL_PROFILES.get(_name, DEFAULT_PROFILE)
101
+ MODEL_PROFILES[_name] = ModelProfile(
102
+ yaw=_base.yaw, turntable=_base.turntable, physics=_base.physics,
103
+ fly=_base.fly, background='sky', bloom=_base.bloom)
104
+ for _name in _BLOOM_MODELS:
105
+ _base = MODEL_PROFILES.get(_name, DEFAULT_PROFILE)
106
+ MODEL_PROFILES[_name] = ModelProfile(
107
+ yaw=_base.yaw, turntable=_base.turntable, physics=_base.physics,
108
+ fly=_base.fly, background=_base.background, bloom=True)
109
+
110
+
111
+ def default_env_prefix() -> str | None:
112
+ """Path prefix of the bundled demo environment cubemap (or an override).
113
+
114
+ ``OPENGLCONTEXT_ENV_CUBEMAP`` wins; otherwise the faces shipped under the
115
+ package's ``resources/environment`` are used so the ``cube`` background works
116
+ out of the box. Returns None if no face set is found.
117
+ """
118
+ override = os.environ.get('OPENGLCONTEXT_ENV_CUBEMAP', '').strip()
119
+ if override:
120
+ return override
121
+ here = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # OpenGLContext/
122
+ prefix = os.path.join(here, 'resources', 'environment', 'pimbackground_')
123
+ if os.path.exists(prefix + 'UP.jpg'):
124
+ return prefix
125
+ return None
126
+
127
+
128
+ def profile_for(name: str) -> ModelProfile:
129
+ """Return the :class:`ModelProfile` for a model name (default if unlisted)."""
130
+ return MODEL_PROFILES.get(name, DEFAULT_PROFILE)
131
+
132
+
133
+ def resolve_bloom(name: str) -> bool:
134
+ """Whether the per-model profile asks for HDR bloom (emissive glow)."""
135
+ return profile_for(name).bloom
136
+
137
+
138
+ def resolve_background(config: ViewerOptions, name: str) -> Any:
139
+ """Background spec for a model: explicit --background wins, else the per-model
140
+ profile ('cube'/'sky'), else the demo default (black 'none').
141
+
142
+ The fallback is `_background_default`, the run's own default, not the live
143
+ `config.background`: the browser writes each model's resolved background back
144
+ onto the config, so reading it here would latch an env model's 'sky' onto
145
+ every plain model browsed after it -- and a self-lit scene shown against an
146
+ environment loses its metered exposure and clips to white.
147
+ """
148
+ if getattr(config, '_background_explicit', False):
149
+ return config.background
150
+ prof = profile_for(name)
151
+ if prof.background == 'cube':
152
+ return 'cube'
153
+ if prof.background == 'sky':
154
+ return 'sky'
155
+ return getattr(config, '_background_default', config.background)
156
+
157
+
158
+ def demos_first(catalogue: Sequence[Any]) -> list:
159
+ """The catalogue reordered so the conformance fixtures come last.
160
+
161
+ Most of the Khronos sample set exists to exercise one glTF feature -- a bare
162
+ triangle, a sparse accessor, forty ``Compare*`` grids -- and stepping
163
+ through those to reach something worth looking at is not browsing. Which is
164
+ which comes from the shared table (:mod:`OpenGLContext.loaders.gltf_demos`),
165
+ so this and the viewer's shelf cannot disagree.
166
+
167
+ Nothing is dropped: the fixtures are still there, they are just not what
168
+ opens first. Each half keeps the catalogue's own order.
169
+ """
170
+ entries = list(catalogue)
171
+ demos = [entry for entry in entries
172
+ if not gltf_demos.scene_for(entry['name']).feature_test]
173
+ fixtures = [entry for entry in entries
174
+ if gltf_demos.scene_for(entry['name']).feature_test]
175
+ return demos + fixtures
176
+
177
+
178
+ def resolve_start_index(names: Sequence[str], start: str) -> int:
179
+ """Catalogue index to open on for a ``--model``/``MODEL`` selector.
180
+
181
+ The selector is a model name, a 0-based catalogue index (wrapped, so `-1`-ish
182
+ over-runs still land on a model), or empty for the first model. An unknown
183
+ name opens the first model rather than failing -- the catalogue is fetched
184
+ from the network and its contents vary.
185
+ """
186
+ start = (start or '').strip()
187
+ if not start or not names:
188
+ return 0
189
+ if start.isnumeric():
190
+ return int(start) % len(names)
191
+ return names.index(start) if start in names else 0
192
+
193
+
194
+ def resolve_view(config: ViewerOptions, name: str) -> tuple[float, bool]:
195
+ """(yaw, turntable) for a model, honouring --no-rotate / explicit --turntable /
196
+ explicit --yaw over the per-model profile."""
197
+ prof = profile_for(name)
198
+ if getattr(config, 'no_rotate', False):
199
+ turntable = False
200
+ elif getattr(config, '_turntable_explicit', False):
201
+ turntable = True
202
+ else:
203
+ turntable = prof.turntable
204
+ yaw = config.yaw if getattr(config, '_yaw_explicit', False) else prof.yaw
205
+ return yaw, turntable
206
+
207
+
208
+ def resolve_physics(config: ViewerOptions, name: str) -> tuple[bool, bool]:
209
+ """(physics, fly) for a model. A --capture run never walks (deterministic
210
+ frame); an explicit --physics/--no-physics overrides the per-model profile."""
211
+ prof = profile_for(name)
212
+ if getattr(config, 'capture', None):
213
+ return False, False
214
+ if getattr(config, '_physics_explicit', False):
215
+ return bool(config.physics), prof.fly
216
+ return prof.physics, prof.fly
217
+
218
+
219
+ #: How big the reference thumbnail is, in pixels at the reference font size.
220
+ REFERENCE_SIZE = (320, 240)
221
+
222
+
223
+ class ReferencePicture(HUDGroup):
224
+ """The current model's own reference screenshot, labelled, in the corner.
225
+
226
+ What makes this a *comparison* rather than a gallery: the rendering and the
227
+ picture it should look like, on screen together. It is an ordinary HUD
228
+ group, so the picture cache behind it fetches the URL, decodes it off the
229
+ render thread and evicts it when the browser has moved on.
230
+ """
231
+
232
+ PROTO = 'ReferencePicture'
233
+
234
+ def __init__(self, **named: Any) -> None:
235
+ picture = Picture(width=REFERENCE_SIZE[0], height=REFERENCE_SIZE[1])
236
+ named.setdefault('anchor', 'top-right')
237
+ named.setdefault('children', [picture, Readout(label='reference',
238
+ align='center')])
239
+ super(ReferencePicture, self).__init__(**named)
240
+ self.picture = picture
241
+
242
+ @property
243
+ def url(self) -> str:
244
+ return str(self.picture.url)
245
+
246
+ @url.setter
247
+ def url(self, value: str) -> None:
248
+ self.picture.url = value or ''
249
+ # An entry with no reference of its own shows nothing rather than the
250
+ # last model's picture with the wrong label under it.
251
+ self.visible = bool(value)
252
+
253
+
254
+ class TestContext(ViewerContext):
255
+ """The viewer, browsing the sample catalogue instead of one file."""
256
+
257
+ def hasSceneToShow(self) -> bool:
258
+ """Always: the catalogue is what this browser shows, not one file."""
259
+ return True
260
+
261
+ def prepareSource(self) -> None:
262
+ self.source = None
263
+ self.overlayError = False
264
+ self._ref_current: Any = None
265
+ try:
266
+ self.catalog: Any = demos_first(gltf.fetch_sample_catalog())
267
+ except Exception as err:
268
+ print("Could not fetch the model catalogue: %s" % err)
269
+ self.catalog = demos_first(
270
+ [{'name': n, 'display': n, 'screenshot_url': None}
271
+ for n in gltf.SAMPLE_MODELS])
272
+ start = getattr(self.options, 'model', None) or os.environ.get('MODEL', '')
273
+ self.index = resolve_start_index([e['name'] for e in self.catalog], start)
274
+
275
+ def loadScene(self) -> Any:
276
+ entry = self.catalog[self.index]
277
+ label = "[%d/%d] %s" % (self.index + 1, len(self.catalog), entry['display'])
278
+ self._ref_current = entry['name']
279
+ print("Loading %s ..." % label)
280
+ try:
281
+ scene = gltf.load_sample(entry['name'])
282
+ except Exception as err:
283
+ self._label = "%s\nFAILED: %s" % (label, str(err).splitlines()[0][:50])
284
+ self.overlayError = True
285
+ print(" " + self._label.replace("\n", " "))
286
+ return None
287
+ self._label = label
288
+ self.overlayError = False
289
+ return scene
290
+
291
+ # -- async catalogue loading -----------------------------------------
292
+ def requestInitialScene(self) -> None:
293
+ """Pull the initial catalogue model in the background instead of a file."""
294
+ self._request_current_model()
295
+
296
+ def _request_current_model(self) -> None:
297
+ """Kick off a background load of the model at ``self.index``. The window keeps
298
+ rendering the previous model (with a "Loading ..." overlay) until it lands."""
299
+ entry = self.catalog[self.index]
300
+ name = entry['name']
301
+ label = "[%d/%d] %s" % (self.index + 1, len(self.catalog), entry['display'])
302
+ self._pending_name = name
303
+ self._pending_label = label
304
+ print("Loading %s ..." % label)
305
+ self.requestScene(lambda: gltf.load_sample(name),
306
+ label="Loading %s ..." % label)
307
+
308
+ def applyLoadedScene(self, scene: Any) -> None:
309
+ # buildScenegraph reads _ref_current for the per-model profile, so set the
310
+ # newly loaded model's identity before building.
311
+ self._ref_current = self._pending_name
312
+ self._label = self._pending_label
313
+ self.overlayError = False
314
+ self.buildScenegraph(scene)
315
+ self.onSceneReady()
316
+
317
+ def applyFailedLoad(self, error: BaseException | None) -> None:
318
+ self._ref_current = self._pending_name
319
+ self.overlayError = True
320
+ msg = str(error).splitlines()[0][:50] if error else 'load failed'
321
+ self._label = "%s\nFAILED: %s" % (self._pending_label, msg)
322
+ self.overlayText = self._label
323
+ print(" " + self._label.replace("\n", " "))
324
+
325
+ def onSceneReady(self) -> None:
326
+ self.sceneLoaded = True
327
+ self._apply_physics_profile()
328
+
329
+ def buildScenegraph(self, scene: Any) -> None:
330
+ # Apply the per-model facing/turntable/background BEFORE the base builds the
331
+ # scenegraph (it reads the framing options and builds the background there).
332
+ self.options.yaw, self.options.turntable = resolve_view(
333
+ self.options, self._ref_current)
334
+ self.options.background = resolve_background(self.options, self._ref_current)
335
+ # Bloom is read per frame from the env var, so toggling it per model works.
336
+ os.environ['OPENGLCONTEXT_BLOOM'] = '1' if resolve_bloom(self._ref_current) else '0'
337
+ ViewerContext.buildScenegraph(self, scene)
338
+ self.overlayText = self._label
339
+
340
+ def setupWalking(self) -> None:
341
+ # Enable physics for the *initial* model only if its profile asks for it.
342
+ physics, fly = resolve_physics(self.options, getattr(self, '_ref_current', ''))
343
+ self.options.physics = physics
344
+ ViewerContext.setupWalking(self)
345
+ if physics and self.physicsPlatform is not None:
346
+ self._physics_scene = self.sg
347
+ if fly:
348
+ self.physicsPlatform.set_fly(True)
349
+
350
+ def _apply_physics_profile(self) -> None:
351
+ """Turn physics on/off for the newly loaded model per its profile.
352
+
353
+ Called on model *switch* (after `setupWalking` has captured the free-fly
354
+ manager). Rebuilds the collision world when the scene changed so a physics
355
+ model gets its own world, and hands control back to free-fly otherwise.
356
+ """
357
+ if getattr(self, 'sg', None) is None or self.options.capture:
358
+ return
359
+ physics, fly = resolve_physics(self.options, self._ref_current)
360
+ if physics:
361
+ if getattr(self, '_physics_scene', None) is not self.sg:
362
+ self.physicsPlatform = None # force a rebuild for this scene
363
+ if self.enablePhysics(True):
364
+ self._physics_scene = self.sg
365
+ if fly and self.physicsPlatform is not None:
366
+ self.physicsPlatform.set_fly(True)
367
+ elif self.physicsWalking:
368
+ self.enablePhysics(False)
369
+
370
+ # Catalogue navigation keys. Deliberately NOT the arrow keys: those drive
371
+ # free-fly camera movement, so binding them to next/prev model made walking
372
+ # around a model a pain (every turn also jumped to another model).
373
+ NEXT_MODEL_KEYS = ('n', '<pagedown>')
374
+ PREV_MODEL_KEYS = ('p', '<pageup>')
375
+
376
+ # -- catalogue navigation --------------------------------------------
377
+ def setupCallbacks(self) -> None: # pragma: no cover - binds live event handlers
378
+ # Skip the viewer's PageUp/PageDown viewpoint bindings (this browser has no
379
+ # per-model cameras); n/p and PageUp/PageDown advance the *model* instead.
380
+ from OpenGLContext import testingcontext
381
+ testingcontext.getInteractive().setupCallbacks(self)
382
+ for key in self.NEXT_MODEL_KEYS:
383
+ self.addEventHandler('keyboard', name=key, function=self._next_model)
384
+ for key in self.PREV_MODEL_KEYS:
385
+ self.addEventHandler('keyboard', name=key, function=self._prev_model)
386
+ self.addEventHandler('keyboard', name='<F2>', function=self.requestScreenshot)
387
+
388
+ def _next_model(self, event: Any = None) -> None:
389
+ self._go(1)
390
+
391
+ def _prev_model(self, event: Any = None) -> None:
392
+ self._go(-1)
393
+
394
+ def _go(self, delta: int) -> None:
395
+ self.index = (self.index + delta) % len(self.catalog)
396
+ self._request_current_model()
397
+
398
+ # -- the reference thumbnail -----------------------------------------
399
+ def setupCaption(self):
400
+ """The caption, plus the reference screenshot beside it.
401
+
402
+ The reference is what makes this a comparison rather than a gallery, so
403
+ it is a picture in the same HUD layer as the caption -- one tree, one
404
+ batched draw, and the cache behind it downloads and evicts on its own.
405
+ """
406
+ layer = ViewerContext.setupCaption(self)
407
+ self.referencePicture = ReferencePicture()
408
+ layer.children = list(layer.children) + [self.referencePicture]
409
+ return layer
410
+
411
+ def referenceURL(self) -> str:
412
+ """Where the current model's reference screenshot is, or ''."""
413
+ entry = self.catalog[self.index] if self.catalog else {}
414
+ return (entry.get('screenshot_url') or '') if not self.overlayError else ''
415
+
416
+ def updateOverlay(self) -> None:
417
+ ViewerContext.updateOverlay(self)
418
+ self.referencePicture.url = self.referenceURL()
419
+
420
+
421
+ def demo_config(argv: list[str] | None = None) -> ViewerOptions:
422
+ """Parse + massage the browser's config (no GL). Separated so it is testable.
423
+
424
+ The browser centres + turntables every model and ignores embedded cameras, so
425
+ it also defaults to **free-fly, not physics**: a turntable model has no ground,
426
+ and walk mode just drops the avatar until the model is out of view. An explicit
427
+ ``--physics``/``--no-physics`` on the command line is still honoured.
428
+ """
429
+ parser = build_parser(prog='oglc-gltf-demo')
430
+ parser.add_argument('--model', default=os.environ.get('MODEL', ''),
431
+ metavar='NAME|INDEX',
432
+ help='catalogue model to open on, by sample name '
433
+ '(e.g. DamagedHelmet) or 0-based index; '
434
+ 'default: the MODEL env var, else the first model')
435
+ args = parser.parse_args(argv, namespace=ViewerOptions())
436
+ args.no_cameras = True
437
+ args.turntable = True
438
+ # Most models read cleanest on black; the per-model profile (resolve_background)
439
+ # injects a lit/cube background only for the sub-demos that need one (glass to
440
+ # refract, metals to reflect). An explicit --background always wins.
441
+ if args.background is None:
442
+ args.background = 'none'
443
+ tokens = sys.argv[1:] if argv is None else argv
444
+ # Record which view/physics knobs the user set explicitly, so the per-model
445
+ # profiles (resolve_view/resolve_physics) can defer to them.
446
+ args._physics_explicit = any(t in ('--physics', '--no-physics') for t in tokens)
447
+ args._turntable_explicit = '--turntable' in tokens
448
+ args._yaw_explicit = '--yaw' in tokens
449
+ args._background_explicit = '--background' in tokens
450
+ args._background_default = args.background
451
+ if not args._physics_explicit:
452
+ args.physics = False
453
+ # A --capture run wants a fixed, reference-comparable orientation; the
454
+ # turntable would spin the model away from the reference pose. Freeze it for
455
+ # captures (honour an explicit --turntable).
456
+ if args.capture and not args._turntable_explicit:
457
+ args.turntable = False
458
+ return args
459
+
460
+
461
+ def apply_environment(args: ViewerOptions) -> None:
462
+ """Set the render env so metals reflect a real environment in the browser.
463
+
464
+ Loads the bundled environment cubemap into the IBL probe and pins full IBL, so
465
+ metals / dielectric specular / sheen reflect it instead of a near-black env
466
+ (SpecularTest, ToyCar, MetalRoughSpheres, NegativeScaleTest, ...). Safe on heavy
467
+ models (the earlier stall was the CubeBackground skybox, now built only for a
468
+ 'cube' background). Reflections read too dim at the shadow-tuned 0.4 IBL
469
+ intensity, so lift it for browsing (an explicit --ibl-intensity still wins).
470
+ """
471
+ # An explicit --environment (a cubemap prefix, an .hdr, or a bundled HDRI name)
472
+ # is owned entirely by apply_render_env; only fall back to the bundled cubemap
473
+ # when the user named no environment of their own.
474
+ env = default_env_prefix()
475
+ if env and not args.environment:
476
+ os.environ.setdefault('OPENGLCONTEXT_ENV_CUBEMAP', env)
477
+ os.environ.setdefault('OPENGLCONTEXT_IBL', 'full')
478
+ if args.ibl_intensity is None:
479
+ os.environ['OPENGLCONTEXT_IBL_INTENSITY'] = '0.9'
480
+
481
+
482
+ def main(argv: list[str] | None = None) -> Any:
483
+ args = demo_config(argv)
484
+ apply_environment(args)
485
+ apply_render_env(args) # an explicit --ibl-intensity still wins here
486
+ TestContext.options = args
487
+ return TestContext.ContextMainLoop(size=args.size) if args.size \
488
+ else TestContext.ContextMainLoop()
489
+
490
+
491
+ if __name__ == "__main__": # pragma: no cover - CLI entry point
492
+ main()