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.
- OpenGLContext/__init__.py +151 -0
- OpenGLContext/arrays.py +2 -0
- OpenGLContext/atlas.py +300 -0
- OpenGLContext/audio/__init__.py +32 -0
- OpenGLContext/audio/scene.py +130 -0
- OpenGLContext/audio/settings.py +82 -0
- OpenGLContext/bin/__init__.py +0 -0
- OpenGLContext/bin/choosecontext.py +224 -0
- OpenGLContext/bin/choosefonts.py +209 -0
- OpenGLContext/bin/gltest.py +121 -0
- OpenGLContext/bin/gltf_demo.py +492 -0
- OpenGLContext/bin/gltf_regression.py +645 -0
- OpenGLContext/bin/gltf_view.py +30 -0
- OpenGLContext/bin/keyboardevents.py +93 -0
- OpenGLContext/bin/lorentz.py +109 -0
- OpenGLContext/bin/physics_cook.py +81 -0
- OpenGLContext/bin/profile_view.py +38 -0
- OpenGLContext/bin/terrain_view.py +581 -0
- OpenGLContext/bin/tiles_view.py +39 -0
- OpenGLContext/bin/ui_demo.py +228 -0
- OpenGLContext/bin/view.py +341 -0
- OpenGLContext/bin/vrml_view.py +42 -0
- OpenGLContext/capture.py +125 -0
- OpenGLContext/context.py +1253 -0
- OpenGLContext/contextconfig.py +177 -0
- OpenGLContext/contextdefinition.py +275 -0
- OpenGLContext/debug/__init__.py +6 -0
- OpenGLContext/debug/bufferimage.py +80 -0
- OpenGLContext/debug/leaks.py +13 -0
- OpenGLContext/debug/logcontext.py +9 -0
- OpenGLContext/debug/logs.py +15 -0
- OpenGLContext/debug/state.py +329 -0
- OpenGLContext/displaylist.py +69 -0
- OpenGLContext/doinchildmatrix.py +34 -0
- OpenGLContext/drawcube.py +58 -0
- OpenGLContext/events/__init__.py +10 -0
- OpenGLContext/events/default_config.py +29 -0
- OpenGLContext/events/event.py +65 -0
- OpenGLContext/events/eventhandlermixin.py +214 -0
- OpenGLContext/events/eventmanager.py +269 -0
- OpenGLContext/events/fxevents.py +90 -0
- OpenGLContext/events/glfwevents.py +424 -0
- OpenGLContext/events/glutevents.py +184 -0
- OpenGLContext/events/inputstate.py +129 -0
- OpenGLContext/events/internaltime.py +161 -0
- OpenGLContext/events/keyboardevents.py +153 -0
- OpenGLContext/events/mouseevents.py +473 -0
- OpenGLContext/events/pygameevents.py +162 -0
- OpenGLContext/events/systemtime.py +12 -0
- OpenGLContext/events/timeeventgeneratormanager.py +40 -0
- OpenGLContext/events/timeevents.py +143 -0
- OpenGLContext/events/timer.py +170 -0
- OpenGLContext/events/tkevents.py +172 -0
- OpenGLContext/events/wxevents.py +185 -0
- OpenGLContext/extensionmanager.py +158 -0
- OpenGLContext/framecounter.py +74 -0
- OpenGLContext/frustum.py +135 -0
- OpenGLContext/glfwcontext.py +395 -0
- OpenGLContext/glfwinteractivecontext.py +34 -0
- OpenGLContext/glfwvrmlcontext.py +11 -0
- OpenGLContext/glutcontext.py +224 -0
- OpenGLContext/glutinteractivecontext.py +24 -0
- OpenGLContext/gluttestingcontext.py +11 -0
- OpenGLContext/glutvrmlcontext.py +25 -0
- OpenGLContext/glutvrmltestingcontext.py +48 -0
- OpenGLContext/hud.py +404 -0
- OpenGLContext/interactivecontext.py +29 -0
- OpenGLContext/loaders/__init__.py +1 -0
- OpenGLContext/loaders/base.py +84 -0
- OpenGLContext/loaders/cc0.py +137 -0
- OpenGLContext/loaders/gltf/__init__.py +71 -0
- OpenGLContext/loaders/gltf/accessors.py +255 -0
- OpenGLContext/loaders/gltf/animation.py +611 -0
- OpenGLContext/loaders/gltf/draco.py +166 -0
- OpenGLContext/loaders/gltf/environment_sky.py +416 -0
- OpenGLContext/loaders/gltf/loader.py +80 -0
- OpenGLContext/loaders/gltf/materials.py +298 -0
- OpenGLContext/loaders/gltf/meshes.py +292 -0
- OpenGLContext/loaders/gltf/samples.py +162 -0
- OpenGLContext/loaders/gltf/scene.py +673 -0
- OpenGLContext/loaders/gltf/specular_glossiness.py +154 -0
- OpenGLContext/loaders/gltf/textures.py +155 -0
- OpenGLContext/loaders/gltf/transforms.py +261 -0
- OpenGLContext/loaders/gltf_demos.py +511 -0
- OpenGLContext/loaders/hdr.py +228 -0
- OpenGLContext/loaders/hdri.py +83 -0
- OpenGLContext/loaders/loader.py +255 -0
- OpenGLContext/loaders/obj.py +299 -0
- OpenGLContext/loaders/resolver.py +536 -0
- OpenGLContext/loaders/tiles3d/__init__.py +9 -0
- OpenGLContext/loaders/tiles3d/boundingvolume.py +120 -0
- OpenGLContext/loaders/tiles3d/dem.py +71 -0
- OpenGLContext/loaders/tiles3d/fetch.py +102 -0
- OpenGLContext/loaders/tiles3d/foliage.py +786 -0
- OpenGLContext/loaders/tiles3d/frustum.py +70 -0
- OpenGLContext/loaders/tiles3d/geomorph.py +57 -0
- OpenGLContext/loaders/tiles3d/gltf_uploader.py +246 -0
- OpenGLContext/loaders/tiles3d/loadmanager.py +121 -0
- OpenGLContext/loaders/tiles3d/physics_colliders.py +78 -0
- OpenGLContext/loaders/tiles3d/procedural.py +355 -0
- OpenGLContext/loaders/tiles3d/residency.py +102 -0
- OpenGLContext/loaders/tiles3d/runtime.py +168 -0
- OpenGLContext/loaders/tiles3d/sample.py +212 -0
- OpenGLContext/loaders/tiles3d/scatter.py +77 -0
- OpenGLContext/loaders/tiles3d/screenspaceerror.py +31 -0
- OpenGLContext/loaders/tiles3d/tileset.py +390 -0
- OpenGLContext/loaders/tiles3d/traversal.py +108 -0
- OpenGLContext/loaders/tiles3d/vegetation.py +361 -0
- OpenGLContext/loaders/vrml97.py +133 -0
- OpenGLContext/looptrace.py +307 -0
- OpenGLContext/move/__init__.py +0 -0
- OpenGLContext/move/bindingstore.py +232 -0
- OpenGLContext/move/direct.py +156 -0
- OpenGLContext/move/dragwatcher.py +42 -0
- OpenGLContext/move/examinemanager.py +100 -0
- OpenGLContext/move/followcam.py +121 -0
- OpenGLContext/move/modes.py +522 -0
- OpenGLContext/move/movementmanager.py +74 -0
- OpenGLContext/move/navigation.py +137 -0
- OpenGLContext/move/physicsplatform.py +203 -0
- OpenGLContext/move/physicswalk.py +469 -0
- OpenGLContext/move/smooth.py +68 -0
- OpenGLContext/move/terrainwalk.py +451 -0
- OpenGLContext/move/trackball.py +92 -0
- OpenGLContext/move/viewplatform.py +283 -0
- OpenGLContext/move/viewplatformmixin.py +335 -0
- OpenGLContext/nav/__init__.py +13 -0
- OpenGLContext/nav/navmesh.py +476 -0
- OpenGLContext/passes/__init__.py +0 -0
- OpenGLContext/passes/_flat.py +1317 -0
- OpenGLContext/passes/asyncpick.py +216 -0
- OpenGLContext/passes/bloom.py +257 -0
- OpenGLContext/passes/flatcompat.py +246 -0
- OpenGLContext/passes/flatcore.py +198 -0
- OpenGLContext/passes/flateffects.py +372 -0
- OpenGLContext/passes/ibl.py +711 -0
- OpenGLContext/passes/instancing.py +775 -0
- OpenGLContext/passes/pbrpass.py +829 -0
- OpenGLContext/passes/renderpass.py +79 -0
- OpenGLContext/passes/renderstats.py +55 -0
- OpenGLContext/passes/rendervisitor.py +60 -0
- OpenGLContext/passes/selection.py +450 -0
- OpenGLContext/passes/selectionbuffers.py +442 -0
- OpenGLContext/passes/shaderpass.py +1018 -0
- OpenGLContext/passes/shaderpass_shadow.py +197 -0
- OpenGLContext/passes/shadersource.py +121 -0
- OpenGLContext/passes/shadowcaps.py +182 -0
- OpenGLContext/passes/shadowmap.py +330 -0
- OpenGLContext/passes/shadowmath.py +290 -0
- OpenGLContext/passes/shadowmixin.py +906 -0
- OpenGLContext/passes/shadowpool.py +186 -0
- OpenGLContext/passes/transmission.py +124 -0
- OpenGLContext/physics/__init__.py +16 -0
- OpenGLContext/physics/debugdraw.py +370 -0
- OpenGLContext/physics/demo.py +157 -0
- OpenGLContext/physics/gltf_world.py +185 -0
- OpenGLContext/physics/manager.py +84 -0
- OpenGLContext/physics/threaded.py +93 -0
- OpenGLContext/plugins.py +61 -0
- OpenGLContext/pygamecontext.py +178 -0
- OpenGLContext/pygameinteractivecontext.py +36 -0
- OpenGLContext/pygametestingcontext.py +10 -0
- OpenGLContext/pygamevrmlcontext.py +13 -0
- OpenGLContext/quaternion.py +190 -0
- OpenGLContext/renderoptions.py +280 -0
- OpenGLContext/resources/__init__.py +36 -0
- OpenGLContext/resources/available_contexts.txt +4 -0
- OpenGLContext/resources/available_contexts_txt.py +9 -0
- OpenGLContext/resources/context_icon_png.py +46 -0
- OpenGLContext/resources/context_icon_small_png.py +21 -0
- OpenGLContext/resources/disk_icon_hi_png.py +14 -0
- OpenGLContext/resources/disk_icon_png.py +15 -0
- OpenGLContext/resources/environment/pimbackground_BK.jpg +0 -0
- OpenGLContext/resources/environment/pimbackground_DN.jpg +0 -0
- OpenGLContext/resources/environment/pimbackground_FR.jpg +0 -0
- OpenGLContext/resources/environment/pimbackground_LF.jpg +0 -0
- OpenGLContext/resources/environment/pimbackground_RT.jpg +0 -0
- OpenGLContext/resources/environment/pimbackground_UP.jpg +0 -0
- OpenGLContext/resources/environment/studio_BK.jpg +0 -0
- OpenGLContext/resources/environment/studio_DN.jpg +0 -0
- OpenGLContext/resources/environment/studio_FR.jpg +0 -0
- OpenGLContext/resources/environment/studio_LF.jpg +0 -0
- OpenGLContext/resources/environment/studio_RT.jpg +0 -0
- OpenGLContext/resources/environment/studio_UP.jpg +0 -0
- OpenGLContext/resources/environment/studiobright_BK.jpg +0 -0
- OpenGLContext/resources/environment/studiobright_DN.jpg +0 -0
- OpenGLContext/resources/environment/studiobright_FR.jpg +0 -0
- OpenGLContext/resources/environment/studiobright_LF.jpg +0 -0
- OpenGLContext/resources/environment/studiobright_RT.jpg +0 -0
- OpenGLContext/resources/environment/studiobright_UP.jpg +0 -0
- OpenGLContext/resources/green_arrow_right_png.py +47 -0
- OpenGLContext/resources/legacy_lighting.vert.txt +37 -0
- OpenGLContext/resources/legacy_lighting_vert_txt.py +18 -0
- OpenGLContext/resources/lights.vert.txt +107 -0
- OpenGLContext/resources/lights_vert_txt.py +23 -0
- OpenGLContext/resources/phongprecalc_vert.py +30 -0
- OpenGLContext/resources/phongweights_frag.py +47 -0
- OpenGLContext/resources/pygame_icon_png.py +47 -0
- OpenGLContext/resources/red_arrow_right_png.py +50 -0
- OpenGLContext/resources/simpleshader.frag.txt +3 -0
- OpenGLContext/resources/simpleshader.vert.txt +3 -0
- OpenGLContext/resources/simpleshader_frag_txt.py +9 -0
- OpenGLContext/resources/simpleshader_vert_txt.py +10 -0
- OpenGLContext/scenegraph/__init__.py +19 -0
- OpenGLContext/scenegraph/appearance.py +107 -0
- OpenGLContext/scenegraph/arraygeometry.py +238 -0
- OpenGLContext/scenegraph/audio.py +632 -0
- OpenGLContext/scenegraph/background.py +108 -0
- OpenGLContext/scenegraph/basenodes.py +50 -0
- OpenGLContext/scenegraph/billboard.py +14 -0
- OpenGLContext/scenegraph/boundingvolume.py +494 -0
- OpenGLContext/scenegraph/box.py +180 -0
- OpenGLContext/scenegraph/collision.py +14 -0
- OpenGLContext/scenegraph/coordinate.py +20 -0
- OpenGLContext/scenegraph/coordinatebounded.py +18 -0
- OpenGLContext/scenegraph/cubebackground.py +207 -0
- OpenGLContext/scenegraph/extrusions.py +275 -0
- OpenGLContext/scenegraph/fog.py +123 -0
- OpenGLContext/scenegraph/gear.py +323 -0
- OpenGLContext/scenegraph/group.py +20 -0
- OpenGLContext/scenegraph/grouping.py +140 -0
- OpenGLContext/scenegraph/hdrbackground.py +384 -0
- OpenGLContext/scenegraph/ifscompiler.py +633 -0
- OpenGLContext/scenegraph/imagetexture.py +365 -0
- OpenGLContext/scenegraph/indexedfaceset.py +238 -0
- OpenGLContext/scenegraph/indexedlineset.py +418 -0
- OpenGLContext/scenegraph/indexedpolygons.py +422 -0
- OpenGLContext/scenegraph/inline.py +79 -0
- OpenGLContext/scenegraph/instancedgl.py +187 -0
- OpenGLContext/scenegraph/interpolators.py +122 -0
- OpenGLContext/scenegraph/light.py +213 -0
- OpenGLContext/scenegraph/lod.py +22 -0
- OpenGLContext/scenegraph/material.py +96 -0
- OpenGLContext/scenegraph/material_fields.py +50 -0
- OpenGLContext/scenegraph/mouseover.py +43 -0
- OpenGLContext/scenegraph/nodepath.py +52 -0
- OpenGLContext/scenegraph/nurbs.py +637 -0
- OpenGLContext/scenegraph/nurbssampling.py +101 -0
- OpenGLContext/scenegraph/nurbstess.py +316 -0
- OpenGLContext/scenegraph/nurbstrim.py +54 -0
- OpenGLContext/scenegraph/particles.py +786 -0
- OpenGLContext/scenegraph/pbrmaterial.py +229 -0
- OpenGLContext/scenegraph/pbrmesh.py +558 -0
- OpenGLContext/scenegraph/physicsbody.py +72 -0
- OpenGLContext/scenegraph/pointset.py +257 -0
- OpenGLContext/scenegraph/polygon.py +166 -0
- OpenGLContext/scenegraph/polygonsort.py +119 -0
- OpenGLContext/scenegraph/polygontessellator.py +213 -0
- OpenGLContext/scenegraph/quadrics.py +494 -0
- OpenGLContext/scenegraph/scenegraph.py +21 -0
- OpenGLContext/scenegraph/shadergeometry.py +403 -0
- OpenGLContext/scenegraph/shaders.py +812 -0
- OpenGLContext/scenegraph/shape.py +243 -0
- OpenGLContext/scenegraph/simplebackground.py +34 -0
- OpenGLContext/scenegraph/spherebackground.py +462 -0
- OpenGLContext/scenegraph/switch.py +75 -0
- OpenGLContext/scenegraph/teapot.py +439 -0
- OpenGLContext/scenegraph/teapot_nurbs.py +291 -0
- OpenGLContext/scenegraph/teapot_nurbs_data.py +384 -0
- OpenGLContext/scenegraph/terrain/__init__.py +12 -0
- OpenGLContext/scenegraph/terrain/heightfield.py +172 -0
- OpenGLContext/scenegraph/terrain/splat.py +224 -0
- OpenGLContext/scenegraph/tessellationlod.py +97 -0
- OpenGLContext/scenegraph/text/__init__.py +1 -0
- OpenGLContext/scenegraph/text/_toolsfont.py +200 -0
- OpenGLContext/scenegraph/text/font.py +452 -0
- OpenGLContext/scenegraph/text/fontprovider.py +193 -0
- OpenGLContext/scenegraph/text/fonts/__init__.py +75 -0
- OpenGLContext/scenegraph/text/fonts/font_atlas_10.py +133 -0
- OpenGLContext/scenegraph/text/fonts/font_atlas_12.py +158 -0
- OpenGLContext/scenegraph/text/fonts/font_atlas_14.py +176 -0
- OpenGLContext/scenegraph/text/fonts/font_atlas_16.py +204 -0
- OpenGLContext/scenegraph/text/fonts/font_atlas_18.py +222 -0
- OpenGLContext/scenegraph/text/fonts/font_atlas_20.py +244 -0
- OpenGLContext/scenegraph/text/fonts/font_atlas_24.py +294 -0
- OpenGLContext/scenegraph/text/fonts/font_atlas_28.py +339 -0
- OpenGLContext/scenegraph/text/fonts/font_atlas_32.py +390 -0
- OpenGLContext/scenegraph/text/fontstyle3d.py +62 -0
- OpenGLContext/scenegraph/text/glutfont.py +186 -0
- OpenGLContext/scenegraph/text/pygamefont.py +213 -0
- OpenGLContext/scenegraph/text/shaderfont.py +340 -0
- OpenGLContext/scenegraph/text/shadertext.py +433 -0
- OpenGLContext/scenegraph/text/text.py +75 -0
- OpenGLContext/scenegraph/text/toolsfont.py +819 -0
- OpenGLContext/scenegraph/text/ttfregistry.py +135 -0
- OpenGLContext/scenegraph/text/wglfont.py +228 -0
- OpenGLContext/scenegraph/text/wglfontprovider.py +37 -0
- OpenGLContext/scenegraph/text/wxfont.py +314 -0
- OpenGLContext/scenegraph/texturetransform.py +66 -0
- OpenGLContext/scenegraph/tilesterrain.py +95 -0
- OpenGLContext/scenegraph/timesensor.py +51 -0
- OpenGLContext/scenegraph/transform.py +180 -0
- OpenGLContext/scenegraph/vegetation/__init__.py +20 -0
- OpenGLContext/scenegraph/vegetation/base.py +96 -0
- OpenGLContext/scenegraph/vegetation/billboards.py +145 -0
- OpenGLContext/scenegraph/vegetation/clumps.py +260 -0
- OpenGLContext/scenegraph/vegetation/grid.py +112 -0
- OpenGLContext/scenegraph/vegetation/nearmesh.py +185 -0
- OpenGLContext/scenegraph/vertex.py +54 -0
- OpenGLContext/scenegraph/viewpoint.py +42 -0
- OpenGLContext/scenegraph/winding.py +49 -0
- OpenGLContext/shaders/_brdf_inc.glsl +124 -0
- OpenGLContext/shaders/_common_inc.glsl +24 -0
- OpenGLContext/shaders/_cubemap_inc.glsl +26 -0
- OpenGLContext/shaders/_lights_inc.glsl +32 -0
- OpenGLContext/shaders/_objectid_inc.glsl +14 -0
- OpenGLContext/shaders/_shadow_inc.glsl +254 -0
- OpenGLContext/shaders/_vrml97_lighting_inc.glsl +78 -0
- OpenGLContext/shaders/hdr_background.frag +32 -0
- OpenGLContext/shaders/hdr_background.vert +15 -0
- OpenGLContext/shaders/ibl_brdf.frag +78 -0
- OpenGLContext/shaders/ibl_env.frag +53 -0
- OpenGLContext/shaders/ibl_equirect.frag +19 -0
- OpenGLContext/shaders/ibl_fullscreen.vert +11 -0
- OpenGLContext/shaders/ibl_irradiance.frag +40 -0
- OpenGLContext/shaders/ibl_prefilter.frag +56 -0
- OpenGLContext/shaders/particle.frag +42 -0
- OpenGLContext/shaders/particle.vert +52 -0
- OpenGLContext/shaders/pbr.frag +906 -0
- OpenGLContext/shaders/pbr.vert +70 -0
- OpenGLContext/shaders/shadow_depth.frag +4 -0
- OpenGLContext/shaders/shadow_depth.vert +18 -0
- OpenGLContext/shaders/terrain_splat.frag +80 -0
- OpenGLContext/shaders/terrain_splat.vert +16 -0
- OpenGLContext/shaders/veg_billboard.frag +35 -0
- OpenGLContext/shaders/veg_billboard.vert +17 -0
- OpenGLContext/shaders/veg_clump.frag +27 -0
- OpenGLContext/shaders/veg_mesh.frag +26 -0
- OpenGLContext/shaders/veg_mesh.vert +17 -0
- OpenGLContext/shaders/vrml97_background.frag +17 -0
- OpenGLContext/shaders/vrml97_background.vert +24 -0
- OpenGLContext/shaders/vrml97_lighting.frag +111 -0
- OpenGLContext/shaders/vrml97_lighting.vert +50 -0
- OpenGLContext/shaders/vrml97_line.frag +23 -0
- OpenGLContext/shaders/vrml97_line.vert +24 -0
- OpenGLContext/shaders/vrml97_point.frag +36 -0
- OpenGLContext/shaders/vrml97_point.vert +21 -0
- OpenGLContext/shaders/vrml97_unlit.frag +59 -0
- OpenGLContext/shaders/vrml97_unlit.vert +18 -0
- OpenGLContext/shaders/vrml97_vertex_color.frag +85 -0
- OpenGLContext/shaders/vrml97_vertex_color.vert +34 -0
- OpenGLContext/stalltrace.py +637 -0
- OpenGLContext/testing/__init__.py +67 -0
- OpenGLContext/testing/display.py +30 -0
- OpenGLContext/testing/event_injector.py +564 -0
- OpenGLContext/testing/framebuffer_comparison.py +842 -0
- OpenGLContext/testing/gl_env.py +39 -0
- OpenGLContext/testing/paths.py +23 -0
- OpenGLContext/testing/process_exit.py +30 -0
- OpenGLContext/testing/report_generator.py +594 -0
- OpenGLContext/testing/subprocess_runner.py +411 -0
- OpenGLContext/testingcontext.py +36 -0
- OpenGLContext/tests/test_atlas.py +62 -0
- OpenGLContext/tests/test_configs.py +27 -0
- OpenGLContext/tests/test_polygonsort.py +45 -0
- OpenGLContext/tests/test_utilities.py +84 -0
- OpenGLContext/tests/test_viewplatform.py +39 -0
- OpenGLContext/texture.py +449 -0
- OpenGLContext/texturecache.py +37 -0
- OpenGLContext/triangleutilities.py +89 -0
- OpenGLContext/ui/__init__.py +39 -0
- OpenGLContext/ui/bindings.py +243 -0
- OpenGLContext/ui/console.py +339 -0
- OpenGLContext/ui/debugoverlay.py +519 -0
- OpenGLContext/ui/dialogs.py +124 -0
- OpenGLContext/ui/draw.py +617 -0
- OpenGLContext/ui/gallery.py +425 -0
- OpenGLContext/ui/generate.py +195 -0
- OpenGLContext/ui/geometry.py +88 -0
- OpenGLContext/ui/hudwidgets.py +1053 -0
- OpenGLContext/ui/layout.py +263 -0
- OpenGLContext/ui/metrics.py +218 -0
- OpenGLContext/ui/overlay.py +412 -0
- OpenGLContext/ui/panel.py +459 -0
- OpenGLContext/ui/pictures.py +261 -0
- OpenGLContext/ui/screen.py +177 -0
- OpenGLContext/ui/scroll.py +297 -0
- OpenGLContext/ui/session.py +329 -0
- OpenGLContext/ui/settings.py +273 -0
- OpenGLContext/ui/skin.py +330 -0
- OpenGLContext/ui/widgets.py +1199 -0
- OpenGLContext/userpaths.py +48 -0
- OpenGLContext/utilities.py +94 -0
- OpenGLContext/vectorutilities.py +101 -0
- OpenGLContext/viewer/__init__.py +60 -0
- OpenGLContext/viewer/adapters/__init__.py +150 -0
- OpenGLContext/viewer/adapters/base.py +157 -0
- OpenGLContext/viewer/adapters/gltf.py +31 -0
- OpenGLContext/viewer/adapters/obj.py +20 -0
- OpenGLContext/viewer/adapters/scenegraph.py +71 -0
- OpenGLContext/viewer/adapters/tiles.py +296 -0
- OpenGLContext/viewer/adapters/vrml.py +25 -0
- OpenGLContext/viewer/asyncscene.py +117 -0
- OpenGLContext/viewer/caption.py +126 -0
- OpenGLContext/viewer/capture.py +77 -0
- OpenGLContext/viewer/debug.py +78 -0
- OpenGLContext/viewer/environment.py +131 -0
- OpenGLContext/viewer/framing.py +111 -0
- OpenGLContext/viewer/library.py +235 -0
- OpenGLContext/viewer/menu.py +218 -0
- OpenGLContext/viewer/options.py +150 -0
- OpenGLContext/viewer/overlay.py +63 -0
- OpenGLContext/viewer/sceneviewer.py +1056 -0
- OpenGLContext/viewer/screens.py +172 -0
- OpenGLContext/viewer/source.py +56 -0
- OpenGLContext/visitor.py +68 -0
- OpenGLContext/vrmlcontext.py +72 -0
- OpenGLContext/wxcontext.py +424 -0
- OpenGLContext/wxinteractivecontext.py +25 -0
- OpenGLContext/wxtestingcontext.py +9 -0
- OpenGLContext/wxvrmlcontext.py +13 -0
- openglcontext-3.0.0a1.dist-info/METADATA +650 -0
- openglcontext-3.0.0a1.dist-info/RECORD +417 -0
- openglcontext-3.0.0a1.dist-info/WHEEL +5 -0
- openglcontext-3.0.0a1.dist-info/entry_points.txt +11 -0
- openglcontext-3.0.0a1.dist-info/licenses/license.txt +89 -0
- 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()
|