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,228 @@
|
|
|
1
|
+
#! /usr/bin/env python
|
|
2
|
+
"""Overlay UI demonstration: the settings screen, the console and a skin.
|
|
3
|
+
|
|
4
|
+
Run it and press the keys it prints:
|
|
5
|
+
|
|
6
|
+
oglc-ui # the default flat skin
|
|
7
|
+
oglc-ui --skin # the same screens, painted with nine-slice artwork
|
|
8
|
+
|
|
9
|
+
F10 the settings screen -- every rendering feature the pass reads
|
|
10
|
+
F9 the console, with the engine's own log going into it
|
|
11
|
+
F8 a licence notice, to show a wall of text scrolling
|
|
12
|
+
F7 a yes/no question
|
|
13
|
+
F5 save a screenshot (into the current directory)
|
|
14
|
+
Alt+f the developer overlay -- a HUD layer rather than a panel, so it takes
|
|
15
|
+
no input and these screens draw over it (see docs/hud.html)
|
|
16
|
+
|
|
17
|
+
The artwork the ``--skin`` mode uses is drawn at start-up rather than shipped,
|
|
18
|
+
so the demo shows what a nine-slice does -- corners that stay square while the
|
|
19
|
+
frame stretches -- without a binary in the repository.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import argparse
|
|
25
|
+
import logging
|
|
26
|
+
import os
|
|
27
|
+
import sys
|
|
28
|
+
from typing import Any, List, Optional
|
|
29
|
+
|
|
30
|
+
from OpenGLContext import testingcontext
|
|
31
|
+
from OpenGLContext.contextdefinition import ContextDefinition
|
|
32
|
+
from OpenGLContext.move import modes as movemodes
|
|
33
|
+
from OpenGLContext.scenegraph.appearance import Appearance
|
|
34
|
+
from OpenGLContext.scenegraph.box import Box
|
|
35
|
+
from OpenGLContext.scenegraph.transform import Transform
|
|
36
|
+
from OpenGLContext.scenegraph.light import DirectionalLight
|
|
37
|
+
from OpenGLContext.scenegraph.material import Material
|
|
38
|
+
from OpenGLContext.scenegraph.quadrics import Sphere
|
|
39
|
+
from OpenGLContext.scenegraph.scenegraph import SceneGraph
|
|
40
|
+
from OpenGLContext.scenegraph.shape import Shape
|
|
41
|
+
from OpenGLContext.ui import bindings, console, dialogs, settings
|
|
42
|
+
from OpenGLContext.ui.overlay import OverlayMixin
|
|
43
|
+
|
|
44
|
+
log = logging.getLogger(__name__)
|
|
45
|
+
BaseContext: Any = testingcontext.getInteractive()
|
|
46
|
+
|
|
47
|
+
#: A short licence-shaped wall of text, so the notice screen has to scroll.
|
|
48
|
+
NOTICE = '\n\n'.join([
|
|
49
|
+
'OpenGLContext is distributed under a BSD-style licence.',
|
|
50
|
+
'Redistribution and use in source and binary forms, with or without '
|
|
51
|
+
'modification, are permitted provided that the following conditions are '
|
|
52
|
+
'met: redistributions of source code must retain the above copyright '
|
|
53
|
+
'notice, this list of conditions and the following disclaimer; '
|
|
54
|
+
'redistributions in binary form must reproduce the above copyright notice, '
|
|
55
|
+
'this list of conditions and the following disclaimer in the documentation '
|
|
56
|
+
'and/or other materials provided with the distribution.',
|
|
57
|
+
'THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS '
|
|
58
|
+
'IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, '
|
|
59
|
+
'THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR '
|
|
60
|
+
'PURPOSE ARE DISCLAIMED.',
|
|
61
|
+
] + ['Paragraph %d of a notice nobody can style away.' % (index,)
|
|
62
|
+
for index in range(1, 40)])
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def build_skin(directory: str) -> Any:
|
|
66
|
+
"""Draw a nine-slice set into ``directory`` and return a Skin using it.
|
|
67
|
+
|
|
68
|
+
Generated rather than shipped: what this demonstrates is that one image
|
|
69
|
+
serves every widget size, and an image drawn here is as good a proof of
|
|
70
|
+
that as one drawn in an editor -- with nothing binary to keep in step.
|
|
71
|
+
"""
|
|
72
|
+
from PIL import Image, ImageDraw
|
|
73
|
+
from OpenGLContext.ui.skin import NineSlice, Skin
|
|
74
|
+
|
|
75
|
+
def frame(name: str, fill: Any, edge: Any, size: int = 24,
|
|
76
|
+
border: int = 8) -> str:
|
|
77
|
+
image = Image.new('RGBA', (size, size), (0, 0, 0, 0))
|
|
78
|
+
draw = ImageDraw.Draw(image)
|
|
79
|
+
draw.rounded_rectangle([0, 0, size - 1, size - 1], radius=border - 1,
|
|
80
|
+
fill=fill, outline=edge, width=2)
|
|
81
|
+
path = os.path.join(directory, '%s.png' % (name,))
|
|
82
|
+
image.save(path)
|
|
83
|
+
return path
|
|
84
|
+
|
|
85
|
+
def slice(name: str, fill: Any, edge: Any) -> NineSlice:
|
|
86
|
+
return NineSlice(url=[frame(name, fill, edge)], border=(8, 8, 8, 8))
|
|
87
|
+
|
|
88
|
+
return Skin(
|
|
89
|
+
panelImage=slice('panel', (18, 22, 32, 230), (90, 110, 150, 255)),
|
|
90
|
+
buttonImage=slice('button', (38, 46, 62, 245), (110, 130, 170, 255)),
|
|
91
|
+
buttonHoverImage=slice('button_hover', (64, 88, 130, 250),
|
|
92
|
+
(150, 190, 250, 255)),
|
|
93
|
+
buttonDownImage=slice('button_down', (24, 30, 42, 255),
|
|
94
|
+
(90, 110, 150, 255)),
|
|
95
|
+
buttonDisabledImage=slice('button_off', (28, 30, 34, 180),
|
|
96
|
+
(60, 66, 76, 255)),
|
|
97
|
+
fieldImage=slice('field', (12, 14, 20, 250), (80, 96, 130, 255)),
|
|
98
|
+
switchOffImage=slice('switch_off', (12, 14, 20, 250), (110, 130, 170, 255)),
|
|
99
|
+
switchOnImage=slice('switch_on', (60, 120, 80, 250), (140, 220, 160, 255)),
|
|
100
|
+
trackImage=slice('track', (12, 14, 20, 250), (70, 84, 112, 255)),
|
|
101
|
+
thumbImage=slice('thumb', (120, 150, 200, 255), (200, 220, 255, 255)),
|
|
102
|
+
panelPadding=20.0,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def movement_modes() -> List[Any]:
|
|
107
|
+
"""The ways of moving this demo offers, so the settings screen has some."""
|
|
108
|
+
return [
|
|
109
|
+
movemodes.WalkMode(name='walk'),
|
|
110
|
+
movemodes.FlyMode(name='fly'),
|
|
111
|
+
movemodes.FPSMode(name='fps'),
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class UIDemoContext(OverlayMixin, BaseContext):
|
|
116
|
+
"""A scene with the overlay screens bound to function keys.
|
|
117
|
+
|
|
118
|
+
The mix-in comes first so its event routing runs before the navigation
|
|
119
|
+
mix-in's: while a modal panel is up the movement sampler is not fed at all.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
skinned: bool = False
|
|
123
|
+
skinDirectory: str = ''
|
|
124
|
+
console: Optional[console.ConsolePanel] = None
|
|
125
|
+
skin: Any = None
|
|
126
|
+
|
|
127
|
+
# Supplied by the interactive context this is built on.
|
|
128
|
+
addEventHandler: Any
|
|
129
|
+
frameCounter: Any
|
|
130
|
+
triggerRedraw: Any
|
|
131
|
+
|
|
132
|
+
def OnInit(self) -> None: # pragma: no cover - needs a window
|
|
133
|
+
self.skin = build_skin(self.skinDirectory) if self.skinned else None
|
|
134
|
+
self.sg = SceneGraph(children=[
|
|
135
|
+
DirectionalLight(direction=(-0.4, -1, -0.6), intensity=1.0),
|
|
136
|
+
Transform(translation=(-2, 0, 0), children=[Shape(
|
|
137
|
+
geometry=Sphere(radius=1.0),
|
|
138
|
+
appearance=Appearance(material=Material(
|
|
139
|
+
diffuseColor=(0.7, 0.3, 0.25))))]),
|
|
140
|
+
Transform(translation=(2, 0, 0), children=[Shape(
|
|
141
|
+
geometry=Box(size=(1.6, 1.6, 1.6)),
|
|
142
|
+
appearance=Appearance(material=Material(
|
|
143
|
+
diffuseColor=(0.25, 0.45, 0.7))))]),
|
|
144
|
+
])
|
|
145
|
+
# ``keyboard`` key-downs, not ``keypress``: a function key produces no
|
|
146
|
+
# character, so GLFW raises no keypress for one and a keypress binding
|
|
147
|
+
# would be accepted and then never fire.
|
|
148
|
+
for key, handler in (('<F10>', self.openSettings),
|
|
149
|
+
('<F9>', self.openConsole),
|
|
150
|
+
('<F8>', self.openNotice),
|
|
151
|
+
('<F7>', self.askSomething),
|
|
152
|
+
('<F6>', self.openBindings)):
|
|
153
|
+
self.addEventHandler('keyboard', name=key, state=1,
|
|
154
|
+
function=handler)
|
|
155
|
+
sys.stdout.write(__doc__.split('Run it and press')[1])
|
|
156
|
+
sys.stdout.flush()
|
|
157
|
+
|
|
158
|
+
# -- the screens ------------------------------------------------------
|
|
159
|
+
def _skinned(self, panel: Any) -> Any:
|
|
160
|
+
if self.skin is not None:
|
|
161
|
+
panel.skin = self.skin
|
|
162
|
+
return panel
|
|
163
|
+
|
|
164
|
+
def openSettings(self, event: Any) -> None: # pragma: no cover - needs a window
|
|
165
|
+
panel = settings.settings_panel(self)
|
|
166
|
+
self.pushOverlay(self._skinned(panel))
|
|
167
|
+
|
|
168
|
+
def openBindings(self, event: Any) -> None: # pragma: no cover - needs a window
|
|
169
|
+
panel = bindings.open_bindings(self)
|
|
170
|
+
if panel is not None:
|
|
171
|
+
self._skinned(panel)
|
|
172
|
+
|
|
173
|
+
def openConsole(self, event: Any) -> None: # pragma: no cover - needs a window
|
|
174
|
+
registry = console.CommandRegistry()
|
|
175
|
+
registry.add('shadows', self._shadowsCommand,
|
|
176
|
+
'turn shadows on or off: shadows on|off')
|
|
177
|
+
registry.add('fps', lambda panel: '%.1f' % (
|
|
178
|
+
self.frameCounter.recentFps(),), 'the recent frame rate')
|
|
179
|
+
panel = self._skinned(console.console_panel(registry=registry))
|
|
180
|
+
panel.write('Type "help" for the commands.')
|
|
181
|
+
# Engine warnings go where a player can read them.
|
|
182
|
+
logging.getLogger('OpenGLContext').addHandler(
|
|
183
|
+
console.ConsoleLogHandler(panel))
|
|
184
|
+
self.console = panel
|
|
185
|
+
self.pushOverlay(panel)
|
|
186
|
+
|
|
187
|
+
def _shadowsCommand(self, panel: Any, state: str = '') -> str:
|
|
188
|
+
if state:
|
|
189
|
+
self.contextDefinition.shadows = state.lower() in ('on', '1', 'true')
|
|
190
|
+
self.triggerRedraw(1)
|
|
191
|
+
return 'shadows are %s' % (
|
|
192
|
+
'on' if self.contextDefinition.shadows else 'off',)
|
|
193
|
+
|
|
194
|
+
def openNotice(self, event: Any) -> None: # pragma: no cover - needs a window
|
|
195
|
+
self.pushOverlay(self._skinned(
|
|
196
|
+
dialogs.notice('Licence', NOTICE)))
|
|
197
|
+
|
|
198
|
+
def askSomething(self, event: Any) -> None: # pragma: no cover - needs a window
|
|
199
|
+
self.pushOverlay(self._skinned(dialogs.confirm(
|
|
200
|
+
'Download the high-resolution texture pack?',
|
|
201
|
+
detail='About 40MB, cached for next time.',
|
|
202
|
+
yes='Download', no='Not now',
|
|
203
|
+
on_answer=lambda yes: log.warning('answered %s', yes))))
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def main() -> int:
|
|
207
|
+
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
208
|
+
parser.add_argument('--skin', action='store_true',
|
|
209
|
+
help='paint the screens with generated nine-slice artwork')
|
|
210
|
+
parser.add_argument('--skin-dir', default='',
|
|
211
|
+
help='where to write that artwork (default: a temp dir)')
|
|
212
|
+
options = parser.parse_args()
|
|
213
|
+
directory = options.skin_dir
|
|
214
|
+
if options.skin and not directory:
|
|
215
|
+
import tempfile
|
|
216
|
+
directory = tempfile.mkdtemp(prefix='oglc-ui-skin-')
|
|
217
|
+
UIDemoContext.skinned = bool(options.skin)
|
|
218
|
+
UIDemoContext.skinDirectory = directory
|
|
219
|
+
UIDemoContext.ContextMainLoop(definition=ContextDefinition(
|
|
220
|
+
title='OpenGLContext overlay UI',
|
|
221
|
+
size=(1024, 720),
|
|
222
|
+
movementModes=movement_modes(),
|
|
223
|
+
))
|
|
224
|
+
return 0
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
if __name__ == '__main__':
|
|
228
|
+
sys.exit(main())
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
#! /usr/bin/env python
|
|
2
|
+
"""Walk-around viewer for a 3D scene (``oglc-view``).
|
|
3
|
+
|
|
4
|
+
Opens a local file **or an http(s) URL**, renders it with the metallic/roughness
|
|
5
|
+
PBR pass, and lets you walk through it with the keyboard/mouse. What format the
|
|
6
|
+
source is in is worked out from the source itself, so one command opens them
|
|
7
|
+
all:
|
|
8
|
+
|
|
9
|
+
=============================== ==============================================
|
|
10
|
+
``.gltf`` ``.glb`` a glTF 2.0 model
|
|
11
|
+
``.wrl`` ``.wrz`` ``.vrml`` a VRML97 world
|
|
12
|
+
=============================== ==============================================
|
|
13
|
+
|
|
14
|
+
Every format gets everything the viewer can do. If the file contains no lights, a
|
|
15
|
+
default sun + fill rig is added so the scene is never rendered in the dark;
|
|
16
|
+
shadows are on by default. A scene that brings its own sky keeps it.
|
|
17
|
+
|
|
18
|
+
A model with no camera of its own is centred and auto-framed. The fit is on the
|
|
19
|
+
*model*: a part the file stranded far outside the rest is still drawn but does
|
|
20
|
+
not push the camera back to take it in, and the viewer prints a line saying how
|
|
21
|
+
many such parts there are and how far out they go. See docs/viewer.html.
|
|
22
|
+
|
|
23
|
+
Each camera the scene defines becomes a ``Viewpoint`` node, so PageUp/PageDown
|
|
24
|
+
cycle between them through OpenGLContext's standard viewpoint mechanism; cameras
|
|
25
|
+
are referred to by name where they have one. ``--capture`` renders the scene to a
|
|
26
|
+
PNG and exits, after a short settle delay so the analytic-sky IBL has converged.
|
|
27
|
+
|
|
28
|
+
Usage::
|
|
29
|
+
|
|
30
|
+
oglc-view path/to/model.glb
|
|
31
|
+
oglc-view path/to/world.wrl
|
|
32
|
+
oglc-view https://example.com/model.glb
|
|
33
|
+
oglc-view model.glb --camera aerial --capture shot.png --capture-delay 0.5
|
|
34
|
+
oglc-view model.glb --list-cameras
|
|
35
|
+
GLTF=path/to/model.gltf oglc-view
|
|
36
|
+
|
|
37
|
+
A ``.glb`` is self-contained, so URLs work cleanly. A ``.gltf`` URL fetches only
|
|
38
|
+
that file; models that reference external ``.bin``/texture files by relative URI
|
|
39
|
+
will be missing those (download the whole model set locally instead).
|
|
40
|
+
|
|
41
|
+
Controls (OpenGLContext's default view-platform navigation)::
|
|
42
|
+
|
|
43
|
+
Up / Down arrow walk forward / back
|
|
44
|
+
Left / Right arrow turn (yaw) left / right
|
|
45
|
+
Ctrl + Up/Down look up / down (pitch)
|
|
46
|
+
Alt + Up/Down move up / down (fly)
|
|
47
|
+
Alt + Left/Right strafe (slide) left / right
|
|
48
|
+
- level the horizon
|
|
49
|
+
right-mouse drag orbit / examine about a point
|
|
50
|
+
PgUp / PgDn (or p / n) cycle named cameras (if any)
|
|
51
|
+
g toggle walk (physics) / free-fly
|
|
52
|
+
k pause / resume animation; [ / ] switch animation
|
|
53
|
+
t stop/start the turntable (stopping resets orientation)
|
|
54
|
+
F2 save a screenshot (iso-dated PNG in the current directory)
|
|
55
|
+
Alt + s the same thing through the engine's own handler,
|
|
56
|
+
named for the program: oglc-view-screen-0001.png,
|
|
57
|
+
also in the current directory
|
|
58
|
+
Alt + f the developer overlay: frame rate and time, which
|
|
59
|
+
renderer features are on, what the last frame cost
|
|
60
|
+
in shapes and draw calls, and where the camera is
|
|
61
|
+
(see docs/hud.html)
|
|
62
|
+
|
|
63
|
+
Free-fly by default; ``--physics`` (or ``g``) walks the scene instead, with
|
|
64
|
+
gravity and collision keeping you on the ground and out of walls. ``g`` again
|
|
65
|
+
returns to free-fly, so a viewpoint inside the geometry is never a trap.
|
|
66
|
+
|
|
67
|
+
The viewer itself is :mod:`OpenGLContext.viewer`, which an application can embed
|
|
68
|
+
without this command line, and the formats it opens are the adapters registered
|
|
69
|
+
in :mod:`OpenGLContext.viewer.adapters`; see docs/gltf.html.
|
|
70
|
+
"""
|
|
71
|
+
import argparse
|
|
72
|
+
import os
|
|
73
|
+
import sys
|
|
74
|
+
from typing import Any, Optional
|
|
75
|
+
|
|
76
|
+
os.environ.setdefault('OPENGLCONTEXT_PROFILE', 'core')
|
|
77
|
+
os.environ.setdefault('OPENGLCONTEXT_BACKEND', 'glfw')
|
|
78
|
+
os.environ.setdefault('OPENGLCONTEXT_RENDERER', 'pbr')
|
|
79
|
+
os.environ.setdefault('OPENGLCONTEXT_SHADOWS', '1')
|
|
80
|
+
# The directional-shadow cascade count is left fps-adaptive for interactive use:
|
|
81
|
+
# each extra cascade is a full depth pass over the whole scene (the dominant
|
|
82
|
+
# shadow-pass cost), so the controller sheds cascades when the frame rate sags.
|
|
83
|
+
# It is pinned only for --capture (see apply_render_env), where a reproducible
|
|
84
|
+
# frame matters more than the frame rate.
|
|
85
|
+
# Keep the warm analytic sky, but at reduced strength so the sun's cast shadows
|
|
86
|
+
# read clearly instead of being washed out by full-strength ambient.
|
|
87
|
+
os.environ.setdefault('OPENGLCONTEXT_IBL_INTENSITY', '0.4')
|
|
88
|
+
|
|
89
|
+
from OpenGLContext.viewer.adapters import (
|
|
90
|
+
UnknownSourceType, adapter_for, adapter_named, known_sources,
|
|
91
|
+
)
|
|
92
|
+
from OpenGLContext.viewer.sceneviewer import ViewerContext
|
|
93
|
+
from OpenGLContext.viewer.options import ViewerOptions
|
|
94
|
+
from OpenGLContext.viewer.source import resolve_source
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# -- command line ---------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
def _parse_size(text: str) -> tuple[int, int]:
|
|
100
|
+
"""Parse a ``WxH`` window size into an (int, int) tuple."""
|
|
101
|
+
try:
|
|
102
|
+
w, h = (int(v) for v in text.lower().split('x'))
|
|
103
|
+
return (w, h)
|
|
104
|
+
except Exception:
|
|
105
|
+
raise argparse.ArgumentTypeError("size must be WxH, e.g. 1100x680") from None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _format_names() -> list[str]:
|
|
109
|
+
"""The registered adapter names, for ``--format``'s help."""
|
|
110
|
+
from OpenGLContext import plugins
|
|
111
|
+
return sorted(entry.name for entry in plugins.Adapter.all())
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _parse_vec3(text: str) -> tuple[float, ...]:
|
|
115
|
+
"""Parse an ``X,Y,Z`` world-space point."""
|
|
116
|
+
parts = text.split(',')
|
|
117
|
+
if len(parts) != 3:
|
|
118
|
+
raise argparse.ArgumentTypeError('expected X,Y,Z, got %r' % text)
|
|
119
|
+
return tuple(float(v) for v in parts)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def build_parser(prog: str = 'oglc-view') -> argparse.ArgumentParser:
|
|
123
|
+
"""The viewer's command line.
|
|
124
|
+
|
|
125
|
+
Every option is declared ``default=argparse.SUPPRESS`` so that an option
|
|
126
|
+
nobody passed leaves the corresponding :class:`ViewerOptions` field alone.
|
|
127
|
+
The defaults are the dataclass's, stated once, and the help text below
|
|
128
|
+
names them where a user needs to know.
|
|
129
|
+
"""
|
|
130
|
+
parser = argparse.ArgumentParser(
|
|
131
|
+
prog=prog,
|
|
132
|
+
description='Walk-around viewer for a 3D scene: %s.' % ', '.join(
|
|
133
|
+
# Suffixes and conventional file names; the content types the same
|
|
134
|
+
# adapters answer to are not something anyone types.
|
|
135
|
+
key for key in known_sources() if '/' not in key),
|
|
136
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
137
|
+
argument_default=argparse.SUPPRESS,
|
|
138
|
+
epilog="A local path or an http(s) URL. .glb is self-contained (URLs work "
|
|
139
|
+
"cleanly); a .gltf that references external .bin/textures should be "
|
|
140
|
+
"downloaded locally as a set. Falls back to the GLTF env var.",
|
|
141
|
+
)
|
|
142
|
+
parser.add_argument('source', nargs='?',
|
|
143
|
+
help='scene file path or http(s) URL (or set GLTF=...)')
|
|
144
|
+
parser.add_argument('--format', metavar='NAME',
|
|
145
|
+
help='read the source as this format instead of guessing '
|
|
146
|
+
'from its name (%s); for a URL that serves a scene '
|
|
147
|
+
'from a path with no suffix'
|
|
148
|
+
% ', '.join(_format_names()))
|
|
149
|
+
parser.add_argument('--camera', metavar='NAME|INDEX',
|
|
150
|
+
help='initial camera, by the name the scene gives it '
|
|
151
|
+
'(preferred) or 0-based index')
|
|
152
|
+
parser.add_argument('--list-cameras', action='store_true',
|
|
153
|
+
help="print the scene's camera names and exit")
|
|
154
|
+
parser.add_argument('--no-cameras', action='store_true',
|
|
155
|
+
help="ignore the scene's own cameras; centre and auto-frame it")
|
|
156
|
+
parser.add_argument('--capture', metavar='PATH',
|
|
157
|
+
help='render to PATH (PNG) after settling, then exit')
|
|
158
|
+
parser.add_argument('--capture-delay', type=float, metavar='SECONDS',
|
|
159
|
+
help='seconds to let the scene settle before --capture (default 0.5)')
|
|
160
|
+
parser.add_argument('--frames', type=int, metavar='N',
|
|
161
|
+
help='minimum frames to render before --capture (default 10)')
|
|
162
|
+
parser.add_argument('--shadows', action=argparse.BooleanOptionalAction,
|
|
163
|
+
help='force shadows on/off (default: on)')
|
|
164
|
+
parser.add_argument('--lights', choices=['auto', 'on', 'off'],
|
|
165
|
+
help="default light rig: auto (add only if the file has none), "
|
|
166
|
+
"on (always add), off (never add)")
|
|
167
|
+
parser.add_argument('--ibl-intensity', type=float, metavar='SCALE',
|
|
168
|
+
help='scale the analytic-sky ambient/IBL contribution')
|
|
169
|
+
parser.add_argument('--environment', metavar='PREFIX|HDR|NAME',
|
|
170
|
+
help='image-based environment: a cubemap face prefix '
|
|
171
|
+
'(<PREFIX>{RT,LF,UP,DN,FR,BK}.jpg), an equirectangular '
|
|
172
|
+
'Radiance .hdr panorama (local path or http(s) URL), or a '
|
|
173
|
+
'bundled CC0 HDRI name (e.g. studio_small_03). Reflected '
|
|
174
|
+
'by metals and drawn as the skybox. Pins full IBL.')
|
|
175
|
+
parser.add_argument('--background', metavar='SPEC',
|
|
176
|
+
help="background: 'sky' (default), 'none', or 'R,G,B'")
|
|
177
|
+
parser.add_argument('--size', type=_parse_size, metavar='WxH',
|
|
178
|
+
help='window size, e.g. 1100x680')
|
|
179
|
+
# Streaming sources (3D Tiles): ignored by a format that is read once.
|
|
180
|
+
parser.add_argument('--sse', type=float, metavar='PIXELS',
|
|
181
|
+
help='streamed datasets: max screen-space error in '
|
|
182
|
+
'pixels; lower = more detail (default 16)')
|
|
183
|
+
parser.add_argument('--memory', type=int, metavar='MiB',
|
|
184
|
+
help='streamed datasets: resident tile memory budget '
|
|
185
|
+
'(default 512)')
|
|
186
|
+
parser.add_argument('--no-recenter', dest='no_recenter', action='store_true',
|
|
187
|
+
help='streamed datasets: do not shift a geospatial '
|
|
188
|
+
'(Earth-centred) dataset to the origin')
|
|
189
|
+
parser.add_argument('--cache-dir', dest='cache_dir', metavar='DIR',
|
|
190
|
+
help='where to cache data fetched from a URL')
|
|
191
|
+
parser.add_argument('--physics', action=argparse.BooleanOptionalAction,
|
|
192
|
+
help='walk the model with gravity + collision (default off, so '
|
|
193
|
+
'an isolated/floorless model stays framed instead of the '
|
|
194
|
+
'avatar falling past it); press "g" or pass --physics to walk')
|
|
195
|
+
parser.add_argument('--turntable', action='store_true',
|
|
196
|
+
help='slowly rotate the model')
|
|
197
|
+
parser.add_argument('--no-rotate', dest='no_rotate', action='store_true',
|
|
198
|
+
help='never rotate the model (fixed orientation, to line a '
|
|
199
|
+
'capture up with a reference image)')
|
|
200
|
+
parser.add_argument('--animation', metavar='NAME|INDEX',
|
|
201
|
+
help='play this glTF animation (name or 0-based index); '
|
|
202
|
+
'default: play the first animation if any')
|
|
203
|
+
parser.add_argument('--no-animation', dest='animate', action='store_false',
|
|
204
|
+
help='do not play embedded animations')
|
|
205
|
+
parser.add_argument('--anim-time', type=float, metavar='SECONDS',
|
|
206
|
+
help='pin the animation to this time (deterministic capture)')
|
|
207
|
+
parser.add_argument('--yaw', type=float,
|
|
208
|
+
help='initial model yaw (radians) when auto-framing '
|
|
209
|
+
'(default -0.62, or the YAW env var)')
|
|
210
|
+
parser.add_argument('--margin', type=float, metavar='FACTOR',
|
|
211
|
+
help='auto-frame fit factor (default 1.15); below 1 pulls '
|
|
212
|
+
'the camera in so the model fills more of the frame')
|
|
213
|
+
parser.add_argument('--elevation', type=float, metavar='FRAC',
|
|
214
|
+
help='auto-frame camera height as a fraction of the model '
|
|
215
|
+
'radius (default 0.22)')
|
|
216
|
+
parser.add_argument('--tilt', type=float, metavar='RADIANS',
|
|
217
|
+
help='auto-frame downward camera tilt in radians (default 0.10)')
|
|
218
|
+
parser.add_argument('--eye', type=_parse_vec3, metavar='X,Y,Z',
|
|
219
|
+
help='explicit camera position (world space); with --look-at '
|
|
220
|
+
'this bypasses auto-framing (e.g. an interior shot)')
|
|
221
|
+
parser.add_argument('--look-at', dest='look_at', type=_parse_vec3,
|
|
222
|
+
metavar='X,Y,Z', help='explicit camera target (world space)')
|
|
223
|
+
return parser
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def parse_args(argv: list[str] | None = None, prog: str = 'oglc-view',
|
|
227
|
+
options: ViewerOptions | None = None) -> ViewerOptions:
|
|
228
|
+
"""Parse the viewer's command line into a :class:`ViewerOptions` (no GL).
|
|
229
|
+
|
|
230
|
+
``options`` is filled in and returned when given, so a program built on this
|
|
231
|
+
one can start from its own defaults and let the user override them.
|
|
232
|
+
"""
|
|
233
|
+
return build_parser(prog).parse_args(
|
|
234
|
+
argv, namespace=options if options is not None else ViewerOptions())
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _is_hdr_environment(spec: str | None) -> bool:
|
|
238
|
+
"""Whether ``--environment SPEC`` names a Radiance ``.hdr`` panorama.
|
|
239
|
+
|
|
240
|
+
An equirectangular ``.hdr``/``.pic`` (local path or http(s) URL) is treated as
|
|
241
|
+
an HDR IBL source + skybox; anything else is a six-face cubemap prefix. The
|
|
242
|
+
query string of a URL is ignored so a CDN link with parameters still matches.
|
|
243
|
+
"""
|
|
244
|
+
if not spec:
|
|
245
|
+
return False
|
|
246
|
+
path = spec.split('?', 1)[0].split('#', 1)[0]
|
|
247
|
+
return path.lower().endswith(('.hdr', '.pic'))
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def apply_render_env(args: ViewerOptions) -> None:
|
|
251
|
+
"""Translate render-affecting options into the variables the renderer reads.
|
|
252
|
+
|
|
253
|
+
These are start-up switches read once by the passes (see
|
|
254
|
+
:mod:`OpenGLContext.renderoptions`), so they are set before a context exists
|
|
255
|
+
rather than carried on the options object.
|
|
256
|
+
"""
|
|
257
|
+
if args.shadows is not None:
|
|
258
|
+
os.environ['OPENGLCONTEXT_SHADOWS'] = '1' if args.shadows else '0'
|
|
259
|
+
if args.ibl_intensity is not None:
|
|
260
|
+
os.environ['OPENGLCONTEXT_IBL_INTENSITY'] = str(args.ibl_intensity)
|
|
261
|
+
if args.environment:
|
|
262
|
+
from OpenGLContext.loaders import hdri
|
|
263
|
+
try:
|
|
264
|
+
args.environment = hdri.resolve(args.environment) # catalogue name -> URL
|
|
265
|
+
except KeyError:
|
|
266
|
+
pass # not a catalogue name; treat as a cubemap prefix / path
|
|
267
|
+
environment: str = args.environment
|
|
268
|
+
if _is_hdr_environment(environment):
|
|
269
|
+
# An equirectangular Radiance .hdr (local path or URL): drives the IBL
|
|
270
|
+
# probe and the HDR skybox. The probe loads it via OPENGLCONTEXT_ENV_HDR.
|
|
271
|
+
os.environ['OPENGLCONTEXT_ENV_HDR'] = environment
|
|
272
|
+
else:
|
|
273
|
+
os.environ['OPENGLCONTEXT_ENV_CUBEMAP'] = environment
|
|
274
|
+
os.environ.setdefault('OPENGLCONTEXT_IBL', 'full') # env probe needs full IBL
|
|
275
|
+
elif (args.background == 'none'
|
|
276
|
+
and not os.environ.get('OPENGLCONTEXT_ENV_CUBEMAP')
|
|
277
|
+
and not os.environ.get('OPENGLCONTEXT_ENV_HDR')):
|
|
278
|
+
# A self-lit scene (its own KHR_lights_punctual, black backdrop, NO env probe)
|
|
279
|
+
# must get no analytic-sky IBL, or the ambient sky washes it pale grey instead
|
|
280
|
+
# of the dark scene its lights make. But only when there is genuinely no
|
|
281
|
+
# environment: the browser demo defaults --background 'none' yet loads an env
|
|
282
|
+
# cubemap probe for metals to reflect, so forcing IBL off there rendered every
|
|
283
|
+
# metal black. Honour an explicit env cubemap or HDR panorama.
|
|
284
|
+
os.environ['OPENGLCONTEXT_IBL'] = 'off'
|
|
285
|
+
if args.capture:
|
|
286
|
+
# a --capture run wants a clean frame, not the fps overlay
|
|
287
|
+
os.environ['OPENGLCONTEXT_DISABLE_FPS_DISPLAY'] = '1'
|
|
288
|
+
# A capture must be reproducible: pin the otherwise fps-adaptive cascade
|
|
289
|
+
# count so the shadows don't vary with the frame rate between runs. An
|
|
290
|
+
# explicit user setting still wins.
|
|
291
|
+
os.environ.setdefault('OPENGLCONTEXT_SHADOW_CASCADES', '3')
|
|
292
|
+
# Nobody is watching a capture, and a *mapped* surface is what makes it
|
|
293
|
+
# hang: a compositor throttles the swap to its own frame callback, and
|
|
294
|
+
# with no window on screen consuming frames the swap never returns. A
|
|
295
|
+
# hidden window renders and reads back identically. Both stay
|
|
296
|
+
# overridable, since watching a capture happen is how you find out why
|
|
297
|
+
# it looks wrong.
|
|
298
|
+
os.environ.setdefault('OPENGLCONTEXT_HIDDEN', '1')
|
|
299
|
+
os.environ.setdefault('OPENGLCONTEXT_NO_VSYNC', '1')
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
class TestContext(ViewerContext):
|
|
303
|
+
"""``oglc-view`` itself: the viewing component, configured from the command line."""
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _list_cameras(source: str, format: Optional[str] = None) -> int:
|
|
307
|
+
"""Print the scene's camera names, for choosing one with ``--camera``.
|
|
308
|
+
|
|
309
|
+
The adapter answers, so this works for every format rather than only the one
|
|
310
|
+
the listing was first written for.
|
|
311
|
+
"""
|
|
312
|
+
adapter = adapter_named(format) if format else adapter_for(source)
|
|
313
|
+
scene = adapter.load(source)
|
|
314
|
+
if not scene.cameras:
|
|
315
|
+
sys.stdout.write("(no cameras defined in %s)\n" % os.path.basename(source))
|
|
316
|
+
for i, camera in enumerate(scene.cameras):
|
|
317
|
+
sys.stdout.write("%d: %s\n" % (i, camera.get('name') or 'camera'))
|
|
318
|
+
return 0
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def main(argv: Optional[list[str]] = None, prog: str = 'oglc-view') -> Any:
|
|
322
|
+
parser = build_parser(prog)
|
|
323
|
+
options = parser.parse_args(argv, namespace=ViewerOptions())
|
|
324
|
+
source = options.source or os.environ.get('GLTF')
|
|
325
|
+
if options.list_cameras and not source:
|
|
326
|
+
parser.error('nothing to list cameras for (pass a path or a URL)')
|
|
327
|
+
|
|
328
|
+
if options.list_cameras:
|
|
329
|
+
try:
|
|
330
|
+
return _list_cameras(resolve_source(source) or source, options.format)
|
|
331
|
+
except UnknownSourceType as error:
|
|
332
|
+
parser.error(str(error))
|
|
333
|
+
|
|
334
|
+
apply_render_env(options)
|
|
335
|
+
TestContext.options = options
|
|
336
|
+
return TestContext.ContextMainLoop(size=options.size) if options.size \
|
|
337
|
+
else TestContext.ContextMainLoop()
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
if __name__ == "__main__": # pragma: no cover - CLI entry point
|
|
341
|
+
main()
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#! /usr/bin/env python
|
|
2
|
+
"""``oglc-vrml`` -- the former VRML97-only viewer, now an alias for ``oglc-view``.
|
|
3
|
+
|
|
4
|
+
There is one viewer, and it works out the format from the source, so the command
|
|
5
|
+
that opens a ``.wrl`` is the command that opens a ``.glb``. A VRML world opened
|
|
6
|
+
this way gains everything that was previously only in the glTF viewer: framing,
|
|
7
|
+
loading in the background, camera cycling over the world's own ``Viewpoint``
|
|
8
|
+
nodes, a caption, screenshots, ``--capture``, and walking with gravity and
|
|
9
|
+
collision. See :mod:`OpenGLContext.bin.view`.
|
|
10
|
+
|
|
11
|
+
The old ``--shaders`` / ``--no-shaders`` switches are gone: they reached into the
|
|
12
|
+
render pass from inside ``Redraw`` to turn on shader rendering, and the viewer
|
|
13
|
+
now renders through the core-profile PBR pass as a matter of course. A world
|
|
14
|
+
that genuinely wants the compatibility pipeline gets it from the environment,
|
|
15
|
+
which is where every other renderer switch lives::
|
|
16
|
+
|
|
17
|
+
OPENGLCONTEXT_PROFILE=compatibility oglc-view world.wrl
|
|
18
|
+
|
|
19
|
+
This name is kept for one release cycle so existing scripts and documentation
|
|
20
|
+
keep working, and prints where to go instead. It will then be removed.
|
|
21
|
+
"""
|
|
22
|
+
import sys
|
|
23
|
+
from typing import Any, Optional
|
|
24
|
+
|
|
25
|
+
from OpenGLContext.bin.view import main as view_main
|
|
26
|
+
|
|
27
|
+
#: What to type now. Named here so the notice and the replacement cannot drift.
|
|
28
|
+
REPLACEMENT = 'oglc-view'
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def main(argv: Optional[list] = None) -> Any:
|
|
32
|
+
"""Run ``oglc-view``, having said that is what this now is."""
|
|
33
|
+
sys.stderr.write(
|
|
34
|
+
"oglc-vrml is deprecated and will be removed; use %s instead "
|
|
35
|
+
"(it opens every format, and a world gets the full viewer).\n"
|
|
36
|
+
% REPLACEMENT)
|
|
37
|
+
sys.stderr.flush()
|
|
38
|
+
return view_main(argv, prog='oglc-vrml')
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
if __name__ == "__main__": # pragma: no cover - CLI entry point
|
|
42
|
+
main()
|