From 9b62fad99bec8824369c46f5de9dedc9d975bdf6 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sat, 28 Mar 2026 11:24:01 +0000 Subject: [PATCH 01/26] Minimal Viable OpenGLView This implements a minimal OpenGLView widget for many backends. This widget merely sets up an OpenGL context, and calls back to user code to do the rendering with whatever OpenGL API is appropriate. Includes basic documentation and an example. --- android/pyproject.toml | 1 + .../src/toga_android/widgets/openglview.py | 34 +++++ android/tests_backend/widgets/openglview.py | 7 + changes/4273.feature.md | 1 + cocoa/pyproject.toml | 1 + cocoa/src/toga_cocoa/libs/appkit.py | 11 ++ cocoa/src/toga_cocoa/widgets/openglview.py | 74 +++++++++ cocoa/tests_backend/widgets/openglview.py | 7 + core/src/toga/__init__.pyi | 1 + core/src/toga/widgets/openglview.py | 82 ++++++++++ core/tests/widgets/test_openglview.py | 54 +++++++ docs/en/reference/api/widgets/openglview.md | 49 ++++++ docs/en/reference/data/apis_by_platform.yaml | 13 ++ dummy/pyproject.toml | 1 + dummy/src/toga_dummy/widgets/openglview.py | 16 ++ examples/opengl/CHANGELOG | 1 + examples/opengl/LICENSE | 1 + examples/opengl/README.md | 17 +++ examples/opengl/opengl/__init__.py | 9 ++ examples/opengl/opengl/__main__.py | 4 + examples/opengl/opengl/app.py | 38 +++++ examples/opengl/opengl/renderer_android.py | 143 ++++++++++++++++++ examples/opengl/opengl/renderer_iOS.py | 17 +++ examples/opengl/opengl/renderer_pyopengl.py | 139 +++++++++++++++++ examples/opengl/pyproject.toml | 76 ++++++++++ gtk/pyproject.toml | 1 + gtk/src/toga_gtk/widgets/openglview.py | 41 +++++ gtk/tests_backend/widgets/openglview.py | 7 + iOS/pyproject.toml | 1 + iOS/src/toga_iOS/libs/__init__.py | 2 + iOS/src/toga_iOS/libs/glkit.py | 25 +++ iOS/src/toga_iOS/libs/opengles.py | 34 +++++ iOS/src/toga_iOS/widgets/openglview.py | 63 ++++++++ iOS/tests_backend/widgets/openglview.py | 7 + iOS/tests_backend/window.py | 9 +- qt/pyproject.toml | 1 + qt/src/toga_qt/__init__.py | 17 +++ qt/src/toga_qt/widgets/openglview.py | 44 ++++++ qt/tests_backend/widgets/openglview.py | 7 + testbed/tests/widgets/test_openglview.py | 48 ++++++ 40 files changed, 1102 insertions(+), 2 deletions(-) create mode 100644 android/src/toga_android/widgets/openglview.py create mode 100644 android/tests_backend/widgets/openglview.py create mode 100644 changes/4273.feature.md create mode 100644 cocoa/src/toga_cocoa/widgets/openglview.py create mode 100644 cocoa/tests_backend/widgets/openglview.py create mode 100644 core/src/toga/widgets/openglview.py create mode 100644 core/tests/widgets/test_openglview.py create mode 100644 docs/en/reference/api/widgets/openglview.md create mode 100644 dummy/src/toga_dummy/widgets/openglview.py create mode 100644 examples/opengl/CHANGELOG create mode 100644 examples/opengl/LICENSE create mode 100644 examples/opengl/README.md create mode 100644 examples/opengl/opengl/__init__.py create mode 100644 examples/opengl/opengl/__main__.py create mode 100644 examples/opengl/opengl/app.py create mode 100644 examples/opengl/opengl/renderer_android.py create mode 100644 examples/opengl/opengl/renderer_iOS.py create mode 100644 examples/opengl/opengl/renderer_pyopengl.py create mode 100644 examples/opengl/pyproject.toml create mode 100644 gtk/src/toga_gtk/widgets/openglview.py create mode 100644 gtk/tests_backend/widgets/openglview.py create mode 100644 iOS/src/toga_iOS/libs/glkit.py create mode 100644 iOS/src/toga_iOS/libs/opengles.py create mode 100644 iOS/src/toga_iOS/widgets/openglview.py create mode 100644 iOS/tests_backend/widgets/openglview.py create mode 100644 qt/src/toga_qt/widgets/openglview.py create mode 100644 qt/tests_backend/widgets/openglview.py create mode 100644 testbed/tests/widgets/test_openglview.py diff --git a/android/pyproject.toml b/android/pyproject.toml index 50589c93ec..50adc98109 100644 --- a/android/pyproject.toml +++ b/android/pyproject.toml @@ -90,6 +90,7 @@ Label = "toga_android.widgets.label:Label" MapView = "toga_android.widgets.mapview:MapView" MultilineTextInput = "toga_android.widgets.multilinetextinput:MultilineTextInput" NumberInput = "toga_android.widgets.numberinput:NumberInput" +OpenGLView = "toga_android.widgets.openglview:OpenGLView" OptionContainer = "toga_android.widgets.optioncontainer:OptionContainer" PasswordInput = "toga_android.widgets.passwordinput:PasswordInput" ProgressBar = "toga_android.widgets.progressbar:ProgressBar" diff --git a/android/src/toga_android/widgets/openglview.py b/android/src/toga_android/widgets/openglview.py new file mode 100644 index 0000000000..c86c25501d --- /dev/null +++ b/android/src/toga_android/widgets/openglview.py @@ -0,0 +1,34 @@ +from android.opengl import GLSurfaceView +from java import dynamic_proxy + +from .base import Widget + + +class TogaGLRenderer(dynamic_proxy(GLSurfaceView.Renderer)): + def onSurfaceCreated(self, unused, config): + self.interface.renderer.on_init(self.interface) + + def onDrawFrame(self, unused): + self._redraw() + + def onSurfaceChanged(self, unused, width, height): + self._redraw() + + def _redraw(self): + width = self.impl.native.getWidth() + height = self.impl.native.getHeight() + self.interface.renderer.on_render(self.interface, size=(width, height)) + + +class OpenGLView(Widget): + def create(self): + self.native = GLSurfaceView(self._native_activity) + self.renderer = TogaGLRenderer() + self.renderer.interface = self.interface + self.renderer.impl = self + + self.native.setEGLContextClientVersion(3) + self.native.setRenderer(self.renderer) + + def redraw(self): + self.native.invalidate() diff --git a/android/tests_backend/widgets/openglview.py b/android/tests_backend/widgets/openglview.py new file mode 100644 index 0000000000..a18cce7860 --- /dev/null +++ b/android/tests_backend/widgets/openglview.py @@ -0,0 +1,7 @@ +from android.opengl import GLSurfaceView + +from .base import SimpleProbe + + +class OpenGLViewProbe(SimpleProbe): + native_class = GLSurfaceView diff --git a/changes/4273.feature.md b/changes/4273.feature.md new file mode 100644 index 0000000000..b7ff0277f7 --- /dev/null +++ b/changes/4273.feature.md @@ -0,0 +1 @@ +The Android, Cocoa, Gtk, iOS and Qt backends now have a basic `OpenGLView` widget that provides low-level access to OpenGL rendering. diff --git a/cocoa/pyproject.toml b/cocoa/pyproject.toml index e413bc2f6b..128162fb2d 100644 --- a/cocoa/pyproject.toml +++ b/cocoa/pyproject.toml @@ -90,6 +90,7 @@ Label = "toga_cocoa.widgets.label:Label" MapView = "toga_cocoa.widgets.mapview:MapView" MultilineTextInput = "toga_cocoa.widgets.multilinetextinput:MultilineTextInput" NumberInput = "toga_cocoa.widgets.numberinput:NumberInput" +OpenGLView = "toga_cocoa.widgets.openglview:OpenGLView" OptionContainer = "toga_cocoa.widgets.optioncontainer:OptionContainer" PasswordInput = "toga_cocoa.widgets.passwordinput:PasswordInput" ProgressBar = "toga_cocoa.widgets.progressbar:ProgressBar" diff --git a/cocoa/src/toga_cocoa/libs/appkit.py b/cocoa/src/toga_cocoa/libs/appkit.py index b746348804..9d08138cab 100644 --- a/cocoa/src/toga_cocoa/libs/appkit.py +++ b/cocoa/src/toga_cocoa/libs/appkit.py @@ -525,10 +525,21 @@ class NSLayoutPriority(Enum): NSOpenGLPFARemotePixelBuffer = 91 # can be used to render offline to a pbuffer NSOpenGLPFAAllowOfflineRenderers = 96 # allow use of offline renderers NSOpenGLPFAAcceleratedCompute = 97 # choose a hardware accelerated compute device +NSOpenGLPFAOpenGLProfile = 99 # specify an OpenGL Profile to use NSOpenGLPFAVirtualScreenCount = 128 # number of virtual screens in this format +NSOpenGLProfileVersionLegacy = 0x1000 # choose a Legacy/Pre-OpenGL 3.0 Implementation +NSOpenGLProfileVersion3_2Core = 0x3200 # choose an OpenGL 3.2 Core Implementation +NSOpenGLProfileVersion4_1Core = 0x4100 # choose an OpenGL 4.1 Core Implementation + NSOpenGLCPSwapInterval = 222 +###################################################################### +# NSOpenGLView.h +NSOpenGLView = ObjCClass("NSOpenGLView") +NSOpenGLContext = ObjCClass("NSOpenGLContext") +NSOpenGLPixelFormat = ObjCClass("NSOpenGLPixelFormat") + ###################################################################### # NSOpenPanel.h NSOpenPanel = ObjCClass("NSOpenPanel") diff --git a/cocoa/src/toga_cocoa/widgets/openglview.py b/cocoa/src/toga_cocoa/widgets/openglview.py new file mode 100644 index 0000000000..971cc918d0 --- /dev/null +++ b/cocoa/src/toga_cocoa/widgets/openglview.py @@ -0,0 +1,74 @@ +import ctypes + +from rubicon.objc import objc_method, objc_property +from travertino.size import at_least + +from toga_cocoa.libs import ( + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAOpenGLProfile, + NSOpenGLPixelFormat, + NSOpenGLProfileVersion4_1Core, + NSOpenGLView, + NSRect, +) + +from .base import Widget + + +class TogaOpenGLView(NSOpenGLView): + interface = objc_property(object, weak=True) + impl = objc_property(object, weak=True) + + @objc_method + def prepareOpenGL(self) -> None: + # super().prepareOpenGL() + self.openGLContext.makeCurrentContext() + self.interface.renderer.on_init(self.interface) + + @objc_method + def drawRect_(self, rect: NSRect) -> None: + size = (int(rect.size.width), int(rect.size.height)) + self.openGLContext.makeCurrentContext() + self.interface.renderer.on_render(self.interface, size=size) + self.openGLContext.flushBuffer() + + @objc_method + def initWithFrame_(self, frame: NSRect): + a = ( + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAOpenGLProfile, + NSOpenGLProfileVersion4_1Core, + ) + attributes = (ctypes.c_uint32 * len(a))(*a) + pixel_format = NSOpenGLPixelFormat.alloc().initWithAttributes_(attributes) + # try help pinpoint the failure if we can't set things up correctly + # can't test, as this would only occur on an older machine/macOS version + if pixel_format is None: # pragma: no cover + # warnings cause segfault here, so just print + print("Can't create NSOpenGLPixelFormat with required properties.") + return None + + return self.initWithFrame_pixelFormat_(frame, pixel_format) + + +class OpenGLView(Widget): + def create(self): + self.native = TogaOpenGLView.alloc().init() + # try to fail gracefully if we can't set things up correctly + # can't test, as this would only occur on an older machine/macOS version + if self.native is None: # pragma: no cover + raise RuntimeError("Can't create native OpenGLView widget.") + self.native.interface = self.interface + self.native.impl = self + + # Add the layout constraints + self.add_constraints() + + def redraw(self): + self.native.needsDisplay = True + + # Rehint + def rehint(self): + fitting_size = self.native.fittingSize() + self.interface.intrinsic.height = at_least(fitting_size.height) + self.interface.intrinsic.width = at_least(fitting_size.width) diff --git a/cocoa/tests_backend/widgets/openglview.py b/cocoa/tests_backend/widgets/openglview.py new file mode 100644 index 0000000000..9e3a257b2a --- /dev/null +++ b/cocoa/tests_backend/widgets/openglview.py @@ -0,0 +1,7 @@ +from toga_cocoa.libs import NSOpenGLView + +from .base import SimpleProbe + + +class OpenGLViewProbe(SimpleProbe): + native_class = NSOpenGLView diff --git a/core/src/toga/__init__.pyi b/core/src/toga/__init__.pyi index da118ad4fa..d04b7245ad 100644 --- a/core/src/toga/__init__.pyi +++ b/core/src/toga/__init__.pyi @@ -45,6 +45,7 @@ from toga.widgets.mapview import MapPin as MapPin from toga.widgets.mapview import MapView as MapView from toga.widgets.multilinetextinput import MultilineTextInput as MultilineTextInput from toga.widgets.numberinput import NumberInput as NumberInput +from toga.widgets.openglview import OpenGLView as OpenGLView from toga.widgets.optioncontainer import OptionContainer as OptionContainer from toga.widgets.optioncontainer import OptionItem as OptionItem from toga.widgets.passwordinput import PasswordInput as PasswordInput diff --git a/core/src/toga/widgets/openglview.py b/core/src/toga/widgets/openglview.py new file mode 100644 index 0000000000..a3539fd0e4 --- /dev/null +++ b/core/src/toga/widgets/openglview.py @@ -0,0 +1,82 @@ +from __future__ import annotations + +from typing import Any, Literal, Protocol + +from .base import StyleT, Widget + + +class RendererT(Protocol): + """A protocol that encapsulates OpenGL rendering operations.""" + + def on_init(self, widget: OpenGLView, **kwargs: Any) -> None: + """The method called when the OpenGLView initializes its content. + + :param widget: The view that is being initialized. + :param kwargs: Ensures compatibility with arguments added in future versions. + """ + + def on_render( + self, widget: OpenGLView, size: tuple[int, int], **kwargs: Any + ) -> None: + """The method called when the OpenGLView needs to re-draw its content. + + :param widget: The view that is being rendered. + :param size: The size of the current OpenGL viewport. + :param kwargs: Ensures compatibility with arguments added in future versions. + """ + + +class OpenGLView(Widget): + def __init__( + self, + renderer: RendererT, + id: str | None = None, + style: StyleT | None = None, + **kwargs, + ): + """Create a new OpenGLView. + + The widget calls the renderer's `on_init` once when the widget + implementation is preparing the widget for rendering, and then calls + the renderer's `on_render` whenever the widget contents need to be + redrawn. + + This widget makes no assumptions about the OpenGL library that is used to + perform OpenGL operations. + + :param renderer: The renderer which performs the OpenGL operations. + :param id: The ID for the widget. + :param style: A style object. If no style is provided, a default style will be + applied to the widget. + :param kwargs: Initial style properties. + """ + self._renderer = renderer + super().__init__(id, style, **kwargs) + + def _create(self): + return self.factory.OpenGLView(interface=self) + + @property + def enabled(self) -> Literal[True]: + """Is the widget currently enabled? i.e., can the user interact with the widget? + OpenGL widgets cannot be disabled; this property will always return + True; any attempt to modify it will be ignored. + """ + return True + + @enabled.setter + def enabled(self, value: object) -> None: + pass + + def focus(self) -> None: + """OpenGLWidgets cannot accept input focus.""" + pass + + def redraw(self) -> None: + """Flag that the widget needs to be re-rendered.""" + self._impl.redraw() + + @property + def renderer(self) -> RendererT: + """The widget's renderer, handling initialization and drawing in OpenGL.""" + return self._renderer diff --git a/core/tests/widgets/test_openglview.py b/core/tests/widgets/test_openglview.py new file mode 100644 index 0000000000..b0a41d6054 --- /dev/null +++ b/core/tests/widgets/test_openglview.py @@ -0,0 +1,54 @@ +from unittest.mock import Mock + +import pytest + +import toga +from toga_dummy.utils import assert_action_not_performed, assert_action_performed + + +@pytest.fixture +def renderer(): + renderer = Mock() + # renderer.on_init = Mock() + # renderer.on_render = Mock() + return renderer + + +@pytest.fixture +def widget(renderer): + return toga.OpenGLView(renderer) + + +def test_widget_created(renderer): + """An OpenGLView can be created.""" + widget = toga.OpenGLView(renderer) + assert widget._impl.interface == widget + assert_action_performed(widget, "create OpenGLView") + renderer.on_init.assert_called_once_with(widget) + + +def test_disable_no_op(widget): + """OpenGLView doesn't have a disabled state.""" + # Enabled by default + assert widget.enabled + + # Try to disable the widget + widget.enabled = False + + # Still enabled. + assert widget.enabled + + +def test_focus_noop(widget): + """Focus is a no-op.""" + + widget.focus() + assert_action_not_performed(widget, "focus") + + +def test_redraw(widget, renderer): + """The canvas can be redrawn.""" + widget.redraw() + + assert_action_performed(widget, "redraw") + renderer.on_render.assert_called_once_with(widget, (37, 42)) diff --git a/docs/en/reference/api/widgets/openglview.md b/docs/en/reference/api/widgets/openglview.md new file mode 100644 index 0000000000..07ebe862d2 --- /dev/null +++ b/docs/en/reference/api/widgets/openglview.md @@ -0,0 +1,49 @@ +{{ component_header("OpenGLView", width=300) }} + +## Usage + +OpenGLView provides a surface for rendering 2D and 3D graphics using OpenGL. It is agnostic to the library used to actually perform the OpenGL rendering: some platforms, such as Qt and Android, provide native OpenGL interfaces while for others, notably Cocoa and Gtk, can use existing Python OpenGL wrappers such as PyOpenGL or ModernGL. Currently Toga doesn't provide a cross-platform OpenGL API layer, although it may in the future. + +The OpenGLView expects to be given a renderer object which conforms to the [`RendererT`][toga.widgets.openglview.RendererT] protocol: it needs an `on_init` method which gets called by the implementation layer to perform any OpenGL initialization that is needed (eg. creating shader programs, setting up buffers); and an `on_render` method which gets called to do the actual OpenGL drawing. Both methods get called with the OpenGL context for the view set up and ready to be accessed by OpenGL library calls. + +For example, a renderer that just clears the view using PyOpenGL could be written like this: +``` python +from OpenGL import GL + +class ClearRenderer: + def on_init(self, widget, **kwargs): + # set the clear color to blue + GL.glClearColor(0.0, 0.0, 1.0, 1.0) + + def on_render(self, widget, size, **kwargs): + # clear the OpenGL view + GL.glClear(GL.GL_COLOR_BUFFER_BIT) +``` +The OpenGLView can then be initialized by calling it with the renderer: +``` python +renderer = ClearRenderer() +openglview = OpenGLView(renderer) +``` + +If the renderer changes state and a re-rendering is required, the `redraw` method will schedule the backend to refresh the OpenGLView and trigger `on_render` via the application's event loop. + +## Notes + +- The OpenGLView API should be considered a beta API and may change in the future. +- The renderer object used by the OpenGLView can't be changed after the view is created, but it can hold state and change the way that it renders based on that. +- The OpenGLView is currently only available on the Android, Cocoa, Gtk, iOS and Qt backends. +- OpenGL is deprecated on macOS and iOS, but it is likely to be available for the foreseeable future. +- There are currently no Python OpenGL wrappers for iOS, but `ctypes` can be used to wrap the iOS `opengles` DLL and call out to OpenGL. +- Linux relies on the appropriate OpenGL driver libraries being installed on the system with the system's package manager. + +The version of OpenGL that is available on each platform depends heavily on the platform: mobile and web platforms provide OpenGL ES, while desktop platforms generally provide full OpenGL support. The OpenGLView tries to provide an OpenGL context with the highest OpenGL version available. This is currently: + +- Android: OpenGL ES 3.0 +- MacOS: OpenGL 4.1 +- Qt: OpenGL 4.1 + +## Reference + +::: toga.OpenGLView + +::: toga.widgets.openglview.RendererT diff --git a/docs/en/reference/data/apis_by_platform.yaml b/docs/en/reference/data/apis_by_platform.yaml index c7b83e206e..a0cf2b5feb 100644 --- a/docs/en/reference/data/apis_by_platform.yaml +++ b/docs/en/reference/data/apis_by_platform.yaml @@ -128,6 +128,19 @@ Widgets: - web - textual + OpenGLView: + description: Display 2D and 3D OpenGL graphics. + beta: + - android + - cocoa + - gtk + - iOS + - qt + unsupported: + - textual + - web + - winforms + PasswordInput: description: For entering a password; obscures entered text. beta: diff --git a/dummy/pyproject.toml b/dummy/pyproject.toml index 1f158f702c..b42ba21bd1 100644 --- a/dummy/pyproject.toml +++ b/dummy/pyproject.toml @@ -89,6 +89,7 @@ Label = "toga_dummy.widgets.label:Label" MapView = "toga_dummy.widgets.mapview:MapView" MultilineTextInput = "toga_dummy.widgets.multilinetextinput:MultilineTextInput" NumberInput = "toga_dummy.widgets.numberinput:NumberInput" +OpenGLView = "toga_dummy.widgets.openglview:OpenGLView" OptionContainer = "toga_dummy.widgets.optioncontainer:OptionContainer" PasswordInput = "toga_dummy.widgets.passwordinput:PasswordInput" ProgressBar = "toga_dummy.widgets.progressbar:ProgressBar" diff --git a/dummy/src/toga_dummy/widgets/openglview.py b/dummy/src/toga_dummy/widgets/openglview.py new file mode 100644 index 0000000000..86046dc2ae --- /dev/null +++ b/dummy/src/toga_dummy/widgets/openglview.py @@ -0,0 +1,16 @@ +from .base import Widget + + +class OpenGLView(Widget): + def create(self): + self._action("create OpenGLView") + self.interface.renderer.on_init(self.interface) + + # Resize handlers + + def redraw(self): + self._action("redraw") + self.interface.renderer.on_render(self.interface, self.get_size()) + + def simulate_resize(self): + self.interface.renderer.on_render(self.interface, self.get_size()) diff --git a/examples/opengl/CHANGELOG b/examples/opengl/CHANGELOG new file mode 100644 index 0000000000..c6b095235f --- /dev/null +++ b/examples/opengl/CHANGELOG @@ -0,0 +1 @@ +See Toga releases for change notes. diff --git a/examples/opengl/LICENSE b/examples/opengl/LICENSE new file mode 100644 index 0000000000..f09583bfab --- /dev/null +++ b/examples/opengl/LICENSE @@ -0,0 +1 @@ +Released under the same license as Toga. See the root of the Toga repository for details. diff --git a/examples/opengl/README.md b/examples/opengl/README.md new file mode 100644 index 0000000000..96f859ff64 --- /dev/null +++ b/examples/opengl/README.md @@ -0,0 +1,17 @@ +# OpenGL + +Test app for the +[OpenGLView widget](https://toga.beeware.org/en/stable/reference/api/widgets/openglview.html). + +The following OpenGLView features are present in this example: + +- rendering a simple OpenGL view using PyOpenGL + +## Quickstart + +To run this example: + +``` +$ python -m pip install toga PyOpenGL +$ python -m opengl +``` diff --git a/examples/opengl/opengl/__init__.py b/examples/opengl/opengl/__init__.py new file mode 100644 index 0000000000..86826e638c --- /dev/null +++ b/examples/opengl/opengl/__init__.py @@ -0,0 +1,9 @@ +# Examples of valid version strings +# __version__ = '1.2.3.dev1' # Development release 1 +# __version__ = '1.2.3a1' # Alpha Release 1 +# __version__ = '1.2.3b1' # Beta Release 1 +# __version__ = '1.2.3rc1' # RC Release 1 +# __version__ = '1.2.3' # Final Release +# __version__ = '1.2.3.post1' # Post Release 1 + +__version__ = "0.0.1" diff --git a/examples/opengl/opengl/__main__.py b/examples/opengl/opengl/__main__.py new file mode 100644 index 0000000000..fd87db935c --- /dev/null +++ b/examples/opengl/opengl/__main__.py @@ -0,0 +1,4 @@ +from opengl.app import main + +if __name__ == "__main__": + main().main_loop() diff --git a/examples/opengl/opengl/app.py b/examples/opengl/opengl/app.py new file mode 100644 index 0000000000..01af194a23 --- /dev/null +++ b/examples/opengl/opengl/app.py @@ -0,0 +1,38 @@ +import toga + + +class OpenGLApp(toga.App): + def startup(self): + self.main_window = toga.MainWindow( + size=(800, 500), resizable=True, minimizable=False + ) + + if toga.backend in {"toga_cocoa", "toga_qt", "toga_gtk"}: + from .renderer_pyopengl import Renderer + elif toga.backend == "toga_android": + from .renderer_android import Renderer + elif toga.backend == "toga_iOS": + from .renderer_iOS import Renderer + else: + raise RuntimeError(f"Toga backend {toga.backend} is not supported.") + + renderer = Renderer() + + opengl_view = toga.OpenGLView(renderer, flex=1) + + # Create the outer box with 2 rows + outer_box = toga.Box(children=[opengl_view]) + + # Add the content on the main window + self.main_window.content = outer_box + + # Show the main window + self.main_window.show() + + +def main(): + return OpenGLApp("OpenGL", "org.beeware.toga.examples.opengl") + + +if __name__ == "__main__": + main().main_loop() diff --git a/examples/opengl/opengl/renderer_android.py b/examples/opengl/opengl/renderer_android.py new file mode 100644 index 0000000000..45d90ce70a --- /dev/null +++ b/examples/opengl/opengl/renderer_android.py @@ -0,0 +1,143 @@ +import array + +from android.opengl import GLES32 as GL +from java import jarray, jint +from java.nio import ByteBuffer, ByteOrder + +vertext_shader_source = """ + +layout(location = 0) in vec4 position; + +void main() +{ + gl_Position = position; +} +""" +fragment_shader_source = """ + +out vec4 outputColor; +void main() +{ + outputColor = vec4(1.0f, 1.0f, 1.0f, 1.0f); +} +""" + + +def create_program(shader_list): + program = GL.glCreateProgram() + + for shader in shader_list: + GL.glAttachShader(program, shader) + + GL.glLinkProgram(program) + + # status = GL.glGetProgramiv(program, GL.GL_LINK_STATUS) + # if status == GL.GL_FALSE: + # strInfoLog = GL.glGetProgramInfoLog(program) + # raise RuntimeError("Linker failure: \n" + strInfoLog) + + for shader in shader_list: + GL.glDetachShader(program, shader) + + return program + + +def create_shader(shader_type, shader_code): + shader = GL.glCreateShader(shader_type) + GL.glShaderSource(shader, shader_code) + + GL.glCompileShader(shader) + + # status = None + # GL.glGetShaderiv(shader, GL.GL_COMPILE_STATUS, status) + # if status == GL.GL_FALSE: + # info_log = GL.glGetShaderInfoLog(shader) + # shader_types = { + # GL.GL_VERTEX_SHADER: "vertex", + # GL.GL_GEOMETRY_SHADER: "geometry", + # GL.GL_FRAGMENT_SHADER: "fragment", + # } + # raise RuntimeError( + # f"Compilation failure for {shader_types.get(shader_type, 'unknown')} " + # f"shader:\n{info_log}" + # ) + + return shader + + +def initialize_program(vertex_shader_code, fragment_shader_code): + shaderList = [] + + shaderList.append(create_shader(GL.GL_VERTEX_SHADER, vertex_shader_code)) + shaderList.append(create_shader(GL.GL_FRAGMENT_SHADER, fragment_shader_code)) + + program = create_program(shaderList) + + for shader in shaderList: + GL.glDeleteShader(shader) + + return program + + +def initialize_vbo(vertex_positions): + buffers = jarray(jint)([0]) + GL.glGenBuffers(1, buffers, 0) + vbo = buffers[0] + + vertex_bytes = bytes(array.array("f", vertex_positions)) + nbytes = len(vertex_bytes) + vertex_buffer = ByteBuffer.allocateDirect(nbytes) + vertex_buffer.order(ByteOrder.nativeOrder()) + vertex_buffer.put(vertex_bytes) + vertex_buffer.position(0) + GL.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo) + GL.glBufferData(GL.GL_ARRAY_BUFFER, nbytes, vertex_buffer, GL.GL_STATIC_DRAW) + GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0) + + return vbo + + +class Renderer: + def on_init(self, widget, **kwargs): + GL.glClearColor(1.0, 1.0, 0.0, 1.0) + + vertex_positions = [ + 0.75, + 0.75, + 0.0, + 1.0, + 0.75, + -0.75, + 0.0, + 1.0, + -0.75, + -0.75, + 0.0, + 1.0, + ] + + self.program = initialize_program(vertext_shader_source, fragment_shader_source) + self.vbo = initialize_vbo(vertex_positions) + + buffers = jarray(jint)([0]) + GL.glGenVertexArrays(1, buffers, 0) + vao = buffers[0] + + GL.glBindVertexArray(vao) + + def on_render(self, widget, size, **kwargs): + vertex_dim = 4 + n_vertices = 3 + + GL.glClear(GL.GL_COLOR_BUFFER_BIT) + + GL.glUseProgram(self.program) + + GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vbo) + GL.glEnableVertexAttribArray(0) + GL.glVertexAttribPointer(0, vertex_dim, GL.GL_FLOAT, False, 0, 0) + + GL.glDrawArrays(GL.GL_TRIANGLES, 0, n_vertices) + + GL.glDisableVertexAttribArray(0) + GL.glUseProgram(0) diff --git a/examples/opengl/opengl/renderer_iOS.py b/examples/opengl/opengl/renderer_iOS.py new file mode 100644 index 0000000000..515177056e --- /dev/null +++ b/examples/opengl/opengl/renderer_iOS.py @@ -0,0 +1,17 @@ +from toga_iOS.libs import opengles as GL + +# This is a very basic renderer using ctypes, because there are currently no +# Python Open GL ES wrappers for iOS + +# Constants +GL_COLOR_BUFFER_BIT = 16384 + + +class Renderer: + def on_init(self, widget, **kwargs): + # set the clear color to blue + GL.glClearColor(0.0, 0.0, 1.0, 1.0) + + def on_render(self, widget, size, **kwargs): + # clear the OpenGL view + GL.glClear(GL_COLOR_BUFFER_BIT) diff --git a/examples/opengl/opengl/renderer_pyopengl.py b/examples/opengl/opengl/renderer_pyopengl.py new file mode 100644 index 0000000000..986d949a95 --- /dev/null +++ b/examples/opengl/opengl/renderer_pyopengl.py @@ -0,0 +1,139 @@ +import array + +import OpenGL + +# Get spurious errors with Qt backend +OpenGL.ERROR_CHECKING = False + +from OpenGL import GL # noqa: E402 + +vertext_shader_source = """ +#version 330 core + +layout(location = 0) in vec4 position; + +void main() +{ + gl_Position = position; +} +""" +fragment_shader_source = """ +#version 330 core + +out vec4 outputColor; +void main() +{ + outputColor = vec4(1.0f, 1.0f, 1.0f, 1.0f); +} +""" + + +def create_program(shader_list): + program = GL.glCreateProgram() + + for shader in shader_list: + GL.glAttachShader(program, shader) + + GL.glLinkProgram(program) + + status = GL.glGetProgramiv(program, GL.GL_LINK_STATUS) + if status == GL.GL_FALSE: + strInfoLog = GL.glGetProgramInfoLog(program) + raise RuntimeError("Linker failure: \n" + strInfoLog) + + for shader in shader_list: + GL.glDetachShader(program, shader) + + return program + + +def create_shader(shader_type, shader_code): + shader = GL.glCreateShader(shader_type) + GL.glShaderSource(shader, shader_code) + + GL.glCompileShader(shader) + + status = None + GL.glGetShaderiv(shader, GL.GL_COMPILE_STATUS, status) + if status == GL.GL_FALSE: + info_log = GL.glGetShaderInfoLog(shader) + shader_types = { + GL.GL_VERTEX_SHADER: "vertex", + GL.GL_GEOMETRY_SHADER: "geometry", + GL.GL_FRAGMENT_SHADER: "fragment", + } + raise RuntimeError( + f"Compilation failure for {shader_types.get(shader_type, 'unknown')} " + f"shader:\n{info_log}" + ) + + return shader + + +def initialize_program(vertex_shader_code, fragment_shader_code): + shaderList = [] + + shaderList.append(create_shader(GL.GL_VERTEX_SHADER, vertex_shader_code)) + shaderList.append(create_shader(GL.GL_FRAGMENT_SHADER, fragment_shader_code)) + + program = create_program(shaderList) + + for shader in shaderList: + GL.glDeleteShader(shader) + + return program + + +def initialize_vbo(vertex_positions): + vbo = GL.glGenBuffers(1) + + GL.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo) + GL.glBufferData(GL.GL_ARRAY_BUFFER, vertex_positions, GL.GL_STATIC_DRAW) + GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0) + + return vbo + + +class Renderer: + def on_init(self, widget, **kwargs): + vertex_positions = bytes( + array.array( + "f", + [ + 0.75, + 0.75, + 0.0, + 1.0, + 0.75, + -0.75, + 0.0, + 1.0, + -0.75, + -0.75, + 0.0, + 1.0, + ], + ) + ) + + self.program = initialize_program(vertext_shader_source, fragment_shader_source) + self.vbo = initialize_vbo(vertex_positions) + GL.glBindVertexArray(GL.glGenVertexArrays(1)) + + def on_render(self, widget, size, **kwargs): + vertex_dim = 4 + n_vertices = 3 + + GL.glClearColor(1.0, 1.0, 0.0, 1.0) + GL.glClear(GL.GL_COLOR_BUFFER_BIT) + + GL.glUseProgram(self.program) + + GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vbo) + GL.glEnableVertexAttribArray(0) + GL.glVertexAttribPointer(0, vertex_dim, GL.GL_FLOAT, GL.GL_FALSE, 0, None) + + GL.glDrawArrays(GL.GL_TRIANGLES, 0, n_vertices) + + GL.glDisableVertexAttribArray(0) + GL.glUseProgram(0) diff --git a/examples/opengl/pyproject.toml b/examples/opengl/pyproject.toml new file mode 100644 index 0000000000..9f4b705814 --- /dev/null +++ b/examples/opengl/pyproject.toml @@ -0,0 +1,76 @@ +[build-system] +requires = ["briefcase"] + +[tool.briefcase] +project_name = "OpenGL Demo" +bundle = "org.beeware.toga.examples" +version = "0.0.1" +url = "https://beeware.org" +license = "BSD-3-Clause" +license-files = [ + "LICENSE", +] +author = "Tiberius Yak" +author_email = "tiberius@beeware.org" + +formal_name = "An OpenGL Demo" +description = "A testing app" +requires = [ + "../../travertino", + "../../core", +] + +[tool.briefcase.app.opengl] +sources = ["opengl"] + +[tool.briefcase.app.opengl-qt] +sources = ["opengl", "opengl_qt"] + +[tool.briefcase.app.opengl.macOS] +requires = [ + "../../cocoa", + "std-nslog>=1.0.0", + "PyOpenGL", +] + +[tool.briefcase.app.opengl.linux] +requires = [ + "../../gtk", + "PyOpenGL", +] + +[tool.briefcase.app.opengl-qt.linux] +requires = [ + "../../qt", + "PyOpenGL", +] + +#[tool.briefcase.app.opengl.windows] +#requires = [ +# "../../winforms", +#] + +# Mobile deployments +[tool.briefcase.app.opengl.iOS] +requires = [ + "../../iOS", + "std-nslog>=1.0.0", +] + +[tool.briefcase.app.opengl.android] +requires = [ + "../../android", +] + +base_theme = "Theme.MaterialComponents.Light.DarkActionBar" + +build_gradle_dependencies = [ + "com.google.android.material:material:1.12.0", +] + +# Web deployment +#[tool.briefcase.app.opengl.web] +#requires = [ +# "../../web", +#] +#style_framework = "Shoelace v2.3" diff --git a/gtk/pyproject.toml b/gtk/pyproject.toml index adff5f5b31..3b5281b359 100644 --- a/gtk/pyproject.toml +++ b/gtk/pyproject.toml @@ -92,6 +92,7 @@ Label = "toga_gtk.widgets.label:Label" MapView = "toga_gtk.widgets.mapview:MapView" MultilineTextInput = "toga_gtk.widgets.multilinetextinput:MultilineTextInput" NumberInput = "toga_gtk.widgets.numberinput:NumberInput" +OpenGLView = "toga_gtk.widgets.openglview:OpenGLView" OptionContainer = "toga_gtk.widgets.optioncontainer:OptionContainer" PasswordInput = "toga_gtk.widgets.passwordinput:PasswordInput" ProgressBar = "toga_gtk.widgets.progressbar:ProgressBar" diff --git a/gtk/src/toga_gtk/widgets/openglview.py b/gtk/src/toga_gtk/widgets/openglview.py new file mode 100644 index 0000000000..bdb23f452e --- /dev/null +++ b/gtk/src/toga_gtk/widgets/openglview.py @@ -0,0 +1,41 @@ +from travertino.size import at_least + +from toga_gtk.libs import GTK_VERSION, Gtk + +from .base import Widget + + +class OpenGLView(Widget): + def create(self): + self.native = Gtk.GLArea() + self.native.connect("realize", self.gtk_realize) + self.native.connect("render", self.gtk_render) + + def gtk_realize(self, native): + """Initialize the OpenGL context.""" + ctx = self.native.get_context() + ctx.make_current() + self.interface.on_init(self.interface) + + def gtk_render(self, native, context): + """Render to the OpenGL context.""" + self.interface.on_render(self.interface, size=self._size()) + return True + + def _size(self): + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + width = self.native.get_allocation().width + height = self.native.get_allocation().height + else: # pragma: no-cover-if-gtk3 + width = self.native.compute_bounds(self.native)[1].get_width() + height = self.native.compute_bounds(self.native)[1].get_height() + return width, height + + def redraw(self): + self.native.queue_draw() + + def rehint(self): + width = self.interface._MIN_WIDTH + height = self.interface._MIN_HEIGHT + self.interface.intrinsic.height = at_least(width) + self.interface.intrinsic.width = at_least(height) diff --git a/gtk/tests_backend/widgets/openglview.py b/gtk/tests_backend/widgets/openglview.py new file mode 100644 index 0000000000..4066d70717 --- /dev/null +++ b/gtk/tests_backend/widgets/openglview.py @@ -0,0 +1,7 @@ +from toga_gtk.libs import Gtk + +from .base import SimpleProbe + + +class OpenGLViewProbe(SimpleProbe): + native_class = Gtk.GLArea diff --git a/iOS/pyproject.toml b/iOS/pyproject.toml index 412506be64..f61d2ae0d5 100644 --- a/iOS/pyproject.toml +++ b/iOS/pyproject.toml @@ -90,6 +90,7 @@ Label = "toga_iOS.widgets.label:Label" MapView = "toga_iOS.widgets.mapview:MapView" MultilineTextInput = "toga_iOS.widgets.multilinetextinput:MultilineTextInput" NumberInput = "toga_iOS.widgets.numberinput:NumberInput" +OpenGLView = "toga_iOS.widgets.openglview:OpenGLView" OptionContainer = "toga_iOS.widgets.optioncontainer:OptionContainer" PasswordInput = "toga_iOS.widgets.passwordinput:PasswordInput" ProgressBar = "toga_iOS.widgets.progressbar:ProgressBar" diff --git a/iOS/src/toga_iOS/libs/__init__.py b/iOS/src/toga_iOS/libs/__init__.py index cd4018fa6f..1e05965682 100644 --- a/iOS/src/toga_iOS/libs/__init__.py +++ b/iOS/src/toga_iOS/libs/__init__.py @@ -2,6 +2,8 @@ from .core_graphics import * # NOQA from .core_location import * # NOQA from .foundation import * # NOQA +from .glkit import * # NOQA +from .opengles import * # NOQA from .mapkit import * # NOQA from .uikit import * # NOQA from .webkit import * # NOQA diff --git a/iOS/src/toga_iOS/libs/glkit.py b/iOS/src/toga_iOS/libs/glkit.py new file mode 100644 index 0000000000..aa8c197017 --- /dev/null +++ b/iOS/src/toga_iOS/libs/glkit.py @@ -0,0 +1,25 @@ +########################################################################## +# System/Library/Frameworks/UIKit.framework +########################################################################## +from ctypes import cdll, util + +from rubicon.objc import ObjCClass + +###################################################################### +glkit = cdll.LoadLibrary(util.find_library("GLKit")) +###################################################################### + +###################################################################### +# GLKView.h +GLKView = ObjCClass("GLKView") + +GLKViewDrawableColorFormatRGBA8888 = 0 +GLKViewDrawableDepthFormat24 = 2 +GLKViewDrawableStencilFormat8 = 1 +GLKViewDrawableMultisample4X = 1 + +EAGLContext = ObjCClass("EAGLContext") + +kEAGLRenderingAPIOpenGLES1 = 1 +kEAGLRenderingAPIOpenGLES2 = 2 +kEAGLRenderingAPIOpenGLES3 = 3 diff --git a/iOS/src/toga_iOS/libs/opengles.py b/iOS/src/toga_iOS/libs/opengles.py new file mode 100644 index 0000000000..a700b96573 --- /dev/null +++ b/iOS/src/toga_iOS/libs/opengles.py @@ -0,0 +1,34 @@ +########################################################################## +# System/Library/Frameworks/UIKit.framework +########################################################################## +from ctypes import c_char_p, c_float, c_int, c_ulong, cdll, util + +from rubicon.objc import ObjCClass + +###################################################################### +opengles = cdll.LoadLibrary(util.find_library("OpenGLES")) +###################################################################### + +###################################################################### +# EAGL.h +EAGLContext = ObjCClass("EAGLContext") + +kEAGLRenderingAPIOpenGLES1 = 1 +kEAGLRenderingAPIOpenGLES2 = 2 +kEAGLRenderingAPIOpenGLES3 = 3 + +# Types +GLbitfield = c_ulong + +# Functions +opengles.glGetString.argtypes = [c_int] +opengles.glGetString.restype = c_char_p + +opengles.glClearColor.argtypes = [c_float, c_float, c_float, c_float] +opengles.glClear.restype = None +opengles.glClear.argtypes = [GLbitfield] +opengles.glClear.restype = None + +opengles.glCreateShader.argtypes = [c_int] +opengles.glGetShaderInfoLog.argtypes = [c_int] +opengles.glGetShaderInfoLog.restype = c_char_p diff --git a/iOS/src/toga_iOS/widgets/openglview.py b/iOS/src/toga_iOS/widgets/openglview.py new file mode 100644 index 0000000000..bc7b60cad9 --- /dev/null +++ b/iOS/src/toga_iOS/widgets/openglview.py @@ -0,0 +1,63 @@ +from rubicon.objc import objc_method, objc_property +from travertino.size import at_least + +from toga_iOS.libs import ( + CGRect, + CGSize, + EAGLContext, + GLKView, + GLKViewDrawableColorFormatRGBA8888, + GLKViewDrawableDepthFormat24, + GLKViewDrawableMultisample4X, + GLKViewDrawableStencilFormat8, + kEAGLRenderingAPIOpenGLES3, +) + +from .base import Widget + + +class TogaGLKView(GLKView): + interface = objc_property(object, weak=True) + impl = objc_property(object, weak=True) + initialized = objc_property(bool) + + @objc_method + def drawRect_(self, rect: CGRect) -> None: + if not self.initialized: + self.interface.renderer.on_init(self.interface) + self.initialized = True + + width = rect.size.width + height = rect.size.height + self.interface.renderer.on_render(self.interface, size=(width, height)) + + +class OpenGLView(Widget): + def create(self): + self.native = TogaGLKView.alloc().init() + self.native.interface = self.interface + self.native.impl = self + self.native.initialized = False + self.native.context = EAGLContext.alloc().initWithAPI_( + kEAGLRenderingAPIOpenGLES3 + ) + + # # Configure renderbuffers created by the view + self.native.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888 + self.native.drawableDepthFormat = GLKViewDrawableDepthFormat24 + self.native.drawableStencilFormat = GLKViewDrawableStencilFormat8 + + # # Enable multisampling + self.native.drawableMultisample = GLKViewDrawableMultisample4X + + # Add the layout constraints + self.add_constraints() + + def redraw(self): + self.native.setNeedsDisplay() + + # Rehint + def rehint(self): + fitting_size = self.native.systemLayoutSizeFittingSize(CGSize(0, 0)) + self.interface.intrinsic.width = at_least(fitting_size.width) + self.interface.intrinsic.height = at_least(fitting_size.height) diff --git a/iOS/tests_backend/widgets/openglview.py b/iOS/tests_backend/widgets/openglview.py new file mode 100644 index 0000000000..eeadd52660 --- /dev/null +++ b/iOS/tests_backend/widgets/openglview.py @@ -0,0 +1,7 @@ +from toga_iOS.widgets.openglview import TogaGLKView + +from .base import SimpleProbe + + +class OpenGLViewProbe(SimpleProbe): + native_class = TogaGLKView diff --git a/iOS/tests_backend/window.py b/iOS/tests_backend/window.py index d229c27e39..085c76313f 100644 --- a/iOS/tests_backend/window.py +++ b/iOS/tests_backend/window.py @@ -39,8 +39,13 @@ async def _wait_for_assertion(self, assertion, timeout=5, polling_interval=0.1): def _assert_container_layout(self): # If the window has been laid out, the origin should be at least at the - # position of the top bar height. - assert self.impl.container.content.native.frame.origin.y >= self.top_bar_height + # position of the top bar height. These are floating point numbers, so + # the test needs to be approximate. + tolerance = 1e-6 + assert ( + self.impl.container.content.native.frame.origin.y + >= self.top_bar_height - tolerance + ) def _assert_window_state(self, state): # Create an assertion function that the window's instantaneous state is a diff --git a/qt/pyproject.toml b/qt/pyproject.toml index 46a45a1cf8..d1e6aac2e8 100644 --- a/qt/pyproject.toml +++ b/qt/pyproject.toml @@ -83,6 +83,7 @@ Label = "toga_qt.widgets.label:Label" MapView = "toga_qt.widgets.mapview:MapView" MultilineTextInput = "toga_qt.widgets.multilinetextinput:MultilineTextInput" NumberInput = "toga_qt.widgets.numberinput:NumberInput" +OpenGLView = "toga_qt.widgets.openglview:OpenGLView" OptionContainer = "toga_qt.widgets.optioncontainer:OptionContainer" PasswordInput = "toga_qt.widgets.passwordinput:PasswordInput" ProgressBar = "toga_qt.widgets.progressbar:ProgressBar" diff --git a/qt/src/toga_qt/__init__.py b/qt/src/toga_qt/__init__.py index 1fb1a84eae..4d73ee942c 100644 --- a/qt/src/toga_qt/__init__.py +++ b/qt/src/toga_qt/__init__.py @@ -1,3 +1,20 @@ import travertino __version__ = travertino._package_version(__file__, __name__) + + +def _init_opengl(): + """OpenGL initialization needed before QApplication started. + + This sets the default OpenGL profile and version. + """ + from PySide6.QtGui import QSurfaceFormat + + surface_format = QSurfaceFormat.defaultFormat() + surface_format.setProfile(QSurfaceFormat.OpenGLContextProfile.CoreProfile) + surface_format.setVersion(4, 1) + QSurfaceFormat.setDefaultFormat(surface_format) + + +_init_opengl() +del _init_opengl diff --git a/qt/src/toga_qt/widgets/openglview.py b/qt/src/toga_qt/widgets/openglview.py new file mode 100644 index 0000000000..2b3b9bd658 --- /dev/null +++ b/qt/src/toga_qt/widgets/openglview.py @@ -0,0 +1,44 @@ +from PySide6.QtOpenGLWidgets import QOpenGLWidget +from travertino.size import at_least + +from .base import Widget + + +class TogaOpenGLWidget(QOpenGLWidget): + def __init__(self, impl, *args, **kwargs): + super().__init__(*args, **kwargs) + self.impl = impl + self.interface = impl.interface + + def initializeGL(self): + print("here") + self.interface.renderer.on_init(self.interface) + + def resizeGL(self, w, h): + self._redraw() + + def paintGL(self): + self._redraw() + + def _redraw(self): + size = self.impl.native.size() + width = size.width() + height = size.height() + self.interface.renderer.on_render(self.interface, size=(width, height)) + + +class OpenGLView(Widget): + def create(self): + self.native = TogaOpenGLWidget(self) + + def redraw(self): + self.native.update() + + def rehint(self): + size = self.native.sizeHint() + self.interface.intrinsic.width = at_least( + max(size.width(), self.interface._MIN_WIDTH) + ) + self.interface.intrinsic.height = at_least( + max(size.height(), self.interface._MIN_HEIGHT) + ) diff --git a/qt/tests_backend/widgets/openglview.py b/qt/tests_backend/widgets/openglview.py new file mode 100644 index 0000000000..033cb03cd3 --- /dev/null +++ b/qt/tests_backend/widgets/openglview.py @@ -0,0 +1,7 @@ +from toga_qt.widgets.openglview import TogaOpenGLWidget + +from .base import SimpleProbe + + +class OpenGLViewProbe(SimpleProbe): + native_class = TogaOpenGLWidget diff --git a/testbed/tests/widgets/test_openglview.py b/testbed/tests/widgets/test_openglview.py new file mode 100644 index 0000000000..f2bef77e76 --- /dev/null +++ b/testbed/tests/widgets/test_openglview.py @@ -0,0 +1,48 @@ +from unittest.mock import Mock + +import pytest + +import toga + +# from .probe import get_probe +from .properties import ( # noqa: F401 + test_enable_noop, + test_flex_widget_size, + test_focus_noop, +) + + +@pytest.fixture +def renderer(): + renderer = Mock() + return renderer + + +# Widget fixture must be async to force OpenGL to be +# initialized on the main thread +@pytest.fixture +async def widget(renderer): + return toga.OpenGLView(renderer, flex=1) + + +async def test_callbacks(probe, widget, renderer): + renderer.on_init.assert_called_once_with(widget) + await probe.redraw("OpenGlView widget initialized", 0.1) + + # different backends render different numbers of times with + # different arguments + renderer.on_render.assert_called() + assert renderer.on_render.call_args[0] == (widget,) + assert "size" in renderer.on_render.call_args[1] + assert isinstance(renderer.on_render.call_args[1]["size"], tuple) + assert len(renderer.on_render.call_args[1]["size"]) == 2 + + renderer.reset_mock() + + widget.redraw() + await probe.redraw("OpenGlView widget redraw requested", 0.1) + renderer.on_render.assert_called() + assert renderer.on_render.call_args[0] == (widget,) + assert "size" in renderer.on_render.call_args[1] + assert isinstance(renderer.on_render.call_args[1]["size"], tuple) + assert len(renderer.on_render.call_args[1]["size"]) == 2 From f644cc015103ee1bde05f0b9fdf4dbd625906d55 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sat, 28 Mar 2026 11:48:57 +0000 Subject: [PATCH 02/26] Spelling; fix Gtk errors. --- changes/4273.feature.md | 2 +- docs/en/reference/api/widgets/openglview.md | 4 ++-- docs/spelling_wordlist | 5 +++++ gtk/src/toga_gtk/widgets/openglview.py | 4 ++-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/changes/4273.feature.md b/changes/4273.feature.md index b7ff0277f7..47bb97429d 100644 --- a/changes/4273.feature.md +++ b/changes/4273.feature.md @@ -1 +1 @@ -The Android, Cocoa, Gtk, iOS and Qt backends now have a basic `OpenGLView` widget that provides low-level access to OpenGL rendering. +The Android, Cocoa, GTK, iOS and Qt backends now have a basic `OpenGLView` widget that provides low-level access to OpenGL rendering. diff --git a/docs/en/reference/api/widgets/openglview.md b/docs/en/reference/api/widgets/openglview.md index 07ebe862d2..b6ce3683f9 100644 --- a/docs/en/reference/api/widgets/openglview.md +++ b/docs/en/reference/api/widgets/openglview.md @@ -2,7 +2,7 @@ ## Usage -OpenGLView provides a surface for rendering 2D and 3D graphics using OpenGL. It is agnostic to the library used to actually perform the OpenGL rendering: some platforms, such as Qt and Android, provide native OpenGL interfaces while for others, notably Cocoa and Gtk, can use existing Python OpenGL wrappers such as PyOpenGL or ModernGL. Currently Toga doesn't provide a cross-platform OpenGL API layer, although it may in the future. +OpenGLView provides a surface for rendering 2D and 3D graphics using OpenGL. It is agnostic to the library used to actually perform the OpenGL rendering: some platforms, such as Qt and Android, provide native OpenGL interfaces while for others, notably Cocoa and GTK, can use existing Python OpenGL wrappers such as PyOpenGL or ModernGL. Currently Toga doesn't provide a cross-platform OpenGL API layer, although it may in the future. The OpenGLView expects to be given a renderer object which conforms to the [`RendererT`][toga.widgets.openglview.RendererT] protocol: it needs an `on_init` method which gets called by the implementation layer to perform any OpenGL initialization that is needed (eg. creating shader programs, setting up buffers); and an `on_render` method which gets called to do the actual OpenGL drawing. Both methods get called with the OpenGL context for the view set up and ready to be accessed by OpenGL library calls. @@ -31,7 +31,7 @@ If the renderer changes state and a re-rendering is required, the `redraw` metho - The OpenGLView API should be considered a beta API and may change in the future. - The renderer object used by the OpenGLView can't be changed after the view is created, but it can hold state and change the way that it renders based on that. -- The OpenGLView is currently only available on the Android, Cocoa, Gtk, iOS and Qt backends. +- The OpenGLView is currently only available on the Android, Cocoa, GTK, iOS and Qt backends. - OpenGL is deprecated on macOS and iOS, but it is likely to be available for the foreseeable future. - There are currently no Python OpenGL wrappers for iOS, but `ctypes` can be used to wrap the iOS `opengles` DLL and call out to OpenGL. - Linux relies on the appropriate OpenGL driver libraries being installed on the system with the system's package manager. diff --git a/docs/spelling_wordlist b/docs/spelling_wordlist index 22987d5b12..e9c1680b59 100644 --- a/docs/spelling_wordlist +++ b/docs/spelling_wordlist @@ -104,6 +104,7 @@ MapView MDN misconfigured MkDocs +ModernGL Monetization MultilineTextInput MultilineTextInputs @@ -111,6 +112,8 @@ namespace NumberInput NumberInput's NumLock +OpenGL +OpenGLView OpenStreetMap OpenSUSE OptionContainer @@ -137,6 +140,7 @@ Pygame PyGObject PyGObject's PyInstaller +PyOpenGL PyPI pyPlayground PyScript @@ -154,6 +158,7 @@ rehint rehinted RemoteCommand Ren +renderer resizable reStructuredText RGB diff --git a/gtk/src/toga_gtk/widgets/openglview.py b/gtk/src/toga_gtk/widgets/openglview.py index bdb23f452e..43b2ca47f9 100644 --- a/gtk/src/toga_gtk/widgets/openglview.py +++ b/gtk/src/toga_gtk/widgets/openglview.py @@ -15,11 +15,11 @@ def gtk_realize(self, native): """Initialize the OpenGL context.""" ctx = self.native.get_context() ctx.make_current() - self.interface.on_init(self.interface) + self.interface.renderer.on_init(self.interface) def gtk_render(self, native, context): """Render to the OpenGL context.""" - self.interface.on_render(self.interface, size=self._size()) + self.interface.renderer.on_render(self.interface, size=self._size()) return True def _size(self): From 05dfeec21fd6c3f1786af29b421417ca112425d2 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sat, 28 Mar 2026 13:42:51 +0000 Subject: [PATCH 03/26] Better names for android drawing arguments. --- android/src/toga_android/widgets/openglview.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/src/toga_android/widgets/openglview.py b/android/src/toga_android/widgets/openglview.py index c86c25501d..e189dae95d 100644 --- a/android/src/toga_android/widgets/openglview.py +++ b/android/src/toga_android/widgets/openglview.py @@ -5,13 +5,13 @@ class TogaGLRenderer(dynamic_proxy(GLSurfaceView.Renderer)): - def onSurfaceCreated(self, unused, config): + def onSurfaceCreated(self, gl_api, config): self.interface.renderer.on_init(self.interface) - def onDrawFrame(self, unused): + def onDrawFrame(self, gl_api): self._redraw() - def onSurfaceChanged(self, unused, width, height): + def onSurfaceChanged(self, gl_api, width, height): self._redraw() def _redraw(self): From aa3d480f9eac82349c9196305ed43f436f03e476 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 30 Mar 2026 20:27:13 +0100 Subject: [PATCH 04/26] Add shadertoy example. --- cocoa/src/toga_cocoa/libs/appkit.py | 2 + cocoa/src/toga_cocoa/widgets/openglview.py | 57 ++- examples/shadertoy/CHANGELOG | 1 + examples/shadertoy/LICENSE | 1 + examples/shadertoy/README.md | 17 + examples/shadertoy/pyproject.toml | 76 ++++ examples/shadertoy/shadertoy/__init__.py | 9 + examples/shadertoy/shadertoy/__main__.py | 4 + examples/shadertoy/shadertoy/app.py | 90 +++++ .../shadertoy/shadertoy/renderer_pyopengl.py | 352 ++++++++++++++++++ qt/src/toga_qt/widgets/openglview.py | 17 +- 11 files changed, 618 insertions(+), 8 deletions(-) create mode 100644 examples/shadertoy/CHANGELOG create mode 100644 examples/shadertoy/LICENSE create mode 100644 examples/shadertoy/README.md create mode 100644 examples/shadertoy/pyproject.toml create mode 100644 examples/shadertoy/shadertoy/__init__.py create mode 100644 examples/shadertoy/shadertoy/__main__.py create mode 100644 examples/shadertoy/shadertoy/app.py create mode 100644 examples/shadertoy/shadertoy/renderer_pyopengl.py diff --git a/cocoa/src/toga_cocoa/libs/appkit.py b/cocoa/src/toga_cocoa/libs/appkit.py index 9d08138cab..3f05a4a50c 100644 --- a/cocoa/src/toga_cocoa/libs/appkit.py +++ b/cocoa/src/toga_cocoa/libs/appkit.py @@ -734,6 +734,8 @@ def NSTextAlignment(alignment): NSToolbarItem.declare_property("itemIdentifier") ###################################################################### # NSTrackingArea.h +NSTrackingArea = ObjCClass("NSTrackingArea") + NSTrackingMouseEnteredAndExited = 0x01 NSTrackingMouseMoved = 0x02 NSTrackingCursorUpdate = 0x04 diff --git a/cocoa/src/toga_cocoa/widgets/openglview.py b/cocoa/src/toga_cocoa/widgets/openglview.py index 971cc918d0..1aa4f9e5ea 100644 --- a/cocoa/src/toga_cocoa/widgets/openglview.py +++ b/cocoa/src/toga_cocoa/widgets/openglview.py @@ -18,18 +18,42 @@ class TogaOpenGLView(NSOpenGLView): interface = objc_property(object, weak=True) impl = objc_property(object, weak=True) + mouse_state = objc_property(object) @objc_method def prepareOpenGL(self) -> None: - # super().prepareOpenGL() self.openGLContext.makeCurrentContext() - self.interface.renderer.on_init(self.interface) + try: + self.interface.renderer.on_init(self.interface) + except Exception as exc: + print(exc) + raise @objc_method def drawRect_(self, rect: NSRect) -> None: - size = (int(rect.size.width), int(rect.size.height)) + # Get size in GL pixels + backingBounds = self.convertRectToBacking(self.bounds) + size = (backingBounds.size.width, backingBounds.size.height) + + # Get current mouse position in GL pixels + position = self.convertPoint( + self.window.mouseLocationOutsideOfEventStream(), + fromView=None, + ) + scale = self.backingScaleFactor + pointer = (position.x * scale, position.y * scale) + self.openGLContext.makeCurrentContext() - self.interface.renderer.on_render(self.interface, size=size) + try: + self.interface.renderer.on_render( + self.interface, + size=size, + pointer=pointer, + buttons=tuple(self.mouse_state), + ) + except Exception as exc: + print(exc) + raise self.openGLContext.flushBuffer() @objc_method @@ -50,6 +74,30 @@ def initWithFrame_(self, frame: NSRect): return self.initWithFrame_pixelFormat_(frame, pixel_format) + @objc_method + def mouseDown_(self, event) -> None: + self.mouse_state[0] = True + + @objc_method + def mouseUp_(self, event) -> None: + self.mouse_state[0] = False + + @objc_method + def otherMouseDown_(self, event) -> None: + self.mouse_state[1] = True + + @objc_method + def otherMouseUp_(self, event) -> None: + self.mouse_state[1] = False + + @objc_method + def rightMouseDown_(self, event) -> None: + self.mouse_state[2] = True + + @objc_method + def rightMouseUp_(self, event) -> None: + self.mouse_state[2] = False + class OpenGLView(Widget): def create(self): @@ -60,6 +108,7 @@ def create(self): raise RuntimeError("Can't create native OpenGLView widget.") self.native.interface = self.interface self.native.impl = self + self.native.mouse_state = [False, False, False] # Add the layout constraints self.add_constraints() diff --git a/examples/shadertoy/CHANGELOG b/examples/shadertoy/CHANGELOG new file mode 100644 index 0000000000..c6b095235f --- /dev/null +++ b/examples/shadertoy/CHANGELOG @@ -0,0 +1 @@ +See Toga releases for change notes. diff --git a/examples/shadertoy/LICENSE b/examples/shadertoy/LICENSE new file mode 100644 index 0000000000..f09583bfab --- /dev/null +++ b/examples/shadertoy/LICENSE @@ -0,0 +1 @@ +Released under the same license as Toga. See the root of the Toga repository for details. diff --git a/examples/shadertoy/README.md b/examples/shadertoy/README.md new file mode 100644 index 0000000000..670e6cf3d1 --- /dev/null +++ b/examples/shadertoy/README.md @@ -0,0 +1,17 @@ +# OpenGL + +Test app for the +[OpenGLView widget](https://toga.beeware.org/en/stable/reference/api/widgets/openglview.html). + +The following OpenGLView features are present in this example: + +- rendering simple Shadertoy OpenGL views using PyOpenGL + +## Quickstart + +To run this example: + +``` +$ python -m pip install toga PyOpenGL +$ python -m opengl +``` diff --git a/examples/shadertoy/pyproject.toml b/examples/shadertoy/pyproject.toml new file mode 100644 index 0000000000..2f6bdc0ca5 --- /dev/null +++ b/examples/shadertoy/pyproject.toml @@ -0,0 +1,76 @@ +[build-system] +requires = ["briefcase"] + +[tool.briefcase] +project_name = "OpenGL Shadertoy Demo" +bundle = "org.beeware.toga.examples" +version = "0.0.1" +url = "https://beeware.org" +license = "BSD-3-Clause" +license-files = [ + "LICENSE", +] +author = "Tiberius Yak" +author_email = "tiberius@beeware.org" + +formal_name = "An OpenGL Shadertoy Demo" +description = "A testing app" +requires = [ + "../../travertino", + "../../core", +] + +[tool.briefcase.app.shadertoy] +sources = ["shadertoy"] + +[tool.briefcase.app.shadertoy-qt] +sources = ["shadertoy", "shadertoy_qt"] + +[tool.briefcase.app.shadertoy.macOS] +requires = [ + "../../cocoa", + "std-nslog>=1.0.0", + "PyOpenGL", +] + +[tool.briefcase.app.shadertoy.linux] +requires = [ + "../../gtk", + "PyOpenGL", +] + +[tool.briefcase.app.shadertoy-qt.linux] +requires = [ + "../../qt", + "PyOpenGL", +] + +#[tool.briefcase.app.shadertoy.windows] +#requires = [ +# "../../winforms", +#] + +# Mobile deployments +[tool.briefcase.app.shadertoy.iOS] +requires = [ + "../../iOS", + "std-nslog>=1.0.0", +] + +[tool.briefcase.app.shadertoy.android] +requires = [ + "../../android", +] + +base_theme = "Theme.MaterialComponents.Light.DarkActionBar" + +build_gradle_dependencies = [ + "com.google.android.material:material:1.12.0", +] + +# Web deployment +#[tool.briefcase.app.shadertoy.web] +#requires = [ +# "../../web", +#] +#style_framework = "Shoelace v2.3" diff --git a/examples/shadertoy/shadertoy/__init__.py b/examples/shadertoy/shadertoy/__init__.py new file mode 100644 index 0000000000..86826e638c --- /dev/null +++ b/examples/shadertoy/shadertoy/__init__.py @@ -0,0 +1,9 @@ +# Examples of valid version strings +# __version__ = '1.2.3.dev1' # Development release 1 +# __version__ = '1.2.3a1' # Alpha Release 1 +# __version__ = '1.2.3b1' # Beta Release 1 +# __version__ = '1.2.3rc1' # RC Release 1 +# __version__ = '1.2.3' # Final Release +# __version__ = '1.2.3.post1' # Post Release 1 + +__version__ = "0.0.1" diff --git a/examples/shadertoy/shadertoy/__main__.py b/examples/shadertoy/shadertoy/__main__.py new file mode 100644 index 0000000000..39825dad4a --- /dev/null +++ b/examples/shadertoy/shadertoy/__main__.py @@ -0,0 +1,4 @@ +from shadertoy.app import main + +if __name__ == "__main__": + main().main_loop() diff --git a/examples/shadertoy/shadertoy/app.py b/examples/shadertoy/shadertoy/app.py new file mode 100644 index 0000000000..4340bb9881 --- /dev/null +++ b/examples/shadertoy/shadertoy/app.py @@ -0,0 +1,90 @@ +import asyncio + +import toga +import toga.sources +from toga.constants import COLUMN, MONOSPACE + +SOURCE = """ +// Replace this with shaders from shadertoy.com +// This is a simple example, so it doesn't handle channel, video or sound. +// +// Good examples to try: +// - Protean Clouds: https://www.shadertoy.com/view/3l23Rh +// - Star Nest: https://www.shadertoy.com/view/XlfGRj +// - Cyber Fuji 2020: https://www.shadertoy.com/view/Wt33Wf +// - Neural Stanford Bunny: https://www.shadertoy.com/view/wtVyWK +// - Seascape: https://www.shadertoy.com/view/Ms2SD1 +// - Flame: https://www.shadertoy.com/view/MdX3zr +// - Input - Mouse - https://www.shadertoy.com/view/Mss3zH + + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + // Normalized pixel coordinates (from 0 to 1) + vec2 uv = fragCoord/iResolution.xy; + + // Time varying pixel color + vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4)); + + // Output to screen + fragColor = vec4(col,1.0); +} +""" + + +class ShadertoyApp(toga.App): + def startup(self): + self.main_window = toga.MainWindow( + size=(960, 960), resizable=True, minimizable=False + ) + + self.shadertoy_source = toga.MultilineTextInput( + value=SOURCE, + on_change=self.source_changed, + font_family=["Noto Sans Mono", MONOSPACE], + flex=1.0, + ) + + if toga.backend in {"toga_cocoa", "toga_qt", "toga_gtk"}: + from .renderer_pyopengl import Renderer + else: + raise RuntimeError(f"Toga backend {toga.backend} is not supported.") + + self.renderer = Renderer(SOURCE) + + opengl_view = toga.OpenGLView(self.renderer, flex=1.5) + + async def animate(): + while True: + await asyncio.sleep(0.01) + # print('here') + opengl_view.redraw() + + loop = asyncio.get_running_loop() + loop.create_task(animate()) + + # Create the outer box with 2 rows + outer_box = toga.Box( + children=[ + opengl_view, + self.shadertoy_source, + ], + direction=COLUMN, + ) + + # Add the content on the main window + self.main_window.content = outer_box + + # Show the main window + self.main_window.show() + + def source_changed(self, widget, **kwargs): + self.renderer.set_source(widget.value) + + +def main(): + return ShadertoyApp("Shadertoy Example", "org.beeware.toga.examples.shadertoy") + + +if __name__ == "__main__": + main().main_loop() diff --git a/examples/shadertoy/shadertoy/renderer_pyopengl.py b/examples/shadertoy/shadertoy/renderer_pyopengl.py new file mode 100644 index 0000000000..1ca459ac96 --- /dev/null +++ b/examples/shadertoy/shadertoy/renderer_pyopengl.py @@ -0,0 +1,352 @@ +import array +import datetime +import time +from contextlib import contextmanager +from dataclasses import dataclass + +import OpenGL + +# Get spurious errors with Qt backend +OpenGL.ERROR_CHECKING = False + +from OpenGL import GL # noqa: E402 + +vertext_shader_source = """ +#version 330 core + +layout(location = 0) in vec4 position; + +void main() +{ + gl_Position = position; +} +""" +fragment_shader_source = """ +#version 330 core + +uniform vec4 input_color = vec4(1.0, 0.0, 0.0, 1.0); + +out vec4 output_color; +void main() +{ + output_color = input_color; +} +""" +FRAGMENT_SHADER_TEMPLATE = """#version 330 core + +uniform vec3 iResolution; +uniform float iTime; +uniform float iTimeDelta; +uniform float iFrame; +uniform float iFrameRate; +uniform vec4 iMouse; +uniform vec4 iDate; + +out vec4 output_color; + +{source} + +void main() {{ + mainImage(output_color, gl_FragCoord.xy); +}} +""" + + +uniform_setters = { + (GL.GL_FLOAT, False): GL.glUniform1f, + (GL.GL_FLOAT_VEC2, False): GL.glUniform2f, + (GL.GL_FLOAT_VEC3, False): GL.glUniform3f, + (GL.GL_FLOAT_VEC4, False): GL.glUniform4f, + (GL.GL_FLOAT, True): GL.glUniform1fv, + (GL.GL_FLOAT_VEC2, True): GL.glUniform2fv, + (GL.GL_FLOAT_VEC3, True): GL.glUniform3fv, + (GL.GL_FLOAT_VEC4, True): GL.glUniform4fv, +} + + +@dataclass +class Shader: + shader_type: int + source: str + + def create(self): + self.id = GL.glCreateShader(self.shader_type) + GL.glShaderSource(self.id, self.source) + + GL.glCompileShader(self.id) + + status = GL.glGetShaderiv(self.id, GL.GL_COMPILE_STATUS) + if status == GL.GL_FALSE: + info_log = GL.glGetShaderInfoLog(self.id).decode("utf-8") + raise RuntimeError( + f"Compilation failure for {self.shader_type} shader:\n{info_log}" + ) + + def delete(self): + GL.glDeleteShader(self.id) + del self.id + + +@dataclass +class Program: + shaders: list[Shader] + + def create(self): + for shader in self.shaders: + shader.create() + + self.id = GL.glCreateProgram() + + for shader in self.shaders: + GL.glAttachShader(self.id, shader.id) + + GL.glLinkProgram(self.id) + + status = GL.glGetProgramiv(self.id, GL.GL_LINK_STATUS) + if status == GL.GL_FALSE: + strInfoLog = GL.glGetProgramInfoLog(self.id).decode("utf-8") + raise RuntimeError("Linker failure: \n" + strInfoLog) + + for shader in self.shaders: + GL.glDetachShader(self.id, shader.id) + + for shader in self.shaders: + shader.delete() + + def delete(self): + if hasattr(self, "id"): + GL.glDeleteProgram(self.id) + del self.id + + @contextmanager + def use(self): + GL.glUseProgram(self.id) + try: + yield + finally: + GL.glUseProgram(0) + + def active_attributes(self): + data = [ + GL.glGetActiveAttrib(self.id, i) + for i in range(GL.glGetProgramiv(self.id, GL.GL_ACTIVE_ATTRIBUTES)) + ] + return { + name.decode("utf-8"): (i, size, uniform_type) + for i, (name, size, uniform_type) in enumerate(data) + } + + def active_uniforms(self): + data = [ + GL.glGetActiveUniform(self.id, i) + for i in range(GL.glGetProgramiv(self.id, GL.GL_ACTIVE_UNIFORMS)) + ] + return { + name.decode("utf-8"): ( + i, + size, + uniform_type, + uniform_setters[uniform_type, size > 1], + ) + for i, (name, size, uniform_type) in enumerate(data) + } + + def attribute(self, item): + return GL.glGetAttribLocation(self.id, item) + + def set_uniforms(self, uniforms): + for uniform, (loc, size, _, setter) in self.active_uniforms().items(): + if uniform in uniforms: + if size == 1: + setter(loc, *uniforms[uniform]) + else: + setter(loc, size, uniforms[uniform]) + + +@dataclass +class Buffer: + buffer_type: int + usage: int + + def create(self): + self.id = GL.glGenBuffers(1) + + def set_data(self, data): + GL.glBufferData(self.buffer_type, data, self.usage) + + def bind(self): + GL.glBindBuffer(self.buffer_type, self.id) + + def unbind(self): + GL.glBindBuffer(self.buffer_type, 0) + + def __enter__(self): + self.bind() + + def __exit__(self, exc_type, exc_value, traceback): + self.unbind() + return False + + +@dataclass +class VertexArray: + def create(self): + self.id = GL.glGenVertexArrays(1) + + def bind(self): + GL.glBindVertexArray(self.id) + + def unbind(self): + GL.glBindVertexArray(0) + + def __enter__(self): + self.bind() + + def __exit__(self, exc_type, exc_value, traceback): + self.unbind() + return False + + +class Renderer: + source: str + + def __init__(self, source): + self.set_source(source) + + def set_source(self, source): + self.fragment_shader_source = FRAGMENT_SHADER_TEMPLATE.format(source=source) + self.source_updated = True + + def initialize_vbo(self, vertex_positions): + self.vbo = Buffer(GL.GL_ARRAY_BUFFER, GL.GL_STATIC_DRAW) + self.vbo.create() + with self.vbo: + self.vbo.set_data(vertex_positions) + + def initialize_program(self, vertex_shader_code, fragment_shader_code): + self.program = Program( + [ + Shader(GL.GL_VERTEX_SHADER, vertex_shader_code), + Shader(GL.GL_FRAGMENT_SHADER, fragment_shader_code), + ] + ) + self.program.create() + + def initialize_vao(self, attribute): + attribute_loc = self.program.attribute(attribute) + self.vao = VertexArray() + self.vao.create() + with self.vao: + with self.vbo: + GL.glEnableVertexAttribArray(attribute_loc) + GL.glVertexAttribPointer(0, 4, GL.GL_FLOAT, GL.GL_FALSE, 0, None) + + def update_program(self): + self.initialize_program(vertext_shader_source, self.fragment_shader_source) + self.initialize_vao("position") + + def on_init(self, widget, **kwargs): + self.source_updated = False + vertex_positions = bytes( + array.array( + "f", + [ + 1.0, + 1.0, + 0.0, + 1.0, # triangle 1 + 1.0, + -1.0, + 0.0, + 1.0, + -1.0, + -1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, # triangle 2 + -1.0, + -1.0, + 0.0, + 1.0, + -1.0, + 1.0, + 0.0, + 1.0, + ], + ) + ) + self.initialize_vbo(vertex_positions) + self.update_program() + + self.start = time.time() + self.timestamp = 0 + self.frame = 0 + self.deltas = [] + self.pointer = (0, 0) + self.mouse_down = False + self.drag_start = (0, 0) + + def on_render( + self, + widget, + size, + pointer=(-1, -1), + buttons=(False, False, False), + **kwargs, + ): + if self.source_updated: + self.program.delete() + self.on_init(widget) + n_vertices = 6 + if 0 <= pointer[0] < size[0] and 0 <= pointer[1] < size[1]: + mouse_down = any(buttons) + if mouse_down: + self.pointer = pointer + if not self.mouse_down: + # new mouse down + self.drag_start = pointer + self.mouse_down = True + elif self.drag_start[1] > 0: + # mouse was newly down, still down + self.drag_start = (self.drag_start[0], -self.drag_start[1]) + elif self.mouse_down: + # new mouse up + self.drag_start = (-self.drag_start[0], self.drag_start[1]) + self.mouse_down = False + + GL.glClearColor(0.0, 0.0, 1.0, 1.0) + GL.glClear(GL.GL_COLOR_BUFFER_BIT) + + t = time.time() - self.start + self.deltas.append(t - self.timestamp) + frame_rate = len(self.deltas) / sum(self.deltas) + self.timestamp = t + dt = datetime.datetime.now() + + uniforms = { + "iResolution": (*size, 1.0), + "iMouse": (*self.pointer, *self.drag_start), + "iTime": (self.timestamp,), + "iTimeDelta": (self.deltas[-1],), + "iFrame": (self.frame,), + "iFrameRate": (frame_rate,), + "iDate": ( + dt.year - 1, + dt.month - 1, + dt.day, + dt.hour * 3600 + dt.minute * 60 + dt.second + dt.microsecond / 1e-6, + ), + } + + with self.program.use(): + with self.vao: + self.program.set_uniforms(uniforms) + + GL.glDrawArrays(GL.GL_TRIANGLES, 0, n_vertices) + + self.frame += 1 + if len(self.deltas) > 100: + del self.deltas[0] diff --git a/qt/src/toga_qt/widgets/openglview.py b/qt/src/toga_qt/widgets/openglview.py index 2b3b9bd658..521524721a 100644 --- a/qt/src/toga_qt/widgets/openglview.py +++ b/qt/src/toga_qt/widgets/openglview.py @@ -11,7 +11,6 @@ def __init__(self, impl, *args, **kwargs): self.interface = impl.interface def initializeGL(self): - print("here") self.interface.renderer.on_init(self.interface) def resizeGL(self, w, h): @@ -21,10 +20,20 @@ def paintGL(self): self._redraw() def _redraw(self): + pixel_ratio = self.devicePixelRatio() size = self.impl.native.size() - width = size.width() - height = size.height() - self.interface.renderer.on_render(self.interface, size=(width, height)) + width = size.width() * pixel_ratio + height = size.height() * pixel_ratio + mouse_postion = self.mapFromGlobal(self.cursor().pos()) + pointer = ( + mouse_postion.x() * pixel_ratio, + height - mouse_postion.y() * pixel_ratio, + ) + self.interface.renderer.on_render( + self.interface, + size=(width, height), + pointer=pointer, + ) class OpenGLView(Widget): From c3ab44537300e93a104d21196e704993c832180f Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 30 Mar 2026 21:38:35 +0100 Subject: [PATCH 05/26] Fix new pre-commit errors. --- docs/en/reference/api/widgets/openglview.md | 7 +++++-- examples/opengl/README.md | 5 ++--- examples/shadertoy/README.md | 5 ++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/en/reference/api/widgets/openglview.md b/docs/en/reference/api/widgets/openglview.md index b6ce3683f9..03ddf1b690 100644 --- a/docs/en/reference/api/widgets/openglview.md +++ b/docs/en/reference/api/widgets/openglview.md @@ -2,11 +2,12 @@ ## Usage -OpenGLView provides a surface for rendering 2D and 3D graphics using OpenGL. It is agnostic to the library used to actually perform the OpenGL rendering: some platforms, such as Qt and Android, provide native OpenGL interfaces while for others, notably Cocoa and GTK, can use existing Python OpenGL wrappers such as PyOpenGL or ModernGL. Currently Toga doesn't provide a cross-platform OpenGL API layer, although it may in the future. +OpenGLView provides a surface for rendering 2D and 3D graphics using OpenGL. It is agnostic to the library used to actually perform the OpenGL rendering: some platforms, such as Qt and Android, provide native OpenGL interfaces while for others, notably Cocoa and GTK, can use existing Python OpenGL wrappers such as PyOpenGL or ModernGL. Currently Toga doesn't provide a cross-platform OpenGL API layer, although it may in the future. The OpenGLView expects to be given a renderer object which conforms to the [`RendererT`][toga.widgets.openglview.RendererT] protocol: it needs an `on_init` method which gets called by the implementation layer to perform any OpenGL initialization that is needed (eg. creating shader programs, setting up buffers); and an `on_render` method which gets called to do the actual OpenGL drawing. Both methods get called with the OpenGL context for the view set up and ready to be accessed by OpenGL library calls. For example, a renderer that just clears the view using PyOpenGL could be written like this: + ``` python from OpenGL import GL @@ -19,7 +20,9 @@ class ClearRenderer: # clear the OpenGL view GL.glClear(GL.GL_COLOR_BUFFER_BIT) ``` + The OpenGLView can then be initialized by calling it with the renderer: + ``` python renderer = ClearRenderer() openglview = OpenGLView(renderer) @@ -36,7 +39,7 @@ If the renderer changes state and a re-rendering is required, the `redraw` metho - There are currently no Python OpenGL wrappers for iOS, but `ctypes` can be used to wrap the iOS `opengles` DLL and call out to OpenGL. - Linux relies on the appropriate OpenGL driver libraries being installed on the system with the system's package manager. -The version of OpenGL that is available on each platform depends heavily on the platform: mobile and web platforms provide OpenGL ES, while desktop platforms generally provide full OpenGL support. The OpenGLView tries to provide an OpenGL context with the highest OpenGL version available. This is currently: +The version of OpenGL that is available on each platform depends heavily on the platform: mobile and web platforms provide OpenGL ES, while desktop platforms generally provide full OpenGL support. The OpenGLView tries to provide an OpenGL context with the highest OpenGL version available. This is currently: - Android: OpenGL ES 3.0 - MacOS: OpenGL 4.1 diff --git a/examples/opengl/README.md b/examples/opengl/README.md index 96f859ff64..b1dd57f3cc 100644 --- a/examples/opengl/README.md +++ b/examples/opengl/README.md @@ -1,7 +1,6 @@ # OpenGL -Test app for the -[OpenGLView widget](https://toga.beeware.org/en/stable/reference/api/widgets/openglview.html). +Test app for the [OpenGLView widget](https://toga.beeware.org/en/stable/reference/api/widgets/openglview.html). The following OpenGLView features are present in this example: @@ -11,7 +10,7 @@ The following OpenGLView features are present in this example: To run this example: -``` +```text $ python -m pip install toga PyOpenGL $ python -m opengl ``` diff --git a/examples/shadertoy/README.md b/examples/shadertoy/README.md index 670e6cf3d1..6b3814278f 100644 --- a/examples/shadertoy/README.md +++ b/examples/shadertoy/README.md @@ -1,7 +1,6 @@ # OpenGL -Test app for the -[OpenGLView widget](https://toga.beeware.org/en/stable/reference/api/widgets/openglview.html). +Test app for the [OpenGLView widget](https://toga.beeware.org/en/stable/reference/api/widgets/openglview.html). The following OpenGLView features are present in this example: @@ -11,7 +10,7 @@ The following OpenGLView features are present in this example: To run this example: -``` +```text $ python -m pip install toga PyOpenGL $ python -m opengl ``` From 5130aa2544fe3d92612744fedf1f2a43bb2b633d Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 31 Mar 2026 10:00:22 +0100 Subject: [PATCH 06/26] Better layout and status reporting in Shadertoy demo. --- cocoa/src/toga_cocoa/widgets/openglview.py | 13 +- examples/shadertoy/shadertoy/app.py | 25 +- .../shadertoy/shadertoy/renderer_pyopengl.py | 230 +++++++++++------- 3 files changed, 163 insertions(+), 105 deletions(-) diff --git a/cocoa/src/toga_cocoa/widgets/openglview.py b/cocoa/src/toga_cocoa/widgets/openglview.py index 1aa4f9e5ea..e00a0cbb8a 100644 --- a/cocoa/src/toga_cocoa/widgets/openglview.py +++ b/cocoa/src/toga_cocoa/widgets/openglview.py @@ -23,11 +23,7 @@ class TogaOpenGLView(NSOpenGLView): @objc_method def prepareOpenGL(self) -> None: self.openGLContext.makeCurrentContext() - try: - self.interface.renderer.on_init(self.interface) - except Exception as exc: - print(exc) - raise + self.interface.renderer.on_init(self.interface) @objc_method def drawRect_(self, rect: NSRect) -> None: @@ -51,10 +47,9 @@ def drawRect_(self, rect: NSRect) -> None: pointer=pointer, buttons=tuple(self.mouse_state), ) - except Exception as exc: - print(exc) - raise - self.openGLContext.flushBuffer() + finally: + # show what was drawn up to any error + self.openGLContext.flushBuffer() @objc_method def initWithFrame_(self, frame: NSRect): diff --git a/examples/shadertoy/shadertoy/app.py b/examples/shadertoy/shadertoy/app.py index 4340bb9881..9d9f3bdaa9 100644 --- a/examples/shadertoy/shadertoy/app.py +++ b/examples/shadertoy/shadertoy/app.py @@ -35,15 +35,17 @@ class ShadertoyApp(toga.App): def startup(self): self.main_window = toga.MainWindow( - size=(960, 960), resizable=True, minimizable=False + size=(960, 800), resizable=True, minimizable=False ) self.shadertoy_source = toga.MultilineTextInput( value=SOURCE, on_change=self.source_changed, font_family=["Noto Sans Mono", MONOSPACE], + font_size=12, flex=1.0, ) + self.message = toga.Label("Starting...", flex=1.0) if toga.backend in {"toga_cocoa", "toga_qt", "toga_gtk"}: from .renderer_pyopengl import Renderer @@ -52,12 +54,12 @@ def startup(self): self.renderer = Renderer(SOURCE) - opengl_view = toga.OpenGLView(self.renderer, flex=1.5) + opengl_view = toga.OpenGLView(self.renderer, width=640, height=360) async def animate(): while True: - await asyncio.sleep(0.01) - # print('here') + self.message.text = self.renderer.message + await asyncio.sleep(0.005) opengl_view.redraw() loop = asyncio.get_running_loop() @@ -66,10 +68,21 @@ async def animate(): # Create the outer box with 2 rows outer_box = toga.Box( children=[ - opengl_view, + toga.Box( + children=[ + opengl_view, + toga.ScrollContainer( + content=self.message, + flex=1.0, + height=360, + ), + ], + gap=4, + ), self.shadertoy_source, ], direction=COLUMN, + gap=4, ) # Add the content on the main window @@ -78,7 +91,7 @@ async def animate(): # Show the main window self.main_window.show() - def source_changed(self, widget, **kwargs): + async def source_changed(self, widget, **kwargs): self.renderer.set_source(widget.value) diff --git a/examples/shadertoy/shadertoy/renderer_pyopengl.py b/examples/shadertoy/shadertoy/renderer_pyopengl.py index 1ca459ac96..5be28ed564 100644 --- a/examples/shadertoy/shadertoy/renderer_pyopengl.py +++ b/examples/shadertoy/shadertoy/renderer_pyopengl.py @@ -1,6 +1,7 @@ import array import datetime import time +import traceback from contextlib import contextmanager from dataclasses import dataclass @@ -64,6 +65,10 @@ } +class OpenGLError(RuntimeError): + pass + + @dataclass class Shader: shader_type: int @@ -78,13 +83,14 @@ def create(self): status = GL.glGetShaderiv(self.id, GL.GL_COMPILE_STATUS) if status == GL.GL_FALSE: info_log = GL.glGetShaderInfoLog(self.id).decode("utf-8") - raise RuntimeError( + raise OpenGLError( f"Compilation failure for {self.shader_type} shader:\n{info_log}" ) def delete(self): - GL.glDeleteShader(self.id) - del self.id + if hasattr(self, "id"): + GL.glDeleteShader(self.id) + del self.id @dataclass @@ -105,7 +111,7 @@ def create(self): status = GL.glGetProgramiv(self.id, GL.GL_LINK_STATUS) if status == GL.GL_FALSE: strInfoLog = GL.glGetProgramInfoLog(self.id).decode("utf-8") - raise RuntimeError("Linker failure: \n" + strInfoLog) + raise OpenGLError("Linker failure: \n" + strInfoLog) for shader in self.shaders: GL.glDetachShader(self.id, shader.id) @@ -207,10 +213,27 @@ def __exit__(self, exc_type, exc_value, traceback): return False +RENDER_MESSAGE = """Rendering... +Frame: {iFrame[0]} +Frame rate: {iFrameRate[0]:.3f} fps + +Resolution: {iResolution} +Mouse: {iMouse} + +Time: {iTime[0]:.3f} s +Time delta: {iTimeDelta[0]:.3f} s +Date: {iDate[0]}-{iDate[1]}-{iDate[2]} {iDate[3]:.3f} + +{deltas} +""" + + class Renderer: source: str + message: str def __init__(self, source): + self.message = "Uninitialized." self.set_source(source) def set_source(self, source): @@ -244,43 +267,6 @@ def initialize_vao(self, attribute): def update_program(self): self.initialize_program(vertext_shader_source, self.fragment_shader_source) self.initialize_vao("position") - - def on_init(self, widget, **kwargs): - self.source_updated = False - vertex_positions = bytes( - array.array( - "f", - [ - 1.0, - 1.0, - 0.0, - 1.0, # triangle 1 - 1.0, - -1.0, - 0.0, - 1.0, - -1.0, - -1.0, - 0.0, - 1.0, - 1.0, - 1.0, - 0.0, - 1.0, # triangle 2 - -1.0, - -1.0, - 0.0, - 1.0, - -1.0, - 1.0, - 0.0, - 1.0, - ], - ) - ) - self.initialize_vbo(vertex_positions) - self.update_program() - self.start = time.time() self.timestamp = 0 self.frame = 0 @@ -289,6 +275,49 @@ def on_init(self, widget, **kwargs): self.mouse_down = False self.drag_start = (0, 0) + def on_init(self, widget, **kwargs): + try: + self.source_updated = False + vertex_positions = bytes( + array.array( + "f", + [ + 1.0, + 1.0, + 0.0, + 1.0, # triangle 1 + 1.0, + -1.0, + 0.0, + 1.0, + -1.0, + -1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, # triangle 2 + -1.0, + -1.0, + 0.0, + 1.0, + -1.0, + 1.0, + 0.0, + 1.0, + ], + ) + ) + self.initialize_vbo(vertex_positions) + self.update_program() + except OpenGLError as exc: + self.message = exc.args[0] + except Exception: + self.message = traceback.format_exc() + else: + self.message = "Initialization successful" + def on_render( self, widget, @@ -297,56 +326,77 @@ def on_render( buttons=(False, False, False), **kwargs, ): - if self.source_updated: - self.program.delete() - self.on_init(widget) - n_vertices = 6 - if 0 <= pointer[0] < size[0] and 0 <= pointer[1] < size[1]: - mouse_down = any(buttons) - if mouse_down: - self.pointer = pointer - if not self.mouse_down: - # new mouse down - self.drag_start = pointer - self.mouse_down = True - elif self.drag_start[1] > 0: - # mouse was newly down, still down - self.drag_start = (self.drag_start[0], -self.drag_start[1]) - elif self.mouse_down: - # new mouse up - self.drag_start = (-self.drag_start[0], self.drag_start[1]) - self.mouse_down = False - GL.glClearColor(0.0, 0.0, 1.0, 1.0) GL.glClear(GL.GL_COLOR_BUFFER_BIT) - t = time.time() - self.start - self.deltas.append(t - self.timestamp) - frame_rate = len(self.deltas) / sum(self.deltas) - self.timestamp = t - dt = datetime.datetime.now() - - uniforms = { - "iResolution": (*size, 1.0), - "iMouse": (*self.pointer, *self.drag_start), - "iTime": (self.timestamp,), - "iTimeDelta": (self.deltas[-1],), - "iFrame": (self.frame,), - "iFrameRate": (frame_rate,), - "iDate": ( - dt.year - 1, - dt.month - 1, - dt.day, - dt.hour * 3600 + dt.minute * 60 + dt.second + dt.microsecond / 1e-6, - ), - } - - with self.program.use(): - with self.vao: - self.program.set_uniforms(uniforms) - - GL.glDrawArrays(GL.GL_TRIANGLES, 0, n_vertices) + if self.source_updated: + try: + self.program.delete() + self.update_program() + except OpenGLError as exc: + self.message = exc.args[0] + return + except Exception: + self.message = traceback.format_exc() + return + else: + self.source_updated = False + self.message = "Update successful" + return - self.frame += 1 - if len(self.deltas) > 100: - del self.deltas[0] + try: + n_vertices = 6 + if 0 <= pointer[0] < size[0] and 0 <= pointer[1] < size[1]: + mouse_down = any(buttons) + if mouse_down: + self.pointer = pointer + if not self.mouse_down: + # new mouse down + self.drag_start = pointer + self.mouse_down = True + elif self.drag_start[1] > 0: + # mouse was newly down, still down + self.drag_start = (self.drag_start[0], -self.drag_start[1]) + elif self.mouse_down: + # new mouse up + self.drag_start = (-self.drag_start[0], self.drag_start[1]) + self.mouse_down = False + + t = time.time() - self.start + self.deltas.append(t - self.timestamp + 1e-6) + frame_rate = len(self.deltas) / sum(self.deltas) + self.timestamp = t + dt = datetime.datetime.now() + + uniforms = { + "iResolution": (*size, 1.0), + "iMouse": (*self.pointer, *self.drag_start), + "iTime": (self.timestamp,), + "iTimeDelta": (self.deltas[-1],), + "iFrame": (self.frame,), + "iFrameRate": (frame_rate,), + "iDate": ( + dt.year, + dt.month, + dt.day, + dt.hour * 3600 + + dt.minute * 60 + + dt.second + + (dt.microsecond / 1e6), + ), + } + + with self.program.use(): + with self.vao: + self.program.set_uniforms(uniforms) + + GL.glDrawArrays(GL.GL_TRIANGLES, 0, n_vertices) + + self.frame += 1 + if len(self.deltas) > 100: + del self.deltas[0] + except Exception: + self.message = traceback.format_exc() + raise + else: + self.message = RENDER_MESSAGE.format(**uniforms, deltas=len(self.deltas)) From 4e431c69a29e5d62f4667ebd08cd63c615d17823 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 31 Mar 2026 23:07:10 +0100 Subject: [PATCH 07/26] [WIP] Working out mouse/touch support. --- .../src/toga_android/widgets/openglview.py | 50 ++++++++++++-- android/tests_backend/widgets/openglview.py | 17 +++++ cocoa/src/toga_cocoa/libs/appkit.py | 4 ++ cocoa/src/toga_cocoa/widgets/openglview.py | 19 +++--- cocoa/tests_backend/widgets/openglview.py | 61 ++++++++++++++++- core/src/toga/widgets/openglview.py | 20 +++++- .../shadertoy/shadertoy/renderer_pyopengl.py | 2 +- gtk/tests_backend/widgets/openglview.py | 1 + iOS/tests_backend/widgets/openglview.py | 1 + qt/src/toga_qt/widgets/openglview.py | 19 ++++++ qt/tests_backend/widgets/openglview.py | 67 +++++++++++++++++++ testbed/tests/widgets/test_openglview.py | 37 +++++++++- 12 files changed, 278 insertions(+), 20 deletions(-) diff --git a/android/src/toga_android/widgets/openglview.py b/android/src/toga_android/widgets/openglview.py index e189dae95d..e65870f6e1 100644 --- a/android/src/toga_android/widgets/openglview.py +++ b/android/src/toga_android/widgets/openglview.py @@ -1,10 +1,20 @@ +import weakref + from android.opengl import GLSurfaceView +from android.view import MotionEvent from java import dynamic_proxy -from .base import Widget +from toga.widgets.openglview import TOUCH + +from .base import Widget, suppress_reference_error class TogaGLRenderer(dynamic_proxy(GLSurfaceView.Renderer)): + def __init__(self, impl): + super().__init__() + self.impl = weakref.proxy(impl) + self.interface = weakref.proxy(impl.interface) + def onSurfaceCreated(self, gl_api, config): self.interface.renderer.on_init(self.interface) @@ -17,15 +27,45 @@ def onSurfaceChanged(self, gl_api, width, height): def _redraw(self): width = self.impl.native.getWidth() height = self.impl.native.getHeight() - self.interface.renderer.on_render(self.interface, size=(width, height)) + self.interface.renderer.on_render( + self.interface, + size=(width, height), + pointer=self.impl.pointer, + buttons=self.impl.buttons, + ) + + +class TouchListener(dynamic_proxy(GLSurfaceView.OnTouchListener)): + def __init__(self, impl): + super().__init__() + self.impl = weakref.proxy(impl) + self.interface = weakref.proxy(impl.interface) + + def onTouch(self, canvas, event): + with suppress_reference_error(): + x, y = map(self.impl.scale_out, (event.getX(), event.getY())) + if (action := event.getAction()) == MotionEvent.ACTION_DOWN: + self.interface.pointer = (x, y) + self.interface.buttons = frozenset([TOUCH]) + elif action == MotionEvent.ACTION_MOVE: + self.interface.pointer = (x, y) + self.interface.buttons = frozenset([TOUCH]) + elif action == MotionEvent.ACTION_UP: + self.interface.on_release(x, y) + self.interface.buttons = frozenset() + else: # pragma: no cover + self.interface.pointer = None + self.interface.buttons = frozenset() + return True class OpenGLView(Widget): def create(self): + self.pointer = None + self.button = (False,) self.native = GLSurfaceView(self._native_activity) - self.renderer = TogaGLRenderer() - self.renderer.interface = self.interface - self.renderer.impl = self + self.renderer = TogaGLRenderer(self) + self.listener = TouchListener(self) self.native.setEGLContextClientVersion(3) self.native.setRenderer(self.renderer) diff --git a/android/tests_backend/widgets/openglview.py b/android/tests_backend/widgets/openglview.py index a18cce7860..a14d58cfdc 100644 --- a/android/tests_backend/widgets/openglview.py +++ b/android/tests_backend/widgets/openglview.py @@ -1,7 +1,24 @@ from android.opengl import GLSurfaceView +from android.os import SystemClock +from android.view import MotionEvent + +from toga.widgets.openglview import TOUCH from .base import SimpleProbe class OpenGLViewProbe(SimpleProbe): native_class = GLSurfaceView + buttons = frozenset({TOUCH}) + + def motion_event(self, action, x, y): + time = SystemClock.uptimeMillis() + super().motion_event( + time, time, action, x * self.scale_factor, y * self.scale_factor + ) + + async def touch_down(self, x, y): + self.motion_event(MotionEvent.ACTION_DOWN, x, y) + + async def touch_up(self, x, y): + self.motion_event(MotionEvent.ACTION_UP, x, y) diff --git a/cocoa/src/toga_cocoa/libs/appkit.py b/cocoa/src/toga_cocoa/libs/appkit.py index 3f05a4a50c..cc0a7e847f 100644 --- a/cocoa/src/toga_cocoa/libs/appkit.py +++ b/cocoa/src/toga_cocoa/libs/appkit.py @@ -330,6 +330,10 @@ class NSEventType(IntEnum): KeyDown = 10 KeyUp = 11 + OtherMouseDown = 25 + OtherMouseUp = 26 + OtherMouseDreagged = 27 + ###################################################################### # NSFont.h diff --git a/cocoa/src/toga_cocoa/widgets/openglview.py b/cocoa/src/toga_cocoa/widgets/openglview.py index e00a0cbb8a..1e46ff0417 100644 --- a/cocoa/src/toga_cocoa/widgets/openglview.py +++ b/cocoa/src/toga_cocoa/widgets/openglview.py @@ -3,6 +3,7 @@ from rubicon.objc import objc_method, objc_property from travertino.size import at_least +from toga.widgets.openglview import LEFT, MIDDLE, RIGHT from toga_cocoa.libs import ( NSOpenGLPFADoubleBuffer, NSOpenGLPFAOpenGLProfile, @@ -18,7 +19,7 @@ class TogaOpenGLView(NSOpenGLView): interface = objc_property(object, weak=True) impl = objc_property(object, weak=True) - mouse_state = objc_property(object) + buttons: set = objc_property(object) @objc_method def prepareOpenGL(self) -> None: @@ -45,7 +46,7 @@ def drawRect_(self, rect: NSRect) -> None: self.interface, size=size, pointer=pointer, - buttons=tuple(self.mouse_state), + buttons=frozenset(self.buttons), ) finally: # show what was drawn up to any error @@ -71,27 +72,27 @@ def initWithFrame_(self, frame: NSRect): @objc_method def mouseDown_(self, event) -> None: - self.mouse_state[0] = True + self.buttons.add(LEFT) @objc_method def mouseUp_(self, event) -> None: - self.mouse_state[0] = False + self.buttons.discard(LEFT) @objc_method def otherMouseDown_(self, event) -> None: - self.mouse_state[1] = True + self.buttons.add(MIDDLE) @objc_method def otherMouseUp_(self, event) -> None: - self.mouse_state[1] = False + self.buttons.discard(MIDDLE) @objc_method def rightMouseDown_(self, event) -> None: - self.mouse_state[2] = True + self.buttons.add(RIGHT) @objc_method def rightMouseUp_(self, event) -> None: - self.mouse_state[2] = False + self.buttons.discard(RIGHT) class OpenGLView(Widget): @@ -103,7 +104,7 @@ def create(self): raise RuntimeError("Can't create native OpenGLView widget.") self.native.interface = self.interface self.native.impl = self - self.native.mouse_state = [False, False, False] + self.native.buttons = set() # Add the layout constraints self.add_constraints() diff --git a/cocoa/tests_backend/widgets/openglview.py b/cocoa/tests_backend/widgets/openglview.py index 9e3a257b2a..152f75fa23 100644 --- a/cocoa/tests_backend/widgets/openglview.py +++ b/cocoa/tests_backend/widgets/openglview.py @@ -1,7 +1,66 @@ -from toga_cocoa.libs import NSOpenGLView +from toga.widgets.openglview import LEFT, MIDDLE, RIGHT +from toga_cocoa.libs import NSEvent, NSEventType, NSOpenGLView, NSPoint from .base import SimpleProbe class OpenGLViewProbe(SimpleProbe): native_class = NSOpenGLView + buttons = frozenset({LEFT, MIDDLE, RIGHT}) + + async def mouse_state(self, buttons: frozenset, x=0, y=0): + methods = [ + self.left_mouse_down, + self.middle_mouse_down, + self.right_mouse_down, + ] + for button in buttons: + method = methods[button] + await method(x, y) + + async def reset_buttons(self, x=0, y=0): + self.native.buttons.clear() + await self.redraw("Buttons cleared") + + async def left_mouse_down(self, x=0, y=0): + event = self._button_event(NSEventType.LeftMouseDown) + self.native.mouseDown_(event) + await self.redraw("Left mouse is down") + + async def left_mouse_up(self, x=0, y=0): + event = self._button_event(NSEventType.LeftMouseUp) + self.native.mouseUp_(event) + await self.redraw("Left mouse is up") + + async def middle_mouse_down(self, x=0, y=0): + event = self._button_event(NSEventType.OtherMouseDown) + self.native.otherMouseDown_(event) + await self.redraw("Left mouse is down") + + async def middle_mouse_up(self, x=0, y=0): + event = self._button_event(NSEventType.OtherMouseUp) + self.native.otherMouseUp_(event) + await self.redraw("Left mouse is up") + + async def right_mouse_down(self, x=0, y=0): + event = self._button_event(NSEventType.RightMouseDown) + self.native.rightMouseDown_(event) + await self.redraw("Left mouse is down") + + async def right_mouse_up(self, x=0, y=0): + event = self._button_event(NSEventType.RightMouseUp) + self.native.rightMouseUp_(event) + await self.redraw("Left mouse is up") + + def _button_event(self, event_type, x=0, y=0): + return NSEvent.mouseEventWithType( + event_type, + location=self.native.convertPoint(NSPoint(x, y), toView=None), + modifierFlags=0, + timestamp=0, + windowNumber=self.native.window.windowNumber, + context=None, + eventNumber=0, + clickCount=1, + pressure=1.0 if event_type == NSEventType.LeftMouseDown else 0.0, + ) diff --git a/core/src/toga/widgets/openglview.py b/core/src/toga/widgets/openglview.py index a3539fd0e4..2d21fd4893 100644 --- a/core/src/toga/widgets/openglview.py +++ b/core/src/toga/widgets/openglview.py @@ -4,6 +4,11 @@ from .base import StyleT, Widget +# Button constants +LEFT = TOUCH = 0 +MIDDLE = 1 +RIGHT = 2 + class RendererT(Protocol): """A protocol that encapsulates OpenGL rendering operations.""" @@ -16,12 +21,23 @@ def on_init(self, widget: OpenGLView, **kwargs: Any) -> None: """ def on_render( - self, widget: OpenGLView, size: tuple[int, int], **kwargs: Any + self, + widget: OpenGLView, + size: tuple[int, int], + pointer: tuple[int, int] | None = None, + buttons: frozenset[int] = frozenset(), + **kwargs: Any, ) -> None: """The method called when the OpenGLView needs to re-draw its content. + This is called with basic information about the state of the UI. + :param widget: The view that is being rendered. - :param size: The size of the current OpenGL viewport. + :param size: The size of the current OpenGL viewport in OpenGL pixels. + :param pointer: The location of the pointer (mouse or touch) in OpenGL + coordinates, or None if not known (eg. when no touch on mobile) + :param buttons: The set of mouse buttons that is currently down, + or `{TOUCH}` if currently being touched. :param kwargs: Ensures compatibility with arguments added in future versions. """ diff --git a/examples/shadertoy/shadertoy/renderer_pyopengl.py b/examples/shadertoy/shadertoy/renderer_pyopengl.py index 5be28ed564..9812fb8a9d 100644 --- a/examples/shadertoy/shadertoy/renderer_pyopengl.py +++ b/examples/shadertoy/shadertoy/renderer_pyopengl.py @@ -323,7 +323,7 @@ def on_render( widget, size, pointer=(-1, -1), - buttons=(False, False, False), + buttons=frozenset(), **kwargs, ): GL.glClearColor(0.0, 0.0, 1.0, 1.0) diff --git a/gtk/tests_backend/widgets/openglview.py b/gtk/tests_backend/widgets/openglview.py index 4066d70717..dfbec9d01a 100644 --- a/gtk/tests_backend/widgets/openglview.py +++ b/gtk/tests_backend/widgets/openglview.py @@ -5,3 +5,4 @@ class OpenGLViewProbe(SimpleProbe): native_class = Gtk.GLArea + buttons = frozenset() diff --git a/iOS/tests_backend/widgets/openglview.py b/iOS/tests_backend/widgets/openglview.py index eeadd52660..55b93baffe 100644 --- a/iOS/tests_backend/widgets/openglview.py +++ b/iOS/tests_backend/widgets/openglview.py @@ -5,3 +5,4 @@ class OpenGLViewProbe(SimpleProbe): native_class = TogaGLKView + buttons = frozenset() diff --git a/qt/src/toga_qt/widgets/openglview.py b/qt/src/toga_qt/widgets/openglview.py index 521524721a..a769de4e66 100644 --- a/qt/src/toga_qt/widgets/openglview.py +++ b/qt/src/toga_qt/widgets/openglview.py @@ -1,8 +1,18 @@ +from PySide6.QtCore import Qt from PySide6.QtOpenGLWidgets import QOpenGLWidget +from PySide6.QtWidgets import QApplication from travertino.size import at_least +from toga.widgets.openglview import LEFT, MIDDLE, RIGHT + from .base import Widget +BUTTON_MAP = { + LEFT: Qt.MouseButton.LeftButton, + MIDDLE: Qt.MouseButton.MiddleButton, + RIGHT: Qt.MouseButton.RightButton, +} + class TogaOpenGLWidget(QOpenGLWidget): def __init__(self, impl, *args, **kwargs): @@ -29,10 +39,19 @@ def _redraw(self): mouse_postion.x() * pixel_ratio, height - mouse_postion.y() * pixel_ratio, ) + qt_buttons = QApplication.mouseButtons() + buttons = frozenset( + { + button + for button, qt_button in BUTTON_MAP.items() + if qt_buttons & qt_button + } + ) self.interface.renderer.on_render( self.interface, size=(width, height), pointer=pointer, + buttons=buttons, ) diff --git a/qt/tests_backend/widgets/openglview.py b/qt/tests_backend/widgets/openglview.py index 033cb03cd3..26d260561b 100644 --- a/qt/tests_backend/widgets/openglview.py +++ b/qt/tests_backend/widgets/openglview.py @@ -1,3 +1,5 @@ +from PySide6.QtCore import QCoreApplication, QEvent, QPoint, Qt +from PySide6.QtGui import QMouseEvent from toga_qt.widgets.openglview import TogaOpenGLWidget from .base import SimpleProbe @@ -5,3 +7,68 @@ class OpenGLViewProbe(SimpleProbe): native_class = TogaOpenGLWidget + buttons = frozenset() + + async def mouse_state(self, buttons: frozenset, x=0, y=0): + methods = [ + self.left_mouse_down, + self.middle_mouse_down, + self.right_mouse_down, + ] + for button in buttons: + method = methods[button] + await method(x, y) + + async def reset_buttons(self, x=0, y=0): + for method in [ + self.left_mouse_up, + self.middle_mouse_up, + self.right_mouse_up, + ]: + method(x, y) + await self.redraw("Buttons cleared") + + async def left_mouse_down(self, x=0, y=0): + self._emit_event( + QEvent.Type.MouseButtonPress, x, y, button=Qt.MouseButton.LeftButton + ) + + async def left_mouse_up(self, x, y): + self._emit_event( + QEvent.Type.MouseButtonRelease, x, y, button=Qt.MouseButton.LeftButton + ) + + async def middle_mouse_down(self, x=0, y=0): + self._emit_event( + QEvent.Type.MouseButtonPress, x, y, button=Qt.MouseButton.MiddleButton + ) + + async def middle_mouse_up(self, x, y): + self._emit_event( + QEvent.Type.MouseButtonRelease, x, y, button=Qt.MouseButton.MiddleButton + ) + + async def right_mouse_down(self, x=0, y=0): + self._emit_event( + QEvent.Type.MouseButtonPress, x, y, button=Qt.MouseButton.RightButton + ) + + async def right_mouse_up(self, x, y): + self._emit_event( + QEvent.Type.MouseButtonRelease, x, y, button=Qt.MouseButton.RightButton + ) + + def _emit_event( + self, + event_type: QEvent.Type, + x, + y, + button=Qt.MouseButton.LeftButton, + ): + pos = QPoint(x, y) + global_pos = self.native.mapToGlobal(pos) + event = QMouseEvent( + event_type, pos, global_pos, button, button, Qt.KeyboardModifier.NoModifier + ) + app = QCoreApplication.instance() + app.postEvent(self.native, event) diff --git a/testbed/tests/widgets/test_openglview.py b/testbed/tests/widgets/test_openglview.py index f2bef77e76..13a485f4b3 100644 --- a/testbed/tests/widgets/test_openglview.py +++ b/testbed/tests/widgets/test_openglview.py @@ -1,3 +1,4 @@ +from itertools import chain, combinations from unittest.mock import Mock import pytest @@ -27,7 +28,7 @@ async def widget(renderer): async def test_callbacks(probe, widget, renderer): renderer.on_init.assert_called_once_with(widget) - await probe.redraw("OpenGlView widget initialized", 0.1) + await probe.redraw("OpenGLView widget initialized", 0.1) # different backends render different numbers of times with # different arguments @@ -40,9 +41,41 @@ async def test_callbacks(probe, widget, renderer): renderer.reset_mock() widget.redraw() - await probe.redraw("OpenGlView widget redraw requested", 0.1) + await probe.redraw("OpenGLView widget redraw requested", 0.1) renderer.on_render.assert_called() assert renderer.on_render.call_args[0] == (widget,) assert "size" in renderer.on_render.call_args[1] assert isinstance(renderer.on_render.call_args[1]["size"], tuple) assert len(renderer.on_render.call_args[1]["size"]) == 2 + + +async def test_buttons(probe, widget, renderer): + if not probe.buttons: + pytest.skip("Backend does not support buttons.") + + await probe.redraw("OpenGLView widget initialized", 0.1) + + button_states = [ + frozenset(combination) + for combination in chain.from_iterable( + combinations(probe.buttons, r) for r in range(len(probe.buttons) + 1) + ) + ] + for buttons in button_states: + await probe.reset_buttons() + await probe.redraw("Reset buttons", 0.01) + renderer.on_render.reset_mock() + + await probe.mouse_state(buttons) + await probe.redraw(f"Mouse events sent {buttons}", 0.01) + widget.redraw() + await probe.redraw("OpenGLView widget redraw requested", 0.1) + + assert renderer.on_render.call_args[0] == (widget,) + assert "size" in renderer.on_render.call_args[1] + assert isinstance(renderer.on_render.call_args[1]["size"], tuple) + assert len(renderer.on_render.call_args[1]["size"]) == 2 + + assert "buttons" in renderer.on_render.call_args[1] + assert isinstance(renderer.on_render.call_args[1]["buttons"], frozenset) + assert renderer.on_render.call_args[1]["buttons"] == buttons From c7cb27fdefa5748357cd9cae1679a4215332cc4d Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 1 Apr 2026 11:11:48 +0100 Subject: [PATCH 08/26] More work on position and button support. Should cover everything except Gtk 4. --- android/tests_backend/widgets/openglview.py | 8 ++++ cocoa/tests_backend/widgets/openglview.py | 2 +- gtk/src/toga_gtk/widgets/openglview.py | 41 ++++++++++++++++- gtk/tests_backend/widgets/openglview.py | 51 ++++++++++++++++++++- iOS/src/toga_iOS/widgets/openglview.py | 34 ++++++++++++-- iOS/tests_backend/widgets/openglview.py | 45 +++++++++++++++++- qt/tests_backend/widgets/openglview.py | 4 +- testbed/tests/widgets/test_openglview.py | 2 +- 8 files changed, 175 insertions(+), 12 deletions(-) diff --git a/android/tests_backend/widgets/openglview.py b/android/tests_backend/widgets/openglview.py index a14d58cfdc..275153c88c 100644 --- a/android/tests_backend/widgets/openglview.py +++ b/android/tests_backend/widgets/openglview.py @@ -11,6 +11,14 @@ class OpenGLViewProbe(SimpleProbe): native_class = GLSurfaceView buttons = frozenset({TOUCH}) + async def button_state(self, buttons: frozenset, x=0, y=0): + if TOUCH in buttons: + await self.touch_down(x, y) + + async def reset_buttons(self, buttons: frozenset, x=0, y=0): + await self.touch_up(x, y) + await self.redraw("Touch cleared") + def motion_event(self, action, x, y): time = SystemClock.uptimeMillis() super().motion_event( diff --git a/cocoa/tests_backend/widgets/openglview.py b/cocoa/tests_backend/widgets/openglview.py index 152f75fa23..a3b8fd014f 100644 --- a/cocoa/tests_backend/widgets/openglview.py +++ b/cocoa/tests_backend/widgets/openglview.py @@ -8,7 +8,7 @@ class OpenGLViewProbe(SimpleProbe): native_class = NSOpenGLView buttons = frozenset({LEFT, MIDDLE, RIGHT}) - async def mouse_state(self, buttons: frozenset, x=0, y=0): + async def button_state(self, buttons: frozenset, x=0, y=0): methods = [ self.left_mouse_down, self.middle_mouse_down, diff --git a/gtk/src/toga_gtk/widgets/openglview.py b/gtk/src/toga_gtk/widgets/openglview.py index 43b2ca47f9..36c7ff6e91 100644 --- a/gtk/src/toga_gtk/widgets/openglview.py +++ b/gtk/src/toga_gtk/widgets/openglview.py @@ -1,9 +1,12 @@ from travertino.size import at_least -from toga_gtk.libs import GTK_VERSION, Gtk +from toga.widgets.openglview import LEFT, MIDDLE, RIGHT +from toga_gtk.libs import GTK_VERSION, Gdk, Gtk from .base import Widget +BUTTONS = [LEFT, MIDDLE, RIGHT] + class OpenGLView(Widget): def create(self): @@ -11,6 +14,22 @@ def create(self): self.native.connect("realize", self.gtk_realize) self.native.connect("render", self.gtk_render) + self.buttons = set() + self.position = None + + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + self.native.connect("button-press-event", self.gtk_button_press) + self.native.connect("button-release-event", self.gtk_button_release) + self.native.connect("motion-notify-event", self.gtk_motion_notify) + self.native.set_events( + Gdk.EventMask.BUTTON_PRESS_MASK + | Gdk.EventMask.BUTTON_RELEASE_MASK + | Gdk.EventMask.BUTTON_MOTION_MASK + ) + else: # pragma: no-cover-if-gtk3 + # Currently unsure of how best to integrate GTK 4 gestures with mouse state + pass + def gtk_realize(self, native): """Initialize the OpenGL context.""" ctx = self.native.get_context() @@ -19,9 +38,27 @@ def gtk_realize(self, native): def gtk_render(self, native, context): """Render to the OpenGL context.""" - self.interface.renderer.on_render(self.interface, size=self._size()) + self.interface.renderer.on_render( + self.interface, + size=self._size(), + buttons=self.buttons, + position=self.position, + ) return True + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + + def gtk_button_press(self, obj, event): + self.buttons.add(BUTTONS[event.button]) + self.position = (event.x, event.y) + + def gtk_button_release(self, obj, event): + self.buttons.discard(BUTTONS[event.button]) + self.position = (event.x, event.y) + + def gtk_motion_notify(self, obj, event): + self.position = (event.x, event.y) + def _size(self): if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 width = self.native.get_allocation().width diff --git a/gtk/tests_backend/widgets/openglview.py b/gtk/tests_backend/widgets/openglview.py index dfbec9d01a..9e6a80e6fd 100644 --- a/gtk/tests_backend/widgets/openglview.py +++ b/gtk/tests_backend/widgets/openglview.py @@ -1,8 +1,55 @@ -from toga_gtk.libs import Gtk +from toga.widgets.openglview import LEFT, MIDDLE, RIGHT +from toga_gtk.libs import GTK_VERSION, Gdk, Gtk from .base import SimpleProbe +BUTTONS = [LEFT, MIDDLE, RIGHT] + class OpenGLViewProbe(SimpleProbe): native_class = Gtk.GLArea - buttons = frozenset() + if GTK_VERSION < (4, 0, 0): + buttons = frozenset({LEFT, MIDDLE, RIGHT}) + else: + # Not supported yet + buttons = frozenset() + + if GTK_VERSION < (4, 0, 0): + + async def button_state(self, buttons: frozenset, x=0, y=0): + for button in buttons: + await self.button_down(button, x, y) + + async def reset_buttons(self, x=0, y=0): + for button in BUTTONS: + await self.button_up(button, x, y) + await self.redraw("Buttons cleared") + + def _emit_event(self, event_type, x, y, button=1, state=0, emit_name=None): + event = Gdk.Event.new(event_type) + event.button = button + event.x = x + event.y = y + if event_type == Gdk.EventType.MOTION_NOTIFY: + state = state | getattr(Gdk.ModifierType, f"BUTTON{button}_MASK") + event.state = state + + self.native.emit( + emit_name or event_type.name.lower().replace("_", "-") + "-event", event + ) + + async def button_down(self, button, x, y): + self._emit_event( + Gdk.EventType.BUTTON_PRESS, + x, + y, + button=BUTTONS.index(button), + ) + + async def button_up(self, button, x, y): + self._emit_event( + Gdk.EventType.BUTTON_RELEASE, + x, + y, + button=BUTTONS.index(button), + ) diff --git a/iOS/src/toga_iOS/widgets/openglview.py b/iOS/src/toga_iOS/widgets/openglview.py index bc7b60cad9..c6b85208c0 100644 --- a/iOS/src/toga_iOS/widgets/openglview.py +++ b/iOS/src/toga_iOS/widgets/openglview.py @@ -1,6 +1,7 @@ from rubicon.objc import objc_method, objc_property from travertino.size import at_least +from toga.widgets.openglview import TOUCH from toga_iOS.libs import ( CGRect, CGSize, @@ -20,6 +21,8 @@ class TogaGLKView(GLKView): interface = objc_property(object, weak=True) impl = objc_property(object, weak=True) initialized = objc_property(bool) + pointer = objc_property(object) + buttons = objc_property(set) @objc_method def drawRect_(self, rect: CGRect) -> None: @@ -29,7 +32,30 @@ def drawRect_(self, rect: CGRect) -> None: width = rect.size.width height = rect.size.height - self.interface.renderer.on_render(self.interface, size=(width, height)) + self.interface.renderer.on_render( + self.interface, + size=(width, height), + pointer=self.pointer, + buttons=frozenset(self.buttons), + ) + + @objc_method + def touchesBegan_withEvent_(self, touches, event) -> None: + position = touches.allObjects()[0].locationInView(self) + self.buttons.add(TOUCH) + self.pointer = (position.x, position.y) + + @objc_method + def touchesMoved_withEvent_(self, touches, event) -> None: + position = touches.allObjects()[0].locationInView(self) + self.buttons.add(TOUCH) + self.pointer = (position.x, position.y) + + @objc_method + def touchesEnded_withEvent_(self, touches, event) -> None: + position = touches.allObjects()[0].locationInView(self) + self.buttons.discard(TOUCH) + self.pointer = (position.x, position.y) class OpenGLView(Widget): @@ -37,17 +63,19 @@ def create(self): self.native = TogaGLKView.alloc().init() self.native.interface = self.interface self.native.impl = self + self.native.pointer = None + self.native.buttons = set() self.native.initialized = False self.native.context = EAGLContext.alloc().initWithAPI_( kEAGLRenderingAPIOpenGLES3 ) - # # Configure renderbuffers created by the view + # Configure renderbuffers created by the view self.native.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888 self.native.drawableDepthFormat = GLKViewDrawableDepthFormat24 self.native.drawableStencilFormat = GLKViewDrawableStencilFormat8 - # # Enable multisampling + # Enable multisampling self.native.drawableMultisample = GLKViewDrawableMultisample4X # Add the layout constraints diff --git a/iOS/tests_backend/widgets/openglview.py b/iOS/tests_backend/widgets/openglview.py index 55b93baffe..4f996e1e36 100644 --- a/iOS/tests_backend/widgets/openglview.py +++ b/iOS/tests_backend/widgets/openglview.py @@ -1,8 +1,51 @@ +from rubicon.objc import NSObject, NSPoint, ObjCClass, objc_method + +from toga.widgets.openglview import TOUCH from toga_iOS.widgets.openglview import TogaGLKView from .base import SimpleProbe +# Touch events generate a Set of 1 event. +NSSet = ObjCClass("NSSet") + + +# UITouch objects can't be instantiated; but we only care about 1 method, so +# create a mock that satisfies our needs. +class MockTouch(NSObject): + @objc_method + def locationInView(self, view) -> NSPoint: + return self.position + class OpenGLViewProbe(SimpleProbe): native_class = TogaGLKView - buttons = frozenset() + buttons = frozenset({TOUCH}) + + async def button_state(self, buttons: frozenset, x=0, y=0): + if TOUCH in buttons: + await self.touch_down(x, y) + + async def reset_buttons(self, buttons: frozenset, x=0, y=0): + await self.touch_up(x, y) + await self.redraw("Touch cleared") + + async def touch_down(self, x, y): + touch = MockTouch.alloc().init() + touches = NSSet.setWithObject(touch) + + touch.position = NSPoint(x, y) + self.native.touchesBegan(touches, withEvent=None) + + async def touch_move(self, x, y): + touch = MockTouch.alloc().init() + touches = NSSet.setWithObject(touch) + + touch.position = NSPoint(x, y) + self.native.touchesMoved(touches, withEvent=None) + + async def touch_up(self, x, y): + touch = MockTouch.alloc().init() + touches = NSSet.setWithObject(touch) + + touch.position = NSPoint(x, y) + self.native.touchesEnded(touches, withEvent=None) diff --git a/qt/tests_backend/widgets/openglview.py b/qt/tests_backend/widgets/openglview.py index 26d260561b..06e7d3b7a0 100644 --- a/qt/tests_backend/widgets/openglview.py +++ b/qt/tests_backend/widgets/openglview.py @@ -9,7 +9,7 @@ class OpenGLViewProbe(SimpleProbe): native_class = TogaOpenGLWidget buttons = frozenset() - async def mouse_state(self, buttons: frozenset, x=0, y=0): + async def button_state(self, buttons: frozenset, x=0, y=0): methods = [ self.left_mouse_down, self.middle_mouse_down, @@ -25,7 +25,7 @@ async def reset_buttons(self, x=0, y=0): self.middle_mouse_up, self.right_mouse_up, ]: - method(x, y) + await method(x, y) await self.redraw("Buttons cleared") async def left_mouse_down(self, x=0, y=0): diff --git a/testbed/tests/widgets/test_openglview.py b/testbed/tests/widgets/test_openglview.py index 13a485f4b3..188a3b358b 100644 --- a/testbed/tests/widgets/test_openglview.py +++ b/testbed/tests/widgets/test_openglview.py @@ -66,7 +66,7 @@ async def test_buttons(probe, widget, renderer): await probe.redraw("Reset buttons", 0.01) renderer.on_render.reset_mock() - await probe.mouse_state(buttons) + await probe.button_state(buttons) await probe.redraw(f"Mouse events sent {buttons}", 0.01) widget.redraw() await probe.redraw("OpenGLView widget redraw requested", 0.1) From e06c5057d9e66d9ad9ab31949f63fc10718c44a3 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 1 Apr 2026 19:28:17 +0100 Subject: [PATCH 09/26] Get shadertoy mostly working on Android; refactor renderers. --- .../src/toga_android/widgets/openglview.py | 2 +- examples/shadertoy/shadertoy/app.py | 63 +++-- .../shadertoy/shadertoy/renderer_android.py | 237 ++++++++++++++++ .../shadertoy/shadertoy/renderer_pyopengl.py | 266 ++++-------------- examples/shadertoy/shadertoy/utils_android.py | 211 ++++++++++++++ .../shadertoy/shadertoy/utils_pyopengl.py | 184 ++++++++++++ 6 files changed, 723 insertions(+), 240 deletions(-) create mode 100644 examples/shadertoy/shadertoy/renderer_android.py create mode 100644 examples/shadertoy/shadertoy/utils_android.py create mode 100644 examples/shadertoy/shadertoy/utils_pyopengl.py diff --git a/android/src/toga_android/widgets/openglview.py b/android/src/toga_android/widgets/openglview.py index e65870f6e1..7b03e2bf94 100644 --- a/android/src/toga_android/widgets/openglview.py +++ b/android/src/toga_android/widgets/openglview.py @@ -62,7 +62,7 @@ def onTouch(self, canvas, event): class OpenGLView(Widget): def create(self): self.pointer = None - self.button = (False,) + self.buttons = frozenset() self.native = GLSurfaceView(self._native_activity) self.renderer = TogaGLRenderer(self) self.listener = TouchListener(self) diff --git a/examples/shadertoy/shadertoy/app.py b/examples/shadertoy/shadertoy/app.py index 9d9f3bdaa9..1aa5d90d1c 100644 --- a/examples/shadertoy/shadertoy/app.py +++ b/examples/shadertoy/shadertoy/app.py @@ -34,9 +34,12 @@ class ShadertoyApp(toga.App): def startup(self): - self.main_window = toga.MainWindow( - size=(960, 800), resizable=True, minimizable=False - ) + if toga.backend == "toga_android": + self.main_window = toga.MainWindow() + else: + self.main_window = toga.MainWindow( + size=(960, 800), resizable=True, minimizable=False + ) self.shadertoy_source = toga.MultilineTextInput( value=SOURCE, @@ -49,6 +52,8 @@ def startup(self): if toga.backend in {"toga_cocoa", "toga_qt", "toga_gtk"}: from .renderer_pyopengl import Renderer + elif toga.backend == "toga_android": + from .renderer_android import Renderer else: raise RuntimeError(f"Toga backend {toga.backend} is not supported.") @@ -65,25 +70,39 @@ async def animate(): loop = asyncio.get_running_loop() loop.create_task(animate()) - # Create the outer box with 2 rows - outer_box = toga.Box( - children=[ - toga.Box( - children=[ - opengl_view, - toga.ScrollContainer( - content=self.message, - flex=1.0, - height=360, - ), - ], - gap=4, - ), - self.shadertoy_source, - ], - direction=COLUMN, - gap=4, - ) + if toga.backend == "toga_android": + # vertical layout + outer_box = toga.Box( + children=[ + opengl_view, + toga.ScrollContainer( + content=self.message, + ), + self.shadertoy_source, + ], + direction=COLUMN, + gap=4, + ) + else: + # Create the outer box with 2 rows + outer_box = toga.Box( + children=[ + toga.Box( + children=[ + opengl_view, + toga.ScrollContainer( + content=self.message, + flex=1.0, + height=360, + ), + ], + gap=4, + ), + self.shadertoy_source, + ], + direction=COLUMN, + gap=4, + ) # Add the content on the main window self.main_window.content = outer_box diff --git a/examples/shadertoy/shadertoy/renderer_android.py b/examples/shadertoy/shadertoy/renderer_android.py new file mode 100644 index 0000000000..423182d7f1 --- /dev/null +++ b/examples/shadertoy/shadertoy/renderer_android.py @@ -0,0 +1,237 @@ +import array +import datetime +import time +import traceback + +from .utils_android import ( + GL, + Buffer, + BufferType, + BufferUsage, + OpenGLError, + Program, + Shader, + ShaderType, + VertexArray, +) + +vertext_shader_source = """#version 300 es + +precision highp float; + +in vec4 position; + +void main() +{ + gl_Position = position; +} +""" +FRAGMENT_SHADER_TEMPLATE = """#version 300 es + +precision highp float; + +uniform vec3 iResolution; +uniform float iTime; +uniform float iTimeDelta; +uniform float iFrame; +uniform float iFrameRate; +uniform vec4 iMouse; +uniform vec4 iDate; + +out vec4 output_color; + +{source} + +void main() {{ + mainImage(output_color, gl_FragCoord.xy); +}} +""" + +RENDER_MESSAGE = """Rendering... +Frame: {iFrame[0]} +Frame rate: {iFrameRate[0]:.3f} fps + +Resolution: {iResolution} +Mouse: {iMouse} + +Time: {iTime[0]:.3f} s +Time delta: {iTimeDelta[0]:.3f} s +Date: {iDate[0]}-{iDate[1]}-{iDate[2]} {iDate[3]:.3f} + +{deltas} +""" + + +class Renderer: + source: str + message: str + + def __init__(self, source): + self.message = "Uninitialized." + self.set_source(source) + + def set_source(self, source): + self.fragment_shader_source = FRAGMENT_SHADER_TEMPLATE.format(source=source) + self.source_updated = True + + def initialize_vbo(self, data): + data_bytes = bytes(array.array("f", data)) + self.vbo = Buffer(BufferType.array, BufferUsage.static_draw) + self.vbo.create() + with self.vbo: + self.vbo.set_data(data_bytes) + + def initialize_program(self, vertex_shader_code, fragment_shader_code): + self.program = Program( + [ + Shader(ShaderType.vertex, vertex_shader_code), + Shader(ShaderType.fragment, fragment_shader_code), + ] + ) + self.program.create() + + def initialize_vao(self, attribute): + attribute_loc = self.program.attribute(attribute) + self.vao = VertexArray() + self.vao.create() + with self.vao: + with self.vbo: + GL.glEnableVertexAttribArray(attribute_loc) + GL.glVertexAttribPointer(0, 4, GL.GL_FLOAT, False, 0, 0) + + def update_program(self): + self.initialize_program(vertext_shader_source, self.fragment_shader_source) + self.initialize_vao("position") + self.start = time.time() + self.timestamp = 0 + self.frame = 0 + self.deltas = [] + self.pointer = (0, 0) + self.mouse_down = False + self.drag_start = (0, 0) + + def on_init(self, widget, **kwargs): + try: + self.source_updated = False + vertex_positions = [ + 1.0, # triangle 1 + 1.0, + 0.0, + 1.0, + 1.0, + -1.0, + 0.0, + 1.0, + -1.0, + -1.0, + 0.0, + 1.0, + 1.0, # triangle 2 + 1.0, + 0.0, + 1.0, + -1.0, + -1.0, + 0.0, + 1.0, + -1.0, + 1.0, + 0.0, + 1.0, + ] + self.initialize_vbo(vertex_positions) + self.update_program() + except OpenGLError as exc: + self.message = exc.args[0] + raise + except Exception: + self.message = traceback.format_exc() + raise + else: + self.message = "Initialization successful" + + def on_render( + self, + widget, + size, + pointer=(-1, -1), + buttons=frozenset(), + **kwargs, + ): + GL.glClearColor(0.0, 0.0, 1.0, 1.0) + GL.glClear(GL.GL_COLOR_BUFFER_BIT) + + if self.source_updated: + try: + self.program.delete() + self.update_program() + except OpenGLError as exc: + self.message = exc.args[0] + return + except Exception: + self.message = traceback.format_exc() + return + else: + self.source_updated = False + self.message = "Update successful" + return + + try: + n_vertices = 6 + if pointer and 0 <= pointer[0] < size[0] and 0 <= pointer[1] < size[1]: + mouse_down = any(buttons) + if mouse_down: + self.pointer = pointer + if not self.mouse_down: + # new mouse down + self.drag_start = pointer + self.mouse_down = True + elif self.drag_start[1] > 0: + # mouse was newly down, still down + self.drag_start = (self.drag_start[0], -self.drag_start[1]) + elif self.mouse_down: + # new mouse up + self.drag_start = (-self.drag_start[0], self.drag_start[1]) + self.mouse_down = False + elif pointer is None: + pointer = (0, 0) + self.drag_start = (0, 0) + + t = time.time() - self.start + self.deltas.append(t - self.timestamp + 1e-6) + frame_rate = len(self.deltas) / sum(self.deltas) + self.timestamp = t + dt = datetime.datetime.now() + + uniforms = { + "iResolution": (*size, 1.0), + "iMouse": (*self.pointer, *self.drag_start), + "iTime": (self.timestamp,), + "iTimeDelta": (self.deltas[-1],), + "iFrame": (self.frame,), + "iFrameRate": (frame_rate,), + "iDate": ( + dt.year, + dt.month, + dt.day, + dt.hour * 3600 + + dt.minute * 60 + + dt.second + + (dt.microsecond / 1e6), + ), + } + + with self.program.use(): + with self.vao: + self.program.set_uniforms(uniforms) + + GL.glDrawArrays(GL.GL_TRIANGLES, 0, n_vertices) + + self.frame += 1 + if len(self.deltas) > 100: + del self.deltas[0] + except Exception: + self.message = traceback.format_exc() + raise + else: + self.message = RENDER_MESSAGE.format(**uniforms, deltas=len(self.deltas)) diff --git a/examples/shadertoy/shadertoy/renderer_pyopengl.py b/examples/shadertoy/shadertoy/renderer_pyopengl.py index 9812fb8a9d..42ab56bb3a 100644 --- a/examples/shadertoy/shadertoy/renderer_pyopengl.py +++ b/examples/shadertoy/shadertoy/renderer_pyopengl.py @@ -2,15 +2,18 @@ import datetime import time import traceback -from contextlib import contextmanager -from dataclasses import dataclass -import OpenGL - -# Get spurious errors with Qt backend -OpenGL.ERROR_CHECKING = False - -from OpenGL import GL # noqa: E402 +from .utils_pyopengl import ( + GL, + Buffer, + BufferType, + BufferUsage, + OpenGLError, + Program, + Shader, + ShaderType, + VertexArray, +) vertext_shader_source = """ #version 330 core @@ -22,17 +25,6 @@ gl_Position = position; } """ -fragment_shader_source = """ -#version 330 core - -uniform vec4 input_color = vec4(1.0, 0.0, 0.0, 1.0); - -out vec4 output_color; -void main() -{ - output_color = input_color; -} -""" FRAGMENT_SHADER_TEMPLATE = """#version 330 core uniform vec3 iResolution; @@ -52,167 +44,6 @@ }} """ - -uniform_setters = { - (GL.GL_FLOAT, False): GL.glUniform1f, - (GL.GL_FLOAT_VEC2, False): GL.glUniform2f, - (GL.GL_FLOAT_VEC3, False): GL.glUniform3f, - (GL.GL_FLOAT_VEC4, False): GL.glUniform4f, - (GL.GL_FLOAT, True): GL.glUniform1fv, - (GL.GL_FLOAT_VEC2, True): GL.glUniform2fv, - (GL.GL_FLOAT_VEC3, True): GL.glUniform3fv, - (GL.GL_FLOAT_VEC4, True): GL.glUniform4fv, -} - - -class OpenGLError(RuntimeError): - pass - - -@dataclass -class Shader: - shader_type: int - source: str - - def create(self): - self.id = GL.glCreateShader(self.shader_type) - GL.glShaderSource(self.id, self.source) - - GL.glCompileShader(self.id) - - status = GL.glGetShaderiv(self.id, GL.GL_COMPILE_STATUS) - if status == GL.GL_FALSE: - info_log = GL.glGetShaderInfoLog(self.id).decode("utf-8") - raise OpenGLError( - f"Compilation failure for {self.shader_type} shader:\n{info_log}" - ) - - def delete(self): - if hasattr(self, "id"): - GL.glDeleteShader(self.id) - del self.id - - -@dataclass -class Program: - shaders: list[Shader] - - def create(self): - for shader in self.shaders: - shader.create() - - self.id = GL.glCreateProgram() - - for shader in self.shaders: - GL.glAttachShader(self.id, shader.id) - - GL.glLinkProgram(self.id) - - status = GL.glGetProgramiv(self.id, GL.GL_LINK_STATUS) - if status == GL.GL_FALSE: - strInfoLog = GL.glGetProgramInfoLog(self.id).decode("utf-8") - raise OpenGLError("Linker failure: \n" + strInfoLog) - - for shader in self.shaders: - GL.glDetachShader(self.id, shader.id) - - for shader in self.shaders: - shader.delete() - - def delete(self): - if hasattr(self, "id"): - GL.glDeleteProgram(self.id) - del self.id - - @contextmanager - def use(self): - GL.glUseProgram(self.id) - try: - yield - finally: - GL.glUseProgram(0) - - def active_attributes(self): - data = [ - GL.glGetActiveAttrib(self.id, i) - for i in range(GL.glGetProgramiv(self.id, GL.GL_ACTIVE_ATTRIBUTES)) - ] - return { - name.decode("utf-8"): (i, size, uniform_type) - for i, (name, size, uniform_type) in enumerate(data) - } - - def active_uniforms(self): - data = [ - GL.glGetActiveUniform(self.id, i) - for i in range(GL.glGetProgramiv(self.id, GL.GL_ACTIVE_UNIFORMS)) - ] - return { - name.decode("utf-8"): ( - i, - size, - uniform_type, - uniform_setters[uniform_type, size > 1], - ) - for i, (name, size, uniform_type) in enumerate(data) - } - - def attribute(self, item): - return GL.glGetAttribLocation(self.id, item) - - def set_uniforms(self, uniforms): - for uniform, (loc, size, _, setter) in self.active_uniforms().items(): - if uniform in uniforms: - if size == 1: - setter(loc, *uniforms[uniform]) - else: - setter(loc, size, uniforms[uniform]) - - -@dataclass -class Buffer: - buffer_type: int - usage: int - - def create(self): - self.id = GL.glGenBuffers(1) - - def set_data(self, data): - GL.glBufferData(self.buffer_type, data, self.usage) - - def bind(self): - GL.glBindBuffer(self.buffer_type, self.id) - - def unbind(self): - GL.glBindBuffer(self.buffer_type, 0) - - def __enter__(self): - self.bind() - - def __exit__(self, exc_type, exc_value, traceback): - self.unbind() - return False - - -@dataclass -class VertexArray: - def create(self): - self.id = GL.glGenVertexArrays(1) - - def bind(self): - GL.glBindVertexArray(self.id) - - def unbind(self): - GL.glBindVertexArray(0) - - def __enter__(self): - self.bind() - - def __exit__(self, exc_type, exc_value, traceback): - self.unbind() - return False - - RENDER_MESSAGE = """Rendering... Frame: {iFrame[0]} Frame rate: {iFrameRate[0]:.3f} fps @@ -240,17 +71,18 @@ def set_source(self, source): self.fragment_shader_source = FRAGMENT_SHADER_TEMPLATE.format(source=source) self.source_updated = True - def initialize_vbo(self, vertex_positions): - self.vbo = Buffer(GL.GL_ARRAY_BUFFER, GL.GL_STATIC_DRAW) + def initialize_vbo(self, data): + data_bytes = bytes(array.array("f", data)) + self.vbo = Buffer(BufferType.array, BufferUsage.static_draw) self.vbo.create() with self.vbo: - self.vbo.set_data(vertex_positions) + self.vbo.set_data(data_bytes) def initialize_program(self, vertex_shader_code, fragment_shader_code): self.program = Program( [ - Shader(GL.GL_VERTEX_SHADER, vertex_shader_code), - Shader(GL.GL_FRAGMENT_SHADER, fragment_shader_code), + Shader(ShaderType.vertex, vertex_shader_code), + Shader(ShaderType.fragment, fragment_shader_code), ] ) self.program.create() @@ -278,43 +110,40 @@ def update_program(self): def on_init(self, widget, **kwargs): try: self.source_updated = False - vertex_positions = bytes( - array.array( - "f", - [ - 1.0, - 1.0, - 0.0, - 1.0, # triangle 1 - 1.0, - -1.0, - 0.0, - 1.0, - -1.0, - -1.0, - 0.0, - 1.0, - 1.0, - 1.0, - 0.0, - 1.0, # triangle 2 - -1.0, - -1.0, - 0.0, - 1.0, - -1.0, - 1.0, - 0.0, - 1.0, - ], - ) - ) + vertex_positions = [ + 1.0, # triangle 1 + 1.0, + 0.0, + 1.0, + 1.0, + -1.0, + 0.0, + 1.0, + -1.0, + -1.0, + 0.0, + 1.0, + 1.0, # triangle 2 + 1.0, + 0.0, + 1.0, + -1.0, + -1.0, + 0.0, + 1.0, + -1.0, + 1.0, + 0.0, + 1.0, + ] self.initialize_vbo(vertex_positions) self.update_program() except OpenGLError as exc: self.message = exc.args[0] + raise except Exception: self.message = traceback.format_exc() + raise else: self.message = "Initialization successful" @@ -346,7 +175,7 @@ def on_render( try: n_vertices = 6 - if 0 <= pointer[0] < size[0] and 0 <= pointer[1] < size[1]: + if pointer and 0 <= pointer[0] < size[0] and 0 <= pointer[1] < size[1]: mouse_down = any(buttons) if mouse_down: self.pointer = pointer @@ -361,6 +190,9 @@ def on_render( # new mouse up self.drag_start = (-self.drag_start[0], self.drag_start[1]) self.mouse_down = False + elif pointer is None: + pointer = (0, 0) + self.drag_start = (0, 0) t = time.time() - self.start self.deltas.append(t - self.timestamp + 1e-6) diff --git a/examples/shadertoy/shadertoy/utils_android.py b/examples/shadertoy/shadertoy/utils_android.py new file mode 100644 index 0000000000..0fa82d577b --- /dev/null +++ b/examples/shadertoy/shadertoy/utils_android.py @@ -0,0 +1,211 @@ +from contextlib import contextmanager +from dataclasses import dataclass +from enum import IntEnum + +from android.opengl import GLES32 as GL +from java import jarray, jbyte, jint +from java.nio import ByteBuffer, ByteOrder + + +class ShaderType(IntEnum): + vertex = GL.GL_VERTEX_SHADER + fragment = GL.GL_FRAGMENT_SHADER + + +class BufferType(IntEnum): + array = GL.GL_ARRAY_BUFFER + + +class BufferUsage(IntEnum): + dynamic_draw = GL.GL_DYNAMIC_DRAW + static_draw = GL.GL_STATIC_DRAW + + +uniform_setters = { + (GL.GL_FLOAT, False): GL.glUniform1f, + (GL.GL_FLOAT_VEC2, False): GL.glUniform2f, + (GL.GL_FLOAT_VEC3, False): GL.glUniform3f, + (GL.GL_FLOAT_VEC4, False): GL.glUniform4f, + (GL.GL_FLOAT, True): GL.glUniform1fv, + (GL.GL_FLOAT_VEC2, True): GL.glUniform2fv, + (GL.GL_FLOAT_VEC3, True): GL.glUniform3fv, + (GL.GL_FLOAT_VEC4, True): GL.glUniform4fv, +} + + +class OpenGLError(RuntimeError): + pass + + +@dataclass +class Shader: + shader_type: int + source: str + + def create(self): + self.id = GL.glCreateShader(self.shader_type) + GL.glShaderSource(self.id, self.source) + + GL.glCompileShader(self.id) + + status_p = jarray(jint)([0]) + GL.glGetShaderiv(self.id, GL.GL_COMPILE_STATUS, status_p, 0) + status = status_p[0] + if not status: + info_log = GL.glGetShaderInfoLog(self.id) + raise OpenGLError( + f"Compilation failure for {self.shader_type} shader:\n{info_log}" + ) + + def delete(self): + if hasattr(self, "id"): + GL.glDeleteShader(self.id) + del self.id + + +@dataclass +class Program: + shaders: list[Shader] + + def create(self): + for shader in self.shaders: + shader.create() + + self.id = GL.glCreateProgram() + + for shader in self.shaders: + GL.glAttachShader(self.id, shader.id) + + GL.glLinkProgram(self.id) + + status_p = jarray(jint)([0]) + GL.glGetProgramiv(self.id, GL.GL_LINK_STATUS, status_p, 0) + status = status_p[0] + if not status: + strInfoLog = GL.glGetProgramInfoLog(self.id) + raise OpenGLError("Linker failure: \n" + strInfoLog) + + for shader in self.shaders: + GL.glDetachShader(self.id, shader.id) + + for shader in self.shaders: + shader.delete() + + def delete(self): + if hasattr(self, "id"): + GL.glDeleteProgram(self.id) + del self.id + + @contextmanager + def use(self): + GL.glUseProgram(self.id) + try: + yield + finally: + GL.glUseProgram(0) + + def active_attributes(self): + data = [ + GL.glGetActiveAttrib(self.id, i) + for i in range(GL.glGetProgramiv(self.id, GL.GL_ACTIVE_ATTRIBUTES)) + ] + return { + name.decode("utf-8"): (i, size, uniform_type) + for i, (name, size, uniform_type) in enumerate(data) + } + + def active_uniforms(self): + param = jarray(jint)([0]) + GL.glGetProgramiv(self.id, GL.GL_ACTIVE_UNIFORMS, param, 0) + n_uniforms = param[0] + + data = {} + for i in range(n_uniforms): + length = jarray(jint)([0]) + size = jarray(jint)([0]) + type = jarray(jint)([0]) + name = jarray(jbyte)(b"\0x00" * 255) + GL.glGetActiveUniform( + self.id, + i, + 255, + length, + 0, + size, + 0, + type, + 0, + name, + 0, + ) + data[bytes(name[: length[0]]).decode("utf-8")] = ( + i, + size[0], + type[0], + uniform_setters[type[0], size[0] > 1], + ) + return data + + def attribute(self, item): + return GL.glGetAttribLocation(self.id, item) + + def set_uniforms(self, uniforms): + for uniform, (loc, size, _, setter) in self.active_uniforms().items(): + if uniform in uniforms: + if size == 1: + setter(loc, *uniforms[uniform]) + else: + setter(loc, size, uniforms[uniform]) + + +@dataclass +class Buffer: + buffer_type: int + usage: int + + def create(self): + buffers = jarray(jint)([0]) + GL.glGenBuffers(1, buffers, 0) + self.id = buffers[0] + + def set_data(self, data): + nbytes = len(data) + buffer = ByteBuffer.allocateDirect(nbytes) + buffer.order(ByteOrder.nativeOrder()) + buffer.put(data) + buffer.position(0) + GL.glBufferData(self.buffer_type, nbytes, buffer, self.usage) + + def bind(self): + GL.glBindBuffer(self.buffer_type, self.id) + + def unbind(self): + GL.glBindBuffer(self.buffer_type, 0) + + def __enter__(self): + self.bind() + + def __exit__(self, exc_type, exc_value, traceback): + self.unbind() + return False + + +@dataclass +class VertexArray: + def create(self): + buffers = jarray(jint)([0]) + GL.glGenVertexArrays(1, buffers, 0) + self.id = buffers[0] + + def bind(self): + GL.glBindVertexArray(self.id) + + def unbind(self): + GL.glBindVertexArray(0) + + def __enter__(self): + self.bind() + + def __exit__(self, exc_type, exc_value, traceback): + self.unbind() + return False diff --git a/examples/shadertoy/shadertoy/utils_pyopengl.py b/examples/shadertoy/shadertoy/utils_pyopengl.py new file mode 100644 index 0000000000..47cf822b15 --- /dev/null +++ b/examples/shadertoy/shadertoy/utils_pyopengl.py @@ -0,0 +1,184 @@ +from contextlib import contextmanager +from dataclasses import dataclass +from enum import IntEnum + +import OpenGL + +# Get spurious errors with Qt backend +OpenGL.ERROR_CHECKING = False + +from OpenGL import GL # noqa: E402 + + +class ShaderType(IntEnum): + vertex = GL.GL_VERTEX_SHADER + fragment = GL.GL_FRAGMENT_SHADER + + +class BufferType(IntEnum): + array = GL.GL_ARRAY_BUFFER + + +class BufferUsage(IntEnum): + dynamic_draw = GL.GL_DYNAMIC_DRAW + static_draw = GL.GL_STATIC_DRAW + + +uniform_setters = { + (GL.GL_FLOAT, False): GL.glUniform1f, + (GL.GL_FLOAT_VEC2, False): GL.glUniform2f, + (GL.GL_FLOAT_VEC3, False): GL.glUniform3f, + (GL.GL_FLOAT_VEC4, False): GL.glUniform4f, + (GL.GL_FLOAT, True): GL.glUniform1fv, + (GL.GL_FLOAT_VEC2, True): GL.glUniform2fv, + (GL.GL_FLOAT_VEC3, True): GL.glUniform3fv, + (GL.GL_FLOAT_VEC4, True): GL.glUniform4fv, +} + + +class OpenGLError(RuntimeError): + pass + + +@dataclass +class Shader: + shader_type: int + source: str + + def create(self): + self.id = GL.glCreateShader(self.shader_type) + GL.glShaderSource(self.id, self.source) + + GL.glCompileShader(self.id) + + status = GL.glGetShaderiv(self.id, GL.GL_COMPILE_STATUS) + if status == GL.GL_FALSE: + info_log = GL.glGetShaderInfoLog(self.id).decode("utf-8") + raise OpenGLError( + f"Compilation failure for {self.shader_type} shader:\n{info_log}" + ) + + def delete(self): + if hasattr(self, "id"): + GL.glDeleteShader(self.id) + del self.id + + +@dataclass +class Program: + shaders: list[Shader] + + def create(self): + for shader in self.shaders: + shader.create() + + self.id = GL.glCreateProgram() + + for shader in self.shaders: + GL.glAttachShader(self.id, shader.id) + + GL.glLinkProgram(self.id) + + status = GL.glGetProgramiv(self.id, GL.GL_LINK_STATUS) + if status == GL.GL_FALSE: + strInfoLog = GL.glGetProgramInfoLog(self.id).decode("utf-8") + raise OpenGLError("Linker failure: \n" + strInfoLog) + + for shader in self.shaders: + GL.glDetachShader(self.id, shader.id) + + for shader in self.shaders: + shader.delete() + + def delete(self): + if hasattr(self, "id"): + GL.glDeleteProgram(self.id) + del self.id + + @contextmanager + def use(self): + GL.glUseProgram(self.id) + try: + yield + finally: + GL.glUseProgram(0) + + def active_attributes(self): + data = [ + GL.glGetActiveAttrib(self.id, i) + for i in range(GL.glGetProgramiv(self.id, GL.GL_ACTIVE_ATTRIBUTES)) + ] + return { + name.decode("utf-8"): (i, size, uniform_type) + for i, (name, size, uniform_type) in enumerate(data) + } + + def active_uniforms(self): + data = [ + GL.glGetActiveUniform(self.id, i) + for i in range(GL.glGetProgramiv(self.id, GL.GL_ACTIVE_UNIFORMS)) + ] + return { + name.decode("utf-8"): ( + i, + size, + uniform_type, + uniform_setters[uniform_type, size > 1], + ) + for i, (name, size, uniform_type) in enumerate(data) + } + + def attribute(self, item): + return GL.glGetAttribLocation(self.id, item) + + def set_uniforms(self, uniforms): + for uniform, (loc, size, _, setter) in self.active_uniforms().items(): + if uniform in uniforms: + if size == 1: + setter(loc, *uniforms[uniform]) + else: + setter(loc, size, uniforms[uniform]) + + +@dataclass +class Buffer: + buffer_type: int + usage: int + + def create(self): + self.id = GL.glGenBuffers(1) + + def set_data(self, data): + GL.glBufferData(self.buffer_type, data, self.usage) + + def bind(self): + GL.glBindBuffer(self.buffer_type, self.id) + + def unbind(self): + GL.glBindBuffer(self.buffer_type, 0) + + def __enter__(self): + self.bind() + + def __exit__(self, exc_type, exc_value, traceback): + self.unbind() + return False + + +@dataclass +class VertexArray: + def create(self): + self.id = GL.glGenVertexArrays(1) + + def bind(self): + GL.glBindVertexArray(self.id) + + def unbind(self): + GL.glBindVertexArray(0) + + def __enter__(self): + self.bind() + + def __exit__(self, exc_type, exc_value, traceback): + self.unbind() + return False From c006fa15a29f114253ce8b8bb83c14230e23a167 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 2 Apr 2026 13:51:55 +0100 Subject: [PATCH 10/26] Refactoring and clean-up of Shadertoy example. --- .../src/toga_android/widgets/openglview.py | 28 +- examples/shadertoy/shadertoy/app.py | 46 ++- .../shadertoy/shadertoy/renderer_android.py | 237 ----------- .../shadertoy/shadertoy/renderer_pyopengl.py | 234 ----------- .../shadertoy/shadertoy/shadertoy_renderer.py | 384 ++++++++++++++++++ examples/shadertoy/shadertoy/utils_android.py | 135 +++++- .../shadertoy/shadertoy/utils_pyopengl.py | 84 +++- 7 files changed, 625 insertions(+), 523 deletions(-) delete mode 100644 examples/shadertoy/shadertoy/renderer_android.py delete mode 100644 examples/shadertoy/shadertoy/renderer_pyopengl.py create mode 100644 examples/shadertoy/shadertoy/shadertoy_renderer.py diff --git a/android/src/toga_android/widgets/openglview.py b/android/src/toga_android/widgets/openglview.py index 7b03e2bf94..1d997c235e 100644 --- a/android/src/toga_android/widgets/openglview.py +++ b/android/src/toga_android/widgets/openglview.py @@ -25,12 +25,20 @@ def onSurfaceChanged(self, gl_api, width, height): self._redraw() def _redraw(self): - width = self.impl.native.getWidth() - height = self.impl.native.getHeight() + native = self.impl.native + width = native.getWidth() + height = native.getHeight() + # Pointer coordinates are in device-independent, top-left origin coords + # We need drawing pixel, bottom-left origin coordinates + scale = native.getContext().getResources().getDisplayMetrics().densityDpi / 160 + pointer = self.impl.pointer + if pointer: + x, y = pointer + pointer = (scale * x, height - (scale * y)) self.interface.renderer.on_render( self.interface, size=(width, height), - pointer=self.impl.pointer, + pointer=pointer, buttons=self.impl.buttons, ) @@ -44,18 +52,15 @@ def __init__(self, impl): def onTouch(self, canvas, event): with suppress_reference_error(): x, y = map(self.impl.scale_out, (event.getX(), event.getY())) + self.impl.pointer = (x, y) if (action := event.getAction()) == MotionEvent.ACTION_DOWN: - self.interface.pointer = (x, y) - self.interface.buttons = frozenset([TOUCH]) + self.impl.buttons = frozenset([TOUCH]) elif action == MotionEvent.ACTION_MOVE: - self.interface.pointer = (x, y) - self.interface.buttons = frozenset([TOUCH]) + self.impl.buttons = frozenset([TOUCH]) elif action == MotionEvent.ACTION_UP: - self.interface.on_release(x, y) - self.interface.buttons = frozenset() + self.impl.buttons = frozenset() else: # pragma: no cover - self.interface.pointer = None - self.interface.buttons = frozenset() + self.impl.buttons = frozenset() return True @@ -69,6 +74,7 @@ def create(self): self.native.setEGLContextClientVersion(3) self.native.setRenderer(self.renderer) + self.native.setOnTouchListener(self.listener) def redraw(self): self.native.invalidate() diff --git a/examples/shadertoy/shadertoy/app.py b/examples/shadertoy/shadertoy/app.py index 1aa5d90d1c..f97aa27c6e 100644 --- a/examples/shadertoy/shadertoy/app.py +++ b/examples/shadertoy/shadertoy/app.py @@ -1,12 +1,16 @@ import asyncio import toga -import toga.sources from toga.constants import COLUMN, MONOSPACE +from .shadertoy_renderer import ShadertoyRenderer + +# The default shadertoy mainImage fragment shader source to use. +# This should display slowly changing hues. SOURCE = """ // Replace this with shaders from shadertoy.com -// This is a simple example, so it doesn't handle channel, video or sound. +// This is a simple example, so it doesn't handle channels, video or +// sound, so not all shaders on shadertoy.com will work. // // Good examples to try: // - Protean Clouds: https://www.shadertoy.com/view/3l23Rh @@ -16,18 +20,33 @@ // - Seascape: https://www.shadertoy.com/view/Ms2SD1 // - Flame: https://www.shadertoy.com/view/MdX3zr // - Input - Mouse - https://www.shadertoy.com/view/Mss3zH +// +// Or write your own: replace mainImage with shader code that sets +// fragColor based on fragCoord. Uniforms provided by the renderer +// are: +// +// iResolution: vec3 - w, h, pixel shape +// iTime: float - seconds since start of shader +// iTimeDelta: float - seconds since last frame +// iFrame: float - count of frames since start of shader +// iFrameRate: float - frames per second +// iMouse: vec4 - (x, y, start_x, start_y) +// iDate: vec4 - (year, month, day, seconds) +// +// See the docs on Shadertoy, particularly for the values in iMouse +// where the signs of start_x, start_y convey button state info. +void mainImage(out vec4 fragColor, in vec2 fragCoord) { + // Set the color values for each pixel. -void mainImage( out vec4 fragColor, in vec2 fragCoord ) -{ // Normalized pixel coordinates (from 0 to 1) vec2 uv = fragCoord/iResolution.xy; // Time varying pixel color - vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4)); + vec3 col = 0.5 + 0.5 * cos(iTime + uv.xyx + vec3(0, 2, 4)); // Output to screen - fragColor = vec4(col,1.0); + fragColor = vec4(col, 1.0); } """ @@ -49,15 +68,7 @@ def startup(self): flex=1.0, ) self.message = toga.Label("Starting...", flex=1.0) - - if toga.backend in {"toga_cocoa", "toga_qt", "toga_gtk"}: - from .renderer_pyopengl import Renderer - elif toga.backend == "toga_android": - from .renderer_android import Renderer - else: - raise RuntimeError(f"Toga backend {toga.backend} is not supported.") - - self.renderer = Renderer(SOURCE) + self.renderer = ShadertoyRenderer(SOURCE) opengl_view = toga.OpenGLView(self.renderer, width=640, height=360) @@ -84,7 +95,7 @@ async def animate(): gap=4, ) else: - # Create the outer box with 2 rows + # Desktop layout outer_box = toga.Box( children=[ toga.Box( @@ -111,7 +122,8 @@ async def animate(): self.main_window.show() async def source_changed(self, widget, **kwargs): - self.renderer.set_source(widget.value) + """Update the mainImage source in the renderer with the widget value.""" + self.renderer.source = widget.value def main(): diff --git a/examples/shadertoy/shadertoy/renderer_android.py b/examples/shadertoy/shadertoy/renderer_android.py deleted file mode 100644 index 423182d7f1..0000000000 --- a/examples/shadertoy/shadertoy/renderer_android.py +++ /dev/null @@ -1,237 +0,0 @@ -import array -import datetime -import time -import traceback - -from .utils_android import ( - GL, - Buffer, - BufferType, - BufferUsage, - OpenGLError, - Program, - Shader, - ShaderType, - VertexArray, -) - -vertext_shader_source = """#version 300 es - -precision highp float; - -in vec4 position; - -void main() -{ - gl_Position = position; -} -""" -FRAGMENT_SHADER_TEMPLATE = """#version 300 es - -precision highp float; - -uniform vec3 iResolution; -uniform float iTime; -uniform float iTimeDelta; -uniform float iFrame; -uniform float iFrameRate; -uniform vec4 iMouse; -uniform vec4 iDate; - -out vec4 output_color; - -{source} - -void main() {{ - mainImage(output_color, gl_FragCoord.xy); -}} -""" - -RENDER_MESSAGE = """Rendering... -Frame: {iFrame[0]} -Frame rate: {iFrameRate[0]:.3f} fps - -Resolution: {iResolution} -Mouse: {iMouse} - -Time: {iTime[0]:.3f} s -Time delta: {iTimeDelta[0]:.3f} s -Date: {iDate[0]}-{iDate[1]}-{iDate[2]} {iDate[3]:.3f} - -{deltas} -""" - - -class Renderer: - source: str - message: str - - def __init__(self, source): - self.message = "Uninitialized." - self.set_source(source) - - def set_source(self, source): - self.fragment_shader_source = FRAGMENT_SHADER_TEMPLATE.format(source=source) - self.source_updated = True - - def initialize_vbo(self, data): - data_bytes = bytes(array.array("f", data)) - self.vbo = Buffer(BufferType.array, BufferUsage.static_draw) - self.vbo.create() - with self.vbo: - self.vbo.set_data(data_bytes) - - def initialize_program(self, vertex_shader_code, fragment_shader_code): - self.program = Program( - [ - Shader(ShaderType.vertex, vertex_shader_code), - Shader(ShaderType.fragment, fragment_shader_code), - ] - ) - self.program.create() - - def initialize_vao(self, attribute): - attribute_loc = self.program.attribute(attribute) - self.vao = VertexArray() - self.vao.create() - with self.vao: - with self.vbo: - GL.glEnableVertexAttribArray(attribute_loc) - GL.glVertexAttribPointer(0, 4, GL.GL_FLOAT, False, 0, 0) - - def update_program(self): - self.initialize_program(vertext_shader_source, self.fragment_shader_source) - self.initialize_vao("position") - self.start = time.time() - self.timestamp = 0 - self.frame = 0 - self.deltas = [] - self.pointer = (0, 0) - self.mouse_down = False - self.drag_start = (0, 0) - - def on_init(self, widget, **kwargs): - try: - self.source_updated = False - vertex_positions = [ - 1.0, # triangle 1 - 1.0, - 0.0, - 1.0, - 1.0, - -1.0, - 0.0, - 1.0, - -1.0, - -1.0, - 0.0, - 1.0, - 1.0, # triangle 2 - 1.0, - 0.0, - 1.0, - -1.0, - -1.0, - 0.0, - 1.0, - -1.0, - 1.0, - 0.0, - 1.0, - ] - self.initialize_vbo(vertex_positions) - self.update_program() - except OpenGLError as exc: - self.message = exc.args[0] - raise - except Exception: - self.message = traceback.format_exc() - raise - else: - self.message = "Initialization successful" - - def on_render( - self, - widget, - size, - pointer=(-1, -1), - buttons=frozenset(), - **kwargs, - ): - GL.glClearColor(0.0, 0.0, 1.0, 1.0) - GL.glClear(GL.GL_COLOR_BUFFER_BIT) - - if self.source_updated: - try: - self.program.delete() - self.update_program() - except OpenGLError as exc: - self.message = exc.args[0] - return - except Exception: - self.message = traceback.format_exc() - return - else: - self.source_updated = False - self.message = "Update successful" - return - - try: - n_vertices = 6 - if pointer and 0 <= pointer[0] < size[0] and 0 <= pointer[1] < size[1]: - mouse_down = any(buttons) - if mouse_down: - self.pointer = pointer - if not self.mouse_down: - # new mouse down - self.drag_start = pointer - self.mouse_down = True - elif self.drag_start[1] > 0: - # mouse was newly down, still down - self.drag_start = (self.drag_start[0], -self.drag_start[1]) - elif self.mouse_down: - # new mouse up - self.drag_start = (-self.drag_start[0], self.drag_start[1]) - self.mouse_down = False - elif pointer is None: - pointer = (0, 0) - self.drag_start = (0, 0) - - t = time.time() - self.start - self.deltas.append(t - self.timestamp + 1e-6) - frame_rate = len(self.deltas) / sum(self.deltas) - self.timestamp = t - dt = datetime.datetime.now() - - uniforms = { - "iResolution": (*size, 1.0), - "iMouse": (*self.pointer, *self.drag_start), - "iTime": (self.timestamp,), - "iTimeDelta": (self.deltas[-1],), - "iFrame": (self.frame,), - "iFrameRate": (frame_rate,), - "iDate": ( - dt.year, - dt.month, - dt.day, - dt.hour * 3600 - + dt.minute * 60 - + dt.second - + (dt.microsecond / 1e6), - ), - } - - with self.program.use(): - with self.vao: - self.program.set_uniforms(uniforms) - - GL.glDrawArrays(GL.GL_TRIANGLES, 0, n_vertices) - - self.frame += 1 - if len(self.deltas) > 100: - del self.deltas[0] - except Exception: - self.message = traceback.format_exc() - raise - else: - self.message = RENDER_MESSAGE.format(**uniforms, deltas=len(self.deltas)) diff --git a/examples/shadertoy/shadertoy/renderer_pyopengl.py b/examples/shadertoy/shadertoy/renderer_pyopengl.py deleted file mode 100644 index 42ab56bb3a..0000000000 --- a/examples/shadertoy/shadertoy/renderer_pyopengl.py +++ /dev/null @@ -1,234 +0,0 @@ -import array -import datetime -import time -import traceback - -from .utils_pyopengl import ( - GL, - Buffer, - BufferType, - BufferUsage, - OpenGLError, - Program, - Shader, - ShaderType, - VertexArray, -) - -vertext_shader_source = """ -#version 330 core - -layout(location = 0) in vec4 position; - -void main() -{ - gl_Position = position; -} -""" -FRAGMENT_SHADER_TEMPLATE = """#version 330 core - -uniform vec3 iResolution; -uniform float iTime; -uniform float iTimeDelta; -uniform float iFrame; -uniform float iFrameRate; -uniform vec4 iMouse; -uniform vec4 iDate; - -out vec4 output_color; - -{source} - -void main() {{ - mainImage(output_color, gl_FragCoord.xy); -}} -""" - -RENDER_MESSAGE = """Rendering... -Frame: {iFrame[0]} -Frame rate: {iFrameRate[0]:.3f} fps - -Resolution: {iResolution} -Mouse: {iMouse} - -Time: {iTime[0]:.3f} s -Time delta: {iTimeDelta[0]:.3f} s -Date: {iDate[0]}-{iDate[1]}-{iDate[2]} {iDate[3]:.3f} - -{deltas} -""" - - -class Renderer: - source: str - message: str - - def __init__(self, source): - self.message = "Uninitialized." - self.set_source(source) - - def set_source(self, source): - self.fragment_shader_source = FRAGMENT_SHADER_TEMPLATE.format(source=source) - self.source_updated = True - - def initialize_vbo(self, data): - data_bytes = bytes(array.array("f", data)) - self.vbo = Buffer(BufferType.array, BufferUsage.static_draw) - self.vbo.create() - with self.vbo: - self.vbo.set_data(data_bytes) - - def initialize_program(self, vertex_shader_code, fragment_shader_code): - self.program = Program( - [ - Shader(ShaderType.vertex, vertex_shader_code), - Shader(ShaderType.fragment, fragment_shader_code), - ] - ) - self.program.create() - - def initialize_vao(self, attribute): - attribute_loc = self.program.attribute(attribute) - self.vao = VertexArray() - self.vao.create() - with self.vao: - with self.vbo: - GL.glEnableVertexAttribArray(attribute_loc) - GL.glVertexAttribPointer(0, 4, GL.GL_FLOAT, GL.GL_FALSE, 0, None) - - def update_program(self): - self.initialize_program(vertext_shader_source, self.fragment_shader_source) - self.initialize_vao("position") - self.start = time.time() - self.timestamp = 0 - self.frame = 0 - self.deltas = [] - self.pointer = (0, 0) - self.mouse_down = False - self.drag_start = (0, 0) - - def on_init(self, widget, **kwargs): - try: - self.source_updated = False - vertex_positions = [ - 1.0, # triangle 1 - 1.0, - 0.0, - 1.0, - 1.0, - -1.0, - 0.0, - 1.0, - -1.0, - -1.0, - 0.0, - 1.0, - 1.0, # triangle 2 - 1.0, - 0.0, - 1.0, - -1.0, - -1.0, - 0.0, - 1.0, - -1.0, - 1.0, - 0.0, - 1.0, - ] - self.initialize_vbo(vertex_positions) - self.update_program() - except OpenGLError as exc: - self.message = exc.args[0] - raise - except Exception: - self.message = traceback.format_exc() - raise - else: - self.message = "Initialization successful" - - def on_render( - self, - widget, - size, - pointer=(-1, -1), - buttons=frozenset(), - **kwargs, - ): - GL.glClearColor(0.0, 0.0, 1.0, 1.0) - GL.glClear(GL.GL_COLOR_BUFFER_BIT) - - if self.source_updated: - try: - self.program.delete() - self.update_program() - except OpenGLError as exc: - self.message = exc.args[0] - return - except Exception: - self.message = traceback.format_exc() - return - else: - self.source_updated = False - self.message = "Update successful" - return - - try: - n_vertices = 6 - if pointer and 0 <= pointer[0] < size[0] and 0 <= pointer[1] < size[1]: - mouse_down = any(buttons) - if mouse_down: - self.pointer = pointer - if not self.mouse_down: - # new mouse down - self.drag_start = pointer - self.mouse_down = True - elif self.drag_start[1] > 0: - # mouse was newly down, still down - self.drag_start = (self.drag_start[0], -self.drag_start[1]) - elif self.mouse_down: - # new mouse up - self.drag_start = (-self.drag_start[0], self.drag_start[1]) - self.mouse_down = False - elif pointer is None: - pointer = (0, 0) - self.drag_start = (0, 0) - - t = time.time() - self.start - self.deltas.append(t - self.timestamp + 1e-6) - frame_rate = len(self.deltas) / sum(self.deltas) - self.timestamp = t - dt = datetime.datetime.now() - - uniforms = { - "iResolution": (*size, 1.0), - "iMouse": (*self.pointer, *self.drag_start), - "iTime": (self.timestamp,), - "iTimeDelta": (self.deltas[-1],), - "iFrame": (self.frame,), - "iFrameRate": (frame_rate,), - "iDate": ( - dt.year, - dt.month, - dt.day, - dt.hour * 3600 - + dt.minute * 60 - + dt.second - + (dt.microsecond / 1e6), - ), - } - - with self.program.use(): - with self.vao: - self.program.set_uniforms(uniforms) - - GL.glDrawArrays(GL.GL_TRIANGLES, 0, n_vertices) - - self.frame += 1 - if len(self.deltas) > 100: - del self.deltas[0] - except Exception: - self.message = traceback.format_exc() - raise - else: - self.message = RENDER_MESSAGE.format(**uniforms, deltas=len(self.deltas)) diff --git a/examples/shadertoy/shadertoy/shadertoy_renderer.py b/examples/shadertoy/shadertoy/shadertoy_renderer.py new file mode 100644 index 0000000000..7bf30e70c9 --- /dev/null +++ b/examples/shadertoy/shadertoy/shadertoy_renderer.py @@ -0,0 +1,384 @@ +""" +Generic Shadertoy Renderer +========================== + +This renderer allows the use of basic Shadertoy-style fragment +rendering. It provides a single image shader, pointer state and time +data, but doesn't provide additional shaders, textures, channels, +video or sound support. + +Differences between OpenGL APIs are handled in the appropriate +utils object: which one is selected depends on the toga backend. +The utils provide abstractions for Shaders, Programs, Buffers and +Vertex Array Objects which make the code in the renderer cleaner +and hide the OpenGL code somewhat. +""" + +import array +import datetime +import time +import traceback + +import toga + +# Import appropriate utility objects for OpenGL implementation +if toga.backend in {"toga_cocoa", "toga_gtk", "toga_qt"}: + from .utils_pyopengl import ( + GL, + VERSION_HEADER, + Buffer, + BufferType, + BufferUsage, + OpenGLError, + Program, + Shader, + ShaderType, + VertexArrayObject, + ) +elif toga.backend == "toga_android": + from .utils_android import ( + GL, + VERSION_HEADER, + Buffer, + BufferType, + BufferUsage, + OpenGLError, + Program, + Shader, + ShaderType, + VertexArrayObject, + ) +else: + raise RuntimeError("No renderer for backend.") + + +#: Vertex shader: displays triangles in 2D. +VERTEX_SHADER_SOURCE = f"""{VERSION_HEADER} + +precision highp float; + +in vec4 position; + +void main() {{ + gl_Position = position; +}} +""" + +#: Fragment shader: calls mainImage to set pixel colors. +FRAGMENT_SHADER_TEMPLATE = """{header} + +precision highp float; + +uniform vec3 iResolution; +uniform float iTime; +uniform float iTimeDelta; +uniform float iFrame; +uniform float iFrameRate; +uniform vec4 iMouse; +uniform vec4 iDate; + +out vec4 output_color; + +{source} + +void main() {{ + mainImage(output_color, gl_FragCoord.xy); +}} +""" + +#: Message to display in side-panel giving info about +#: values of uniforms. +UNIFORM_VALUES = """Frame: {iFrame[0]} +Frame rate: {iFrameRate[0]:.3f} fps + +Resolution: {iResolution} +Mouse: {iMouse} + +Time: {iTime[0]:.3f} s +Time delta: {iTimeDelta[0]:.3f} s +Date: {iDate[0]}-{iDate[1]}-{iDate[2]} {iDate[3]:.3f} +""" + +#: Vertex coordinates for a square filling the viewport. +# fmt: off +VERTEX_POSITIONS = [ + 1.0, 1.0, # triangle 1: bottom right + 1.0, -1.0, + -1.0, -1.0, + 1.0, 1.0, # triangle 2: top left + -1.0, -1.0, + -1.0, 1.0, +] +# fmt: on + +#: Number of vertices +N_VERTICES = len(VERTEX_POSITIONS) // 2 + + +class ShadertoyRenderer: + """Renderer for Shadertoy fragment renderers. + + This renderer expects to be provided with GLSL code that implements the + mainImage function in the fragment shader. This code is provided via the + `source` property. The message attribute is set to information about the + state of the renderer, either: + - the data provided to the uniforms; or + - status/error messages + """ + + #: A message containing information about the renderer's state. + message: str + + #: Whether or not the source has changed and needs to be updated + # on the next render. + source_updated: bool = False + + #: The start time of the shader run in seconds since the epoch. + start: float + + #: The time stamp of the last frame. + timestamp: float + + #: The frame count. + frame: int + + #: A list of the last 100 time deltas. + deltas: list[float] + + #: The position of the pointer, or None if not available. + pointer: tuple[int, int] | None + + #: Whether the mouse button (or user touch) is down or up. + mouse_down: bool + + #: The starting location of most recent drag operation. + drag_start: tuple[int, int] | None + + #: The source for the mainImage function. + _source: str + + def __init__(self, source): + self.message = "Uninitialized." + self.source = source + + @property + def source(self) -> str: + return self._source + + @source.setter + def source(self, source): + """Set a new value for the source. + + This is merged into the fragment shader template and then the + source is marked for updating on the next render. + """ + self._source = source + self.fragment_shader_source = FRAGMENT_SHADER_TEMPLATE.format( + source=source, + header=VERSION_HEADER, + ) + self.source_updated = True + + def on_init(self, widget, **kwargs): + """Initialize the OpenGL state.""" + self.message = "Initializing...\n\n" + try: + self.source_updated = False + self._initialize_vbo(VERTEX_POSITIONS) + self._update_program() + except OpenGLError as exc: + # Add OpenGL error to messages + # This is most likely an error in the source, so this is important + # user feedback. + self.message += exc.args[0] + raise + except Exception: + # Add other errors to messages + # This is most likely caused by a bug in the renderer. + self.message += traceback.format_exc() + raise + else: + # Indicate success. + self.message += "Initialization successful" + + def on_render( + self, + widget, + size, + pointer=(-1, -1), + buttons=frozenset(), + **kwargs, + ): + """Render a frame using OpenGL.""" + self.message = "Rendering...\n\n" + GL.glClearColor(0.0, 0.0, 1.0, 1.0) + GL.glClear(GL.GL_COLOR_BUFFER_BIT) + + # Handle a pending source update. + if self.source_updated: + try: + self.program.delete() + self._update_program() + except OpenGLError as exc: + # Add OpenGL error to messages + # This is most likely an error in the source, so this is important + # user feedback. + self.message = exc.args[0] + raise + except Exception: + # Add other errors to messages + # This is most likely caused by a bug in the renderer. + self.message = traceback.format_exc() + raise + else: + # Indicate success. + self.source_updated = False + self.message += "Update successful\n\n" + + try: + # Update the state for the uniforms + self._set_pointer(size, pointer, buttons) + t = time.time() - self.start + self.deltas.append(t - self.timestamp + 1e-6) + if sum(self.deltas) == 0: + frame_rate = 0.0 + else: + frame_rate = len(self.deltas) / sum(self.deltas) + self.timestamp = t + dt = datetime.datetime.now() + + # Set the uniform values into a dictionary to be applied later + uniforms = { + "iResolution": (*size, 1.0), + "iMouse": (*self.pointer, *self.drag_start), + "iTime": (self.timestamp,), + "iTimeDelta": (self.deltas[-1],), + "iFrame": (self.frame,), + "iFrameRate": (frame_rate,), + "iDate": ( + dt.year, + dt.month, + dt.day, + dt.hour * 3600 + + dt.minute * 60 + + dt.second + + (dt.microsecond / 1e6), + ), + } + + # draw! + with self.program.use(): + self.program.set_uniforms(uniforms) + with self.vao: + GL.glDrawArrays(GL.GL_TRIANGLES, 0, N_VERTICES) + + # Update frame and time delta information + self.frame += 1 + if len(self.deltas) > 100: + del self.deltas[0] + except Exception: + # Display error messages + self.message += traceback.format_exc() + raise + else: + # Display uniform values in message. + self.message += UNIFORM_VALUES.format(**uniforms) + + def _initialize_vbo(self, data): + """Set the vertex buffer data. + + This is never updated, so we call once during init. + """ + data_bytes = bytes(array.array("f", data)) + self.vbo = Buffer(BufferType.array, BufferUsage.static_draw) + self.vbo.create(data_bytes) + + def _initialize_program(self, vertex_shader_code, fragment_shader_code): + """Initialize the shader program. + + This: + - creates the shaders and the program + - compiles the shaders + - links the program + - checks for errors + - deletes the compiled shaders + + This needs to be called every time the source changes. + """ + self.program = Program( + [ + Shader(ShaderType.vertex, vertex_shader_code), + Shader(ShaderType.fragment, fragment_shader_code), + ] + ) + self.program.create() + + def _initialize_vao(self, attribute): + """Initialize the vertex attribute object. + + This creates a vertex attribute object for the specified attribute, + and then binds the vertex buffer object to that attribute. + + This needs to be called every time the program changes. + """ + self.vao = VertexArrayObject() + self.vao.create() + with self.vao: + self.program.bind_attribute_buffer(attribute, self.vbo, size=2) + + def _update_program(self): + """Update the program and related data when the source changes. + + This: + - initializes the program + - initializes the vertex attribute object + - resets the state used for the uniforms + """ + self._initialize_program(VERTEX_SHADER_SOURCE, self.fragment_shader_source) + self._initialize_vao("position") + + # Reset the state variables for the uniforms. + self.start = time.time() + self.timestamp = 0 + self.frame = 0 + self.deltas = [] + self.pointer = (0, 0) + self.mouse_down = False + self.drag_start = (0, 0) + + def _set_pointer(self, size, pointer, buttons): + """Set the values for the pointer data. + + Shadertoy follows the following conventions: + - if no buttons are pressed (or a touch/drag is not underway) the position + does not update + - if the button/touch has *just* started, drag start coordinates are positive + - if the drag is continuing, the drag start y coordinate is negative + - if the drag has ended, the drag start x coordinate is also set negative + + We also have to handle the case where no pointer information is available. + This happens most often in mobile before the user has interacted with the + OpenGLView widget. In this case we set everything to 0. + """ + if ( + pointer is not None + and 0 <= pointer[0] < size[0] + and 0 <= pointer[1] < size[1] + ): + if buttons: + self.pointer = pointer + if not self.mouse_down: + # new mouse down + self.drag_start = pointer + self.mouse_down = True + elif self.drag_start[1] > 0: + # mouse was newly down, still down + self.drag_start = (self.drag_start[0], -self.drag_start[1]) + elif self.mouse_down: + # new mouse up + self.drag_start = (-self.drag_start[0], self.drag_start[1]) + self.mouse_down = False + elif pointer is None: + self.pointer = (0, 0) + self.drag_start = (0, 0) + self.mouse_down = False diff --git a/examples/shadertoy/shadertoy/utils_android.py b/examples/shadertoy/shadertoy/utils_android.py index 0fa82d577b..83b1158c77 100644 --- a/examples/shadertoy/shadertoy/utils_android.py +++ b/examples/shadertoy/shadertoy/utils_android.py @@ -1,3 +1,5 @@ +"""Utility objects and methods for PyOpenGL""" + from contextlib import contextmanager from dataclasses import dataclass from enum import IntEnum @@ -6,21 +8,32 @@ from java import jarray, jbyte, jint from java.nio import ByteBuffer, ByteOrder +#: Shader version header: we want OpenGL ES GLSL 3 +VERSION_HEADER = """#version 300 es""" + class ShaderType(IntEnum): + """Enum for the different OpenGL shader types.""" + vertex = GL.GL_VERTEX_SHADER fragment = GL.GL_FRAGMENT_SHADER class BufferType(IntEnum): + """Enum for the different OpenGL buffer types.""" + array = GL.GL_ARRAY_BUFFER class BufferUsage(IntEnum): + """Enum for the different OpenGL buffer usage hints.""" + dynamic_draw = GL.GL_DYNAMIC_DRAW static_draw = GL.GL_STATIC_DRAW +#: Map from OpenGL data types to corresponding setter functions +#: This is far from complete. uniform_setters = { (GL.GL_FLOAT, False): GL.glUniform1f, (GL.GL_FLOAT_VEC2, False): GL.glUniform2f, @@ -34,23 +47,60 @@ class BufferUsage(IntEnum): class OpenGLError(RuntimeError): + """OpenGL-specific runtime errors.""" + pass +def v_func(v_size=1, jtype=jint): + """Decorator for functions expecting a pointer to an output vector. + + A number of OpenGL functions have a suffix of the form {l}{t}v, + which expect an argument of an array of length l of type t (and + an offset into it) which is used to return values by setting them + into the array. + + This wrapper wraps these functions to create a java array of the + correct type, call the function, and then unpack the values from + the array. + """ + + def wrapper(func): + def call_v_function(*args, v_size=1, jtype=jint): + data = jarray(jtype)([0] * v_size) + func(*args, data, 0) + if v_size == 1: + return data[0] + else: + return tuple(data) + + return call_v_function + + return wrapper + + +glGetShader = v_func()(GL.glGetShaderiv) +glGetProgram = v_func()(GL.glGetProgramiv) +glGenBuffer = v_func()(GL.glGenBuffers) +glGenVertexArray = v_func()(GL.glGenVertexArrays) + + @dataclass class Shader: + """A dataclass that encapsulates an OpenGL shader.""" + shader_type: int source: str def create(self): + """Create and compile the shader.""" self.id = GL.glCreateShader(self.shader_type) GL.glShaderSource(self.id, self.source) GL.glCompileShader(self.id) - status_p = jarray(jint)([0]) - GL.glGetShaderiv(self.id, GL.GL_COMPILE_STATUS, status_p, 0) - status = status_p[0] + # Handle errors + status = glGetShader(self.id, GL.GL_COMPILE_STATUS) if not status: info_log = GL.glGetShaderInfoLog(self.id) raise OpenGLError( @@ -58,6 +108,7 @@ def create(self): ) def delete(self): + """Delete the shader.""" if hasattr(self, "id"): GL.glDeleteShader(self.id) del self.id @@ -65,9 +116,12 @@ def delete(self): @dataclass class Program: + """A dataclass that encapsulates an OpenGL program.""" + shaders: list[Shader] def create(self): + """Create the program and its shaders and link them.""" for shader in self.shaders: shader.create() @@ -78,26 +132,27 @@ def create(self): GL.glLinkProgram(self.id) - status_p = jarray(jint)([0]) - GL.glGetProgramiv(self.id, GL.GL_LINK_STATUS, status_p, 0) - status = status_p[0] + # Handle errors + status = glGetProgram(self.id, GL.GL_LINK_STATUS) if not status: strInfoLog = GL.glGetProgramInfoLog(self.id) raise OpenGLError("Linker failure: \n" + strInfoLog) + # clean up shaders for shader in self.shaders: GL.glDetachShader(self.id, shader.id) - for shader in self.shaders: shader.delete() def delete(self): + """Delete the program.""" if hasattr(self, "id"): GL.glDeleteProgram(self.id) del self.id @contextmanager def use(self): + """Context manager that sets the program for use and resets it when done.""" GL.glUseProgram(self.id) try: yield @@ -105,6 +160,11 @@ def use(self): GL.glUseProgram(0) def active_attributes(self): + """Get information about the program's active attributes. + + Returns a dictionary of attribute names mapping to the location, size and + type of the attribute. + """ data = [ GL.glGetActiveAttrib(self.id, i) for i in range(GL.glGetProgramiv(self.id, GL.GL_ACTIVE_ATTRIBUTES)) @@ -115,12 +175,16 @@ def active_attributes(self): } def active_uniforms(self): - param = jarray(jint)([0]) - GL.glGetProgramiv(self.id, GL.GL_ACTIVE_UNIFORMS, param, 0) - n_uniforms = param[0] + """Get information about the program's active uniforms. + + Returns a dictionary of uniform names mapping to the location, size, + type and setter function of the attribute. + """ + n_uniforms = glGetProgram(self.id, GL.GL_ACTIVE_UNIFORMS) data = {} for i in range(n_uniforms): + # Need to get values out via Java arrays length = jarray(jint)([0]) size = jarray(jint)([0]) type = jarray(jint)([0]) @@ -147,9 +211,11 @@ def active_uniforms(self): return data def attribute(self, item): + """Return the location of an active attribute.""" return GL.glGetAttribLocation(self.id, item) def set_uniforms(self, uniforms): + """Set the values of active uniforms from a dictionary of values.""" for uniform, (loc, size, _, setter) in self.active_uniforms().items(): if uniform in uniforms: if size == 1: @@ -157,55 +223,86 @@ def set_uniforms(self, uniforms): else: setter(loc, size, uniforms[uniform]) + def bind_attribute_buffer(self, attribute, vbo, *, size=4): + """Bind an active attribute to a vertex buffer object.""" + loc = self.attribute(attribute) + with vbo: + GL.glEnableVertexAttribArray(loc) + GL.glVertexAttribPointer(loc, size, GL.GL_FLOAT, False, 0, 0) + @dataclass class Buffer: + """A dataclass that encapsulates an OpenGL buffer. + + This can be used as a context manager to automatically bind and unbind + the buffer for use. + """ + buffer_type: int usage: int - def create(self): - buffers = jarray(jint)([0]) - GL.glGenBuffers(1, buffers, 0) - self.id = buffers[0] + def create(self, data): + """Generate a new buffer and set the data into it.""" + self.id = glGenBuffer(1) + self.set_data(data) def set_data(self, data): + """Set data into a buffer. + + This automatically binds the buffer. + """ nbytes = len(data) buffer = ByteBuffer.allocateDirect(nbytes) buffer.order(ByteOrder.nativeOrder()) buffer.put(data) buffer.position(0) - GL.glBufferData(self.buffer_type, nbytes, buffer, self.usage) + with self: + GL.glBufferData(self.buffer_type, nbytes, buffer, self.usage) def bind(self): + """Bind the buffer so that it is the current buffer.""" GL.glBindBuffer(self.buffer_type, self.id) def unbind(self): + """Unbind the buffer so that it is no longer the current buffer.""" GL.glBindBuffer(self.buffer_type, 0) def __enter__(self): + """Enter the context manager, binding the buffer.""" self.bind() def __exit__(self, exc_type, exc_value, traceback): + """Exit the context manager, unbinding the buffer.""" self.unbind() return False @dataclass -class VertexArray: +class VertexArrayObject: + """A dataclass that encapsulates an OpenGL vertex array object. + + This can be used as a context manager to automatically bind and unbind + the vertex array object for use. + """ + def create(self): - buffers = jarray(jint)([0]) - GL.glGenVertexArrays(1, buffers, 0) - self.id = buffers[0] + """Generate a new vertex array object.""" + self.id = glGenVertexArray(1) def bind(self): + """Make the vertex array object the current vertex array object.""" GL.glBindVertexArray(self.id) def unbind(self): + """Make the vertex array object no longer the current vertex array object.""" GL.glBindVertexArray(0) def __enter__(self): + """Enter the context manager, binding the vertex array object.""" self.bind() def __exit__(self, exc_type, exc_value, traceback): + """Exit the context manager, unbinding the vertex array object.""" self.unbind() return False diff --git a/examples/shadertoy/shadertoy/utils_pyopengl.py b/examples/shadertoy/shadertoy/utils_pyopengl.py index 47cf822b15..2aae0e4f9c 100644 --- a/examples/shadertoy/shadertoy/utils_pyopengl.py +++ b/examples/shadertoy/shadertoy/utils_pyopengl.py @@ -1,3 +1,5 @@ +"""Utility objects and methods for PyOpenGL""" + from contextlib import contextmanager from dataclasses import dataclass from enum import IntEnum @@ -9,21 +11,32 @@ from OpenGL import GL # noqa: E402 +#: Shader version header: this should work on most modern desktops +VERSION_HEADER = "#version 330 core" + class ShaderType(IntEnum): + """Enum for the different OpenGL shader types.""" + vertex = GL.GL_VERTEX_SHADER fragment = GL.GL_FRAGMENT_SHADER class BufferType(IntEnum): + """Enum for the different OpenGL buffer types.""" + array = GL.GL_ARRAY_BUFFER class BufferUsage(IntEnum): + """Enum for the different OpenGL buffer usage hints.""" + dynamic_draw = GL.GL_DYNAMIC_DRAW static_draw = GL.GL_STATIC_DRAW +#: Map from OpenGL data types to corresponding setter functions +#: This is far from complete. uniform_setters = { (GL.GL_FLOAT, False): GL.glUniform1f, (GL.GL_FLOAT_VEC2, False): GL.glUniform2f, @@ -37,20 +50,26 @@ class BufferUsage(IntEnum): class OpenGLError(RuntimeError): + """OpenGL-specific runtime errors.""" + pass @dataclass class Shader: + """A dataclass that encapsulates an OpenGL shader.""" + shader_type: int source: str def create(self): + """Create and compile the shader.""" self.id = GL.glCreateShader(self.shader_type) GL.glShaderSource(self.id, self.source) GL.glCompileShader(self.id) + # Handle errors status = GL.glGetShaderiv(self.id, GL.GL_COMPILE_STATUS) if status == GL.GL_FALSE: info_log = GL.glGetShaderInfoLog(self.id).decode("utf-8") @@ -59,6 +78,7 @@ def create(self): ) def delete(self): + """Delete the shader.""" if hasattr(self, "id"): GL.glDeleteShader(self.id) del self.id @@ -66,9 +86,12 @@ def delete(self): @dataclass class Program: + """A dataclass that encapsulates an OpenGL program.""" + shaders: list[Shader] def create(self): + """Create the program and its shaders and link them.""" for shader in self.shaders: shader.create() @@ -79,24 +102,27 @@ def create(self): GL.glLinkProgram(self.id) + # Handle errors status = GL.glGetProgramiv(self.id, GL.GL_LINK_STATUS) if status == GL.GL_FALSE: strInfoLog = GL.glGetProgramInfoLog(self.id).decode("utf-8") raise OpenGLError("Linker failure: \n" + strInfoLog) + # clean up shaders for shader in self.shaders: GL.glDetachShader(self.id, shader.id) - for shader in self.shaders: shader.delete() def delete(self): + """Delete the program.""" if hasattr(self, "id"): GL.glDeleteProgram(self.id) del self.id @contextmanager def use(self): + """Context manager that sets the program for use and resets it when done.""" GL.glUseProgram(self.id) try: yield @@ -104,6 +130,11 @@ def use(self): GL.glUseProgram(0) def active_attributes(self): + """Get information about the program's active attributes. + + Returns a dictionary of attribute names mapping to the location, size and + type of the attribute. + """ data = [ GL.glGetActiveAttrib(self.id, i) for i in range(GL.glGetProgramiv(self.id, GL.GL_ACTIVE_ATTRIBUTES)) @@ -114,11 +145,16 @@ def active_attributes(self): } def active_uniforms(self): + """Get information about the program's active uniforms. + + Returns a dictionary of uniform names mapping to the location, size, + type and setter function of the attribute. + """ data = [ GL.glGetActiveUniform(self.id, i) for i in range(GL.glGetProgramiv(self.id, GL.GL_ACTIVE_UNIFORMS)) ] - return { + uniforms = { name.decode("utf-8"): ( i, size, @@ -127,11 +163,14 @@ def active_uniforms(self): ) for i, (name, size, uniform_type) in enumerate(data) } + return uniforms def attribute(self, item): + """Return the location of an active attribute.""" return GL.glGetAttribLocation(self.id, item) def set_uniforms(self, uniforms): + """Set the values of active uniforms from a dictionary of values.""" for uniform, (loc, size, _, setter) in self.active_uniforms().items(): if uniform in uniforms: if size == 1: @@ -139,46 +178,81 @@ def set_uniforms(self, uniforms): else: setter(loc, size, uniforms[uniform]) + def bind_attribute_buffer(self, attribute, vbo, *, size=4): + """Bind an active attribute to a vertex buffer object.""" + loc = self.attribute(attribute) + with vbo: + GL.glEnableVertexAttribArray(loc) + GL.glVertexAttribPointer(loc, size, GL.GL_FLOAT, GL.GL_FALSE, 0, None) + @dataclass class Buffer: + """A dataclass that encapsulates an OpenGL buffer. + + This can be used as a context manager to automatically bind and unbind + the buffer for use. + """ + buffer_type: int usage: int - def create(self): + def create(self, data): + """Generate a new buffer and set the data into it.""" self.id = GL.glGenBuffers(1) + self.set_data(data) def set_data(self, data): - GL.glBufferData(self.buffer_type, data, self.usage) + """Set data into a buffer. + + This automatically binds the buffer.. + """ + with self: + GL.glBufferData(self.buffer_type, data, self.usage) def bind(self): + """Bind the buffer so that it is the current buffer.""" GL.glBindBuffer(self.buffer_type, self.id) def unbind(self): + """Unbind the buffer so that it is no longer the current buffer.""" GL.glBindBuffer(self.buffer_type, 0) def __enter__(self): + """Enter the context manager, binding the buffer.""" self.bind() def __exit__(self, exc_type, exc_value, traceback): + """Exit the context manager, unbinding the buffer.""" self.unbind() return False @dataclass -class VertexArray: +class VertexArrayObject: + """A dataclass that encapsulates an OpenGL vertex array object. + + This can be used as a context manager to automatically bind and unbind + the vertex array object for use. + """ + def create(self): + """Generate a new vertex array object.""" self.id = GL.glGenVertexArrays(1) def bind(self): + """Make the vertex array object the current vertex array object.""" GL.glBindVertexArray(self.id) def unbind(self): + """Make the vertex array object no longer the current vertex array object.""" GL.glBindVertexArray(0) def __enter__(self): + """Enter the context manager, binding the vertex array object.""" self.bind() def __exit__(self, exc_type, exc_value, traceback): + """Exit the context manager, unbinding the vertex array object.""" self.unbind() return False From 7539f0d50a09923ccb67019a83a79100a6e28d3e Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 2 Apr 2026 21:03:27 +0100 Subject: [PATCH 11/26] Add Pyglet backend. Really should use Pyglet's high-level API. --- examples/shadertoy/shadertoy/utils_pyglet.py | 269 +++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 examples/shadertoy/shadertoy/utils_pyglet.py diff --git a/examples/shadertoy/shadertoy/utils_pyglet.py b/examples/shadertoy/shadertoy/utils_pyglet.py new file mode 100644 index 0000000000..0aabc2535b --- /dev/null +++ b/examples/shadertoy/shadertoy/utils_pyglet.py @@ -0,0 +1,269 @@ +"""Utility objects and methods for PyOpenGL""" + +from contextlib import contextmanager +from ctypes import POINTER, byref, c_char, c_char_p, c_int, cast, create_string_buffer +from dataclasses import dataclass +from enum import IntEnum + +from pyglet.gl import gl as GL + +#: Shader version header: this should work on most modern desktops +VERSION_HEADER = "#version 330 core" + + +class ShaderType(IntEnum): + """Enum for the different OpenGL shader types.""" + + vertex = GL.GL_VERTEX_SHADER + fragment = GL.GL_FRAGMENT_SHADER + + +class BufferType(IntEnum): + """Enum for the different OpenGL buffer types.""" + + array = GL.GL_ARRAY_BUFFER + + +class BufferUsage(IntEnum): + """Enum for the different OpenGL buffer usage hints.""" + + dynamic_draw = GL.GL_DYNAMIC_DRAW + static_draw = GL.GL_STATIC_DRAW + + +#: Map from OpenGL data types to corresponding setter functions +#: This is far from complete. +uniform_setters = { + (GL.GL_FLOAT, False): GL.glUniform1f, + (GL.GL_FLOAT_VEC2, False): GL.glUniform2f, + (GL.GL_FLOAT_VEC3, False): GL.glUniform3f, + (GL.GL_FLOAT_VEC4, False): GL.glUniform4f, + (GL.GL_FLOAT, True): GL.glUniform1fv, + (GL.GL_FLOAT_VEC2, True): GL.glUniform2fv, + (GL.GL_FLOAT_VEC3, True): GL.glUniform3fv, + (GL.GL_FLOAT_VEC4, True): GL.glUniform4fv, +} + + +class OpenGLError(RuntimeError): + """OpenGL-specific runtime errors.""" + + pass + + +@dataclass +class Shader: + """A dataclass that encapsulates an OpenGL shader.""" + + shader_type: int + source: str + + def create(self): + """Create and compile the shader.""" + self.id = GL.glCreateShader(self.shader_type) + source_bytes = self.source.encode("utf-8") + GL.glShaderSource( + self.id, + 1, + cast(c_char_p(source_bytes), POINTER(c_char)), + c_int(len(source_bytes)), + ) + + GL.glCompileShader(self.id) + + # Handle errors + status = c_int(0) + GL.glGetShaderiv(self.id, GL.GL_COMPILE_STATUS, byref(status)) + if status == GL.GL_FALSE: + info_log = GL.glGetShaderInfoLog(self.id).decode("utf-8") + raise OpenGLError( + f"Compilation failure for {self.shader_type} shader:\n{info_log}" + ) + + def delete(self): + """Delete the shader.""" + if hasattr(self, "id"): + GL.glDeleteShader(self.id) + del self.id + + +@dataclass +class Program: + """A dataclass that encapsulates an OpenGL program.""" + + shaders: list[Shader] + + def create(self): + """Create the program and its shaders and link them.""" + for shader in self.shaders: + shader.create() + + self.id = GL.glCreateProgram() + + for shader in self.shaders: + GL.glAttachShader(self.id, shader.id) + + GL.glLinkProgram(self.id) + + # Handle errors + status = c_int(0) + GL.glGetProgramiv(self.id, GL.GL_LINK_STATUS, byref(status)) + if status == GL.GL_FALSE: + strInfoLog = GL.glGetProgramInfoLog(self.id).decode("utf-8") + raise OpenGLError("Linker failure: \n" + strInfoLog) + + # clean up shaders + for shader in self.shaders: + GL.glDetachShader(self.id, shader.id) + for shader in self.shaders: + shader.delete() + + def delete(self): + """Delete the program.""" + if hasattr(self, "id"): + GL.glDeleteProgram(self.id) + del self.id + + @contextmanager + def use(self): + """Context manager that sets the program for use and resets it when done.""" + GL.glUseProgram(self.id) + try: + yield + finally: + GL.glUseProgram(0) + + def active_attributes(self): + """Get information about the program's active attributes. + + Returns a dictionary of attribute names mapping to the location, size and + type of the attribute. + """ + data = [ + GL.glGetActiveAttrib(self.id, i) + for i in range(GL.glGetProgramiv(self.id, GL.GL_ACTIVE_ATTRIBUTES)) + ] + return { + name.decode("utf-8"): (i, size, uniform_type) + for i, (name, size, uniform_type) in enumerate(data) + } + + def active_uniforms(self): + """Get information about the program's active uniforms. + + Returns a dictionary of uniform names mapping to the location, size, + type and setter function of the attribute. + """ + n_uniforms = c_int(0) + GL.glGetProgramiv(self.id, GL.GL_ACTIVE_UNIFORMS, byref(n_uniforms)) + uniforms = {} + for i in range(n_uniforms.value): + size = GL.GLint() + uniform_type = GL.GLenum() + buf_size = 192 + name = create_string_buffer(buf_size) + GL.glGetActiveUniform(self.id, i, buf_size, None, size, uniform_type, name) + uniforms[name.value.decode("utf-8")] = ( + i, + size.value, + uniform_type.value, + uniform_setters[uniform_type.value, size.value > 1], + ) + return uniforms + + def attribute(self, item): + """Return the location of an active attribute.""" + buffer = create_string_buffer(item.encode("utf-8")) + return GL.glGetAttribLocation(self.id, buffer) + + def set_uniforms(self, uniforms): + """Set the values of active uniforms from a dictionary of values.""" + for uniform, (loc, size, _, setter) in self.active_uniforms().items(): + if uniform in uniforms: + if size == 1: + setter(loc, *uniforms[uniform]) + else: + setter(loc, size, uniforms[uniform]) + + def bind_attribute_buffer(self, attribute, vbo, *, size=4): + """Bind an active attribute to a vertex buffer object.""" + loc = self.attribute(attribute) + with vbo: + GL.glEnableVertexAttribArray(loc) + GL.glVertexAttribPointer(loc, size, GL.GL_FLOAT, GL.GL_FALSE, 0, None) + + +@dataclass +class Buffer: + """A dataclass that encapsulates an OpenGL buffer. + + This can be used as a context manager to automatically bind and unbind + the buffer for use. + """ + + buffer_type: int + usage: int + + def create(self, data): + """Generate a new buffer and set the data into it.""" + buffer_id = GL.GLuint() + GL.glGenBuffers(1, buffer_id) + self.id = buffer_id.value + self.set_data(data) + + def set_data(self, data): + """Set data into a buffer. + + This automatically binds the buffer.. + """ + with self: + GL.glBufferData(self.buffer_type, len(data), data, self.usage) + + def bind(self): + """Bind the buffer so that it is the current buffer.""" + GL.glBindBuffer(self.buffer_type, self.id) + + def unbind(self): + """Unbind the buffer so that it is no longer the current buffer.""" + GL.glBindBuffer(self.buffer_type, 0) + + def __enter__(self): + """Enter the context manager, binding the buffer.""" + self.bind() + + def __exit__(self, exc_type, exc_value, traceback): + """Exit the context manager, unbinding the buffer.""" + self.unbind() + return False + + +@dataclass +class VertexArrayObject: + """A dataclass that encapsulates an OpenGL vertex array object. + + This can be used as a context manager to automatically bind and unbind + the vertex array object for use. + """ + + def create(self): + """Generate a new vertex array object.""" + id = GL.GLuint() + GL.glGenVertexArrays(1, id) + self.id = id.value + + def bind(self): + """Make the vertex array object the current vertex array object.""" + GL.glBindVertexArray(self.id) + + def unbind(self): + """Make the vertex array object no longer the current vertex array object.""" + GL.glBindVertexArray(0) + + def __enter__(self): + """Enter the context manager, binding the vertex array object.""" + self.bind() + + def __exit__(self, exc_type, exc_value, traceback): + """Exit the context manager, unbinding the vertex array object.""" + self.unbind() + return False From 09ee1acca52a0bc0ada1cd4c217ad66b3dc80bf4 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 3 Apr 2026 16:09:44 +0100 Subject: [PATCH 12/26] Further refactor of shadertoy example; get iOS working. --- examples/shadertoy/shadertoy/app.py | 2 +- .../shadertoy/shadertoy/shadertoy_renderer.py | 51 +-- examples/shadertoy/shadertoy/utils.py | 247 ++++++++++++++ examples/shadertoy/shadertoy/utils_android.py | 323 +++--------------- examples/shadertoy/shadertoy/utils_iOS.py | 88 +++++ examples/shadertoy/shadertoy/utils_pyglet.py | 292 +++------------- .../shadertoy/shadertoy/utils_pyopengl.py | 253 +------------- iOS/src/toga_iOS/libs/opengles.py | 55 ++- iOS/src/toga_iOS/widgets/openglview.py | 4 +- 9 files changed, 526 insertions(+), 789 deletions(-) create mode 100644 examples/shadertoy/shadertoy/utils.py create mode 100644 examples/shadertoy/shadertoy/utils_iOS.py diff --git a/examples/shadertoy/shadertoy/app.py b/examples/shadertoy/shadertoy/app.py index f97aa27c6e..9b1ddfd7a2 100644 --- a/examples/shadertoy/shadertoy/app.py +++ b/examples/shadertoy/shadertoy/app.py @@ -81,7 +81,7 @@ async def animate(): loop = asyncio.get_running_loop() loop.create_task(animate()) - if toga.backend == "toga_android": + if toga.backend in {"toga_android", "toga_iOS"}: # vertical layout outer_box = toga.Box( children=[ diff --git a/examples/shadertoy/shadertoy/shadertoy_renderer.py b/examples/shadertoy/shadertoy/shadertoy_renderer.py index 7bf30e70c9..ccb7fb7906 100644 --- a/examples/shadertoy/shadertoy/shadertoy_renderer.py +++ b/examples/shadertoy/shadertoy/shadertoy_renderer.py @@ -19,38 +19,18 @@ import time import traceback -import toga - -# Import appropriate utility objects for OpenGL implementation -if toga.backend in {"toga_cocoa", "toga_gtk", "toga_qt"}: - from .utils_pyopengl import ( - GL, - VERSION_HEADER, - Buffer, - BufferType, - BufferUsage, - OpenGLError, - Program, - Shader, - ShaderType, - VertexArrayObject, - ) -elif toga.backend == "toga_android": - from .utils_android import ( - GL, - VERSION_HEADER, - Buffer, - BufferType, - BufferUsage, - OpenGLError, - Program, - Shader, - ShaderType, - VertexArrayObject, - ) -else: - raise RuntimeError("No renderer for backend.") - +from .utils import ( + GL, + VERSION_HEADER, + Buffer, + BufferType, + BufferUsage, + OpenGLError, + Program, + Shader, + ShaderType, + VertexArrayObject, +) #: Vertex shader: displays triangles in 2D. VERTEX_SHADER_SOURCE = f"""{VERSION_HEADER} @@ -92,7 +72,7 @@ Frame rate: {iFrameRate[0]:.3f} fps Resolution: {iResolution} -Mouse: {iMouse} +Mouse: ({iMouse[0]:.1f}, {iMouse[1]:.1f}) ({iMouse[2]:.1f}, {iMouse[3]:.1f}) Time: {iTime[0]:.3f} s Time delta: {iTimeDelta[0]:.3f} s @@ -183,9 +163,9 @@ def on_init(self, widget, **kwargs): """Initialize the OpenGL state.""" self.message = "Initializing...\n\n" try: - self.source_updated = False self._initialize_vbo(VERTEX_POSITIONS) self._update_program() + self.source_updated = False except OpenGLError as exc: # Add OpenGL error to messages # This is most likely an error in the source, so this is important @@ -217,7 +197,8 @@ def on_render( # Handle a pending source update. if self.source_updated: try: - self.program.delete() + if hasattr(self, "program"): + self.program.delete() self._update_program() except OpenGLError as exc: # Add OpenGL error to messages diff --git a/examples/shadertoy/shadertoy/utils.py b/examples/shadertoy/shadertoy/utils.py new file mode 100644 index 0000000000..79d8895f7b --- /dev/null +++ b/examples/shadertoy/shadertoy/utils.py @@ -0,0 +1,247 @@ +import os +from contextlib import contextmanager +from dataclasses import dataclass +from enum import IntEnum + +import toga + +if toga.backend in {"toga_cocoa", "toga_gtk", "toga_qt"}: + if os.environ.get("TOGA_OPENGL") == "pyglet": + from . import utils_pyglet as GL + else: + from . import utils_pyopengl as GL +elif toga.backend == "toga_android": + from . import utils_android as GL +elif toga.backend == "toga_iOS": + from . import utils_iOS as GL +else: + raise RuntimeError("No OpenGL for backend.") + + +VERSION_HEADER = GL.VERSION_HEADER + + +class ShaderType(IntEnum): + """Enum for the different OpenGL shader types.""" + + vertex = GL.GL_VERTEX_SHADER + fragment = GL.GL_FRAGMENT_SHADER + + +class BufferType(IntEnum): + """Enum for the different OpenGL buffer types.""" + + array = GL.GL_ARRAY_BUFFER + + +class BufferUsage(IntEnum): + """Enum for the different OpenGL buffer usage hints.""" + + dynamic_draw = GL.GL_DYNAMIC_DRAW + static_draw = GL.GL_STATIC_DRAW + + +#: Map from OpenGL data types to corresponding setter functions +#: This is far from complete. +uniform_setters = { + (GL.GL_FLOAT, False): GL.glUniform1f, + (GL.GL_FLOAT_VEC2, False): GL.glUniform2f, + (GL.GL_FLOAT_VEC3, False): GL.glUniform3f, + (GL.GL_FLOAT_VEC4, False): GL.glUniform4f, + (GL.GL_FLOAT, True): GL.glUniform1fv, + (GL.GL_FLOAT_VEC2, True): GL.glUniform2fv, + (GL.GL_FLOAT_VEC3, True): GL.glUniform3fv, + (GL.GL_FLOAT_VEC4, True): GL.glUniform4fv, +} + + +class OpenGLError(RuntimeError): + """OpenGL-specific runtime errors.""" + + pass + + +@dataclass +class Shader: + """A dataclass that encapsulates an OpenGL shader.""" + + shader_type: int + source: str + + def create(self): + """Create and compile the shader.""" + self.id = GL.glCreateShader(self.shader_type) + GL.glShaderSource(self.id, self.source) + + GL.glCompileShader(self.id) + + # Handle errors + status = GL.glGetShaderiv(self.id, GL.GL_COMPILE_STATUS) + if status == GL.GL_FALSE: + info_log = GL.glGetShaderInfoLog(self.id) + raise OpenGLError( + f"Compilation failure for {self.shader_type} shader:\n{info_log}" + ) + + def delete(self): + """Delete the shader.""" + if hasattr(self, "id"): + GL.glDeleteShader(self.id) + del self.id + + +@dataclass +class Program: + """A dataclass that encapsulates an OpenGL program.""" + + shaders: list[Shader] + + def create(self): + """Create the program and its shaders and link them.""" + for shader in self.shaders: + shader.create() + + self.id = GL.glCreateProgram() + + for shader in self.shaders: + GL.glAttachShader(self.id, shader.id) + + GL.glLinkProgram(self.id) + + # Handle errors + status = GL.glGetProgramiv(self.id, GL.GL_LINK_STATUS) + if status == GL.GL_FALSE: + strInfoLog = GL.glGetProgramInfoLog(self.id) + raise OpenGLError(f"Linker failure: \n{strInfoLog}") + + # clean up shaders + for shader in self.shaders: + GL.glDetachShader(self.id, shader.id) + for shader in self.shaders: + shader.delete() + + def delete(self): + """Delete the program.""" + if hasattr(self, "id"): + GL.glDeleteProgram(self.id) + del self.id + + @contextmanager + def use(self): + """Context manager that sets the program for use and resets it when done.""" + GL.glUseProgram(self.id) + try: + yield + finally: + GL.glUseProgram(0) + + def active_uniforms(self): + """Get information about the program's active uniforms. + + Returns a dictionary of uniform names mapping to the location, size, + type and setter function of the attribute. + """ + n_uniforms = GL.glGetProgramiv(self.id, GL.GL_ACTIVE_UNIFORMS) + data = [GL.glGetActiveUniform(self.id, i) for i in range(n_uniforms)] + uniforms = { + name: ( + i, + size, + uniform_type, + uniform_setters[uniform_type, size > 1], + ) + for i, (name, size, uniform_type) in enumerate(data) + } + return uniforms + + def attribute(self, item): + """Return the location of an active attribute.""" + return GL.glGetAttribLocation(self.id, item) + + def set_uniforms(self, uniforms): + """Set the values of active uniforms from a dictionary of values.""" + for uniform, (loc, size, _, setter) in self.active_uniforms().items(): + if uniform in uniforms: + if size == 1: + setter(loc, *uniforms[uniform]) + else: + setter(loc, size, uniforms[uniform]) + + def bind_attribute_buffer(self, attribute, vbo, *, size=4): + """Bind an active attribute to a vertex buffer object.""" + loc = self.attribute(attribute) + with vbo: + GL.glEnableVertexAttribArray(loc) + GL.glVertexAttribPointer(loc, size, GL.GL_FLOAT, GL.GL_FALSE, 0, None) + + +@dataclass +class Buffer: + """A dataclass that encapsulates an OpenGL buffer. + + This can be used as a context manager to automatically bind and unbind + the buffer for use. + """ + + buffer_type: int + usage: int + + def create(self, data): + """Generate a new buffer and set the data into it.""" + self.id = GL.glGenBuffers(1) + self.set_data(data) + + def set_data(self, data): + """Set data into a buffer. + + This automatically binds the buffer.. + """ + with self: + GL.glBufferData(self.buffer_type, data, self.usage) + + def bind(self): + """Bind the buffer so that it is the current buffer.""" + GL.glBindBuffer(self.buffer_type, self.id) + + def unbind(self): + """Unbind the buffer so that it is no longer the current buffer.""" + GL.glBindBuffer(self.buffer_type, 0) + + def __enter__(self): + """Enter the context manager, binding the buffer.""" + self.bind() + + def __exit__(self, exc_type, exc_value, traceback): + """Exit the context manager, unbinding the buffer.""" + self.unbind() + return False + + +@dataclass +class VertexArrayObject: + """A dataclass that encapsulates an OpenGL vertex array object. + + This can be used as a context manager to automatically bind and unbind + the vertex array object for use. + """ + + def create(self): + """Generate a new vertex array object.""" + self.id = GL.glGenVertexArrays(1) + + def bind(self): + """Make the vertex array object the current vertex array object.""" + GL.glBindVertexArray(self.id) + + def unbind(self): + """Make the vertex array object no longer the current vertex array object.""" + GL.glBindVertexArray(0) + + def __enter__(self): + """Enter the context manager, binding the vertex array object.""" + self.bind() + + def __exit__(self, exc_type, exc_value, traceback): + """Exit the context manager, unbinding the vertex array object.""" + self.unbind() + return False diff --git a/examples/shadertoy/shadertoy/utils_android.py b/examples/shadertoy/shadertoy/utils_android.py index 83b1158c77..c88ec738af 100644 --- a/examples/shadertoy/shadertoy/utils_android.py +++ b/examples/shadertoy/shadertoy/utils_android.py @@ -1,8 +1,4 @@ -"""Utility objects and methods for PyOpenGL""" - -from contextlib import contextmanager -from dataclasses import dataclass -from enum import IntEnum +"""Utility objects and methods for android.opengl""" from android.opengl import GLES32 as GL from java import jarray, jbyte, jint @@ -11,45 +7,7 @@ #: Shader version header: we want OpenGL ES GLSL 3 VERSION_HEADER = """#version 300 es""" - -class ShaderType(IntEnum): - """Enum for the different OpenGL shader types.""" - - vertex = GL.GL_VERTEX_SHADER - fragment = GL.GL_FRAGMENT_SHADER - - -class BufferType(IntEnum): - """Enum for the different OpenGL buffer types.""" - - array = GL.GL_ARRAY_BUFFER - - -class BufferUsage(IntEnum): - """Enum for the different OpenGL buffer usage hints.""" - - dynamic_draw = GL.GL_DYNAMIC_DRAW - static_draw = GL.GL_STATIC_DRAW - - -#: Map from OpenGL data types to corresponding setter functions -#: This is far from complete. -uniform_setters = { - (GL.GL_FLOAT, False): GL.glUniform1f, - (GL.GL_FLOAT_VEC2, False): GL.glUniform2f, - (GL.GL_FLOAT_VEC3, False): GL.glUniform3f, - (GL.GL_FLOAT_VEC4, False): GL.glUniform4f, - (GL.GL_FLOAT, True): GL.glUniform1fv, - (GL.GL_FLOAT_VEC2, True): GL.glUniform2fv, - (GL.GL_FLOAT_VEC3, True): GL.glUniform3fv, - (GL.GL_FLOAT_VEC4, True): GL.glUniform4fv, -} - - -class OpenGLError(RuntimeError): - """OpenGL-specific runtime errors.""" - - pass +# Adapt some functions to common API def v_func(v_size=1, jtype=jint): @@ -66,7 +24,7 @@ def v_func(v_size=1, jtype=jint): """ def wrapper(func): - def call_v_function(*args, v_size=1, jtype=jint): + def call_v_function(*args): data = jarray(jtype)([0] * v_size) func(*args, data, 0) if v_size == 1: @@ -79,230 +37,51 @@ def call_v_function(*args, v_size=1, jtype=jint): return wrapper -glGetShader = v_func()(GL.glGetShaderiv) -glGetProgram = v_func()(GL.glGetProgramiv) -glGenBuffer = v_func()(GL.glGenBuffers) -glGenVertexArray = v_func()(GL.glGenVertexArrays) - - -@dataclass -class Shader: - """A dataclass that encapsulates an OpenGL shader.""" - - shader_type: int - source: str - - def create(self): - """Create and compile the shader.""" - self.id = GL.glCreateShader(self.shader_type) - GL.glShaderSource(self.id, self.source) - - GL.glCompileShader(self.id) - - # Handle errors - status = glGetShader(self.id, GL.GL_COMPILE_STATUS) - if not status: - info_log = GL.glGetShaderInfoLog(self.id) - raise OpenGLError( - f"Compilation failure for {self.shader_type} shader:\n{info_log}" - ) - - def delete(self): - """Delete the shader.""" - if hasattr(self, "id"): - GL.glDeleteShader(self.id) - del self.id - - -@dataclass -class Program: - """A dataclass that encapsulates an OpenGL program.""" - - shaders: list[Shader] - - def create(self): - """Create the program and its shaders and link them.""" - for shader in self.shaders: - shader.create() - - self.id = GL.glCreateProgram() - - for shader in self.shaders: - GL.glAttachShader(self.id, shader.id) - - GL.glLinkProgram(self.id) - - # Handle errors - status = glGetProgram(self.id, GL.GL_LINK_STATUS) - if not status: - strInfoLog = GL.glGetProgramInfoLog(self.id) - raise OpenGLError("Linker failure: \n" + strInfoLog) - - # clean up shaders - for shader in self.shaders: - GL.glDetachShader(self.id, shader.id) - for shader in self.shaders: - shader.delete() - - def delete(self): - """Delete the program.""" - if hasattr(self, "id"): - GL.glDeleteProgram(self.id) - del self.id - - @contextmanager - def use(self): - """Context manager that sets the program for use and resets it when done.""" - GL.glUseProgram(self.id) - try: - yield - finally: - GL.glUseProgram(0) - - def active_attributes(self): - """Get information about the program's active attributes. - - Returns a dictionary of attribute names mapping to the location, size and - type of the attribute. - """ - data = [ - GL.glGetActiveAttrib(self.id, i) - for i in range(GL.glGetProgramiv(self.id, GL.GL_ACTIVE_ATTRIBUTES)) - ] - return { - name.decode("utf-8"): (i, size, uniform_type) - for i, (name, size, uniform_type) in enumerate(data) - } - - def active_uniforms(self): - """Get information about the program's active uniforms. - - Returns a dictionary of uniform names mapping to the location, size, - type and setter function of the attribute. - """ - n_uniforms = glGetProgram(self.id, GL.GL_ACTIVE_UNIFORMS) - - data = {} - for i in range(n_uniforms): - # Need to get values out via Java arrays - length = jarray(jint)([0]) - size = jarray(jint)([0]) - type = jarray(jint)([0]) - name = jarray(jbyte)(b"\0x00" * 255) - GL.glGetActiveUniform( - self.id, - i, - 255, - length, - 0, - size, - 0, - type, - 0, - name, - 0, - ) - data[bytes(name[: length[0]]).decode("utf-8")] = ( - i, - size[0], - type[0], - uniform_setters[type[0], size[0] > 1], - ) - return data - - def attribute(self, item): - """Return the location of an active attribute.""" - return GL.glGetAttribLocation(self.id, item) - - def set_uniforms(self, uniforms): - """Set the values of active uniforms from a dictionary of values.""" - for uniform, (loc, size, _, setter) in self.active_uniforms().items(): - if uniform in uniforms: - if size == 1: - setter(loc, *uniforms[uniform]) - else: - setter(loc, size, uniforms[uniform]) - - def bind_attribute_buffer(self, attribute, vbo, *, size=4): - """Bind an active attribute to a vertex buffer object.""" - loc = self.attribute(attribute) - with vbo: - GL.glEnableVertexAttribArray(loc) - GL.glVertexAttribPointer(loc, size, GL.GL_FLOAT, False, 0, 0) - - -@dataclass -class Buffer: - """A dataclass that encapsulates an OpenGL buffer. - - This can be used as a context manager to automatically bind and unbind - the buffer for use. - """ - - buffer_type: int - usage: int - - def create(self, data): - """Generate a new buffer and set the data into it.""" - self.id = glGenBuffer(1) - self.set_data(data) - - def set_data(self, data): - """Set data into a buffer. - - This automatically binds the buffer. - """ - nbytes = len(data) - buffer = ByteBuffer.allocateDirect(nbytes) - buffer.order(ByteOrder.nativeOrder()) - buffer.put(data) - buffer.position(0) - with self: - GL.glBufferData(self.buffer_type, nbytes, buffer, self.usage) - - def bind(self): - """Bind the buffer so that it is the current buffer.""" - GL.glBindBuffer(self.buffer_type, self.id) - - def unbind(self): - """Unbind the buffer so that it is no longer the current buffer.""" - GL.glBindBuffer(self.buffer_type, 0) - - def __enter__(self): - """Enter the context manager, binding the buffer.""" - self.bind() - - def __exit__(self, exc_type, exc_value, traceback): - """Exit the context manager, unbinding the buffer.""" - self.unbind() - return False - - -@dataclass -class VertexArrayObject: - """A dataclass that encapsulates an OpenGL vertex array object. - - This can be used as a context manager to automatically bind and unbind - the vertex array object for use. - """ - - def create(self): - """Generate a new vertex array object.""" - self.id = glGenVertexArray(1) - - def bind(self): - """Make the vertex array object the current vertex array object.""" - GL.glBindVertexArray(self.id) - - def unbind(self): - """Make the vertex array object no longer the current vertex array object.""" - GL.glBindVertexArray(0) - - def __enter__(self): - """Enter the context manager, binding the vertex array object.""" - self.bind() - - def __exit__(self, exc_type, exc_value, traceback): - """Exit the context manager, unbinding the vertex array object.""" - self.unbind() - return False +glGetShaderiv = v_func()(GL.glGetShaderiv) +glGetProgramiv = v_func()(GL.glGetProgramiv) +glGenBuffers = v_func()(GL.glGenBuffers) +glGenVertexArrays = v_func()(GL.glGenVertexArrays) + + +def glVertexAttribPointer(loc, size, type, normalize, stride, offset): + offset = 0 if offset is None else offset + GL.glVertexAttribPointer(loc, size, type, bool(normalize), stride, offset) + + +def glGetActiveUniform(id, loc): + length = jarray(jint)([0]) + size = jarray(jint)([0]) + type = jarray(jint)([0]) + name = jarray(jbyte)(b"\0x00" * 255) + GL.glGetActiveUniform( + id, + loc, + 255, + length, + 0, + size, + 0, + type, + 0, + name, + 0, + ) + return bytes(name[: length[0]]).decode("utf-8"), size[0], type[0] + + +def glBufferData(buffer_type, data, usage): + nbytes = len(data) + buffer = ByteBuffer.allocateDirect(nbytes) + buffer.order(ByteOrder.nativeOrder()) + buffer.put(data) + buffer.position(0) + GL.glBufferData(buffer_type, nbytes, buffer, usage) + + +def __getattr__(name): + value = getattr(GL, name, None) + if value is not None: + globals()[name] = value + return value + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/examples/shadertoy/shadertoy/utils_iOS.py b/examples/shadertoy/shadertoy/utils_iOS.py new file mode 100644 index 0000000000..8b0ed2703a --- /dev/null +++ b/examples/shadertoy/shadertoy/utils_iOS.py @@ -0,0 +1,88 @@ +"""Utility objects and methods for iOS""" + +from ctypes import byref, c_char_p, c_int, c_uint, create_string_buffer + +from toga_iOS.libs import opengles as GL + +#: Shader version header: we want OpenGL ES GLSL 3 +VERSION_HEADER = """#version 300 es""" + + +# Adapt some functions to common API + + +def v_func(v_size=1, ctype=c_int): + """Decorator for functions expecting a pointer to an output vector. + + A number of OpenGL functions have a suffix of the form {l}{t}v, + which expect an argument of an array of length l of type t (and + an offset into it) which is used to return values by setting them + into the array. + + This wrapper wraps these functions to create a java array of the + correct type, call the function, and then unpack the values from + the array. + """ + + def wrapper(func): + def call_v_function(*args): + data = (ctype * v_size)(*([0] * v_size)) + func(*args, byref(data)) + if v_size == 1: + return data[0] + else: + return tuple(data) + + return call_v_function + + return wrapper + + +glGetShaderiv = v_func()(GL.glGetShaderiv) +glGetProgramiv = v_func()(GL.glGetProgramiv) +glGenBuffers = v_func()(GL.glGenBuffers) +glGenVertexArrays = v_func()(GL.glGenVertexArrays) + + +def glShaderSource(id, source): + source_bytes = c_char_p(source.encode("utf-8")) + GL.glShaderSource( + id, + 1, + source_bytes, + None, + ) + + +def glGetActiveUniform(id, loc): + size = c_int() + type = c_uint() + name = create_string_buffer(255) + GL.glGetActiveUniform( + id, + loc, + 255, + None, + byref(size), + byref(type), + name, + ) + return name.value.decode("utf-8"), size.value, type.value + + +def glGetAttribLocation(id, item): + buffer = create_string_buffer(item.encode("utf-8")) + return GL.glGetAttribLocation(id, buffer) + + +def glBufferData(buffer_type, data: bytes, usage): + GL.glBufferData(buffer_type, len(data), data, usage) + + +def __getattr__(name): + value = getattr(GL, name, None) + if value is not None: + globals()[name] = value + return value + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/examples/shadertoy/shadertoy/utils_pyglet.py b/examples/shadertoy/shadertoy/utils_pyglet.py index 0aabc2535b..d333d2da71 100644 --- a/examples/shadertoy/shadertoy/utils_pyglet.py +++ b/examples/shadertoy/shadertoy/utils_pyglet.py @@ -1,269 +1,85 @@ -"""Utility objects and methods for PyOpenGL""" +"""Utility objects and methods for Pyglet""" -from contextlib import contextmanager from ctypes import POINTER, byref, c_char, c_char_p, c_int, cast, create_string_buffer -from dataclasses import dataclass -from enum import IntEnum from pyglet.gl import gl as GL #: Shader version header: this should work on most modern desktops VERSION_HEADER = "#version 330 core" +# Adapt some functions to common API -class ShaderType(IntEnum): - """Enum for the different OpenGL shader types.""" - vertex = GL.GL_VERTEX_SHADER - fragment = GL.GL_FRAGMENT_SHADER +def glShaderSource(id, source): + source_bytes = source.encode("utf-8") + GL.glShaderSource( + id, + 1, + cast(c_char_p(source_bytes), POINTER(c_char)), + c_int(len(source_bytes)), + ) -class BufferType(IntEnum): - """Enum for the different OpenGL buffer types.""" +def glGetShaderiv(id, param): + status = c_int(0) + GL.glGetShaderiv(id, param, byref(status)) + return status.value - array = GL.GL_ARRAY_BUFFER +def glGetShaderInfoLog(id): + raise NotImplementedError() -class BufferUsage(IntEnum): - """Enum for the different OpenGL buffer usage hints.""" - dynamic_draw = GL.GL_DYNAMIC_DRAW - static_draw = GL.GL_STATIC_DRAW +def glGetProgramiv(id, param): + status = c_int(0) + GL.glGetProgramiv(id, param, byref(status)) + return status.value -#: Map from OpenGL data types to corresponding setter functions -#: This is far from complete. -uniform_setters = { - (GL.GL_FLOAT, False): GL.glUniform1f, - (GL.GL_FLOAT_VEC2, False): GL.glUniform2f, - (GL.GL_FLOAT_VEC3, False): GL.glUniform3f, - (GL.GL_FLOAT_VEC4, False): GL.glUniform4f, - (GL.GL_FLOAT, True): GL.glUniform1fv, - (GL.GL_FLOAT_VEC2, True): GL.glUniform2fv, - (GL.GL_FLOAT_VEC3, True): GL.glUniform3fv, - (GL.GL_FLOAT_VEC4, True): GL.glUniform4fv, -} +def glGetProgramInfoLog(id): + raise NotImplementedError() -class OpenGLError(RuntimeError): - """OpenGL-specific runtime errors.""" +def glGetActiveUniform(id, loc): + size = GL.GLint() + uniform_type = GL.GLenum() + buf_size = 192 + name = create_string_buffer(buf_size) + GL.glGetActiveUniform(id, loc, buf_size, None, size, uniform_type, name) + return name.value.decode("utf-8"), size.value, uniform_type.value - pass +def glGetAttribLocation(id, attrib): + buffer = create_string_buffer(attrib.encode("utf-8")) + return GL.glGetAttribLocation(id, buffer) -@dataclass -class Shader: - """A dataclass that encapsulates an OpenGL shader.""" - shader_type: int - source: str +def glBufferData(buffer_type, data: bytes, usage): + GL.glBufferData(buffer_type, GL.GLsizeiptr(len(data)), data, usage) - def create(self): - """Create and compile the shader.""" - self.id = GL.glCreateShader(self.shader_type) - source_bytes = self.source.encode("utf-8") - GL.glShaderSource( - self.id, - 1, - cast(c_char_p(source_bytes), POINTER(c_char)), - c_int(len(source_bytes)), - ) - GL.glCompileShader(self.id) - - # Handle errors - status = c_int(0) - GL.glGetShaderiv(self.id, GL.GL_COMPILE_STATUS, byref(status)) - if status == GL.GL_FALSE: - info_log = GL.glGetShaderInfoLog(self.id).decode("utf-8") - raise OpenGLError( - f"Compilation failure for {self.shader_type} shader:\n{info_log}" - ) - - def delete(self): - """Delete the shader.""" - if hasattr(self, "id"): - GL.glDeleteShader(self.id) - del self.id - - -@dataclass -class Program: - """A dataclass that encapsulates an OpenGL program.""" - - shaders: list[Shader] - - def create(self): - """Create the program and its shaders and link them.""" - for shader in self.shaders: - shader.create() - - self.id = GL.glCreateProgram() - - for shader in self.shaders: - GL.glAttachShader(self.id, shader.id) - - GL.glLinkProgram(self.id) - - # Handle errors - status = c_int(0) - GL.glGetProgramiv(self.id, GL.GL_LINK_STATUS, byref(status)) - if status == GL.GL_FALSE: - strInfoLog = GL.glGetProgramInfoLog(self.id).decode("utf-8") - raise OpenGLError("Linker failure: \n" + strInfoLog) - - # clean up shaders - for shader in self.shaders: - GL.glDetachShader(self.id, shader.id) - for shader in self.shaders: - shader.delete() - - def delete(self): - """Delete the program.""" - if hasattr(self, "id"): - GL.glDeleteProgram(self.id) - del self.id - - @contextmanager - def use(self): - """Context manager that sets the program for use and resets it when done.""" - GL.glUseProgram(self.id) - try: - yield - finally: - GL.glUseProgram(0) - - def active_attributes(self): - """Get information about the program's active attributes. - - Returns a dictionary of attribute names mapping to the location, size and - type of the attribute. - """ - data = [ - GL.glGetActiveAttrib(self.id, i) - for i in range(GL.glGetProgramiv(self.id, GL.GL_ACTIVE_ATTRIBUTES)) - ] - return { - name.decode("utf-8"): (i, size, uniform_type) - for i, (name, size, uniform_type) in enumerate(data) - } - - def active_uniforms(self): - """Get information about the program's active uniforms. - - Returns a dictionary of uniform names mapping to the location, size, - type and setter function of the attribute. - """ - n_uniforms = c_int(0) - GL.glGetProgramiv(self.id, GL.GL_ACTIVE_UNIFORMS, byref(n_uniforms)) - uniforms = {} - for i in range(n_uniforms.value): - size = GL.GLint() - uniform_type = GL.GLenum() - buf_size = 192 - name = create_string_buffer(buf_size) - GL.glGetActiveUniform(self.id, i, buf_size, None, size, uniform_type, name) - uniforms[name.value.decode("utf-8")] = ( - i, - size.value, - uniform_type.value, - uniform_setters[uniform_type.value, size.value > 1], - ) - return uniforms - - def attribute(self, item): - """Return the location of an active attribute.""" - buffer = create_string_buffer(item.encode("utf-8")) - return GL.glGetAttribLocation(self.id, buffer) - - def set_uniforms(self, uniforms): - """Set the values of active uniforms from a dictionary of values.""" - for uniform, (loc, size, _, setter) in self.active_uniforms().items(): - if uniform in uniforms: - if size == 1: - setter(loc, *uniforms[uniform]) - else: - setter(loc, size, uniforms[uniform]) - - def bind_attribute_buffer(self, attribute, vbo, *, size=4): - """Bind an active attribute to a vertex buffer object.""" - loc = self.attribute(attribute) - with vbo: - GL.glEnableVertexAttribArray(loc) - GL.glVertexAttribPointer(loc, size, GL.GL_FLOAT, GL.GL_FALSE, 0, None) - - -@dataclass -class Buffer: - """A dataclass that encapsulates an OpenGL buffer. - - This can be used as a context manager to automatically bind and unbind - the buffer for use. - """ - - buffer_type: int - usage: int - - def create(self, data): - """Generate a new buffer and set the data into it.""" +def glGenBuffers(n): + if n == 1: buffer_id = GL.GLuint() - GL.glGenBuffers(1, buffer_id) - self.id = buffer_id.value - self.set_data(data) - - def set_data(self, data): - """Set data into a buffer. - - This automatically binds the buffer.. - """ - with self: - GL.glBufferData(self.buffer_type, len(data), data, self.usage) - - def bind(self): - """Bind the buffer so that it is the current buffer.""" - GL.glBindBuffer(self.buffer_type, self.id) + GL.glGenBuffers(n, buffer_id) + return buffer_id.value + else: + raise NotImplementedError() - def unbind(self): - """Unbind the buffer so that it is no longer the current buffer.""" - GL.glBindBuffer(self.buffer_type, 0) - def __enter__(self): - """Enter the context manager, binding the buffer.""" - self.bind() - - def __exit__(self, exc_type, exc_value, traceback): - """Exit the context manager, unbinding the buffer.""" - self.unbind() - return False - - -@dataclass -class VertexArrayObject: - """A dataclass that encapsulates an OpenGL vertex array object. - - This can be used as a context manager to automatically bind and unbind - the vertex array object for use. - """ - - def create(self): - """Generate a new vertex array object.""" +def glGenVertexArrays(n): + if n == 1: id = GL.GLuint() GL.glGenVertexArrays(1, id) - self.id = id.value - - def bind(self): - """Make the vertex array object the current vertex array object.""" - GL.glBindVertexArray(self.id) - - def unbind(self): - """Make the vertex array object no longer the current vertex array object.""" - GL.glBindVertexArray(0) - - def __enter__(self): - """Enter the context manager, binding the vertex array object.""" - self.bind() - - def __exit__(self, exc_type, exc_value, traceback): - """Exit the context manager, unbinding the vertex array object.""" - self.unbind() - return False + return id.value + else: + raise NotImplementedError() + + +def __getattr__(name): + value = getattr(GL, name, None) + if value is not None: + globals()[name] = value + return value + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/examples/shadertoy/shadertoy/utils_pyopengl.py b/examples/shadertoy/shadertoy/utils_pyopengl.py index 2aae0e4f9c..f4273b7e62 100644 --- a/examples/shadertoy/shadertoy/utils_pyopengl.py +++ b/examples/shadertoy/shadertoy/utils_pyopengl.py @@ -1,9 +1,5 @@ """Utility objects and methods for PyOpenGL""" -from contextlib import contextmanager -from dataclasses import dataclass -from enum import IntEnum - import OpenGL # Get spurious errors with Qt backend @@ -14,245 +10,26 @@ #: Shader version header: this should work on most modern desktops VERSION_HEADER = "#version 330 core" - -class ShaderType(IntEnum): - """Enum for the different OpenGL shader types.""" - - vertex = GL.GL_VERTEX_SHADER - fragment = GL.GL_FRAGMENT_SHADER - - -class BufferType(IntEnum): - """Enum for the different OpenGL buffer types.""" - - array = GL.GL_ARRAY_BUFFER - - -class BufferUsage(IntEnum): - """Enum for the different OpenGL buffer usage hints.""" - - dynamic_draw = GL.GL_DYNAMIC_DRAW - static_draw = GL.GL_STATIC_DRAW - - -#: Map from OpenGL data types to corresponding setter functions -#: This is far from complete. -uniform_setters = { - (GL.GL_FLOAT, False): GL.glUniform1f, - (GL.GL_FLOAT_VEC2, False): GL.glUniform2f, - (GL.GL_FLOAT_VEC3, False): GL.glUniform3f, - (GL.GL_FLOAT_VEC4, False): GL.glUniform4f, - (GL.GL_FLOAT, True): GL.glUniform1fv, - (GL.GL_FLOAT_VEC2, True): GL.glUniform2fv, - (GL.GL_FLOAT_VEC3, True): GL.glUniform3fv, - (GL.GL_FLOAT_VEC4, True): GL.glUniform4fv, -} - - -class OpenGLError(RuntimeError): - """OpenGL-specific runtime errors.""" - - pass - - -@dataclass -class Shader: - """A dataclass that encapsulates an OpenGL shader.""" - - shader_type: int - source: str - - def create(self): - """Create and compile the shader.""" - self.id = GL.glCreateShader(self.shader_type) - GL.glShaderSource(self.id, self.source) - - GL.glCompileShader(self.id) - - # Handle errors - status = GL.glGetShaderiv(self.id, GL.GL_COMPILE_STATUS) - if status == GL.GL_FALSE: - info_log = GL.glGetShaderInfoLog(self.id).decode("utf-8") - raise OpenGLError( - f"Compilation failure for {self.shader_type} shader:\n{info_log}" - ) - - def delete(self): - """Delete the shader.""" - if hasattr(self, "id"): - GL.glDeleteShader(self.id) - del self.id - - -@dataclass -class Program: - """A dataclass that encapsulates an OpenGL program.""" - - shaders: list[Shader] - - def create(self): - """Create the program and its shaders and link them.""" - for shader in self.shaders: - shader.create() - - self.id = GL.glCreateProgram() - - for shader in self.shaders: - GL.glAttachShader(self.id, shader.id) - - GL.glLinkProgram(self.id) - - # Handle errors - status = GL.glGetProgramiv(self.id, GL.GL_LINK_STATUS) - if status == GL.GL_FALSE: - strInfoLog = GL.glGetProgramInfoLog(self.id).decode("utf-8") - raise OpenGLError("Linker failure: \n" + strInfoLog) - - # clean up shaders - for shader in self.shaders: - GL.glDetachShader(self.id, shader.id) - for shader in self.shaders: - shader.delete() - - def delete(self): - """Delete the program.""" - if hasattr(self, "id"): - GL.glDeleteProgram(self.id) - del self.id - - @contextmanager - def use(self): - """Context manager that sets the program for use and resets it when done.""" - GL.glUseProgram(self.id) - try: - yield - finally: - GL.glUseProgram(0) - - def active_attributes(self): - """Get information about the program's active attributes. - - Returns a dictionary of attribute names mapping to the location, size and - type of the attribute. - """ - data = [ - GL.glGetActiveAttrib(self.id, i) - for i in range(GL.glGetProgramiv(self.id, GL.GL_ACTIVE_ATTRIBUTES)) - ] - return { - name.decode("utf-8"): (i, size, uniform_type) - for i, (name, size, uniform_type) in enumerate(data) - } - - def active_uniforms(self): - """Get information about the program's active uniforms. - - Returns a dictionary of uniform names mapping to the location, size, - type and setter function of the attribute. - """ - data = [ - GL.glGetActiveUniform(self.id, i) - for i in range(GL.glGetProgramiv(self.id, GL.GL_ACTIVE_UNIFORMS)) - ] - uniforms = { - name.decode("utf-8"): ( - i, - size, - uniform_type, - uniform_setters[uniform_type, size > 1], - ) - for i, (name, size, uniform_type) in enumerate(data) - } - return uniforms - - def attribute(self, item): - """Return the location of an active attribute.""" - return GL.glGetAttribLocation(self.id, item) - - def set_uniforms(self, uniforms): - """Set the values of active uniforms from a dictionary of values.""" - for uniform, (loc, size, _, setter) in self.active_uniforms().items(): - if uniform in uniforms: - if size == 1: - setter(loc, *uniforms[uniform]) - else: - setter(loc, size, uniforms[uniform]) - - def bind_attribute_buffer(self, attribute, vbo, *, size=4): - """Bind an active attribute to a vertex buffer object.""" - loc = self.attribute(attribute) - with vbo: - GL.glEnableVertexAttribArray(loc) - GL.glVertexAttribPointer(loc, size, GL.GL_FLOAT, GL.GL_FALSE, 0, None) - - -@dataclass -class Buffer: - """A dataclass that encapsulates an OpenGL buffer. - - This can be used as a context manager to automatically bind and unbind - the buffer for use. - """ - - buffer_type: int - usage: int - - def create(self, data): - """Generate a new buffer and set the data into it.""" - self.id = GL.glGenBuffers(1) - self.set_data(data) - - def set_data(self, data): - """Set data into a buffer. - - This automatically binds the buffer.. - """ - with self: - GL.glBufferData(self.buffer_type, data, self.usage) - - def bind(self): - """Bind the buffer so that it is the current buffer.""" - GL.glBindBuffer(self.buffer_type, self.id) - - def unbind(self): - """Unbind the buffer so that it is no longer the current buffer.""" - GL.glBindBuffer(self.buffer_type, 0) - - def __enter__(self): - """Enter the context manager, binding the buffer.""" - self.bind() - - def __exit__(self, exc_type, exc_value, traceback): - """Exit the context manager, unbinding the buffer.""" - self.unbind() - return False +# Adapt some functions to common API -@dataclass -class VertexArrayObject: - """A dataclass that encapsulates an OpenGL vertex array object. +def glGetShaderInfoLog(id): + return GL.glGetShaderInfoLog(id).decode("utf-8") - This can be used as a context manager to automatically bind and unbind - the vertex array object for use. - """ - def create(self): - """Generate a new vertex array object.""" - self.id = GL.glGenVertexArrays(1) +def glGetProgramInfoLog(id): + return GL.glGetProgramInfoLog(id).decode("utf-8") - def bind(self): - """Make the vertex array object the current vertex array object.""" - GL.glBindVertexArray(self.id) - def unbind(self): - """Make the vertex array object no longer the current vertex array object.""" - GL.glBindVertexArray(0) +def glGetActiveUniform(id, loc): + name, size, uniform_type = GL.glGetActiveUniform(id, loc) + return name.decode("utf-8"), size, uniform_type - def __enter__(self): - """Enter the context manager, binding the vertex array object.""" - self.bind() - def __exit__(self, exc_type, exc_value, traceback): - """Exit the context manager, unbinding the vertex array object.""" - self.unbind() - return False +def __getattr__(name): + value = getattr(GL, name) + if value is not None: + globals()[name] = value + return value + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/iOS/src/toga_iOS/libs/opengles.py b/iOS/src/toga_iOS/libs/opengles.py index a700b96573..2aec6f2710 100644 --- a/iOS/src/toga_iOS/libs/opengles.py +++ b/iOS/src/toga_iOS/libs/opengles.py @@ -1,7 +1,7 @@ ########################################################################## # System/Library/Frameworks/UIKit.framework ########################################################################## -from ctypes import c_char_p, c_float, c_int, c_ulong, cdll, util +from ctypes import POINTER, c_char_p, c_float, c_int, c_uint, c_ulong, cdll, util from rubicon.objc import ObjCClass @@ -19,6 +19,10 @@ # Types GLbitfield = c_ulong +GLint = c_int +GLuint = c_uint +GLsizei = c_ulong +GLfloat = c_float # Functions opengles.glGetString.argtypes = [c_int] @@ -29,6 +33,51 @@ opengles.glClear.argtypes = [GLbitfield] opengles.glClear.restype = None -opengles.glCreateShader.argtypes = [c_int] -opengles.glGetShaderInfoLog.argtypes = [c_int] +opengles.glCreateShader.argtypes = [GLuint] +opengles.glShaderSource.argtypes = [GLuint, GLsizei, POINTER(c_char_p), POINTER(c_int)] +opengles.glCompileShader.argtypes = [GLuint] +opengles.glGetShaderInfoLog.argtypes = [GLuint] opengles.glGetShaderInfoLog.restype = c_char_p + +opengles.glUniform1f.argtypes = [GLint, GLfloat] +opengles.glUniform2f.argtypes = [GLint, GLfloat, GLfloat] +opengles.glUniform3f.argtypes = [GLint, GLfloat, GLfloat, GLfloat] +opengles.glUniform4f.argtypes = [GLint, GLfloat, GLfloat, GLfloat, GLfloat] + +opengles.glUniform1fv.argtypes = [GLint, GLsizei, POINTER(GLfloat)] +opengles.glUniform2fv.argtypes = [GLint, GLsizei, POINTER(GLfloat)] +opengles.glUniform3fv.argtypes = [GLint, GLsizei, POINTER(GLfloat)] +opengles.glUniform4fv.argtypes = [GLint, GLsizei, POINTER(GLfloat)] + +# Constants +opengles.GL_FALSE = False + +opengles.GL_DEPTH_BUFFER_BIT = 0x200 +opengles.GL_COLOR_BUFFER_BIT = 0x4000 +opengles.GL_STENCIL_BUFFER_BIT = 0x400 + +opengles.GL_VENDOR = 0x1F00 +opengles.GL_RENDERER = 0x1F01 +opengles.GL_VERSION = 0x1F02 +opengles.GL_SHADING_LANGUAGE_VERSION = 0x8B8C + +opengles.GL_FRAGMENT_SHADER = 0x8B30 +opengles.GL_VERTEX_SHADER = 0x8B31 + +opengles.GL_ARRAY_BUFFER = 0x8892 + +opengles.GL_DYNAMIC_DRAW = 0x88E8 +opengles.GL_STATIC_DRAW = 0x88E4 + +opengles.GL_FLOAT = 0x1406 +opengles.GL_FLOAT_VEC2 = 0x8B50 +opengles.GL_FLOAT_VEC3 = 0x8B51 +opengles.GL_FLOAT_VEC4 = 0x8B52 + +opengles.GL_COMPILE_STATUS = 0x8B81 + +opengles.GL_LINK_STATUS = 0x8B82 +opengles.GL_ACTIVE_UNIFORMS = 0x8B86 +opengles.GL_ACTIVE_ATTRIBUTES = 0x8B89 + +opengles.GL_TRIANGLES = 0x4 diff --git a/iOS/src/toga_iOS/widgets/openglview.py b/iOS/src/toga_iOS/widgets/openglview.py index c6b85208c0..12777f3231 100644 --- a/iOS/src/toga_iOS/widgets/openglview.py +++ b/iOS/src/toga_iOS/widgets/openglview.py @@ -20,9 +20,9 @@ class TogaGLKView(GLKView): interface = objc_property(object, weak=True) impl = objc_property(object, weak=True) - initialized = objc_property(bool) + initialized = objc_property(object) pointer = objc_property(object) - buttons = objc_property(set) + buttons = objc_property(object) @objc_method def drawRect_(self, rect: CGRect) -> None: From f27b1a339aa9964116265e79c503404eb135eb4d Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 8 Apr 2026 12:31:13 +0100 Subject: [PATCH 13/26] Fix for Cocoa OpenGLView to enable depth buffer. --- cocoa/src/toga_cocoa/widgets/openglview.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cocoa/src/toga_cocoa/widgets/openglview.py b/cocoa/src/toga_cocoa/widgets/openglview.py index 1e46ff0417..27ea03a293 100644 --- a/cocoa/src/toga_cocoa/widgets/openglview.py +++ b/cocoa/src/toga_cocoa/widgets/openglview.py @@ -5,6 +5,7 @@ from toga.widgets.openglview import LEFT, MIDDLE, RIGHT from toga_cocoa.libs import ( + NSOpenGLPFADepthSize, NSOpenGLPFADoubleBuffer, NSOpenGLPFAOpenGLProfile, NSOpenGLPixelFormat, @@ -55,6 +56,8 @@ def drawRect_(self, rect: NSRect) -> None: @objc_method def initWithFrame_(self, frame: NSRect): a = ( + NSOpenGLPFADepthSize, + 24, NSOpenGLPFADoubleBuffer, NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core, From dab7a9cc799127fc2942af9b59591ddf0937a805 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 8 Apr 2026 12:32:52 +0100 Subject: [PATCH 14/26] Update shadertoy example for iOS support. --- examples/shadertoy/shadertoy/app.py | 2 +- .../shadertoy/shadertoy/shadertoy_renderer.py | 4 +- examples/shadertoy/shadertoy/utils.py | 153 +++++++++--------- 3 files changed, 80 insertions(+), 79 deletions(-) diff --git a/examples/shadertoy/shadertoy/app.py b/examples/shadertoy/shadertoy/app.py index 9b1ddfd7a2..0adbfb2d6e 100644 --- a/examples/shadertoy/shadertoy/app.py +++ b/examples/shadertoy/shadertoy/app.py @@ -53,7 +53,7 @@ class ShadertoyApp(toga.App): def startup(self): - if toga.backend == "toga_android": + if toga.backend in {"toga_android", "toga_iOS"}: self.main_window = toga.MainWindow() else: self.main_window = toga.MainWindow( diff --git a/examples/shadertoy/shadertoy/shadertoy_renderer.py b/examples/shadertoy/shadertoy/shadertoy_renderer.py index ccb7fb7906..098009a394 100644 --- a/examples/shadertoy/shadertoy/shadertoy_renderer.py +++ b/examples/shadertoy/shadertoy/shadertoy_renderer.py @@ -126,13 +126,13 @@ class ShadertoyRenderer: deltas: list[float] #: The position of the pointer, or None if not available. - pointer: tuple[int, int] | None + pointer: tuple[int, int] #: Whether the mouse button (or user touch) is down or up. mouse_down: bool #: The starting location of most recent drag operation. - drag_start: tuple[int, int] | None + drag_start: tuple[int, int] #: The source for the mainImage function. _source: str diff --git a/examples/shadertoy/shadertoy/utils.py b/examples/shadertoy/shadertoy/utils.py index 79d8895f7b..c49bee0d28 100644 --- a/examples/shadertoy/shadertoy/utils.py +++ b/examples/shadertoy/shadertoy/utils.py @@ -1,4 +1,5 @@ import os +from collections.abc import Callable from contextlib import contextmanager from dataclasses import dataclass from enum import IntEnum @@ -61,6 +62,78 @@ class OpenGLError(RuntimeError): pass +@dataclass +class Buffer: + """A dataclass that encapsulates an OpenGL buffer. + + This can be used as a context manager to automatically bind and unbind + the buffer for use. + """ + + buffer_type: int + usage: int + + def create(self, data: bytes): + """Generate a new buffer and set the data into it.""" + self.id = GL.glGenBuffers(1) + self.set_data(data) + + def set_data(self, data: bytes): + """Set data into a buffer. + + This automatically binds the buffer.. + """ + with self: + GL.glBufferData(self.buffer_type, data, self.usage) + + def bind(self): + """Bind the buffer so that it is the current buffer.""" + GL.glBindBuffer(self.buffer_type, self.id) + + def unbind(self): + """Unbind the buffer so that it is no longer the current buffer.""" + GL.glBindBuffer(self.buffer_type, 0) + + def __enter__(self): + """Enter the context manager, binding the buffer.""" + self.bind() + + def __exit__(self, exc_type, exc_value, traceback): + """Exit the context manager, unbinding the buffer.""" + self.unbind() + return False + + +@dataclass +class VertexArrayObject: + """A dataclass that encapsulates an OpenGL vertex array object. + + This can be used as a context manager to automatically bind and unbind + the vertex array object for use. + """ + + def create(self): + """Generate a new vertex array object.""" + self.id = GL.glGenVertexArrays(1) + + def bind(self): + """Make the vertex array object the current vertex array object.""" + GL.glBindVertexArray(self.id) + + def unbind(self): + """Make the vertex array object no longer the current vertex array object.""" + GL.glBindVertexArray(0) + + def __enter__(self): + """Enter the context manager, binding the vertex array object.""" + self.bind() + + def __exit__(self, exc_type, exc_value, traceback): + """Exit the context manager, unbinding the vertex array object.""" + self.unbind() + return False + + @dataclass class Shader: """A dataclass that encapsulates an OpenGL shader.""" @@ -135,7 +208,7 @@ def use(self): finally: GL.glUseProgram(0) - def active_uniforms(self): + def active_uniforms(self) -> dict[str, tuple[int, int, int, Callable]]: """Get information about the program's active uniforms. Returns a dictionary of uniform names mapping to the location, size, @@ -154,11 +227,11 @@ def active_uniforms(self): } return uniforms - def attribute(self, item): + def attribute(self, item: str) -> int: """Return the location of an active attribute.""" return GL.glGetAttribLocation(self.id, item) - def set_uniforms(self, uniforms): + def set_uniforms(self, uniforms: dict[str, tuple]): """Set the values of active uniforms from a dictionary of values.""" for uniform, (loc, size, _, setter) in self.active_uniforms().items(): if uniform in uniforms: @@ -167,81 +240,9 @@ def set_uniforms(self, uniforms): else: setter(loc, size, uniforms[uniform]) - def bind_attribute_buffer(self, attribute, vbo, *, size=4): + def bind_attribute_buffer(self, attribute: str, vbo: Buffer, *, size: int = 4): """Bind an active attribute to a vertex buffer object.""" loc = self.attribute(attribute) with vbo: GL.glEnableVertexAttribArray(loc) GL.glVertexAttribPointer(loc, size, GL.GL_FLOAT, GL.GL_FALSE, 0, None) - - -@dataclass -class Buffer: - """A dataclass that encapsulates an OpenGL buffer. - - This can be used as a context manager to automatically bind and unbind - the buffer for use. - """ - - buffer_type: int - usage: int - - def create(self, data): - """Generate a new buffer and set the data into it.""" - self.id = GL.glGenBuffers(1) - self.set_data(data) - - def set_data(self, data): - """Set data into a buffer. - - This automatically binds the buffer.. - """ - with self: - GL.glBufferData(self.buffer_type, data, self.usage) - - def bind(self): - """Bind the buffer so that it is the current buffer.""" - GL.glBindBuffer(self.buffer_type, self.id) - - def unbind(self): - """Unbind the buffer so that it is no longer the current buffer.""" - GL.glBindBuffer(self.buffer_type, 0) - - def __enter__(self): - """Enter the context manager, binding the buffer.""" - self.bind() - - def __exit__(self, exc_type, exc_value, traceback): - """Exit the context manager, unbinding the buffer.""" - self.unbind() - return False - - -@dataclass -class VertexArrayObject: - """A dataclass that encapsulates an OpenGL vertex array object. - - This can be used as a context manager to automatically bind and unbind - the vertex array object for use. - """ - - def create(self): - """Generate a new vertex array object.""" - self.id = GL.glGenVertexArrays(1) - - def bind(self): - """Make the vertex array object the current vertex array object.""" - GL.glBindVertexArray(self.id) - - def unbind(self): - """Make the vertex array object no longer the current vertex array object.""" - GL.glBindVertexArray(0) - - def __enter__(self): - """Enter the context manager, binding the vertex array object.""" - self.bind() - - def __exit__(self, exc_type, exc_value, traceback): - """Exit the context manager, unbinding the vertex array object.""" - self.unbind() - return False From 582c5c5462da033a7800ca2a769e8b5b82a8e2b0 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 8 Apr 2026 14:27:30 +0100 Subject: [PATCH 15/26] Update opengl example to be an obj file viewer app. --- examples/opengl/LICENSE | 2 + examples/opengl/opengl/app.py | 83 +- examples/opengl/opengl/matrix.py | 163 + examples/opengl/opengl/obj_file.py | 222 + examples/opengl/opengl/obj_file_renderer.py | 324 + examples/opengl/opengl/renderer_android.py | 143 - examples/opengl/opengl/renderer_iOS.py | 17 - examples/opengl/opengl/renderer_pyopengl.py | 139 - .../opengl/opengl/resources/LICENSE_well.txt | 121 + examples/opengl/opengl/resources/__init__.py | 0 examples/opengl/opengl/resources/well.mtl | 180 + examples/opengl/opengl/resources/well.obj | 7152 +++++++++++++++++ examples/opengl/opengl/utils.py | 305 + examples/opengl/opengl/utils_android.py | 87 + examples/opengl/opengl/utils_iOS.py | 88 + examples/opengl/opengl/utils_pyglet.py | 100 + examples/opengl/opengl/utils_pyopengl.py | 35 + 17 files changed, 8841 insertions(+), 320 deletions(-) create mode 100644 examples/opengl/opengl/matrix.py create mode 100644 examples/opengl/opengl/obj_file.py create mode 100644 examples/opengl/opengl/obj_file_renderer.py delete mode 100644 examples/opengl/opengl/renderer_android.py delete mode 100644 examples/opengl/opengl/renderer_iOS.py delete mode 100644 examples/opengl/opengl/renderer_pyopengl.py create mode 100644 examples/opengl/opengl/resources/LICENSE_well.txt create mode 100644 examples/opengl/opengl/resources/__init__.py create mode 100644 examples/opengl/opengl/resources/well.mtl create mode 100644 examples/opengl/opengl/resources/well.obj create mode 100644 examples/opengl/opengl/utils.py create mode 100644 examples/opengl/opengl/utils_android.py create mode 100644 examples/opengl/opengl/utils_iOS.py create mode 100644 examples/opengl/opengl/utils_pyglet.py create mode 100644 examples/opengl/opengl/utils_pyopengl.py diff --git a/examples/opengl/LICENSE b/examples/opengl/LICENSE index f09583bfab..0d82ce97b0 100644 --- a/examples/opengl/LICENSE +++ b/examples/opengl/LICENSE @@ -1 +1,3 @@ Released under the same license as Toga. See the root of the Toga repository for details. + +The well.obj and well.mtl files in the resources directory are CC0 1.0 licensed. See resources/LICENSE_well.txt for details. diff --git a/examples/opengl/opengl/app.py b/examples/opengl/opengl/app.py index 01af194a23..9ac0893d65 100644 --- a/examples/opengl/opengl/app.py +++ b/examples/opengl/opengl/app.py @@ -1,37 +1,78 @@ +import asyncio + import toga +import toga.sources +from .obj_file import parse_obj_file +from .obj_file_renderer import ObjFileRenderer -class OpenGLApp(toga.App): - def startup(self): - self.main_window = toga.MainWindow( - size=(800, 500), resizable=True, minimizable=False - ) - if toga.backend in {"toga_cocoa", "toga_qt", "toga_gtk"}: - from .renderer_pyopengl import Renderer - elif toga.backend == "toga_android": - from .renderer_android import Renderer - elif toga.backend == "toga_iOS": - from .renderer_iOS import Renderer - else: - raise RuntimeError(f"Toga backend {toga.backend} is not supported.") +class ObjFile(toga.Document): + description = "Wavefront Obj File" + extensions = ["obj"] - renderer = Renderer() + def create(self): + # Create the main window for the document. The window has a single widget; + # when that widget changes, the document is modified. + self.renderer = ObjFileRenderer() - opengl_view = toga.OpenGLView(renderer, flex=1) + opengl_view = toga.OpenGLView(self.renderer, flex=1) + + async def animate(): + while True: + await asyncio.sleep(0.01) + opengl_view.redraw() + + loop = asyncio.get_running_loop() + self.task = loop.create_task(animate()) - # Create the outer box with 2 rows outer_box = toga.Box(children=[opengl_view]) - # Add the content on the main window - self.main_window.content = outer_box + self.main_window = toga.DocumentWindow( + doc=self, + content=outer_box, + on_close=self.stop_animation, + ) - # Show the main window - self.main_window.show() + def read(self): + # Read the content of the file represented by the document, and populate the + # widgets in the main window with that content. + try: + self.renderer.data = parse_obj_file(self.path) + except Exception: + import traceback + + traceback.print_exc() + dialog = toga.StackTraceDialog( + title=f"Error loading {self.path}", + message="Exception while reading file.", + content=traceback.format_exc(), + ) + # hold reference while task is running + self._dialog_task = asyncio.create_task(self.main_window.dialog(dialog)) + # remove reference when done + self._dialog_task.add_done_callback( + lambda task: self.__delattr__("_dialog_task") + ) + self.main_window.close() + + def stop_animation(self, *args, **kwargs): + self.task.cancel() + return True + + +class OpenGLApp(toga.App): + def startup(self): + self.main_window = None + self.documents.open(self.paths.app / "resources" / "well.obj") def main(): - return OpenGLApp("OpenGL", "org.beeware.toga.examples.opengl") + return OpenGLApp( + "Obj File Viewer Example", + "org.beeware.toga.examples.opengl", + document_types=[ObjFile], + ) if __name__ == "__main__": diff --git a/examples/opengl/opengl/matrix.py b/examples/opengl/opengl/matrix.py new file mode 100644 index 0000000000..ba9a339da6 --- /dev/null +++ b/examples/opengl/opengl/matrix.py @@ -0,0 +1,163 @@ +from math import cos, pi, sin, tan + + +# try to avoid a NumPy dependency in an example +class Matrix: + data: list[list[int]] + shape: tuple[int, int] + strides: tuple[int, int] + + def __init__(self, data=None, shape=None, strides=None): + if shape is None: + rows = len(data) + cols = max(len(row) for row in data) + shape = (rows, cols) + self.shape = tuple(shape) + if strides is None: + strides = (shape[1], 1) + self.strides = tuple(strides) + self.data = [0.0] * ( + (shape[0] - 1) * strides[0] + (shape[1] - 1) * strides[1] + 1 + ) + if data is not None: + for i, row in enumerate(data): + if i >= shape[0]: + break + for j, value in enumerate(row): + if j >= shape[1]: + break + self[i, j] = value + + def __getitem__(self, index): + return self.data[index[0] * self.strides[0] + index[1] * self.strides[1]] + + def __setitem__(self, index, value): + self.data[index[0] * self.strides[0] + index[1] * self.strides[1]] = value + + def __mul__(self, other): + if isinstance(other, (int, float, complex)): + result = Matrix(shape=self.shape) + result.data = [other * value for value in self.data] + else: + return NotImplemented + + def __rmul__(self, other): + if isinstance(other, (int, float, complex)): + result = Matrix(shape=self.shape) + result.data = [other * value for value in self.data] + else: + return NotImplemented + + def __matmul__(self, other): + if isinstance(other, Matrix) and self.shape[1] == other.shape[0]: + result = Matrix(shape=(self.shape[0], other.shape[1])) + for i in range(self.shape[0]): + for j in range(other.shape[1]): + result[i, j] = sum( + self[i, k] * other[k, j] for k in range(self.shape[1]) + ) + return result + elif isinstance(other, (int, float, complex)): + return self * other + else: + return NotImplemented + + def __rmatmul__(self, other): + if isinstance(other, (int, float, complex)): + return self * other + else: + return NotImplemented + + def __iter__(self): + yield from self.data + + def __str__(self): + return "\n".join( + ", ".join(f"{self[i, j]:5.2f}" for j in range(self.shape[1])) + for i in range(self.shape[0]) + ) + + def transpose(self): + result = Matrix(shape=self.shape[::-1], strides=self.strides[::-1]) + result.data = self.data.copy() + return result + + @classmethod + def identity(cls, n): + result = cls(shape=(n, n)) + for i in range(n): + result[i, i] = 1.0 + return result + + @classmethod + def translation(cls, *v): + n = len(v) + result = cls.identity(n + 1) + for i in range(n): + result[n, i] = v[i] + return result + + @classmethod + def skewing(cls, *v): + n = len(v) + result = cls.identity(n + 1) + for i in range(n): + result[i, n] = v[i] + return result + + @classmethod + def scaling(cls, *v): + n = len(v) + result = cls.identity(n + 1) + for i in range(n): + result[i, i] = v[i] + return result + + @classmethod + def rotation(cls, angle, d0=0, d1=1, n=4): + c = cos(angle) + s = sin(angle) + result = cls.identity(n) + result[d0, d0] = c + result[d0, d1] = s + result[d1, d0] = -s + result[d1, d1] = c + return result + + @classmethod + def rotation_x(cls, angle): + return cls.rotation(angle, 1, 2) + + @classmethod + def rotation_y(cls, angle): + return cls.rotation(angle, 0, 2) + + @classmethod + def rotation_z(cls, angle): + return cls.rotation(angle, 0, 1) + + @classmethod + def perspective(self, fov, aspect, near_clip, far_clip): + f = tan((pi - fov) / 2) + scale = 1 / (near_clip - far_clip) + return Matrix( + [ + [f / aspect, 0.0, 0.0], + [0.0, f, 0.0, 0.0], + [0.0, 0.0, (near_clip + far_clip) * scale, -1.0], + [0.0, 0.0, near_clip * far_clip * scale / 2, 0.0], + ], + shape=(4, 4), + ) + + @classmethod + def projection(self, width, height, depth): + return Matrix( + [ + [2 / width, 0.0, 0.0], + [0.0, 2 / height, 0.0, 0.0], + [0.0, 0.0, 1 / depth, 0.0], + [0.0, 0.0, 0.0, 1.0], + ], + shape=(4, 4), + ) diff --git a/examples/opengl/opengl/obj_file.py b/examples/opengl/opengl/obj_file.py new file mode 100644 index 0000000000..a2436e9407 --- /dev/null +++ b/examples/opengl/opengl/obj_file.py @@ -0,0 +1,222 @@ +"""A very basic Wavefront Obj/Mtl file reader + +This handles polygon-face geometries, and basic ambient/diffuse/specular +materials, but not texture maps. +""" + +from dataclasses import dataclass, field +from pathlib import Path + + +def bbox(vertices): + """Get the bounding box of a collection of vertices. + + Assumes vertices are a list of coordinates `[x0, y0, z0, x1, y1, z1, ...]`. + """ + x0 = min(vertices[::3]) + y0 = min(vertices[1::3]) + z0 = min(vertices[2::3]) + x1 = max(vertices[::3]) + y1 = max(vertices[1::3]) + z1 = max(vertices[2::3]) + return (x0, y0, z0, x1, y1, z1) + + +@dataclass +class Material: + """A simple ambient/diffuse/specular material.""" + + name: str | None = None + shininess: float = 0.0 + ambient: tuple[float, float, float] = (0.0, 0.0, 0.0) + diffuse: tuple[float, float, float] = (1.0, 1.0, 1.0) + specular: tuple[float, float, float] = (0.0, 0.0, 0.0) + emissive: tuple[float, float, float] = (0.0, 0.0, 0.0) + refraction: float = 1.0 + opacity: float = 1.0 + illum: int = 0 + transmission: tuple[float, float, float] = (0.0, 0.0, 0.0) + + +@dataclass +class Geometry: + """A triangulated geometry patch. + + Data is stored as flat lists. + """ + + vertices: list[float] = field(default_factory=list) + textures: list[float] = field(default_factory=list) + normals: list[float] = field(default_factory=list) + colors: list[float] = field(default_factory=list) + name: str | None = None + material: Material = field(default_factory=Material) + + +def parse_mtl_file(path: Path): + """Material file parser + + :params path: The path to the material file. + :returns: A dictionary mapping material names to Material objects. + """ + materials = {} + print(path) + + with open(path) as f: + for line in f: + # discard comments + line, *comments = line.split("#") + line = line.strip() + if not line: + continue + match line.split(maxsplit=1): + case ["newmtl", name]: + material = Material(name=name) + materials[name] = material + case ["Ns", args]: + material.shininess = float(args) + case ["Ka", args]: + material.ambient = tuple(float(arg) for arg in args.split()) + case ["Kd", args]: + material.diffuse = tuple(float(arg) for arg in args.split()) + case ["Ks", args]: + material.specular = tuple(float(arg) for arg in args.split()) + case ["Tf", args]: + material.filter = tuple(float(arg) for arg in args.split()) + case ["d", args]: + material.opacity = float(args) + case ["Tr", args]: + material.opacity = 1 - float(args) + case ["Ni", args]: + material.refraction = float(args) + case [keyword, *rest]: + # unhandled + print(f"Unhandled keyword: {keyword}", " ".join(rest)) + + print(materials.keys()) + return materials + + +def parse_face_vertex(face_vertex): + """Parse vertex indices in a face. + + :params face_vertex: A face index representation like '1/2/3' or '-1//3'. + :returns: A dictionary mapping index names to pythonic 0-based indices. + """ + parts = { + coord: int(value) + for coord, value in zip( + ["vertex", "texture", "normal"], face_vertex.split("/"), strict=False + ) + if value + } + # normalize indexing + indices = { + coord: index - 1 if index > 0 else index for coord, index in parts.items() + } + return indices + + +def parse_obj_file(path: Path): + """Obj file parser + + This parser assumes .mtl file paths are relative to the directory the .obj file + is in. + + :params path: The path to the material file. + :returns: A list of Geometry objects. + """ + vertices = [] + colors = [] + textures = [] + normals = [] + geometry = Geometry() + material = Material() + materials = {} + geometries = [] + + with open(path) as f: + for line in f: + # discard comments + line, *comments = line.split("#") + line = line.strip() + if not line: + # blank line, skip + continue + match line.split(maxsplit=1): + case ["o", *name]: + if geometry.vertices: + geometries.append(geometry) + if name: + geometry = Geometry(name=name[0], material=material) + else: + geometry = Geometry(material=material) + case ["g", *name]: + # group + if geometry.vertices: + geometries.append(geometry) + if name: + geometry = Geometry(name=name[0], material=material) + else: + geometry = Geometry(material=material) + case ["mtllib", file]: + file = file.strip() + print(path.parent, file) + materials.update(parse_mtl_file(path.parent / file)) + case ["usemtl", name]: + material = materials.get(name, Material(name=name)) + if geometry.vertices: + geometries.append(geometry) + geometry = Geometry(material=material) + else: + geometry.material = material + case ["v", args]: + # vertex + values = [float(arg) for arg in args.split()] + vertices.append(values[:3]) + if len(values) == 6: + colors.append(values[3:]) + else: + colors.append([1.0, 1.0, 1.0]) + case ["vt", args]: + # texture coord + textures.append([float(arg) for arg in args.split()]) + case ["vn", args]: + # normal vector + normals.append([float(arg) for arg in args.split()]) + case ["f", args]: + # face + indices = [parse_face_vertex(arg) for arg in args.split()] + # Obj file spec says faces are *convex* flat polygons, + # so fan triangulations should work. If it doesn't it's a + # bad file. + i = indices[0] + for j, k in zip(indices[1:-1], indices[2:], strict=True): + geometry.vertices.extend(vertices[i["vertex"]]) + geometry.vertices.extend(vertices[j["vertex"]]) + geometry.vertices.extend(vertices[k["vertex"]]) + geometry.colors.extend(colors[i["vertex"]]) + geometry.colors.extend(colors[j["vertex"]]) + geometry.colors.extend(colors[k["vertex"]]) + if textures and "texture" in i: + geometry.textures.extend(textures[i["texture"]]) + geometry.textures.extend(textures[j["texture"]]) + geometry.textures.extend(textures[k["texture"]]) + elif len(textures) == len(vertices): + geometry.textures.extend(textures[i["vertex"]]) + geometry.textures.extend(textures[j["vertex"]]) + geometry.textures.extend(textures[k["vertex"]]) + if normals and "normal" in i: + geometry.normals.extend(normals[i["normal"]]) + geometry.normals.extend(normals[j["normal"]]) + geometry.normals.extend(normals[k["normal"]]) + elif len(normals) == len(vertices): + geometry.normals.extend(normals[i["vertex"]]) + geometry.normals.extend(normals[j["vertex"]]) + geometry.normals.extend(normals[k["vertex"]]) + case [keyword, *rest]: + # unhandled + print(f"Unhandled keyword: {keyword}", " ".join(rest)) + if geometry.vertices: + geometries.append(geometry) + return geometries diff --git a/examples/opengl/opengl/obj_file_renderer.py b/examples/opengl/opengl/obj_file_renderer.py new file mode 100644 index 0000000000..50a752e53f --- /dev/null +++ b/examples/opengl/opengl/obj_file_renderer.py @@ -0,0 +1,324 @@ +""" +Generic Shadertoy Renderer +========================== + +This renderer allows the use of basic Shadertoy-style fragment +rendering. It provides a single image shader, pointer state and time +data, but doesn't provide additional shaders, textures, channels, +video or sound support. + +Differences between OpenGL APIs are handled in the appropriate +utils object: which one is selected depends on the toga backend. +The utils provide abstractions for Shaders, Programs, Buffers and +Vertex Array Objects which make the code in the renderer cleaner +and hide the OpenGL code somewhat. +""" + +import array +import time +import traceback +from math import pi + +from .matrix import Matrix +from .obj_file import bbox +from .utils import ( + GL, + VERSION_HEADER, + Buffer, + BufferType, + BufferUsage, + OpenGLError, + Program, + Shader, + ShaderType, + VertexArrayObject, +) + +#: Vertex shader: displays triangles in 2D. +VERTEX_SHADER_SOURCE = ( + VERSION_HEADER + + """ + +precision highp float; + +in vec4 position; +in vec3 normal; +in vec3 color; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 world; +uniform mat4 world_normal; +uniform vec3 view_position; + +out vec3 vertex_normal; +out vec3 vertex_color; +out vec3 surface_to_view; + +void main() { + vec4 world_position = world * position; + gl_Position = projection * view * world_position; + surface_to_view = view_position - world_position.xyz; + vertex_normal = mat3(world) * normal; + vertex_color = color; +} +""" +) + +#: Fragment shader: calls mainImage to set pixel colors. +FRAGMENT_SHADER_SOURCE = ( + VERSION_HEADER + + """ + +precision highp float; + +in vec3 vertex_normal; +in vec3 vertex_color; +in vec3 surface_to_view; + +uniform vec3 light_direction; +uniform vec3 ambient_light; + +uniform vec3 diffuse; +uniform vec3 ambient; +uniform vec3 emissive; +uniform vec3 specular; +uniform float shininess; +uniform float opacity; + +out vec4 output_color; + +void main() { + vec3 normal = normalize(vertex_normal); + float light = dot(normal, light_direction) * 0.5 + 0.5; + + output_color = vec4( + emissive + + ambient * ambient_light + + diffuse * light, + opacity + ); + + if (shininess > 0) { + vec3 surface_to_view_direction = normalize(surface_to_view); + vec3 half_vector = normalize(light_direction + surface_to_view_direction); + float specular_light = clamp(dot(normal, half_vector), 0.0, 1.0); + + output_color.rgb += specular * pow(specular_light, shininess); + } + //output_color = vec4(vec3(gl_FragCoord.z), 1.0); +} +""" +) + +#: Message to display in side-panel giving info about +#: values of uniforms. +UNIFORM_VALUES = """Frame: {iFrame[0]} +Frame rate: {iFrameRate[0]:.3f} fps + +Resolution: {iResolution} +Mouse: ({iMouse[0]:.1f}, {iMouse[1]:.1f}) ({iMouse[2]:.1f}, {iMouse[3]:.1f}) + +Time: {iTime[0]:.3f} s +Time delta: {iTimeDelta[0]:.3f} s +Date: {iDate[0]}-{iDate[1]}-{iDate[2]} {iDate[3]:.3f} +""" + + +class ObjFileRenderer: + """Renderer for Shadertoy fragment renderers. + + This renderer expects to be provided with GLSL code that implements the + mainImage function in the fragment shader. This code is provided via the + `source` property. The message attribute is set to information about the + state of the renderer, either: + - the data provided to the uniforms; or + - status/error messages + """ + + #: A message containing information about the renderer's state. + message: str + + #: The source for the mainImage function. + _data: list + + data_updated = False + + def __init__(self, data=()): + self.message = "Uninitialized." + self.data = data + self.radius = 1.0 + self.center = (0.0, 0.0, 0.0) + + @property + def data(self): + return self._data + + @data.setter + def data(self, data): + """Set a new value for the data.""" + self._data = data + self.data_updated = True + + def on_init(self, widget, **kwargs): + """Initialize the OpenGL state.""" + self.message = "Initializing...\n\n" + try: + if not self.data: + return + GL.glEnable(GL.GL_CULL_FACE) + GL.glEnable(GL.GL_DEPTH_TEST) + self._initialize_program(VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) + + self.buffers = [] + bboxes = [] + for geometry in self.data: + bboxes.extend(bbox(geometry.vertices)) + buffers = {} + buffers["position"] = self._initialize_buffer(geometry.vertices) + buffers["normal"] = self._initialize_buffer(geometry.normals) + buffers["color"] = self._initialize_buffer(geometry.colors) + self.buffers.append(buffers) + self.bbox = bbox(bboxes) + self.center = tuple((self.bbox[i] + self.bbox[i + 3]) / 2 for i in range(3)) + self.radius = sum( + abs(v - c) for v, c in zip(self.bbox[3:], self.center, strict=True) + ) + + self.vaos = [] + for buffers in self.buffers: + self.vaos.append(self._initialize_attributes(buffers)) + except OpenGLError as exc: + # Add OpenGL error to messages + # This is most likely an error in the source, so this is important + # user feedback. + self.message += exc.args[0] + raise + except Exception: + # Add other errors to messages + # This is most likely caused by a bug in the renderer. + self.message += traceback.format_exc() + raise + else: + # Indicate success. + self.data_updated = False + self.message += "Initialization successful" + print(self.message) + + def on_render( + self, + widget, + size, + pointer=(-1, -1), + buttons=frozenset(), + **kwargs, + ): + """Render a frame using OpenGL.""" + if self.data_updated or not hasattr(self, "program"): + self.on_init(widget) + + self.message = "Rendering...\n\n" + GL.glClearColor(0.0, 0.0, 1.0, 1.0) + GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) + + if not hasattr(self, "program"): + # something seriously wrong, don't try to render + return + + t = time.time() + aspect = size[0] / size[1] + + try: + # Set the uniform values into a dictionary to be applied later + uniforms = { + "projection": Matrix.perspective( + pi / 6, aspect, self.radius / 100, self.radius * 50 + ), + "view": Matrix.translation(0, 0, -3 * self.radius), + "view_position": (0, 0, -3 * self.radius), + "world": Matrix.rotation_y(t) + @ Matrix.translation(-self.center[0], -self.center[1], -self.center[2]), + "world_normals": Matrix.rotation_y(-t).transpose(), + "light_direction": (3 / 5, 0, 3 / 5), + "ambient_light": (0.0, 0.0, 0.0), + } + + # draw! + with self.program.use(): + self.program.set_uniforms(uniforms) + for obj, vao in zip(self.data, self.vaos, strict=True): + n_vertices = len(obj.vertices) // 3 + if hasattr(obj, "material"): + material = { + "diffuse": obj.material.diffuse, + "ambient": obj.material.ambient, + "emissive": obj.material.emissive, + "specular": obj.material.specular, + "opacity": obj.material.opacity, + "shininess": obj.material.shininess, + } + else: + material = { + "diffuse": (1.0, 1.0, 1.0), + "ambient": (0.0, 0.0, 0.0), + "emissive": (0.0, 0.0, 0.0), + "specular": (0.0, 0.0, 0.0), + "opacity": (1.0,), + } + self.program.set_uniforms(material) + with vao: + GL.glDrawArrays(GL.GL_TRIANGLES, 0, n_vertices) + + # Update frame and time delta information + # self.frame += 1 + # if len(self.deltas) > 100: + # del self.deltas[0] + except Exception: + # Display error messages + self.message += traceback.format_exc() + raise + + def _initialize_buffer(self, data): + """Set some buffer data. + + This is never updated, so we call once during init. + """ + data_bytes = bytes(array.array("f", data)) + buffer_object = Buffer(BufferType.array, BufferUsage.static_draw) + buffer_object.create(data_bytes) + return buffer_object + + def _initialize_program(self, vertex_shader_code, fragment_shader_code): + """Initialize the shader program. + + This: + - creates the shaders and the program + - compiles the shaders + - links the program + - checks for errors + - deletes the compiled shaders + + This needs to be called every time the source changes. + """ + self.program = Program( + [ + Shader(ShaderType.vertex, vertex_shader_code), + Shader(ShaderType.fragment, fragment_shader_code), + ] + ) + self.program.create() + + def _initialize_attributes(self, attributes): + """Initialize the vertex attribute object. + + This creates a vertex attribute object for the specified attribute, + and then binds the vertex buffer object to that attribute. + + This needs to be called every time the program changes. + """ + attribute_object = VertexArrayObject() + attribute_object.create() + with attribute_object: + for attribute, buffer in attributes.items(): + self.program.bind_attribute_buffer(attribute, buffer, size=3) + return attribute_object diff --git a/examples/opengl/opengl/renderer_android.py b/examples/opengl/opengl/renderer_android.py deleted file mode 100644 index 45d90ce70a..0000000000 --- a/examples/opengl/opengl/renderer_android.py +++ /dev/null @@ -1,143 +0,0 @@ -import array - -from android.opengl import GLES32 as GL -from java import jarray, jint -from java.nio import ByteBuffer, ByteOrder - -vertext_shader_source = """ - -layout(location = 0) in vec4 position; - -void main() -{ - gl_Position = position; -} -""" -fragment_shader_source = """ - -out vec4 outputColor; -void main() -{ - outputColor = vec4(1.0f, 1.0f, 1.0f, 1.0f); -} -""" - - -def create_program(shader_list): - program = GL.glCreateProgram() - - for shader in shader_list: - GL.glAttachShader(program, shader) - - GL.glLinkProgram(program) - - # status = GL.glGetProgramiv(program, GL.GL_LINK_STATUS) - # if status == GL.GL_FALSE: - # strInfoLog = GL.glGetProgramInfoLog(program) - # raise RuntimeError("Linker failure: \n" + strInfoLog) - - for shader in shader_list: - GL.glDetachShader(program, shader) - - return program - - -def create_shader(shader_type, shader_code): - shader = GL.glCreateShader(shader_type) - GL.glShaderSource(shader, shader_code) - - GL.glCompileShader(shader) - - # status = None - # GL.glGetShaderiv(shader, GL.GL_COMPILE_STATUS, status) - # if status == GL.GL_FALSE: - # info_log = GL.glGetShaderInfoLog(shader) - # shader_types = { - # GL.GL_VERTEX_SHADER: "vertex", - # GL.GL_GEOMETRY_SHADER: "geometry", - # GL.GL_FRAGMENT_SHADER: "fragment", - # } - # raise RuntimeError( - # f"Compilation failure for {shader_types.get(shader_type, 'unknown')} " - # f"shader:\n{info_log}" - # ) - - return shader - - -def initialize_program(vertex_shader_code, fragment_shader_code): - shaderList = [] - - shaderList.append(create_shader(GL.GL_VERTEX_SHADER, vertex_shader_code)) - shaderList.append(create_shader(GL.GL_FRAGMENT_SHADER, fragment_shader_code)) - - program = create_program(shaderList) - - for shader in shaderList: - GL.glDeleteShader(shader) - - return program - - -def initialize_vbo(vertex_positions): - buffers = jarray(jint)([0]) - GL.glGenBuffers(1, buffers, 0) - vbo = buffers[0] - - vertex_bytes = bytes(array.array("f", vertex_positions)) - nbytes = len(vertex_bytes) - vertex_buffer = ByteBuffer.allocateDirect(nbytes) - vertex_buffer.order(ByteOrder.nativeOrder()) - vertex_buffer.put(vertex_bytes) - vertex_buffer.position(0) - GL.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo) - GL.glBufferData(GL.GL_ARRAY_BUFFER, nbytes, vertex_buffer, GL.GL_STATIC_DRAW) - GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0) - - return vbo - - -class Renderer: - def on_init(self, widget, **kwargs): - GL.glClearColor(1.0, 1.0, 0.0, 1.0) - - vertex_positions = [ - 0.75, - 0.75, - 0.0, - 1.0, - 0.75, - -0.75, - 0.0, - 1.0, - -0.75, - -0.75, - 0.0, - 1.0, - ] - - self.program = initialize_program(vertext_shader_source, fragment_shader_source) - self.vbo = initialize_vbo(vertex_positions) - - buffers = jarray(jint)([0]) - GL.glGenVertexArrays(1, buffers, 0) - vao = buffers[0] - - GL.glBindVertexArray(vao) - - def on_render(self, widget, size, **kwargs): - vertex_dim = 4 - n_vertices = 3 - - GL.glClear(GL.GL_COLOR_BUFFER_BIT) - - GL.glUseProgram(self.program) - - GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vbo) - GL.glEnableVertexAttribArray(0) - GL.glVertexAttribPointer(0, vertex_dim, GL.GL_FLOAT, False, 0, 0) - - GL.glDrawArrays(GL.GL_TRIANGLES, 0, n_vertices) - - GL.glDisableVertexAttribArray(0) - GL.glUseProgram(0) diff --git a/examples/opengl/opengl/renderer_iOS.py b/examples/opengl/opengl/renderer_iOS.py deleted file mode 100644 index 515177056e..0000000000 --- a/examples/opengl/opengl/renderer_iOS.py +++ /dev/null @@ -1,17 +0,0 @@ -from toga_iOS.libs import opengles as GL - -# This is a very basic renderer using ctypes, because there are currently no -# Python Open GL ES wrappers for iOS - -# Constants -GL_COLOR_BUFFER_BIT = 16384 - - -class Renderer: - def on_init(self, widget, **kwargs): - # set the clear color to blue - GL.glClearColor(0.0, 0.0, 1.0, 1.0) - - def on_render(self, widget, size, **kwargs): - # clear the OpenGL view - GL.glClear(GL_COLOR_BUFFER_BIT) diff --git a/examples/opengl/opengl/renderer_pyopengl.py b/examples/opengl/opengl/renderer_pyopengl.py deleted file mode 100644 index 986d949a95..0000000000 --- a/examples/opengl/opengl/renderer_pyopengl.py +++ /dev/null @@ -1,139 +0,0 @@ -import array - -import OpenGL - -# Get spurious errors with Qt backend -OpenGL.ERROR_CHECKING = False - -from OpenGL import GL # noqa: E402 - -vertext_shader_source = """ -#version 330 core - -layout(location = 0) in vec4 position; - -void main() -{ - gl_Position = position; -} -""" -fragment_shader_source = """ -#version 330 core - -out vec4 outputColor; -void main() -{ - outputColor = vec4(1.0f, 1.0f, 1.0f, 1.0f); -} -""" - - -def create_program(shader_list): - program = GL.glCreateProgram() - - for shader in shader_list: - GL.glAttachShader(program, shader) - - GL.glLinkProgram(program) - - status = GL.glGetProgramiv(program, GL.GL_LINK_STATUS) - if status == GL.GL_FALSE: - strInfoLog = GL.glGetProgramInfoLog(program) - raise RuntimeError("Linker failure: \n" + strInfoLog) - - for shader in shader_list: - GL.glDetachShader(program, shader) - - return program - - -def create_shader(shader_type, shader_code): - shader = GL.glCreateShader(shader_type) - GL.glShaderSource(shader, shader_code) - - GL.glCompileShader(shader) - - status = None - GL.glGetShaderiv(shader, GL.GL_COMPILE_STATUS, status) - if status == GL.GL_FALSE: - info_log = GL.glGetShaderInfoLog(shader) - shader_types = { - GL.GL_VERTEX_SHADER: "vertex", - GL.GL_GEOMETRY_SHADER: "geometry", - GL.GL_FRAGMENT_SHADER: "fragment", - } - raise RuntimeError( - f"Compilation failure for {shader_types.get(shader_type, 'unknown')} " - f"shader:\n{info_log}" - ) - - return shader - - -def initialize_program(vertex_shader_code, fragment_shader_code): - shaderList = [] - - shaderList.append(create_shader(GL.GL_VERTEX_SHADER, vertex_shader_code)) - shaderList.append(create_shader(GL.GL_FRAGMENT_SHADER, fragment_shader_code)) - - program = create_program(shaderList) - - for shader in shaderList: - GL.glDeleteShader(shader) - - return program - - -def initialize_vbo(vertex_positions): - vbo = GL.glGenBuffers(1) - - GL.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo) - GL.glBufferData(GL.GL_ARRAY_BUFFER, vertex_positions, GL.GL_STATIC_DRAW) - GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0) - - return vbo - - -class Renderer: - def on_init(self, widget, **kwargs): - vertex_positions = bytes( - array.array( - "f", - [ - 0.75, - 0.75, - 0.0, - 1.0, - 0.75, - -0.75, - 0.0, - 1.0, - -0.75, - -0.75, - 0.0, - 1.0, - ], - ) - ) - - self.program = initialize_program(vertext_shader_source, fragment_shader_source) - self.vbo = initialize_vbo(vertex_positions) - GL.glBindVertexArray(GL.glGenVertexArrays(1)) - - def on_render(self, widget, size, **kwargs): - vertex_dim = 4 - n_vertices = 3 - - GL.glClearColor(1.0, 1.0, 0.0, 1.0) - GL.glClear(GL.GL_COLOR_BUFFER_BIT) - - GL.glUseProgram(self.program) - - GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vbo) - GL.glEnableVertexAttribArray(0) - GL.glVertexAttribPointer(0, vertex_dim, GL.GL_FLOAT, GL.GL_FALSE, 0, None) - - GL.glDrawArrays(GL.GL_TRIANGLES, 0, n_vertices) - - GL.glDisableVertexAttribArray(0) - GL.glUseProgram(0) diff --git a/examples/opengl/opengl/resources/LICENSE_well.txt b/examples/opengl/opengl/resources/LICENSE_well.txt new file mode 100644 index 0000000000..0e259d42c9 --- /dev/null +++ b/examples/opengl/opengl/resources/LICENSE_well.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/examples/opengl/opengl/resources/__init__.py b/examples/opengl/opengl/resources/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/opengl/opengl/resources/well.mtl b/examples/opengl/opengl/resources/well.mtl new file mode 100644 index 0000000000..e0a0e023c5 --- /dev/null +++ b/examples/opengl/opengl/resources/well.mtl @@ -0,0 +1,180 @@ +# Materials file from "Modular Village" +# https://opengameart.org/content/modular-village +# License: CC0 1.0 https://creativecommons.org/publicdomain/zero/1.0/ +# See LICENSE_well.txt + +newmtl Stucco +Ka 0.0000 0.0000 0.0000 +Kd 0.9373 0.8941 0.8196 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Wood_Light +Ka 0.0000 0.0000 0.0000 +Kd 0.7373 0.6314 0.4627 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Wood_Medium +Ka 0.0000 0.0000 0.0000 +Kd 0.6275 0.5255 0.3961 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Wood_Dark +Ka 0.0000 0.0000 0.0000 +Kd 0.5176 0.4196 0.3333 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Wood_Darker +Ka 0.0000 0.0000 0.0000 +Kd 0.3843 0.3137 0.2510 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Red_Roof +Ka 0.0000 0.0000 0.0000 +Kd 0.5765 0.2667 0.1922 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Dark_Red_Roof +Ka 0.0000 0.0000 0.0000 +Kd 0.4235 0.1804 0.1176 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Stone_Brit_Gray_Dark +Ka 0.0000 0.0000 0.0000 +Kd 0.4039 0.3529 0.3137 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Stone_Brit_Gray +Ka 0.0000 0.0000 0.0000 +Kd 0.4863 0.4235 0.3725 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Stone_Brit_Gray_Light +Ka 0.0000 0.0000 0.0000 +Kd 0.5725 0.4902 0.4275 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Stone_Bri_Gray_Lighter +Ka 0.0000 0.0000 0.0000 +Kd 0.6627 0.5961 0.5451 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Burlap +Ka 0.0000 0.0000 0.0000 +Kd 0.7255 0.6431 0.5216 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Dirt +Ka 0.0000 0.0000 0.0000 +Kd 0.7686 0.6863 0.5608 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Grass +Ka 0.0000 0.0000 0.0000 +Kd 0.1333 0.5451 0.1333 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Water +Ka 0.0000 0.0000 0.0000 +Kd 0.2000 0.5882 0.8196 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Window +Ka 0.0000 0.0000 0.0000 +Kd 0.6588 0.7059 0.7765 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Iron_Grey_-_Dark +Ka 0.0000 0.0000 0.0000 +Kd 0.3020 0.2863 0.2745 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Iron_Grey +Ka 0.0000 0.0000 0.0000 +Kd 0.4275 0.4196 0.3882 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Iron_Grey_-_Light +Ka 0.0000 0.0000 0.0000 +Kd 0.6196 0.6039 0.5804 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Straw +Ka 0.0000 0.0000 0.0000 +Kd 0.8824 0.7843 0.5255 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Canvas +Ka 0.0000 0.0000 0.0000 +Kd 0.8039 0.7412 0.5961 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Candlelight +Ka 0.0000 0.0000 0.0000 +Kd 1.0000 0.8824 0.5255 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 diff --git a/examples/opengl/opengl/resources/well.obj b/examples/opengl/opengl/resources/well.obj new file mode 100644 index 0000000000..9a306b1f19 --- /dev/null +++ b/examples/opengl/opengl/resources/well.obj @@ -0,0 +1,7152 @@ +# Well prop from "Modular Village" +# https://opengameart.org/content/modular-village +# License: CC0 1.0 https://creativecommons.org/publicdomain/zero/1.0/ +# See LICENSE_well.txt + +mtllib well.mtl +o object1 +g object1 +v 0.44523810 0.90516376 -0.48759427 +v 0.44523810 0.96240573 -0.54483624 +v 0.52619048 0.96240573 -0.54483624 +v 0.52619048 0.90516376 -0.48759427 +v 0.44523810 1.00476190 -0.40476190 +v 0.44523810 1.08571429 -0.40476190 +v 0.44523810 1.49047619 0.00000000 +v 0.44523810 1.40952381 0.00000000 +v 0.52619048 1.49047619 0.00000000 +v 0.52619048 1.08571429 -0.40476190 +v 0.52619048 1.40952381 0.00000000 +v 0.52619048 1.00476190 -0.40476190 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 0.99999999 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 0.99999999 0.00000000 +vt 0.99999999 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.99999999 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -0.00000000 0.70710678 -0.70710678 +vn -0.00000000 0.70710678 -0.70710678 +vn -0.00000000 0.70710678 -0.70710678 +vn -0.00000000 0.70710678 -0.70710678 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.63942740 0.76885148 +vn 0.00000000 -0.63942740 0.76885148 +vn 0.00000000 -0.63942740 0.76885148 +vn 0.00000000 -0.63942740 0.76885148 +vn 1.00000000 -0.00000000 -0.00000000 +vn 1.00000000 -0.00000000 -0.00000000 +vn -0.00000000 0.75059887 -0.66075815 +vn -0.00000000 0.75059887 -0.66075815 +vn -0.00000000 0.75059887 -0.66075815 +vn -0.00000000 0.75059887 -0.66075815 +s 1 +usemtl Wood_Darker +f 1/1/1 2/2/2 3/3/3 +f 1/1/1 3/3/3 4/4/4 +s 2 +f 1/5/5 5/6/6 2/8/8 +f 6/12/7 5/11/6 8/10/10 +f 6/12/7 8/10/10 7/9/9 +f 5/6/6 6/7/7 2/8/8 +s 3 +f 10/16/14 6/15/13 9/13/11 +f 6/15/13 7/14/12 9/13/11 +s 4 +f 12/20/18 10/19/17 11/17/15 +f 10/19/17 9/18/16 11/17/15 +f 3/33/31 12/35/18 4/36/32 +f 3/33/31 10/34/17 12/35/18 +s 5 +f 8/21/19 11/22/20 7/24/22 +f 11/22/20 9/23/21 7/24/22 +s 6 +f 5/28/26 12/27/25 11/26/24 +f 5/28/26 11/26/24 8/25/23 +s 7 +f 4/29/27 12/30/28 1/32/30 +f 12/30/28 5/31/29 1/32/30 +s 8 +f 2/37/33 6/38/34 10/39/35 +f 2/37/33 10/39/35 3/40/36 +o object10 +g object10 +v 0.48571429 0.94919108 -0.03571429 +v 0.50357143 0.92857143 0.00000000 +v 0.48571429 0.90795178 -0.03571429 +v 0.48571429 0.96981073 0.00000000 +v 0.48571429 0.94919108 0.03571429 +v 0.48571429 0.90795178 0.03571429 +v 0.48571429 0.88733212 0.00000000 +v -0.60357143 0.90795178 0.03571429 +v -0.60357143 0.88733212 0.00000000 +v -0.60357143 0.90795178 -0.03571429 +v -0.62142857 0.92857143 0.00000000 +v -0.60357143 0.94919108 -0.03571429 +v -0.60357143 0.96981073 0.00000000 +v -0.60357143 0.94919108 0.03571429 +vt 1.00000000 1.00000000 +vt 0.00000000 0.50000000 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.57894737 +vt 0.00000000 0.57894737 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.50000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.42105263 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.57894737 +vt 1.00000000 1.00000000 +vt 0.00000000 0.50000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.42105263 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.42105263 +vt 0.00000000 0.00000000 +vt 1.00000000 0.50000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.57894737 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.42105263 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vn 0.89442719 0.00000000 -0.44721360 +vn 0.89442719 0.00000000 -0.44721360 +vn 0.89442719 0.00000000 -0.44721360 +vn 0.89442719 0.38729833 -0.22360680 +vn 0.89442719 0.38729833 -0.22360680 +vn 0.89442719 0.38729833 -0.22360680 +vn 0.89442719 0.38729833 0.22360680 +vn 0.89442719 0.38729833 0.22360680 +vn 0.89442719 0.38729833 0.22360680 +vn 0.89442719 0.00000000 0.44721360 +vn 0.89442719 0.00000000 0.44721360 +vn 0.89442719 0.00000000 0.44721360 +vn 0.89442719 -0.38729833 0.22360680 +vn 0.89442719 -0.38729833 0.22360680 +vn 0.89442719 -0.38729833 0.22360680 +vn 0.00000000 -0.86602540 0.50000000 +vn 0.00000000 -0.86602540 0.50000000 +vn 0.00000000 -0.86602540 0.50000000 +vn 0.00000000 -0.86602540 0.50000000 +vn 0.00000000 -0.86602540 -0.50000000 +vn 0.00000000 -0.86602540 -0.50000000 +vn 0.00000000 -0.86602540 -0.50000000 +vn 0.00000000 -0.86602540 -0.50000000 +vn -0.89442719 -0.38729833 -0.22360680 +vn -0.89442719 -0.38729833 -0.22360680 +vn -0.89442719 -0.38729833 -0.22360680 +vn -0.89442719 0.00000000 -0.44721360 +vn -0.89442719 0.00000000 -0.44721360 +vn -0.89442719 0.00000000 -0.44721360 +vn -0.89442719 0.38729833 -0.22360680 +vn -0.89442719 0.38729833 -0.22360680 +vn -0.89442719 0.38729833 -0.22360680 +vn -0.89442719 0.38729833 0.22360680 +vn -0.89442719 0.38729833 0.22360680 +vn -0.89442719 0.38729833 0.22360680 +vn -0.89442719 -0.00000000 0.44721360 +vn -0.89442719 -0.00000000 0.44721360 +vn -0.89442719 -0.00000000 0.44721360 +vn -0.89442719 -0.38729833 0.22360680 +vn -0.89442719 -0.38729833 0.22360680 +vn -0.89442719 -0.38729833 0.22360680 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.86602540 0.50000000 +vn 0.00000000 0.86602540 0.50000000 +vn 0.00000000 0.86602540 0.50000000 +vn 0.00000000 0.86602540 0.50000000 +vn 0.00000000 0.86602540 -0.50000000 +vn 0.00000000 0.86602540 -0.50000000 +vn 0.00000000 0.86602540 -0.50000000 +vn 0.00000000 0.86602540 -0.50000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.89442719 -0.38729833 -0.22360680 +vn 0.89442719 -0.38729833 -0.22360680 +vn 0.89442719 -0.38729833 -0.22360680 +s 1 +usemtl Wood_Medium +f 13/41/37 14/42/38 15/43/39 +s 2 +f 16/44/40 14/45/41 13/46/42 +s 3 +f 17/47/43 14/48/44 16/49/45 +s 4 +f 18/50/46 14/51/47 17/52/48 +s 5 +f 19/53/49 14/54/50 18/55/51 +s 6 +usemtl Wood_Dark +f 18/56/52 20/57/53 19/59/55 +f 20/57/53 21/58/54 19/59/55 +s 7 +f 19/60/56 21/61/57 15/63/59 +f 21/61/57 22/62/58 15/63/59 +s 8 +usemtl Wood_Medium +f 21/64/60 23/65/61 22/66/62 +s 9 +f 22/67/63 23/68/64 24/69/65 +s 10 +f 24/70/66 23/71/67 25/72/68 +s 11 +f 25/73/69 23/74/70 26/75/71 +s 12 +f 26/76/72 23/77/73 20/78/74 +s 13 +f 20/79/75 23/80/76 21/81/77 +s 14 +usemtl Wood_Dark +f 17/82/78 26/83/79 18/85/81 +f 26/83/79 20/84/80 18/85/81 +s 15 +f 16/86/82 25/87/83 17/89/85 +f 25/87/83 26/88/84 17/89/85 +s 16 +f 13/90/86 24/91/87 16/93/89 +f 24/91/87 25/92/88 16/93/89 +s 17 +f 15/94/90 22/95/91 13/97/93 +f 22/95/91 24/96/92 13/97/93 +s 18 +usemtl Wood_Medium +f 15/98/94 14/99/95 19/100/96 +o object11 +g object11 +v 0.42857143 1.26805573 -0.07142857 +v 0.42857143 -0.00000000 -0.07142857 +v 0.42857143 0.00000000 0.07142857 +v 0.42857143 1.26805573 0.07142857 +v 0.32142857 0.00000000 0.07142857 +v 0.32142857 1.26805573 0.07142857 +v 0.32142857 1.26805573 -0.07142857 +v 0.32142857 -0.00000000 -0.07142857 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn 0.00000000 1.00000000 -0.00000000 +vn 0.00000000 1.00000000 -0.00000000 +vn 0.00000000 1.00000000 -0.00000000 +vn 0.00000000 1.00000000 -0.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +s 1 +usemtl Wood_Darker +f 30/104/100 29/103/99 27/101/97 +f 29/103/99 28/102/98 27/101/97 +s 2 +f 32/108/104 31/107/103 29/106/102 +f 32/108/104 29/106/102 30/105/101 +s 3 +f 33/109/105 34/110/106 32/112/108 +f 34/110/106 31/111/107 32/112/108 +s 4 +f 33/116/112 32/115/111 30/114/110 +f 33/116/112 30/114/110 27/113/109 +s 5 +f 27/117/113 28/118/114 33/120/116 +f 28/118/114 34/119/115 33/120/116 +s 6 +f 28/121/117 29/122/118 34/124/120 +f 29/122/118 31/123/119 34/124/120 +o object12 +g object12 +v 0.16683092 0.33928571 -0.33824536 +v 0.13770616 0.33928571 -0.33245208 +v 0.13770616 0.44642857 -0.33245208 +v 0.16683092 0.44642857 -0.33824536 +v 0.15420401 0.32142857 -0.32561845 +v 0.10982399 0.32142857 -0.21847559 +v 0.09332614 0.33928571 -0.22530922 +v 0.09719708 0.33928571 -0.20584868 +v 0.09719708 0.44642857 -0.20584868 +v 0.09332614 0.44642857 -0.22530922 +v 0.07682829 0.33928571 -0.21428571 +v 0.07682829 0.44642857 -0.21428571 +v 0.07682829 0.32142857 -0.23214286 +v -0.07682829 0.33928571 -0.21428571 +v -0.07682829 0.32142857 -0.23214286 +v -0.12120831 0.32142857 -0.33928571 +v 0.12120831 0.32142857 -0.33928571 +v -0.12120831 0.33928571 -0.35714286 +v 0.12120831 0.33928571 -0.35714286 +v -0.13770616 0.33928571 -0.33245208 +v -0.13770616 0.44642857 -0.33245208 +v -0.12120831 0.44642857 -0.35714286 +v -0.12120831 0.46428571 -0.33928571 +v 0.12120831 0.46428571 -0.33928571 +v 0.12120831 0.44642857 -0.35714286 +v 0.07682829 0.46428571 -0.23214286 +v 0.10982399 0.46428571 -0.21847559 +v 0.15420401 0.46428571 -0.32561845 +v 0.21847559 0.46428571 -0.10982399 +v 0.32561845 0.46428571 -0.15420401 +v 0.33824536 0.44642857 -0.16683092 +v 0.33245208 0.44642857 -0.13770616 +v 0.33245208 0.33928571 -0.13770616 +v 0.33824536 0.33928571 -0.16683092 +v 0.32561845 0.32142857 -0.15420401 +v 0.22530922 0.33928571 -0.09332614 +v 0.21847559 0.32142857 -0.10982399 +v 0.20584868 0.33928571 -0.09719708 +v 0.22530922 0.44642857 -0.09332614 +v 0.20584868 0.44642857 -0.09719708 +v 0.21428571 0.33928571 -0.07682829 +v 0.21428571 0.44642857 -0.07682829 +v 0.23214286 0.32142857 -0.07682829 +v 0.23214286 0.32142857 0.07682829 +v 0.21428571 0.33928571 0.07682829 +v 0.22530922 0.33928571 0.09332614 +v 0.33928571 0.32142857 0.12120831 +v 0.33245208 0.33928571 0.13770616 +v 0.35714286 0.33928571 0.12120831 +v 0.35714286 0.44642857 0.12120831 +v 0.33245208 0.44642857 0.13770616 +v 0.33824536 0.33928571 0.16683092 +v 0.33824536 0.44642857 0.16683092 +v 0.32561845 0.32142857 0.15420401 +v 0.16683092 0.33928571 0.33824536 +v 0.15420401 0.32142857 0.32561845 +v 0.10982399 0.32142857 0.21847559 +v 0.21847559 0.32142857 0.10982399 +v 0.09719708 0.33928571 0.20584868 +v 0.20584868 0.33928571 0.09719708 +v 0.09332614 0.33928571 0.22530922 +v 0.09332614 0.44642857 0.22530922 +v 0.09719708 0.44642857 0.20584868 +v 0.10982399 0.46428571 0.21847559 +v 0.13770616 0.44642857 0.33245208 +v 0.15420401 0.46428571 0.32561845 +v 0.16683092 0.44642857 0.33824536 +v 0.32561845 0.46428571 0.15420401 +v 0.13770616 0.33928571 0.33245208 +v 0.12120831 0.33928571 0.35714286 +v 0.12120831 0.44642857 0.35714286 +v 0.12120831 0.32142857 0.33928571 +v -0.12120831 0.33928571 0.35714286 +v -0.12120831 0.32142857 0.33928571 +v -0.07682829 0.32142857 0.23214286 +v 0.07682829 0.32142857 0.23214286 +v -0.13770616 0.33928571 0.33245208 +v -0.09332614 0.33928571 0.22530922 +v -0.07682829 0.33928571 0.21428571 +v -0.09332614 0.44642857 0.22530922 +v -0.07682829 0.44642857 0.21428571 +v 0.07682829 0.33928571 0.21428571 +v 0.07682829 0.44642857 0.21428571 +v -0.07682829 0.46428571 0.23214286 +v 0.07682829 0.46428571 0.23214286 +v 0.12120831 0.46428571 0.33928571 +v -0.12120831 0.46428571 0.33928571 +v -0.13770616 0.44642857 0.33245208 +v -0.12120831 0.44642857 0.35714286 +v -0.16683092 0.33928571 0.33824536 +v -0.16683092 0.44642857 0.33824536 +v -0.15420401 0.32142857 0.32561845 +v -0.33824536 0.33928571 0.16683092 +v -0.32561845 0.32142857 0.15420401 +v -0.21847559 0.32142857 0.10982399 +v -0.10982399 0.32142857 0.21847559 +v -0.20584868 0.33928571 0.09719708 +v -0.09719708 0.33928571 0.20584868 +v -0.22530922 0.33928571 0.09332614 +v -0.22530922 0.44642857 0.09332614 +v -0.20584868 0.44642857 0.09719708 +v -0.21847559 0.46428571 0.10982399 +v -0.10982399 0.46428571 0.21847559 +v -0.09719708 0.44642857 0.20584868 +v -0.15420401 0.46428571 0.32561845 +v -0.32561845 0.46428571 0.15420401 +v -0.33824536 0.44642857 0.16683092 +v -0.33245208 0.44642857 0.13770616 +v -0.33245208 0.33928571 0.13770616 +v -0.35714286 0.33928571 0.12120831 +v -0.35714286 0.44642857 0.12120831 +v -0.33928571 0.32142857 0.12120831 +v -0.35714286 0.33928571 -0.12120831 +v -0.33928571 0.32142857 -0.12120831 +v -0.23214286 0.32142857 -0.07682829 +v -0.23214286 0.32142857 0.07682829 +v -0.21428571 0.33928571 -0.07682829 +v -0.21428571 0.33928571 0.07682829 +v -0.22530922 0.33928571 -0.09332614 +v -0.22530922 0.44642857 -0.09332614 +v -0.21428571 0.44642857 -0.07682829 +v -0.23214286 0.46428571 -0.07682829 +v -0.23214286 0.46428571 0.07682829 +v -0.21428571 0.44642857 0.07682829 +v -0.33928571 0.46428571 0.12120831 +v -0.33928571 0.46428571 -0.12120831 +v -0.35714286 0.44642857 -0.12120831 +v -0.33245208 0.44642857 -0.13770616 +v -0.33245208 0.33928571 -0.13770616 +v -0.33824536 0.33928571 -0.16683092 +v -0.33824536 0.44642857 -0.16683092 +v -0.32561845 0.32142857 -0.15420401 +v -0.21847559 0.32142857 -0.10982399 +v -0.20584868 0.33928571 -0.09719708 +v -0.10982399 0.32142857 -0.21847559 +v -0.09719708 0.33928571 -0.20584868 +v -0.09332614 0.33928571 -0.22530922 +v -0.09332614 0.44642857 -0.22530922 +v -0.09719708 0.44642857 -0.20584868 +v -0.10982399 0.46428571 -0.21847559 +v -0.21847559 0.46428571 -0.10982399 +v -0.20584868 0.44642857 -0.09719708 +v -0.32561845 0.46428571 -0.15420401 +v -0.15420401 0.46428571 -0.32561845 +v -0.16683092 0.44642857 -0.33824536 +v -0.07682829 0.44642857 -0.21428571 +v -0.07682829 0.46428571 -0.23214286 +v -0.15420401 0.32142857 -0.32561845 +v -0.16683092 0.33928571 -0.33824536 +v 0.21847559 0.46428571 0.10982399 +v 0.22530922 0.44642857 0.09332614 +v 0.20584868 0.44642857 0.09719708 +v 0.33928571 0.46428571 0.12120831 +v 0.23214286 0.46428571 0.07682829 +v 0.21428571 0.44642857 0.07682829 +v 0.23214286 0.46428571 -0.07682829 +v 0.33928571 0.46428571 -0.12120831 +v 0.35714286 0.44642857 -0.12120831 +v 0.35714286 0.33928571 -0.12120831 +v 0.33928571 0.32142857 -0.12120831 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.54119610 1.00000000 +vt 0.00000000 0.21824431 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.89694467 +vt 0.64884670 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.58030514 +vt 0.61731657 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.27676865 0.00000000 +vt 1.00000000 0.61830814 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.38169186 +vt 0.27676865 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.61830814 +vt 0.00000000 1.00000000 +vt 0.27676865 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.56645450 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.86878592 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.56645450 1.00000000 +vt 0.00000000 0.13121408 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.23463314 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.13585211 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.86414789 +vt 0.00000000 1.00000000 +vt 0.23463314 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.69134172 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.69134172 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.69134172 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.13121408 +vt 0.43354550 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.89694467 +vt 0.00000000 1.00000000 +vt 0.35115330 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.10305533 +vt 0.35115330 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.78175569 +vt 0.00000000 1.00000000 +vt 0.45880390 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.38169186 +vt 1.00000000 0.00000000 +vt 0.72323135 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.41969486 +vt 0.38268343 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.41969486 +vt 0.38268343 1.00000000 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.72323135 1.00000000 +vt 0.00000000 0.38169186 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.45880390 0.00000000 +vt 1.00000000 0.78175569 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.76536686 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.86414789 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.76536686 1.00000000 +vt 0.00000000 0.13585211 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.35115330 0.00000000 +vt 1.00000000 0.89694467 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.13121408 +vt 0.43354550 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.30865828 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.30865828 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.69134172 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.69134172 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.69134172 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.56645450 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.86878592 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.23463314 0.00000000 +vt 1.00000000 0.86414789 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.10305533 +vt 1.00000000 0.00000000 +vt 0.64884670 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.89694467 +vt 0.64884670 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.13585211 +vt 0.23463314 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.54119610 1.00000000 +vt 0.00000000 0.21824431 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.61731657 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.58030514 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.54119610 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.78175569 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.13121408 +vt 1.00000000 0.00000000 +vt 0.56645450 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.30865828 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.30865828 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.69134172 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.10305533 +vt 0.35115330 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.43354550 0.00000000 +vt 1.00000000 0.86878592 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.21824431 +vt 0.45880390 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.38268343 0.00000000 +vt 1.00000000 0.58030514 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.38268343 0.00000000 +vt 1.00000000 0.58030514 +vt 0.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.61830814 +vt 0.72323135 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.61830814 +vt 0.72323135 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.86414789 +vt 0.76536686 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.30865828 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.30865828 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 0.76536686 1.00000000 +vt 0.00000000 0.13585211 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.21824431 +vt 0.45880390 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.43354550 0.00000000 +vt 1.00000000 0.86878592 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.30865828 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.69134172 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.30865828 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.27676865 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.38169186 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.61731657 1.00000000 +vt 0.00000000 0.41969486 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.41969486 +vt 1.00000000 0.00000000 +vt 0.61731657 1.00000000 +vt 1.00000000 0.00000000 +vt 0.64884670 1.00000000 +vt 0.00000000 0.10305533 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.78175569 +vt 0.54119610 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vn -0.19509032 0.00000000 -0.98078528 +vn -0.19509032 0.00000000 -0.98078528 +vn -0.19509032 0.00000000 -0.98078528 +vn -0.19509032 0.00000000 -0.98078528 +vn -0.17053856 -0.48565274 -0.85735525 +vn -0.17053856 -0.48565274 -0.85735525 +vn -0.17053856 -0.48565274 -0.85735525 +vn -0.65328148 -0.70710678 -0.27059805 +vn -0.65328148 -0.70710678 -0.27059805 +vn -0.65328148 -0.70710678 -0.27059805 +vn -0.65328148 -0.70710678 -0.27059805 +vn -0.75415091 -0.63933827 0.15000994 +vn -0.75415091 -0.63933827 0.15000994 +vn -0.75415091 -0.63933827 0.15000994 +vn -0.98078528 0.00000000 0.19509032 +vn -0.98078528 0.00000000 0.19509032 +vn -0.98078528 0.00000000 0.19509032 +vn -0.98078528 0.00000000 0.19509032 +vn 0.55557023 0.00000000 0.83146961 +vn 0.55557023 0.00000000 0.83146961 +vn 0.55557023 0.00000000 0.83146961 +vn 0.55557023 0.00000000 0.83146961 +vn 0.42719217 -0.63933827 0.63933827 +vn 0.42719217 -0.63933827 0.63933827 +vn 0.42719217 -0.63933827 0.63933827 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn -0.72683068 -0.48565274 -0.48565274 +vn -0.72683068 -0.48565274 -0.48565274 +vn -0.72683068 -0.48565274 -0.48565274 +vn -0.83146961 0.00000000 -0.55557023 +vn -0.83146961 0.00000000 -0.55557023 +vn -0.83146961 0.00000000 -0.55557023 +vn -0.83146961 0.00000000 -0.55557023 +vn -0.72683068 0.48565274 -0.48565274 +vn -0.72683068 0.48565274 -0.48565274 +vn -0.72683068 0.48565274 -0.48565274 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.72683068 0.48565274 -0.48565274 +vn 0.72683068 0.48565274 -0.48565274 +vn 0.72683068 0.48565274 -0.48565274 +vn 0.65328148 0.70710678 0.27059805 +vn 0.65328148 0.70710678 0.27059805 +vn 0.65328148 0.70710678 0.27059805 +vn 0.65328148 0.70710678 0.27059805 +vn -0.65328148 0.70710678 -0.27059805 +vn -0.65328148 0.70710678 -0.27059805 +vn -0.65328148 0.70710678 -0.27059805 +vn -0.65328148 0.70710678 -0.27059805 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.50000000 0.70710678 -0.50000000 +vn 0.50000000 0.70710678 -0.50000000 +vn 0.50000000 0.70710678 -0.50000000 +vn 0.50000000 0.70710678 -0.50000000 +vn 0.85735525 0.48565274 0.17053856 +vn 0.85735525 0.48565274 0.17053856 +vn 0.85735525 0.48565274 0.17053856 +vn 0.98078528 0.00000000 0.19509032 +vn 0.98078528 0.00000000 0.19509032 +vn 0.98078528 0.00000000 0.19509032 +vn 0.98078528 0.00000000 0.19509032 +vn 0.85735525 -0.48565274 0.17053856 +vn 0.85735525 -0.48565274 0.17053856 +vn 0.85735525 -0.48565274 0.17053856 +vn 0.27059805 -0.70710678 0.65328148 +vn 0.27059805 -0.70710678 0.65328148 +vn 0.27059805 -0.70710678 0.65328148 +vn 0.27059805 -0.70710678 0.65328148 +vn -0.15000994 -0.63933827 0.75415091 +vn -0.15000994 -0.63933827 0.75415091 +vn -0.15000994 -0.63933827 0.75415091 +vn -0.19509032 0.00000000 0.98078528 +vn -0.19509032 0.00000000 0.98078528 +vn -0.19509032 0.00000000 0.98078528 +vn -0.19509032 0.00000000 0.98078528 +vn -0.15000994 0.63933827 0.75415091 +vn -0.15000994 0.63933827 0.75415091 +vn -0.15000994 0.63933827 0.75415091 +vn -0.83146961 0.00000000 -0.55557023 +vn -0.83146961 0.00000000 -0.55557023 +vn -0.83146961 0.00000000 -0.55557023 +vn -0.83146961 0.00000000 -0.55557023 +vn -0.63933827 -0.63933827 -0.42719217 +vn -0.63933827 -0.63933827 -0.42719217 +vn -0.63933827 -0.63933827 -0.42719217 +vn -0.70710678 -0.70710678 0.00000000 +vn -0.70710678 -0.70710678 0.00000000 +vn -0.70710678 -0.70710678 0.00000000 +vn -0.70710678 -0.70710678 0.00000000 +vn -0.63933827 -0.63933827 0.42719217 +vn -0.63933827 -0.63933827 0.42719217 +vn -0.63933827 -0.63933827 0.42719217 +vn -0.27059805 -0.70710678 0.65328148 +vn -0.27059805 -0.70710678 0.65328148 +vn -0.27059805 -0.70710678 0.65328148 +vn -0.27059805 -0.70710678 0.65328148 +vn 0.48565274 -0.48565274 0.72683068 +vn 0.48565274 -0.48565274 0.72683068 +vn 0.48565274 -0.48565274 0.72683068 +vn 0.55557023 0.00000000 0.83146961 +vn 0.55557023 0.00000000 0.83146961 +vn 0.55557023 0.00000000 0.83146961 +vn 0.55557023 0.00000000 0.83146961 +vn 0.98078528 0.00000000 -0.19509032 +vn 0.98078528 0.00000000 -0.19509032 +vn 0.98078528 0.00000000 -0.19509032 +vn 0.98078528 0.00000000 -0.19509032 +vn 0.85735525 -0.48565274 -0.17053856 +vn 0.85735525 -0.48565274 -0.17053856 +vn 0.85735525 -0.48565274 -0.17053856 +vn 0.50000000 -0.70710678 0.50000000 +vn 0.50000000 -0.70710678 0.50000000 +vn 0.50000000 -0.70710678 0.50000000 +vn 0.50000000 -0.70710678 0.50000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.50000000 -0.70710678 -0.50000000 +vn -0.50000000 -0.70710678 -0.50000000 +vn -0.50000000 -0.70710678 -0.50000000 +vn -0.50000000 -0.70710678 -0.50000000 +vn -0.75415091 -0.63933827 -0.15000994 +vn -0.75415091 -0.63933827 -0.15000994 +vn -0.75415091 -0.63933827 -0.15000994 +vn -0.98078528 0.00000000 -0.19509032 +vn -0.98078528 0.00000000 -0.19509032 +vn -0.98078528 0.00000000 -0.19509032 +vn -0.98078528 0.00000000 -0.19509032 +vn -0.75415091 0.63933827 -0.15000994 +vn -0.75415091 0.63933827 -0.15000994 +vn -0.75415091 0.63933827 -0.15000994 +vn -0.65328148 0.70710678 0.27059805 +vn -0.65328148 0.70710678 0.27059805 +vn -0.65328148 0.70710678 0.27059805 +vn -0.65328148 0.70710678 0.27059805 +vn -0.17053856 0.48565274 0.85735525 +vn -0.17053856 0.48565274 0.85735525 +vn -0.17053856 0.48565274 0.85735525 +vn 0.50000000 0.70710678 0.50000000 +vn 0.50000000 0.70710678 0.50000000 +vn 0.50000000 0.70710678 0.50000000 +vn 0.50000000 0.70710678 0.50000000 +vn -0.19509032 0.00000000 0.98078528 +vn -0.19509032 0.00000000 0.98078528 +vn -0.19509032 0.00000000 0.98078528 +vn -0.19509032 0.00000000 0.98078528 +vn 0.83146961 -0.00000000 0.55557023 +vn 0.83146961 -0.00000000 0.55557023 +vn 0.83146961 -0.00000000 0.55557023 +vn 0.83146961 -0.00000000 0.55557023 +vn 0.72683068 -0.48565274 0.48565274 +vn 0.72683068 -0.48565274 0.48565274 +vn 0.72683068 -0.48565274 0.48565274 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.65328148 -0.70710678 -0.27059805 +vn -0.65328148 -0.70710678 -0.27059805 +vn -0.65328148 -0.70710678 -0.27059805 +vn -0.65328148 -0.70710678 -0.27059805 +vn -0.42719217 -0.63933827 -0.63933827 +vn -0.42719217 -0.63933827 -0.63933827 +vn -0.42719217 -0.63933827 -0.63933827 +vn -0.55557023 0.00000000 -0.83146961 +vn -0.55557023 0.00000000 -0.83146961 +vn -0.55557023 0.00000000 -0.83146961 +vn -0.55557023 0.00000000 -0.83146961 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.42719217 0.63933827 -0.63933827 +vn 0.42719217 0.63933827 -0.63933827 +vn 0.42719217 0.63933827 -0.63933827 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn -0.65328148 0.70710678 -0.27059805 +vn -0.65328148 0.70710678 -0.27059805 +vn -0.65328148 0.70710678 -0.27059805 +vn -0.65328148 0.70710678 -0.27059805 +vn -0.72683068 0.48565274 0.48565274 +vn -0.72683068 0.48565274 0.48565274 +vn -0.72683068 0.48565274 0.48565274 +vn -0.83146961 -0.00000000 0.55557023 +vn -0.83146961 -0.00000000 0.55557023 +vn -0.83146961 -0.00000000 0.55557023 +vn -0.83146961 -0.00000000 0.55557023 +vn 0.19509032 0.00000000 0.98078528 +vn 0.19509032 0.00000000 0.98078528 +vn 0.19509032 0.00000000 0.98078528 +vn 0.19509032 0.00000000 0.98078528 +vn 0.17053856 -0.48565274 0.85735525 +vn 0.17053856 -0.48565274 0.85735525 +vn 0.17053856 -0.48565274 0.85735525 +vn -0.50000000 -0.70710678 0.50000000 +vn -0.50000000 -0.70710678 0.50000000 +vn -0.50000000 -0.70710678 0.50000000 +vn -0.50000000 -0.70710678 0.50000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.50000000 -0.70710678 -0.50000000 +vn 0.50000000 -0.70710678 -0.50000000 +vn 0.50000000 -0.70710678 -0.50000000 +vn 0.50000000 -0.70710678 -0.50000000 +vn 0.15000994 -0.63933827 -0.75415091 +vn 0.15000994 -0.63933827 -0.75415091 +vn 0.15000994 -0.63933827 -0.75415091 +vn 0.19509032 0.00000000 -0.98078528 +vn 0.19509032 0.00000000 -0.98078528 +vn 0.19509032 0.00000000 -0.98078528 +vn 0.19509032 0.00000000 -0.98078528 +vn 0.15000994 0.63933827 -0.75415091 +vn 0.15000994 0.63933827 -0.75415091 +vn 0.15000994 0.63933827 -0.75415091 +vn 0.50000000 0.70710678 -0.50000000 +vn 0.50000000 0.70710678 -0.50000000 +vn 0.50000000 0.70710678 -0.50000000 +vn 0.50000000 0.70710678 -0.50000000 +vn 0.75415091 0.63933827 -0.15000994 +vn 0.75415091 0.63933827 -0.15000994 +vn 0.75415091 0.63933827 -0.15000994 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn -0.50000000 0.70710678 0.50000000 +vn -0.50000000 0.70710678 0.50000000 +vn -0.50000000 0.70710678 0.50000000 +vn -0.50000000 0.70710678 0.50000000 +vn -0.85735525 0.48565274 -0.17053856 +vn -0.85735525 0.48565274 -0.17053856 +vn -0.85735525 0.48565274 -0.17053856 +vn -0.98078528 -0.00000000 -0.19509032 +vn -0.98078528 -0.00000000 -0.19509032 +vn -0.98078528 -0.00000000 -0.19509032 +vn -0.98078528 -0.00000000 -0.19509032 +vn -0.55557023 0.00000000 0.83146961 +vn -0.55557023 0.00000000 0.83146961 +vn -0.55557023 0.00000000 0.83146961 +vn -0.55557023 0.00000000 0.83146961 +vn -0.48565274 -0.48565274 0.72683068 +vn -0.48565274 -0.48565274 0.72683068 +vn -0.48565274 -0.48565274 0.72683068 +vn -0.70710678 -0.70710678 0.00000000 +vn -0.70710678 -0.70710678 0.00000000 +vn -0.70710678 -0.70710678 0.00000000 +vn -0.70710678 -0.70710678 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.70710678 -0.70710678 0.00000000 +vn 0.70710678 -0.70710678 0.00000000 +vn 0.70710678 -0.70710678 0.00000000 +vn 0.70710678 -0.70710678 0.00000000 +vn 0.63933827 -0.63933827 -0.42719217 +vn 0.63933827 -0.63933827 -0.42719217 +vn 0.63933827 -0.63933827 -0.42719217 +vn 0.83146961 -0.00000000 -0.55557023 +vn 0.83146961 -0.00000000 -0.55557023 +vn 0.83146961 -0.00000000 -0.55557023 +vn 0.83146961 -0.00000000 -0.55557023 +vn 0.63933827 0.63933827 -0.42719217 +vn 0.63933827 0.63933827 -0.42719217 +vn 0.63933827 0.63933827 -0.42719217 +vn 0.70710678 0.70710678 0.00000000 +vn 0.70710678 0.70710678 0.00000000 +vn 0.70710678 0.70710678 0.00000000 +vn 0.70710678 0.70710678 0.00000000 +vn 0.63933827 0.63933827 0.42719217 +vn 0.63933827 0.63933827 0.42719217 +vn 0.63933827 0.63933827 0.42719217 +vn 0.27059805 0.70710678 0.65328148 +vn 0.27059805 0.70710678 0.65328148 +vn 0.27059805 0.70710678 0.65328148 +vn 0.27059805 0.70710678 0.65328148 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn -0.70710678 0.70710678 0.00000000 +vn -0.70710678 0.70710678 0.00000000 +vn -0.70710678 0.70710678 0.00000000 +vn -0.70710678 0.70710678 0.00000000 +vn -0.48565274 0.48565274 -0.72683068 +vn -0.48565274 0.48565274 -0.72683068 +vn -0.48565274 0.48565274 -0.72683068 +vn -0.55557023 0.00000000 -0.83146961 +vn -0.55557023 0.00000000 -0.83146961 +vn -0.55557023 0.00000000 -0.83146961 +vn -0.55557023 0.00000000 -0.83146961 +vn -0.98078528 0.00000000 0.19509032 +vn -0.98078528 0.00000000 0.19509032 +vn -0.98078528 0.00000000 0.19509032 +vn -0.98078528 0.00000000 0.19509032 +vn -0.85735525 -0.48565274 0.17053856 +vn -0.85735525 -0.48565274 0.17053856 +vn -0.85735525 -0.48565274 0.17053856 +vn -0.27059805 -0.70710678 0.65328148 +vn -0.27059805 -0.70710678 0.65328148 +vn -0.27059805 -0.70710678 0.65328148 +vn -0.27059805 -0.70710678 0.65328148 +vn 0.15000994 -0.63933827 0.75415091 +vn 0.15000994 -0.63933827 0.75415091 +vn 0.15000994 -0.63933827 0.75415091 +vn 0.50000000 -0.70710678 0.50000000 +vn 0.50000000 -0.70710678 0.50000000 +vn 0.50000000 -0.70710678 0.50000000 +vn 0.50000000 -0.70710678 0.50000000 +vn 0.75415091 -0.63933827 0.15000994 +vn 0.75415091 -0.63933827 0.15000994 +vn 0.75415091 -0.63933827 0.15000994 +vn 0.98078528 0.00000000 0.19509032 +vn 0.98078528 0.00000000 0.19509032 +vn 0.98078528 0.00000000 0.19509032 +vn 0.98078528 0.00000000 0.19509032 +vn 0.75415091 0.63933827 0.15000994 +vn 0.75415091 0.63933827 0.15000994 +vn 0.75415091 0.63933827 0.15000994 +vn 0.50000000 0.70710678 0.50000000 +vn 0.50000000 0.70710678 0.50000000 +vn 0.50000000 0.70710678 0.50000000 +vn 0.50000000 0.70710678 0.50000000 +vn 0.15000994 0.63933827 0.75415091 +vn 0.15000994 0.63933827 0.75415091 +vn 0.15000994 0.63933827 0.75415091 +vn -0.27059805 0.70710678 0.65328148 +vn -0.27059805 0.70710678 0.65328148 +vn -0.27059805 0.70710678 0.65328148 +vn -0.27059805 0.70710678 0.65328148 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn -0.50000000 0.70710678 -0.50000000 +vn -0.50000000 0.70710678 -0.50000000 +vn -0.50000000 0.70710678 -0.50000000 +vn -0.50000000 0.70710678 -0.50000000 +vn 0.17053856 0.48565274 -0.85735525 +vn 0.17053856 0.48565274 -0.85735525 +vn 0.17053856 0.48565274 -0.85735525 +vn 0.65328148 0.70710678 -0.27059805 +vn 0.65328148 0.70710678 -0.27059805 +vn 0.65328148 0.70710678 -0.27059805 +vn 0.65328148 0.70710678 -0.27059805 +vn -0.55557023 0.00000000 0.83146961 +vn -0.55557023 0.00000000 0.83146961 +vn -0.55557023 0.00000000 0.83146961 +vn -0.55557023 0.00000000 0.83146961 +vn -0.42719217 0.63933827 0.63933827 +vn -0.42719217 0.63933827 0.63933827 +vn -0.42719217 0.63933827 0.63933827 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.65328148 -0.70710678 -0.27059805 +vn 0.65328148 -0.70710678 -0.27059805 +vn 0.65328148 -0.70710678 -0.27059805 +vn 0.65328148 -0.70710678 -0.27059805 +vn 0.17053856 -0.48565274 -0.85735525 +vn 0.17053856 -0.48565274 -0.85735525 +vn 0.17053856 -0.48565274 -0.85735525 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.50000000 -0.70710678 -0.50000000 +vn -0.50000000 -0.70710678 -0.50000000 +vn -0.50000000 -0.70710678 -0.50000000 +vn -0.50000000 -0.70710678 -0.50000000 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.85735525 0.48565274 0.17053856 +vn -0.85735525 0.48565274 0.17053856 +vn -0.85735525 0.48565274 0.17053856 +vn 0.27059805 0.70710678 -0.65328148 +vn 0.27059805 0.70710678 -0.65328148 +vn 0.27059805 0.70710678 -0.65328148 +vn 0.27059805 0.70710678 -0.65328148 +vn 0.19509032 0.00000000 0.98078528 +vn 0.19509032 0.00000000 0.98078528 +vn 0.19509032 0.00000000 0.98078528 +vn 0.19509032 0.00000000 0.98078528 +vn 0.63933827 -0.63933827 0.42719217 +vn 0.63933827 -0.63933827 0.42719217 +vn 0.63933827 -0.63933827 0.42719217 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.27059805 -0.70710678 -0.65328148 +vn 0.27059805 -0.70710678 -0.65328148 +vn 0.27059805 -0.70710678 -0.65328148 +vn 0.27059805 -0.70710678 -0.65328148 +vn -0.48565274 -0.48565274 -0.72683068 +vn -0.48565274 -0.48565274 -0.72683068 +vn -0.48565274 -0.48565274 -0.72683068 +vn 0.27059805 -0.70710678 0.65328148 +vn 0.27059805 -0.70710678 0.65328148 +vn 0.27059805 -0.70710678 0.65328148 +vn 0.27059805 -0.70710678 0.65328148 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -0.48565274 0.48565274 0.72683068 +vn -0.48565274 0.48565274 0.72683068 +vn -0.48565274 0.48565274 0.72683068 +vn -0.27059805 0.70710678 -0.65328148 +vn -0.27059805 0.70710678 -0.65328148 +vn -0.27059805 0.70710678 -0.65328148 +vn -0.27059805 0.70710678 -0.65328148 +vn 0.83146961 0.00000000 0.55557023 +vn 0.83146961 0.00000000 0.55557023 +vn 0.83146961 0.00000000 0.55557023 +vn 0.83146961 0.00000000 0.55557023 +vn 0.75415091 -0.63933827 -0.15000994 +vn 0.75415091 -0.63933827 -0.15000994 +vn 0.75415091 -0.63933827 -0.15000994 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn -0.27059805 -0.70710678 -0.65328148 +vn -0.27059805 -0.70710678 -0.65328148 +vn -0.27059805 -0.70710678 -0.65328148 +vn -0.27059805 -0.70710678 -0.65328148 +vn -0.85735525 -0.48565274 -0.17053856 +vn -0.85735525 -0.48565274 -0.17053856 +vn -0.85735525 -0.48565274 -0.17053856 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn 0.17053856 0.48565274 0.85735525 +vn 0.17053856 0.48565274 0.85735525 +vn 0.17053856 0.48565274 0.85735525 +vn 0.65328148 0.70710678 0.27059805 +vn 0.65328148 0.70710678 0.27059805 +vn 0.65328148 0.70710678 0.27059805 +vn 0.65328148 0.70710678 0.27059805 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn -0.42719217 0.63933827 -0.63933827 +vn -0.42719217 0.63933827 -0.63933827 +vn -0.42719217 0.63933827 -0.63933827 +vn 0.98078528 -0.00000000 -0.19509032 +vn 0.98078528 -0.00000000 -0.19509032 +vn 0.98078528 -0.00000000 -0.19509032 +vn 0.98078528 -0.00000000 -0.19509032 +vn 0.65328148 -0.70710678 0.27059805 +vn 0.65328148 -0.70710678 0.27059805 +vn 0.65328148 -0.70710678 0.27059805 +vn 0.65328148 -0.70710678 0.27059805 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.42719217 -0.63933827 -0.63933827 +vn 0.42719217 -0.63933827 -0.63933827 +vn 0.42719217 -0.63933827 -0.63933827 +vn -0.72683068 -0.48565274 0.48565274 +vn -0.72683068 -0.48565274 0.48565274 +vn -0.72683068 -0.48565274 0.48565274 +vn 0.65328148 -0.70710678 -0.27059805 +vn 0.65328148 -0.70710678 -0.27059805 +vn 0.65328148 -0.70710678 -0.27059805 +vn 0.65328148 -0.70710678 -0.27059805 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.72683068 0.48565274 0.48565274 +vn 0.72683068 0.48565274 0.48565274 +vn 0.72683068 0.48565274 0.48565274 +vn 0.65328148 0.70710678 -0.27059805 +vn 0.65328148 0.70710678 -0.27059805 +vn 0.65328148 0.70710678 -0.27059805 +vn 0.65328148 0.70710678 -0.27059805 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.27059805 0.70710678 -0.65328148 +vn 0.27059805 0.70710678 -0.65328148 +vn 0.27059805 0.70710678 -0.65328148 +vn 0.27059805 0.70710678 -0.65328148 +vn -0.15000994 0.63933827 -0.75415091 +vn -0.15000994 0.63933827 -0.75415091 +vn -0.15000994 0.63933827 -0.75415091 +vn -0.19509032 0.00000000 -0.98078528 +vn -0.19509032 0.00000000 -0.98078528 +vn -0.19509032 0.00000000 -0.98078528 +vn -0.19509032 0.00000000 -0.98078528 +vn -0.27059805 0.70710678 0.65328148 +vn -0.27059805 0.70710678 0.65328148 +vn -0.27059805 0.70710678 0.65328148 +vn -0.27059805 0.70710678 0.65328148 +vn -0.63933827 0.63933827 0.42719217 +vn -0.63933827 0.63933827 0.42719217 +vn -0.63933827 0.63933827 0.42719217 +vn -0.70710678 0.70710678 -0.00000000 +vn -0.70710678 0.70710678 -0.00000000 +vn -0.70710678 0.70710678 -0.00000000 +vn -0.70710678 0.70710678 -0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn -0.27059805 0.70710678 -0.65328148 +vn -0.27059805 0.70710678 -0.65328148 +vn -0.27059805 0.70710678 -0.65328148 +vn -0.27059805 0.70710678 -0.65328148 +vn 0.48565274 0.48565274 -0.72683068 +vn 0.48565274 0.48565274 -0.72683068 +vn 0.48565274 0.48565274 -0.72683068 +vn 0.70710678 0.70710678 -0.00000000 +vn 0.70710678 0.70710678 -0.00000000 +vn 0.70710678 0.70710678 -0.00000000 +vn 0.70710678 0.70710678 -0.00000000 +vn -0.50000000 0.70710678 -0.50000000 +vn -0.50000000 0.70710678 -0.50000000 +vn -0.50000000 0.70710678 -0.50000000 +vn -0.50000000 0.70710678 -0.50000000 +vn 0.55557023 0.00000000 -0.83146961 +vn 0.55557023 0.00000000 -0.83146961 +vn 0.55557023 0.00000000 -0.83146961 +vn 0.55557023 0.00000000 -0.83146961 +vn -0.15000994 -0.63933827 -0.75415091 +vn -0.15000994 -0.63933827 -0.75415091 +vn -0.15000994 -0.63933827 -0.75415091 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.65328148 -0.70710678 0.27059805 +vn -0.65328148 -0.70710678 0.27059805 +vn -0.65328148 -0.70710678 0.27059805 +vn -0.65328148 -0.70710678 0.27059805 +vn -0.17053856 -0.48565274 0.85735525 +vn -0.17053856 -0.48565274 0.85735525 +vn -0.17053856 -0.48565274 0.85735525 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.85735525 0.48565274 -0.17053856 +vn 0.85735525 0.48565274 -0.17053856 +vn 0.85735525 0.48565274 -0.17053856 +vn 0.48565274 0.48565274 0.72683068 +vn 0.48565274 0.48565274 0.72683068 +vn 0.48565274 0.48565274 0.72683068 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.70710678 -0.70710678 -0.00000000 +vn 0.70710678 -0.70710678 -0.00000000 +vn 0.70710678 -0.70710678 -0.00000000 +vn 0.70710678 -0.70710678 -0.00000000 +vn 0.48565274 -0.48565274 -0.72683068 +vn 0.48565274 -0.48565274 -0.72683068 +vn 0.48565274 -0.48565274 -0.72683068 +vn 0.27059805 -0.70710678 -0.65328148 +vn 0.27059805 -0.70710678 -0.65328148 +vn 0.27059805 -0.70710678 -0.65328148 +vn 0.27059805 -0.70710678 -0.65328148 +vn -0.83146961 0.00000000 0.55557023 +vn -0.83146961 0.00000000 0.55557023 +vn -0.83146961 0.00000000 0.55557023 +vn -0.83146961 0.00000000 0.55557023 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.63933827 0.63933827 -0.42719217 +vn -0.63933827 0.63933827 -0.42719217 +vn -0.63933827 0.63933827 -0.42719217 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -0.27059805 -0.70710678 -0.65328148 +vn -0.27059805 -0.70710678 -0.65328148 +vn -0.27059805 -0.70710678 -0.65328148 +vn -0.27059805 -0.70710678 -0.65328148 +vn 0.55557023 0.00000000 -0.83146961 +vn 0.55557023 0.00000000 -0.83146961 +vn 0.55557023 0.00000000 -0.83146961 +vn 0.55557023 0.00000000 -0.83146961 +vn 0.27059805 0.70710678 0.65328148 +vn 0.27059805 0.70710678 0.65328148 +vn 0.27059805 0.70710678 0.65328148 +vn 0.27059805 0.70710678 0.65328148 +vn -0.50000000 0.70710678 0.50000000 +vn -0.50000000 0.70710678 0.50000000 +vn -0.50000000 0.70710678 0.50000000 +vn -0.50000000 0.70710678 0.50000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn -0.65328148 0.70710678 0.27059805 +vn -0.65328148 0.70710678 0.27059805 +vn -0.65328148 0.70710678 0.27059805 +vn -0.65328148 0.70710678 0.27059805 +vn 0.19509032 0.00000000 -0.98078528 +vn 0.19509032 0.00000000 -0.98078528 +vn 0.19509032 0.00000000 -0.98078528 +vn 0.19509032 0.00000000 -0.98078528 +vn 0.72683068 -0.48565274 -0.48565274 +vn 0.72683068 -0.48565274 -0.48565274 +vn 0.72683068 -0.48565274 -0.48565274 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn -0.65328148 -0.70710678 0.27059805 +vn -0.65328148 -0.70710678 0.27059805 +vn -0.65328148 -0.70710678 0.27059805 +vn -0.65328148 -0.70710678 0.27059805 +vn -0.42719217 -0.63933827 0.63933827 +vn -0.42719217 -0.63933827 0.63933827 +vn -0.42719217 -0.63933827 0.63933827 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.42719217 0.63933827 0.63933827 +vn 0.42719217 0.63933827 0.63933827 +vn 0.42719217 0.63933827 0.63933827 +vn -0.75415091 0.63933827 0.15000994 +vn -0.75415091 0.63933827 0.15000994 +vn -0.75415091 0.63933827 0.15000994 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.50000000 -0.70710678 0.50000000 +vn -0.50000000 -0.70710678 0.50000000 +vn -0.50000000 -0.70710678 0.50000000 +vn -0.50000000 -0.70710678 0.50000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.65328148 -0.70710678 0.27059805 +vn 0.65328148 -0.70710678 0.27059805 +vn 0.65328148 -0.70710678 0.27059805 +vn 0.65328148 -0.70710678 0.27059805 +vn 0.50000000 -0.70710678 -0.50000000 +vn 0.50000000 -0.70710678 -0.50000000 +vn 0.50000000 -0.70710678 -0.50000000 +vn 0.50000000 -0.70710678 -0.50000000 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn -0.17053856 0.48565274 -0.85735525 +vn -0.17053856 0.48565274 -0.85735525 +vn -0.17053856 0.48565274 -0.85735525 +vn 0.83146961 0.00000000 -0.55557023 +vn 0.83146961 0.00000000 -0.55557023 +vn 0.83146961 0.00000000 -0.55557023 +vn 0.83146961 0.00000000 -0.55557023 +s 1 +usemtl Stone_Brit_Gray_Light +f 35/125/121 36/126/122 38/128/124 +f 36/126/122 37/127/123 38/128/124 +s 2 +f 35/131/127 39/130/126 36/129/125 +s 3 +f 40/132/128 41/133/129 39/135/131 +f 41/133/129 36/134/130 39/135/131 +s 4 +f 41/138/134 40/137/133 42/136/132 +s 5 +f 41/139/135 42/140/136 43/141/137 +f 41/139/135 43/141/137 44/142/138 +s 6 +usemtl Stone_Bri_Gray_Lighter +f 45/143/139 41/144/140 46/146/142 +f 41/144/140 44/145/141 46/146/142 +s 7 +f 47/149/145 41/148/144 45/147/143 +s 8 +f 47/150/146 45/151/147 49/153/149 +f 45/151/147 48/152/148 49/153/149 +s 9 +f 50/154/150 51/155/151 47/156/152 +f 50/154/150 47/156/152 49/157/153 +s 10 +f 50/158/154 52/159/155 53/160/156 +f 50/158/154 53/160/156 51/161/157 +s 11 +f 52/164/160 50/163/159 54/162/158 +s 12 +f 52/165/161 54/166/162 56/168/164 +f 54/166/162 55/167/163 56/168/164 +s 13 +f 55/171/167 57/170/166 56/169/165 +s 14 +f 56/172/168 57/173/169 58/174/170 +f 56/172/168 58/174/170 59/175/171 +s 15 +f 58/178/174 37/177/173 59/176/172 +s 16 +f 37/179/175 58/180/176 44/182/178 +f 58/180/176 60/181/177 44/182/178 +s 17 +usemtl Stone_Brit_Gray_Light +f 44/183/179 61/184/180 62/185/181 +f 44/183/179 62/185/181 37/186/182 +s 18 +f 64/190/186 62/189/185 63/187/183 +f 62/189/185 61/188/184 63/187/183 +s 19 +f 38/191/187 62/192/188 65/194/190 +f 62/192/188 64/193/189 65/194/190 +s 20 +f 66/197/193 65/196/192 64/195/191 +s 21 +f 67/198/194 68/199/195 66/201/197 +f 68/199/195 65/200/196 66/201/197 +s 22 +f 68/204/200 67/203/199 69/202/198 +s 23 +f 69/205/201 67/206/202 70/207/203 +f 69/205/201 70/207/203 71/208/204 +s 24 +f 71/211/207 70/210/206 72/209/205 +s 25 +f 72/212/208 70/213/209 73/214/210 +f 72/212/208 73/214/210 74/215/211 +s 26 +f 74/218/214 73/217/213 63/216/212 +s 27 +usemtl Stone_Bri_Gray_Lighter +f 70/219/215 75/220/216 73/222/218 +f 75/220/216 76/221/217 73/222/218 +s 28 +f 77/225/221 75/224/220 70/223/219 +s 29 +f 78/226/222 79/227/223 77/229/225 +f 79/227/223 75/228/224 77/229/225 +s 30 +f 80/232/228 79/231/227 78/230/226 +s 31 +f 81/233/229 82/234/230 78/236/232 +f 82/234/230 80/235/231 78/236/232 +s 32 +f 81/239/235 83/238/234 82/237/233 +s 33 +f 82/240/236 83/241/237 84/242/238 +f 82/240/236 84/242/238 85/243/239 +s 34 +usemtl Stone_Brit_Gray +f 86/244/240 82/245/241 87/247/243 +f 82/245/241 85/246/242 87/247/243 +s 35 +f 88/250/246 82/249/245 86/248/244 +s 36 +f 88/251/247 86/252/248 89/253/249 +f 88/251/247 89/253/249 90/254/250 +s 37 +f 88/255/251 90/256/252 92/258/254 +f 90/256/252 91/257/253 92/258/254 +s 38 +f 91/259/255 93/260/256 92/262/258 +f 93/260/256 94/261/257 92/262/258 +s 39 +f 91/265/261 95/264/260 93/263/259 +s 40 +f 93/266/262 95/267/263 97/269/265 +f 95/267/263 96/268/264 97/269/265 +s 41 +f 98/272/268 97/271/267 96/270/266 +s 42 +f 99/273/269 100/274/270 98/275/271 +f 99/273/269 98/275/271 96/276/272 +s 43 +f 99/279/275 101/278/274 100/277/273 +s 44 +f 87/280/276 102/281/277 101/283/279 +f 102/281/277 100/282/278 101/283/279 +s 45 +f 103/284/280 89/285/281 99/287/283 +f 89/285/281 101/286/282 99/287/283 +s 46 +usemtl Stone_Brit_Gray_Light +f 104/288/284 103/289/285 105/291/287 +f 103/289/285 99/290/286 105/291/287 +s 47 +f 106/294/290 103/293/289 104/292/288 +s 48 +f 106/295/291 104/296/292 108/298/294 +f 104/296/292 107/297/293 108/298/294 +s 49 +f 108/299/295 109/300/296 110/301/297 +f 108/299/295 110/301/297 106/302/298 +s 50 +f 108/303/299 111/304/300 109/306/302 +f 111/304/300 112/305/301 109/306/302 +s 51 +f 112/309/305 113/308/304 109/307/303 +s 52 +f 113/310/306 112/311/307 114/312/308 +f 113/310/306 114/312/308 115/313/309 +s 53 +f 116/314/310 113/315/311 117/317/313 +f 113/315/311 115/316/312 117/317/313 +s 54 +f 115/318/314 118/319/315 119/320/316 +f 115/318/314 119/320/316 117/321/317 +s 55 +f 96/324/320 117/323/319 119/322/318 +s 56 +f 118/328/324 121/327/323 120/326/322 +f 118/328/324 120/326/322 119/325/321 +s 57 +f 122/329/325 121/330/326 118/331/327 +f 122/329/325 118/331/327 114/332/328 +s 58 +f 122/335/331 123/334/330 121/333/329 +s 59 +f 111/336/332 107/337/333 122/339/335 +f 107/337/333 123/338/334 122/339/335 +s 60 +usemtl Stone_Bri_Gray_Lighter +f 124/340/336 111/341/337 125/343/339 +f 111/341/337 122/342/338 125/343/339 +s 61 +f 126/346/342 111/345/341 124/344/340 +s 62 +f 126/347/343 124/348/344 128/350/346 +f 124/348/344 127/349/345 128/350/346 +s 63 +f 126/351/347 128/352/348 129/353/349 +f 126/351/347 129/353/349 130/354/350 +s 64 +f 129/355/351 131/356/352 132/357/353 +f 129/355/351 132/357/353 130/358/354 +s 65 +f 129/361/357 133/360/356 131/359/355 +s 66 +f 131/362/358 133/363/359 134/364/360 +f 131/362/358 134/364/360 135/365/361 +s 67 +f 134/368/364 136/367/363 135/366/362 +s 68 +f 135/369/365 136/370/366 138/372/368 +f 136/370/366 137/371/367 138/372/368 +s 69 +f 114/375/371 138/374/370 137/373/369 +s 70 +f 137/379/375 136/378/374 139/376/372 +f 136/378/374 140/377/373 139/376/372 +s 71 +f 125/380/376 139/381/377 140/382/378 +f 125/380/376 140/382/378 141/383/379 +s 72 +f 140/386/382 142/385/381 141/384/380 +s 73 +f 143/387/383 127/388/384 142/390/386 +f 127/388/384 141/389/385 142/390/386 +s 74 +usemtl Stone_Brit_Gray_Light +f 144/391/387 143/392/388 145/394/390 +f 143/392/388 142/393/389 145/394/390 +s 75 +f 146/397/393 143/396/392 144/395/391 +s 76 +f 146/398/394 144/399/395 148/401/397 +f 144/399/395 147/400/396 148/401/397 +s 77 +f 146/402/398 148/403/399 149/404/400 +f 146/402/398 149/404/400 150/405/401 +s 78 +f 149/406/402 151/407/403 152/408/404 +f 149/406/402 152/408/404 150/409/405 +s 79 +f 149/412/408 153/411/407 151/410/406 +s 80 +f 151/413/409 153/414/410 155/416/412 +f 153/414/410 154/415/411 155/416/412 +s 81 +f 155/419/415 154/418/414 156/417/413 +s 82 +f 155/420/416 156/421/417 158/423/419 +f 156/421/417 157/422/418 158/423/419 +s 83 +f 134/426/422 158/425/421 157/424/420 +s 84 +f 134/427/423 157/428/424 142/430/426 +f 157/428/424 159/429/425 142/430/426 +s 85 +f 156/434/430 160/433/429 157/431/427 +f 160/433/429 159/432/428 157/431/427 +s 86 +f 145/435/431 159/436/432 160/437/433 +f 145/435/431 160/437/433 161/438/434 +s 87 +f 161/441/437 160/440/436 162/439/435 +s 88 +f 163/442/438 147/443/439 162/445/441 +f 147/443/439 161/444/440 162/445/441 +s 89 +usemtl Stone_Brit_Gray +f 164/446/442 163/447/443 165/449/445 +f 163/447/443 162/448/444 165/449/445 +s 90 +f 163/452/448 164/451/447 166/450/446 +s 91 +f 167/453/449 153/454/450 166/456/452 +f 153/454/450 163/455/451 166/456/452 +s 92 +f 167/459/455 168/458/454 153/457/453 +s 93 +f 169/460/456 170/461/457 168/462/458 +f 169/460/456 168/462/458 167/463/459 +s 94 +f 169/466/462 171/465/461 170/464/460 +s 95 +f 170/467/463 171/468/464 173/470/466 +f 171/468/464 172/469/465 173/470/466 +s 96 +f 172/473/469 174/472/468 173/471/467 +s 97 +f 173/474/470 174/475/471 176/477/473 +f 174/475/471 175/476/472 176/477/473 +s 98 +f 154/480/476 176/479/475 175/478/474 +s 99 +f 154/481/477 175/482/478 177/483/479 +f 154/481/477 177/483/479 162/484/480 +s 100 +f 174/488/484 178/487/483 175/485/481 +f 178/487/483 177/486/482 175/485/481 +s 101 +f 165/489/485 177/490/486 178/491/487 +f 165/489/485 178/491/487 179/492/488 +s 102 +f 179/495/491 178/494/490 55/493/489 +s 103 +f 55/496/492 178/497/493 172/499/495 +f 178/497/493 174/498/494 172/499/495 +s 104 +usemtl Stone_Bri_Gray_Lighter +f 171/500/496 48/501/497 180/502/498 +f 171/500/496 180/502/498 172/503/499 +s 105 +f 180/506/502 181/505/501 172/504/500 +s 106 +f 46/507/503 60/508/504 180/510/506 +f 60/508/504 181/509/505 180/510/506 +s 107 +usemtl Stone_Brit_Gray +f 182/511/507 54/512/508 171/513/509 +f 182/511/507 171/513/509 169/514/510 +s 108 +f 182/517/513 183/516/512 54/515/511 +s 109 +f 173/518/514 176/519/515 170/521/517 +f 176/519/515 168/520/516 170/521/517 +s 110 +f 167/522/518 166/523/519 169/525/521 +f 166/523/519 182/524/520 169/525/521 +s 111 +f 166/526/522 164/527/523 182/529/525 +f 164/527/523 183/528/524 182/529/525 +s 112 +f 183/530/526 164/531/527 179/533/529 +f 164/531/527 165/532/528 179/533/529 +s 113 +f 177/536/532 165/535/531 162/534/530 +s 114 +usemtl Stone_Brit_Gray_Light +f 162/537/533 160/538/534 154/540/536 +f 160/538/534 156/539/535 154/540/536 +s 115 +usemtl Stone_Brit_Gray +f 153/541/537 168/542/538 154/544/540 +f 168/542/538 176/543/539 154/544/540 +s 116 +usemtl Stone_Brit_Gray_Light +f 150/547/543 152/546/542 133/545/541 +s 117 +f 152/548/544 151/549/545 158/551/547 +f 151/549/545 155/550/546 158/551/547 +s 118 +f 148/552/548 163/553/549 153/554/550 +f 148/552/548 153/554/550 149/555/551 +s 119 +f 147/558/554 163/557/553 148/556/552 +s 120 +f 150/559/555 133/560/556 143/561/557 +f 150/559/555 143/561/557 146/562/558 +s 121 +f 147/563/559 144/564/560 161/566/562 +f 144/564/560 145/565/561 161/566/562 +s 122 +f 159/569/565 145/568/564 142/567/563 +s 123 +usemtl Stone_Bri_Gray_Lighter +f 142/570/566 140/571/567 136/572/568 +f 142/570/566 136/572/568 134/573/569 +s 124 +usemtl Stone_Brit_Gray_Light +f 133/574/570 152/575/571 158/576/572 +f 133/574/570 158/576/572 134/577/573 +s 125 +usemtl Stone_Bri_Gray_Lighter +f 130/580/576 132/579/575 112/578/574 +s 126 +f 135/581/577 138/582/578 131/584/580 +f 138/582/578 132/583/579 131/584/580 +s 127 +f 128/585/581 143/586/582 129/588/584 +f 143/586/582 133/587/583 129/588/584 +s 128 +f 127/591/587 143/590/586 128/589/585 +s 129 +f 127/592/588 124/593/589 141/595/591 +f 124/593/589 125/594/590 141/595/591 +s 130 +f 125/598/594 122/597/593 139/596/592 +s 131 +f 114/599/595 137/600/596 122/602/598 +f 137/600/596 139/601/597 122/602/598 +s 132 +usemtl Stone_Brit_Gray_Light +f 105/603/599 120/604/600 123/606/602 +f 120/604/600 121/605/601 123/606/602 +s 133 +f 115/609/605 114/608/604 118/607/603 +s 134 +usemtl Stone_Bri_Gray_Lighter +f 112/610/606 132/611/607 138/612/608 +f 112/610/606 138/612/608 114/613/609 +s 135 +f 130/614/610 112/615/611 111/616/612 +f 130/614/610 111/616/612 126/617/613 +s 136 +usemtl Stone_Brit_Gray_Light +f 109/618/614 113/619/615 116/620/616 +f 109/618/614 116/620/616 110/621/617 +s 137 +f 110/624/620 116/623/619 95/622/618 +s 138 +f 108/627/623 107/626/622 111/625/621 +s 139 +f 110/628/624 95/629/625 103/630/626 +f 110/628/624 103/630/626 106/631/627 +s 140 +f 105/632/628 123/633/629 104/635/631 +f 123/633/629 107/634/630 104/635/631 +s 141 +f 120/638/634 105/637/633 99/636/632 +s 142 +f 96/639/635 119/640/636 99/642/638 +f 119/640/636 120/641/637 99/642/638 +s 143 +usemtl Stone_Brit_Gray +f 102/646/642 184/645/641 100/643/639 +f 184/645/641 98/644/640 100/643/639 +s 144 +f 185/647/643 184/648/644 85/650/646 +f 184/648/644 102/649/645 85/650/646 +s 145 +f 186/653/649 184/652/648 185/651/647 +s 146 +f 80/654/650 94/655/651 186/656/652 +f 80/654/650 186/656/652 185/657/653 +s 147 +usemtl Stone_Bri_Gray_Lighter +f 85/658/654 187/659/655 188/660/656 +f 85/658/654 188/660/656 185/661/657 +s 148 +f 189/664/660 185/663/659 188/662/658 +s 149 +f 189/665/661 188/666/662 190/667/663 +f 189/665/661 190/667/663 76/668/664 +s 150 +f 188/672/668 187/671/667 191/670/666 +f 188/672/668 191/670/666 190/669/665 +s 151 +f 73/673/669 190/674/670 191/675/671 +f 73/673/669 191/675/671 66/676/672 +s 152 +f 191/679/675 192/678/674 66/677/673 +s 153 +f 192/680/676 191/681/677 84/683/679 +f 191/681/677 187/682/678 84/683/679 +s 154 +usemtl Stone_Brit_Gray +f 97/684/680 98/685/681 184/686/682 +f 97/684/680 184/686/682 186/687/683 +s 155 +usemtl Stone_Brit_Gray_Light +f 95/688/684 116/689/685 117/690/686 +f 95/688/684 117/690/686 96/691/687 +s 156 +usemtl Stone_Brit_Gray +f 92/694/690 94/693/689 80/692/688 +s 157 +f 93/695/691 97/696/692 186/697/693 +f 93/695/691 186/697/693 94/698/694 +s 158 +f 90/699/695 103/700/696 91/702/698 +f 103/700/696 95/701/697 91/702/698 +s 159 +f 89/705/701 103/704/700 90/703/699 +s 160 +f 89/706/702 86/707/703 101/709/705 +f 86/707/703 87/708/704 101/709/705 +s 161 +f 87/712/708 85/711/707 102/710/706 +s 162 +usemtl Stone_Bri_Gray_Lighter +f 85/715/711 84/714/710 187/713/709 +s 163 +f 192/716/712 84/717/713 193/719/715 +f 84/717/713 83/718/714 193/719/715 +s 164 +f 194/720/716 193/721/717 83/722/718 +f 194/720/716 83/722/718 81/723/719 +s 165 +f 194/726/722 67/725/721 193/724/720 +s 166 +usemtl Stone_Brit_Gray +f 92/727/723 80/728/724 82/729/725 +f 92/727/723 82/729/725 88/730/726 +s 167 +usemtl Stone_Bri_Gray_Lighter +f 79/731/727 80/732/728 185/733/729 +f 79/731/727 185/733/729 189/734/730 +s 168 +f 81/735/731 78/736/732 194/738/734 +f 78/736/732 77/737/733 194/738/734 +s 169 +f 73/741/737 76/740/736 190/739/735 +s 170 +f 75/742/738 79/743/739 76/745/741 +f 79/743/739 189/744/740 76/745/741 +s 171 +f 77/746/742 70/747/743 194/749/745 +f 70/747/743 67/748/744 194/749/745 +s 172 +f 193/750/746 67/751/747 192/753/749 +f 67/751/747 66/752/748 192/753/749 +s 173 +usemtl Stone_Brit_Gray_Light +f 66/754/750 64/755/751 73/757/753 +f 64/755/751 63/756/752 73/757/753 +s 174 +f 74/758/754 63/759/755 61/760/756 +f 74/758/754 61/760/756 43/761/757 +s 175 +usemtl Stone_Bri_Gray_Lighter +f 181/765/761 60/764/760 58/763/759 +f 181/765/761 58/763/759 57/762/758 +s 176 +f 172/766/762 181/767/763 57/768/764 +f 172/766/762 57/768/764 55/769/765 +s 177 +usemtl Stone_Brit_Gray +f 54/770/766 183/771/767 55/773/769 +f 183/771/767 179/772/768 55/773/769 +s 178 +usemtl Stone_Bri_Gray_Lighter +f 53/776/772 36/775/771 51/774/770 +s 179 +f 56/777/773 59/778/774 52/780/776 +f 59/778/774 53/779/775 52/780/776 +s 180 +f 49/781/777 171/782/778 50/784/780 +f 171/782/778 54/783/779 50/784/780 +s 181 +f 49/787/783 48/786/782 171/785/781 +s 182 +f 45/788/784 46/789/785 48/791/787 +f 46/789/785 180/790/786 48/791/787 +s 183 +f 44/794/790 60/793/789 46/792/788 +s 184 +usemtl Stone_Brit_Gray_Light +f 43/797/793 61/796/792 44/795/791 +s 185 +f 42/798/794 72/799/795 74/800/796 +f 42/798/794 74/800/796 43/801/797 +s 186 +f 71/802/798 72/803/799 40/805/801 +f 72/803/799 42/804/800 40/805/801 +s 187 +f 39/806/802 69/807/803 40/809/805 +f 69/807/803 71/808/804 40/809/805 +s 188 +usemtl Stone_Bri_Gray_Lighter +f 51/810/806 36/811/807 41/812/808 +f 51/810/806 41/812/808 47/813/809 +s 189 +usemtl Stone_Brit_Gray_Light +f 39/814/810 35/815/811 68/816/812 +f 39/814/810 68/816/812 69/817/813 +s 190 +f 38/818/814 65/819/815 35/821/817 +f 65/819/815 68/820/816 35/821/817 +s 191 +f 37/824/820 62/823/819 38/822/818 +s 192 +usemtl Stone_Bri_Gray_Lighter +f 36/825/821 53/826/822 37/828/824 +f 53/826/822 59/827/823 37/828/824 +o object13 +g object13 +v -0.08785714 0.95950091 0.05357143 +v -0.08785714 0.99043039 0.00000000 +v -0.14285714 0.92857143 0.00000000 +v 0.08785714 0.95950091 0.05357143 +v 0.08785714 0.99043039 0.00000000 +v -0.08785714 0.95950091 -0.05357143 +v 0.08785714 0.95950091 -0.05357143 +v -0.08785714 0.89764195 -0.05357143 +v 0.08785714 0.89764195 -0.05357143 +v -0.08785714 0.86671247 0.00000000 +v 0.08785714 0.86671247 0.00000000 +v -0.08785714 0.89764195 0.05357143 +v 0.08785714 0.89764195 0.05357143 +v 0.14285714 0.92857143 0.00000000 +vt 0.00000000 0.24993517 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 0.24993517 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.50000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.75006483 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.24993517 +vt 0.00000000 1.00000000 +vt 1.00000000 0.75006483 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.50000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.50000000 +vt 1.00000000 0.75006483 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.75006483 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.50000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.24993517 +vt 0.00000000 1.00000000 +vn -0.69774235 0.62037627 0.35817440 +vn -0.69774235 0.62037627 0.35817440 +vn -0.69774235 0.62037627 0.35817440 +vn 0.00000000 0.86602540 0.50000000 +vn 0.00000000 0.86602540 0.50000000 +vn 0.00000000 0.86602540 0.50000000 +vn 0.00000000 0.86602540 0.50000000 +vn 0.00000000 0.86602540 -0.50000000 +vn 0.00000000 0.86602540 -0.50000000 +vn 0.00000000 0.86602540 -0.50000000 +vn 0.00000000 0.86602540 -0.50000000 +vn 0.00000000 -0.00000000 -1.00000000 +vn 0.00000000 -0.00000000 -1.00000000 +vn 0.00000000 -0.00000000 -1.00000000 +vn 0.00000000 -0.00000000 -1.00000000 +vn -0.00000000 -0.86602540 -0.50000000 +vn -0.00000000 -0.86602540 -0.50000000 +vn -0.00000000 -0.86602540 -0.50000000 +vn -0.00000000 -0.86602540 -0.50000000 +vn -0.00000000 -0.86602540 0.50000000 +vn -0.00000000 -0.86602540 0.50000000 +vn -0.00000000 -0.86602540 0.50000000 +vn -0.00000000 -0.86602540 0.50000000 +vn 0.69774235 -0.62037627 0.35817440 +vn 0.69774235 -0.62037627 0.35817440 +vn 0.69774235 -0.62037627 0.35817440 +vn 0.69774235 -0.00000000 0.71634881 +vn 0.69774235 -0.00000000 0.71634881 +vn 0.69774235 -0.00000000 0.71634881 +vn -0.69774235 -0.62037627 0.35817440 +vn -0.69774235 -0.62037627 0.35817440 +vn -0.69774235 -0.62037627 0.35817440 +vn 0.69774235 -0.62037627 -0.35817440 +vn 0.69774235 -0.62037627 -0.35817440 +vn 0.69774235 -0.62037627 -0.35817440 +vn -0.69774235 -0.62037627 -0.35817440 +vn -0.69774235 -0.62037627 -0.35817440 +vn -0.69774235 -0.62037627 -0.35817440 +vn 0.69774235 0.00000000 -0.71634881 +vn 0.69774235 0.00000000 -0.71634881 +vn 0.69774235 0.00000000 -0.71634881 +vn -0.69774235 0.00000000 -0.71634881 +vn -0.69774235 0.00000000 -0.71634881 +vn -0.69774235 0.00000000 -0.71634881 +vn 0.69774235 0.62037627 -0.35817440 +vn 0.69774235 0.62037627 -0.35817440 +vn 0.69774235 0.62037627 -0.35817440 +vn 0.69774235 0.62037627 0.35817440 +vn 0.69774235 0.62037627 0.35817440 +vn 0.69774235 0.62037627 0.35817440 +vn -0.00000000 -0.00000000 1.00000000 +vn -0.00000000 -0.00000000 1.00000000 +vn -0.00000000 -0.00000000 1.00000000 +vn -0.00000000 -0.00000000 1.00000000 +vn -0.69774235 0.00000000 0.71634881 +vn -0.69774235 0.00000000 0.71634881 +vn -0.69774235 0.00000000 0.71634881 +vn -0.69774235 0.62037627 -0.35817440 +vn -0.69774235 0.62037627 -0.35817440 +vn -0.69774235 0.62037627 -0.35817440 +s 1 +usemtl Burlap +f 195/829/825 196/830/826 197/831/827 +s 2 +f 199/835/831 196/834/830 198/832/828 +f 196/834/830 195/833/829 198/832/828 +s 3 +f 201/839/835 200/838/834 199/836/832 +f 200/838/834 196/837/833 199/836/832 +s 4 +f 203/843/839 202/842/838 201/840/836 +f 202/842/838 200/841/837 201/840/836 +s 5 +f 205/847/843 204/846/842 203/844/840 +f 204/846/842 202/845/841 203/844/840 +s 6 +f 207/851/847 206/850/846 205/848/844 +f 206/850/846 204/849/845 205/848/844 +s 7 +f 207/852/848 205/853/849 208/854/850 +s 8 +f 198/855/851 207/856/852 208/857/853 +s 9 +f 204/858/854 206/859/855 197/860/856 +s 10 +f 205/861/857 203/862/858 208/863/859 +s 11 +f 202/864/860 204/865/861 197/866/862 +s 12 +f 203/867/863 201/868/864 208/869/865 +s 13 +f 200/870/866 202/871/867 197/872/868 +s 14 +f 201/873/869 199/874/870 208/875/871 +s 15 +f 199/876/872 198/877/873 208/878/874 +s 16 +f 198/882/878 195/881/877 206/880/876 +f 198/882/878 206/880/876 207/879/875 +s 17 +f 206/883/879 195/884/880 197/885/881 +s 18 +f 196/886/882 200/887/883 197/888/884 +o object14 +g object14 +v -0.54027069 0.76648296 -0.02033718 +v -0.72394416 0.76943367 -0.02544795 +v -0.72394416 0.74004891 -0.02544795 +v -0.54027069 0.74299962 -0.02033718 +v -0.72394416 0.72535653 0.00000000 +v -0.54027069 0.73125794 0.00000000 +v -0.72394416 0.74004891 0.02544795 +v -0.54027069 0.74299962 0.02033718 +v -0.72394416 0.76943367 0.02544795 +v -0.54027069 0.76648296 0.02033718 +v -0.72394416 0.78412605 0.00000000 +v -0.54027069 0.77822464 0.00000000 +v -0.74637327 0.76727174 0.02170337 +v -0.74637327 0.77980218 0.00000000 +v -0.74637327 0.76727174 -0.02170337 +v -0.75455641 0.75474129 0.00000000 +v -0.74637327 0.74221084 -0.02170337 +v -0.74637327 0.72968040 0.00000000 +v -0.74637327 0.74221084 0.02170337 +v -0.54027069 0.75474129 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.45182546 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.50000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.54817454 +vt 1.00000000 0.00000000 +vt 1.00000000 0.54817454 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.50000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.45182546 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.25000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.50000000 1.00000000 +vt 0.00000000 0.75000000 +vt 0.50000000 0.50000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vn 0.02781457 0.00000000 -0.99961310 +vn 0.02781457 0.00000000 -0.99961310 +vn 0.02781457 0.00000000 -0.99961310 +vn 0.02781457 0.00000000 -0.99961310 +vn 0.02781457 -0.86569034 -0.49980655 +vn 0.02781457 -0.86569034 -0.49980655 +vn 0.02781457 -0.86569034 -0.49980655 +vn 0.02781457 -0.86569034 -0.49980655 +vn 0.02781457 -0.86569034 0.49980655 +vn 0.02781457 -0.86569034 0.49980655 +vn 0.02781457 -0.86569034 0.49980655 +vn 0.02781457 -0.86569034 0.49980655 +vn 0.02781457 0.00000000 0.99961310 +vn 0.02781457 -0.00000000 0.99961310 +vn 0.02781457 -0.00000000 0.99961310 +vn 0.02781457 0.00000000 0.99961310 +vn 0.02781457 0.86569034 0.49980655 +vn 0.02781457 0.86569034 0.49980655 +vn 0.02781457 0.86569034 0.49980655 +vn 0.02781457 0.86569034 0.49980655 +vn -0.16467281 0.85420264 0.49317412 +vn -0.16467281 0.85420264 0.49317412 +vn -0.16467281 0.85420264 0.49317412 +vn -0.16467281 0.85420264 0.49317412 +vn -0.16467281 0.85420264 -0.49317412 +vn -0.16467281 0.85420264 -0.49317412 +vn -0.16467281 0.85420264 -0.49317412 +vn -0.16467281 0.85420264 -0.49317412 +vn -0.93569868 0.30553393 -0.17640010 +vn -0.93569868 0.30553393 -0.17640010 +vn -0.93569868 0.30553393 -0.17640010 +vn -0.93569868 0.00000000 -0.35280019 +vn -0.93569868 0.00000000 -0.35280019 +vn -0.93569868 0.00000000 -0.35280019 +vn -0.93569868 -0.30553393 -0.17640010 +vn -0.93569868 -0.30553393 -0.17640010 +vn -0.93569868 -0.30553393 -0.17640010 +vn -0.93569868 -0.30553393 0.17640010 +vn -0.93569868 -0.30553393 0.17640010 +vn -0.93569868 -0.30553393 0.17640010 +vn -0.93569868 0.00000000 0.35280019 +vn -0.93569868 0.00000000 0.35280019 +vn -0.93569868 0.00000000 0.35280019 +vn -0.93569868 0.30553393 0.17640010 +vn -0.93569868 0.30553393 0.17640010 +vn -0.93569868 0.30553393 0.17640010 +vn -0.16467281 -0.00000000 0.98634825 +vn -0.16467281 0.00000000 0.98634825 +vn -0.16467281 0.00000000 0.98634825 +vn -0.16467281 -0.00000000 0.98634825 +vn -0.16467281 -0.85420264 0.49317412 +vn -0.16467281 -0.85420264 0.49317412 +vn -0.16467281 -0.85420264 0.49317412 +vn -0.16467281 -0.85420264 0.49317412 +vn -0.16467281 -0.85420264 -0.49317412 +vn -0.16467281 -0.85420264 -0.49317412 +vn -0.16467281 -0.85420264 -0.49317412 +vn -0.16467281 -0.85420264 -0.49317412 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.02781457 0.86569034 -0.49980655 +vn 0.02781457 0.86569034 -0.49980655 +vn 0.02781457 0.86569034 -0.49980655 +vn 0.02781457 0.86569034 -0.49980655 +vn -0.16467281 0.00000000 -0.98634825 +vn -0.16467281 0.00000000 -0.98634825 +vn -0.16467281 0.00000000 -0.98634825 +vn -0.16467281 0.00000000 -0.98634825 +s 1 +usemtl Wood_Dark +f 212/892/888 211/891/887 209/889/885 +f 211/891/887 210/890/886 209/889/885 +s 2 +f 214/896/892 213/895/891 212/893/889 +f 213/895/891 211/894/890 212/893/889 +s 3 +f 216/900/896 215/899/895 214/897/893 +f 215/899/895 213/898/894 214/897/893 +s 4 +f 218/904/900 217/903/899 216/901/897 +f 217/903/899 215/902/898 216/901/897 +s 5 +f 220/908/904 219/907/903 218/905/901 +f 219/907/903 217/906/902 218/905/901 +s 6 +f 219/912/908 222/911/907 217/909/905 +f 222/911/907 221/910/906 217/909/905 +s 7 +f 210/916/912 223/915/911 219/913/909 +f 223/915/911 222/914/910 219/913/909 +s 8 +usemtl Wood_Medium +f 224/919/915 222/918/914 223/917/913 +s 9 +f 224/922/918 223/921/917 225/920/916 +s 10 +f 224/925/921 225/924/920 226/923/919 +s 11 +f 224/928/924 226/927/923 227/926/922 +s 12 +f 224/931/927 227/930/926 221/929/925 +s 13 +f 224/934/930 221/933/929 222/932/928 +s 14 +usemtl Wood_Dark +f 217/938/934 221/937/933 227/936/932 +f 217/938/934 227/936/932 215/935/931 +s 15 +f 215/942/938 227/941/937 213/939/935 +f 227/941/937 226/940/936 213/939/935 +s 16 +f 213/946/942 226/945/941 211/943/939 +f 226/945/941 225/944/940 211/943/939 +s 17 +f 209/952/948 220/951/947 228/953/949 +f 214/948/944 212/947/943 228/953/949 +f 216/949/945 214/948/944 228/953/949 +f 218/950/946 216/949/945 228/953/949 +f 220/951/947 218/950/946 228/953/949 +f 212/947/943 209/952/948 228/953/949 +s 18 +f 209/957/953 210/956/952 220/954/950 +f 210/956/952 219/955/951 220/954/950 +s 19 +f 211/961/957 225/960/956 210/958/954 +f 225/960/956 223/959/955 210/958/954 +o object15 +g object15 +v 0.25414477 0.00000000 -0.10527021 +v 0.25414477 0.15000000 -0.10527021 +v 0.25414477 0.15000000 0.10527021 +v 0.25414477 0.00000000 0.10527021 +v 0.10527021 0.15000000 0.25414477 +v 0.10527021 0.00000000 0.25414477 +v -0.10527021 0.15000000 0.25414477 +v -0.10527021 0.00000000 0.25414477 +v -0.00000000 0.15000000 0.25414477 +v -0.00000000 0.00000000 0.25414477 +v -0.25414477 0.15000000 0.10527021 +v -0.25414477 0.00000000 0.10527021 +v -0.25414477 0.15000000 -0.10527021 +v -0.25414477 0.00000000 -0.10527021 +v -0.10527021 0.15000000 -0.25414477 +v -0.10527021 0.00000000 -0.25414477 +v 0.10527021 0.15000000 -0.25414477 +v 0.10527021 0.00000000 -0.25414477 +v -0.00000000 0.15000000 -0.25414477 +v -0.00000000 0.00000000 -0.25414477 +v -0.00000000 0.00000000 0.37500000 +v -0.15533009 0.00000000 0.37500000 +v -0.37500000 0.00000000 0.15533009 +v -0.37500000 0.00000000 -0.15533009 +v -0.15533009 0.00000000 -0.37500000 +v -0.00000000 0.00000000 -0.37500000 +v 0.15533009 0.00000000 -0.37500000 +v 0.37500000 0.00000000 -0.15533009 +v 0.37500000 0.00000000 0.15533009 +v 0.15533009 0.00000000 0.37500000 +v -0.15533009 0.15000000 0.37500000 +v -0.37500000 0.15000000 0.15533009 +v 0.15533009 0.15000000 0.37500000 +v -0.00000000 0.15000000 0.37500000 +v 0.37500000 0.15000000 0.15533009 +v 0.37500000 0.15000000 -0.15533009 +v 0.15533009 0.15000000 -0.37500000 +v -0.15533009 0.15000000 -0.37500000 +v -0.00000000 0.15000000 -0.37500000 +v -0.37500000 0.15000000 -0.15533009 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.50000000 +vt 0.00000000 0.50000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.50000000 +vt 0.00000000 0.50000000 +vt 0.83885970 0.50000000 +vt 0.83885970 0.35963972 +vt 0.64036028 0.16114030 +vt 0.35963972 0.16114030 +vt 0.16114030 0.35963972 +vt 0.16114030 0.50000000 +vt 0.00000000 0.50000000 +vt 0.00000000 0.29289322 +vt 0.29289322 0.00000000 +vt 0.70710678 0.00000000 +vt 1.00000000 0.29289322 +vt 1.00000000 0.50000000 +vt 0.16114030 0.64036028 +vt 0.35963972 0.83885970 +vt 0.64036028 0.83885970 +vt 0.83885970 0.64036028 +vt 1.00000000 0.70710678 +vt 0.70710678 1.00000000 +vt 0.29289322 1.00000000 +vt 0.00000000 0.70710678 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.50000000 +vt 0.00000000 0.50000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.50000000 +vt 0.00000000 0.50000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.83885970 0.50000000 +vt 0.83885970 0.35963972 +vt 0.64036028 0.16114030 +vt 0.35963972 0.16114030 +vt 0.16114030 0.35963972 +vt 0.16114030 0.50000000 +vt 0.00000000 0.50000000 +vt 0.00000000 0.29289322 +vt 0.29289322 0.00000000 +vt 0.70710678 0.00000000 +vt 1.00000000 0.29289322 +vt 1.00000000 0.50000000 +vt 0.16114030 0.64036028 +vt 0.35963972 0.83885970 +vt 0.64036028 0.83885970 +vt 0.83885970 0.64036028 +vt 1.00000000 0.70710678 +vt 0.70710678 1.00000000 +vt 0.29289322 1.00000000 +vt 0.00000000 0.70710678 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +s 1 +usemtl Wood_Darker +f 232/965/961 231/964/960 230/963/959 +f 232/965/961 230/963/959 229/962/958 +s 2 +f 234/969/965 233/968/964 231/967/963 +f 234/969/965 231/967/963 232/966/962 +s 3 +f 233/971/967 234/970/966 238/975/971 +f 236/973/969 235/972/968 238/975/971 +f 235/972/968 237/974/970 238/975/971 +f 237/974/970 233/971/967 238/975/971 +s 4 +f 240/979/975 239/978/974 236/976/972 +f 239/978/974 235/977/973 236/976/972 +s 5 +f 242/983/979 241/982/978 239/981/977 +f 242/983/979 239/981/977 240/980/976 +s 6 +f 244/987/983 243/986/982 241/985/981 +f 244/987/983 241/985/981 242/984/980 +s 7 +f 248/993/989 246/991/987 245/990/986 +f 243/989/985 244/988/984 248/993/989 +f 248/993/989 247/992/988 243/989/985 +f 248/993/989 245/990/986 247/992/988 +s 8 +f 254/1005/1001 248/994/990 244/995/991 +f 253/1004/1000 244/995/991 242/996/992 +f 252/1003/999 242/996/992 240/997/993 +f 240/997/993 250/1001/997 251/1002/998 +f 236/998/994 249/1000/996 250/1001/997 +f 258/1013/1009 249/1000/996 234/1006/1002 +f 232/1007/1003 257/1012/1008 258/1013/1009 +f 232/1007/1003 256/1011/1007 257/1012/1008 +f 229/1008/1004 255/1010/1006 256/1011/1007 +f 229/1008/1004 246/1009/1005 255/1010/1006 +f 232/1007/1003 229/1008/1004 256/1011/1007 +f 258/1013/1009 234/1006/1002 232/1007/1003 +f 249/1000/996 238/999/995 234/1006/1002 +f 236/998/994 238/999/995 249/1000/996 +f 240/997/993 236/998/994 250/1001/997 +f 251/1002/998 252/1003/999 240/997/993 +f 252/1003/999 253/1004/1000 242/996/992 +f 246/1009/1005 254/1005/1001 255/1010/1006 +f 246/1009/1005 248/994/990 254/1005/1001 +f 253/1004/1000 254/1005/1001 244/995/991 +s 9 +f 250/1014/1010 259/1015/1011 251/1017/1013 +f 259/1015/1011 260/1016/1012 251/1017/1013 +s 10 +f 249/1023/1019 258/1018/1014 261/1019/1015 +f 259/1020/1016 250/1021/1017 249/1023/1019 +f 249/1023/1019 262/1022/1018 259/1020/1016 +f 249/1023/1019 261/1019/1015 262/1022/1018 +s 11 +f 257/1024/1020 263/1025/1021 261/1026/1022 +f 257/1024/1020 261/1026/1022 258/1027/1023 +s 12 +f 256/1028/1024 264/1029/1025 263/1030/1026 +f 256/1028/1024 263/1030/1026 257/1031/1027 +s 13 +f 255/1032/1028 265/1033/1029 264/1034/1030 +f 255/1032/1028 264/1034/1030 256/1035/1031 +s 14 +f 265/1038/1034 255/1039/1035 254/1041/1037 +f 253/1036/1032 266/1037/1033 254/1041/1037 +f 266/1037/1033 267/1040/1036 254/1041/1037 +f 267/1040/1036 265/1038/1034 254/1041/1037 +s 15 +f 252/1042/1038 268/1043/1039 266/1044/1040 +f 252/1042/1038 266/1044/1040 253/1045/1041 +s 16 +f 251/1046/1042 260/1047/1043 268/1048/1044 +f 251/1046/1042 268/1048/1044 252/1049/1045 +s 17 +f 229/1053/1049 230/1052/1048 246/1050/1046 +f 230/1052/1048 245/1051/1047 246/1050/1046 +s 18 +f 243/1055/1051 247/1054/1050 267/1065/1061 +f 265/1070/1066 267/1065/1061 245/1069/1065 +f 264/1071/1067 265/1070/1066 230/1068/1064 +f 230/1068/1064 231/1067/1063 263/1072/1068 +f 231/1067/1063 261/1073/1069 263/1072/1068 +f 233/1066/1062 262/1060/1056 261/1073/1069 +f 259/1061/1057 262/1060/1056 235/1058/1054 +f 260/1062/1058 259/1061/1057 239/1057/1053 +f 239/1057/1053 241/1056/1052 268/1063/1059 +f 241/1056/1052 266/1064/1060 268/1063/1059 +f 239/1057/1053 268/1063/1059 260/1062/1058 +f 259/1061/1057 235/1058/1054 239/1057/1053 +f 262/1060/1056 237/1059/1055 235/1058/1054 +f 233/1066/1062 237/1059/1055 262/1060/1056 +f 231/1067/1063 233/1066/1062 261/1073/1069 +f 230/1068/1064 263/1072/1068 264/1071/1067 +f 265/1070/1066 245/1069/1065 230/1068/1064 +f 267/1065/1061 247/1054/1050 245/1069/1065 +f 241/1056/1052 243/1055/1051 266/1064/1060 +f 243/1055/1051 267/1065/1061 266/1064/1060 +o object16 +g object16 +v 0.32380952 0.94866766 0.52995333 +v 0.30357143 0.94809524 0.50190476 +v 0.48571429 0.94809524 0.50190476 +v 0.46547619 0.94866766 0.52995333 +v 0.30357143 1.04523810 0.40476190 +v 0.28333333 0.94866766 0.52995333 +v 0.20238095 1.04523810 0.40476190 +v 0.12142857 0.94866766 0.52995333 +v 0.10119048 1.04523810 0.40476190 +v 0.10119048 0.94809524 0.50190476 +v 0.08095238 0.94866766 0.52995333 +v -0.10119048 1.04523810 0.40476190 +v -0.00000000 1.04523810 0.40476190 +v -0.08095238 0.94866766 0.52995333 +v -0.10119048 0.94809524 0.50190476 +v -0.12142857 0.94866766 0.52995333 +v -0.20238095 1.04523810 0.40476190 +v -0.30357143 1.04523810 0.40476190 +v -0.28333333 0.94866766 0.52995333 +v -0.22261905 1.04523810 0.43338289 +v -0.38452381 1.04523810 0.43338289 +v -0.40476190 1.04523810 0.40476190 +v -0.30357143 1.14642857 0.30357143 +v -0.40476190 1.14642857 0.30357143 +v -0.20238095 1.14642857 0.30357143 +v -0.48571429 1.14642857 0.30357143 +v -0.46547619 1.14642857 0.33219242 +v -0.32380952 1.14642857 0.33219242 +v -0.30357143 1.24761905 0.20238095 +v -0.28333333 1.14642857 0.33219242 +v -0.20238095 1.24761905 0.20238095 +v -0.12142857 1.14642857 0.33219242 +v -0.10119048 1.24761905 0.20238095 +v -0.22261905 1.24761905 0.23100194 +v -0.38452381 1.24761905 0.23100194 +v -0.40476190 1.24761905 0.20238095 +v -0.30357143 1.34880952 0.10119048 +v -0.40476190 1.34880952 0.10119048 +v -0.20238095 1.34880952 0.10119048 +v -0.48571429 1.34880952 0.10119048 +v -0.46547619 1.34880952 0.12981146 +v -0.32380952 1.34880952 0.12981146 +v -0.30357143 1.45000000 -0.00000000 +v -0.28333333 1.34880952 0.12981146 +v -0.12142857 1.34880952 0.12981146 +v -0.10119048 1.45000000 -0.00000000 +v -0.28333333 1.34880952 -0.12981146 +v -0.12142857 1.34880952 -0.12981146 +v -0.30357143 1.34880952 -0.10119048 +v -0.32380952 1.34880952 -0.12981146 +v -0.48571429 1.45000000 -0.00000000 +v -0.46547619 1.34880952 -0.12981146 +v -0.48571429 1.34880952 -0.10119048 +v -0.48571429 1.04523810 -0.40476190 +v -0.48571429 0.94809524 -0.50190476 +v -0.48571429 0.91436508 -0.46817460 +v -0.48571429 1.38253968 -0.00000000 +v -0.48571429 1.24761905 -0.20238095 +v -0.48571429 1.14642857 -0.30357143 +v -0.46547619 0.94866766 -0.52995333 +v -0.32380952 0.94866766 -0.52995333 +v -0.30357143 1.04523810 -0.40476190 +v -0.40476190 1.04523810 -0.40476190 +v -0.30357143 0.94809524 -0.50190476 +v -0.28333333 0.94866766 -0.52995333 +v -0.20238095 1.04523810 -0.40476190 +v -0.12142857 0.94866766 -0.52995333 +v -0.10119048 1.04523810 -0.40476190 +v -0.10119048 0.94809524 -0.50190476 +v -0.08095238 0.94866766 -0.52995333 +v -0.00000000 1.04523810 -0.40476190 +v 0.08095238 0.94866766 -0.52995333 +v 0.10119048 1.04523810 -0.40476190 +v 0.10119048 0.94809524 -0.50190476 +v 0.12142857 0.94866766 -0.52995333 +v 0.20238095 1.04523810 -0.40476190 +v 0.30357143 1.04523810 -0.40476190 +v 0.28333333 0.94866766 -0.52995333 +v 0.22261905 1.04523810 -0.43338289 +v 0.38452381 1.04523810 -0.43338289 +v 0.40476190 1.04523810 -0.40476190 +v 0.30357143 1.14642857 -0.30357143 +v 0.40476190 1.14642857 -0.30357143 +v 0.20238095 1.14642857 -0.30357143 +v 0.48571429 1.14642857 -0.30357143 +v 0.46547619 1.14642857 -0.33219242 +v 0.32380952 1.14642857 -0.33219242 +v 0.30357143 1.24761905 -0.20238095 +v 0.28333333 1.14642857 -0.33219242 +v 0.20238095 1.24761905 -0.20238095 +v 0.12142857 1.14642857 -0.33219242 +v 0.10119048 1.24761905 -0.20238095 +v 0.22261905 1.24761905 -0.23100194 +v 0.38452381 1.24761905 -0.23100194 +v 0.40476190 1.24761905 -0.20238095 +v 0.30357143 1.34880952 -0.10119048 +v 0.40476190 1.34880952 -0.10119048 +v 0.20238095 1.34880952 -0.10119048 +v 0.48571429 1.34880952 -0.10119048 +v 0.46547619 1.34880952 -0.12981146 +v 0.32380952 1.34880952 -0.12981146 +v 0.30357143 1.45000000 -0.00000000 +v 0.28333333 1.34880952 -0.12981146 +v 0.12142857 1.34880952 -0.12981146 +v 0.10119048 1.45000000 -0.00000000 +v 0.28333333 1.34880952 0.12981146 +v 0.12142857 1.34880952 0.12981146 +v 0.30357143 1.34880952 0.10119048 +v 0.32380952 1.34880952 0.12981146 +v 0.48571429 1.45000000 -0.00000000 +v 0.46547619 1.34880952 0.12981146 +v 0.48571429 1.34880952 0.10119048 +v 0.48571429 1.04523810 0.40476190 +v 0.48571429 0.91436508 0.46817460 +v 0.48571429 1.38253968 -0.00000000 +v 0.48571429 1.24761905 0.20238095 +v 0.48571429 1.14642857 0.30357143 +v 0.46547619 1.04523810 0.43338289 +v 0.40476190 1.14642857 0.30357143 +v 0.42500000 1.04523810 0.43338289 +v 0.46547619 1.14642857 0.33219242 +v 0.32380952 1.14642857 0.33219242 +v 0.30357143 1.14642857 0.30357143 +v 0.38452381 1.04523810 0.43338289 +v 0.22261905 1.04523810 0.43338289 +v 0.20238095 1.14642857 0.30357143 +v 0.18214286 1.04523810 0.43338289 +v 0.10119048 1.14642857 0.30357143 +v 0.02023810 1.04523810 0.43338289 +v -0.00000000 1.14642857 0.30357143 +v -0.02023810 1.04523810 0.43338289 +v -0.10119048 1.14642857 0.30357143 +v -0.18214286 1.04523810 0.43338289 +v 0.08095238 1.14642857 0.33219242 +v -0.08095238 1.14642857 0.33219242 +v 0.10119048 1.24761905 0.20238095 +v -0.00000000 1.24761905 0.20238095 +v 0.02023810 1.24761905 0.23100194 +v 0.18214286 1.24761905 0.23100194 +v 0.20238095 1.24761905 0.20238095 +v 0.30357143 1.24761905 0.20238095 +v 0.28333333 1.14642857 0.33219242 +v 0.12142857 1.14642857 0.33219242 +v 0.22261905 1.24761905 0.23100194 +v 0.38452381 1.24761905 0.23100194 +v 0.40476190 1.24761905 0.20238095 +v 0.20238095 1.34880952 0.10119048 +v 0.40476190 1.34880952 0.10119048 +v 0.42500000 1.24761905 0.23100194 +v 0.46547619 1.24761905 0.23100194 +v -0.00000000 1.34880952 0.10119048 +v -0.02023810 1.24761905 0.23100194 +v -0.10119048 1.34880952 0.10119048 +v -0.18214286 1.24761905 0.23100194 +v 0.10119048 1.34880952 0.10119048 +v 0.08095238 1.34880952 0.12981146 +v -0.08095238 1.34880952 0.12981146 +v -0.08095238 1.34880952 -0.12981146 +v 0.08095238 1.34880952 -0.12981146 +v -0.10119048 1.34880952 -0.10119048 +v -0.00000000 1.34880952 -0.10119048 +v 0.10119048 1.34880952 -0.10119048 +v 0.18214286 1.24761905 -0.23100194 +v 0.02023810 1.24761905 -0.23100194 +v -0.00000000 1.24761905 -0.20238095 +v -0.02023810 1.24761905 -0.23100194 +v -0.18214286 1.24761905 -0.23100194 +v -0.20238095 1.24761905 -0.20238095 +v -0.10119048 1.24761905 -0.20238095 +v -0.30357143 1.24761905 -0.20238095 +v -0.28333333 1.14642857 -0.33219242 +v -0.12142857 1.14642857 -0.33219242 +v -0.30357143 1.14642857 -0.30357143 +v -0.20238095 1.14642857 -0.30357143 +v -0.10119048 1.14642857 -0.30357143 +v -0.40476190 1.14642857 -0.30357143 +v -0.38452381 1.04523810 -0.43338289 +v -0.22261905 1.04523810 -0.43338289 +v -0.18214286 1.04523810 -0.43338289 +v -0.46547619 1.14642857 -0.33219242 +v -0.32380952 1.14642857 -0.33219242 +v -0.42500000 1.04523810 -0.43338289 +v -0.46547619 1.04523810 -0.43338289 +v -0.40476190 1.24761905 -0.20238095 +v -0.46547619 1.24761905 -0.23100194 +v -0.42500000 1.24761905 -0.23100194 +v -0.40476190 1.34880952 -0.10119048 +v -0.38452381 1.24761905 -0.23100194 +v -0.22261905 1.24761905 -0.23100194 +v -0.20238095 1.34880952 -0.10119048 +v -0.02023810 1.04523810 -0.43338289 +v -0.00000000 1.14642857 -0.30357143 +v 0.02023810 1.04523810 -0.43338289 +v 0.10119048 1.14642857 -0.30357143 +v 0.18214286 1.04523810 -0.43338289 +v 0.08095238 1.14642857 -0.33219242 +v -0.08095238 1.14642857 -0.33219242 +v 0.40476190 1.04523810 0.40476190 +v 0.48571429 1.04523810 -0.40476190 +v 0.48571429 0.94809524 -0.50190476 +v 0.48571429 0.91436508 -0.46817460 +v 0.48571429 1.24761905 -0.20238095 +v 0.44523810 0.91436508 -0.46817460 +v 0.44523810 1.38253968 -0.00000000 +v 0.44523810 0.86329382 -0.40476190 +v 0.44523810 1.26805573 -0.00000000 +v 0.44523810 0.86329382 0.40476190 +v 0.44523810 0.91436508 0.46817460 +v -0.44523810 0.86329382 0.40476190 +v -0.44523810 0.91436508 0.46817460 +v -0.44523810 1.38253968 -0.00000000 +v -0.44523810 1.26805573 -0.00000000 +v -0.44523810 0.86329382 -0.40476190 +v -0.44523810 0.91436508 -0.46817460 +v -0.48571429 0.91436508 0.46817460 +v 0.46547619 1.24761905 -0.23100194 +v 0.42500000 1.24761905 -0.23100194 +v 0.46547619 1.04523810 -0.43338289 +v 0.42500000 1.04523810 -0.43338289 +v 0.46547619 0.94866766 -0.52995333 +v 0.32380952 0.94866766 -0.52995333 +v 0.30357143 0.94809524 -0.50190476 +v -0.48571429 1.04523810 0.40476190 +v -0.48571429 0.94809524 0.50190476 +v -0.48571429 1.24761905 0.20238095 +v -0.46547619 1.24761905 0.23100194 +v -0.42500000 1.24761905 0.23100194 +v -0.46547619 1.04523810 0.43338289 +v -0.42500000 1.04523810 0.43338289 +v -0.46547619 0.94866766 0.52995333 +v -0.32380952 0.94866766 0.52995333 +v -0.30357143 0.94809524 0.50190476 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.90000000 1.00000000 +vt 0.10000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.19354839 1.00000000 +vt 0.00000000 1.00000000 +vt 0.01881720 0.00000000 +vt 0.95161290 0.00000000 +vt 1.00000000 1.00000000 +vt 0.79838710 1.00000000 +vt 0.59677419 1.00000000 +vt 0.39516129 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.10000000 0.00000000 +vt 0.90000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.19354839 1.00000000 +vt 0.00000000 1.00000000 +vt 0.01881720 0.00000000 +vt 0.95161290 0.00000000 +vt 1.00000000 1.00000000 +vt 0.79838710 1.00000000 +vt 0.59677419 1.00000000 +vt 0.39516129 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.90000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.10000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.90000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.10000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.90000000 1.00000000 +vt 0.10000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.10000000 0.00000000 +vt 0.90000000 0.00000000 +vt 0.10000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.90000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.10000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.90000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.90000000 1.00000000 +vt 0.10000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.10000000 0.00000000 +vt 0.90000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.19354839 1.00000000 +vt 0.00000000 1.00000000 +vt 0.01881720 0.00000000 +vt 0.95161290 0.00000000 +vt 1.00000000 1.00000000 +vt 0.79838710 1.00000000 +vt 0.59677419 1.00000000 +vt 0.39516129 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.93227666 +vt 0.77951879 0.50000000 +vt 0.00000000 0.06772334 +vt 0.09835660 0.00000000 +vt 1.00000000 0.50000000 +vt 0.09835660 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.50000000 +vt 0.09835660 1.00000000 +vt 0.00000000 0.93227666 +vt 0.77951879 0.50000000 +vt 0.00000000 0.06772334 +vt 0.09835660 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.10000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.90000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.19354839 1.00000000 +vt 0.00000000 1.00000000 +vt 0.01881720 0.00000000 +vt 0.95161290 0.00000000 +vt 1.00000000 1.00000000 +vt 0.79838710 1.00000000 +vt 0.59677419 1.00000000 +vt 0.39516129 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.18750000 +vt 1.00000000 0.39583333 +vt 1.00000000 0.60416667 +vt 1.00000000 0.81250000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.95833333 +vt 0.00000000 0.04166667 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.90000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.10000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.18750000 +vt 1.00000000 0.39583333 +vt 1.00000000 0.60416667 +vt 1.00000000 0.81250000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.95833333 +vt 0.00000000 0.04166667 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vn -0.00000000 -0.99979182 0.02040391 +vn -0.00000000 -0.99979182 0.02040391 +vn -0.00000000 -0.99979182 0.02040391 +vn -0.00000000 -0.99979182 0.02040391 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.00000000 0.79180001 0.61078043 +vn -0.00000000 0.79180001 0.61078043 +vn -0.00000000 0.79180001 0.61078043 +vn -0.00000000 0.79180001 0.61078043 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.79180001 -0.61078043 +vn 0.00000000 0.79180001 -0.61078043 +vn 0.00000000 0.79180001 -0.61078043 +vn 0.00000000 0.79180001 -0.61078043 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 1.00000000 -0.00000000 -0.00000000 +vn 1.00000000 -0.00000000 -0.00000000 +vn 1.00000000 -0.00000000 -0.00000000 +vn 1.00000000 -0.00000000 -0.00000000 +vn 1.00000000 -0.00000000 -0.00000000 +vn 1.00000000 -0.00000000 -0.00000000 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn -0.00000000 -0.77882107 0.62724615 +vn -0.00000000 -0.77882107 0.62724615 +vn -0.00000000 -0.77882107 0.62724615 +vn -0.00000000 -0.77882107 0.62724615 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.77882107 -0.62724615 +vn -0.00000000 -0.77882107 -0.62724615 +vn -0.00000000 -0.77882107 -0.62724615 +vn -0.00000000 -0.77882107 -0.62724615 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.00000000 -0.99979182 -0.02040391 +vn -0.00000000 -0.99979182 -0.02040391 +vn -0.00000000 -0.99979182 -0.02040391 +vn -0.00000000 -0.99979182 -0.02040391 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.79180001 -0.61078043 +vn 0.00000000 0.79180001 -0.61078043 +vn 0.00000000 0.79180001 -0.61078043 +vn 0.00000000 0.79180001 -0.61078043 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.00000000 -0.99979182 -0.02040391 +vn -0.00000000 -0.99979182 -0.02040391 +vn -0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -0.99979182 -0.02040391 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -1.00000000 -0.00000000 0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn -0.70710678 0.50000000 0.50000000 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.00000000 0.79180001 0.61078043 +vn -0.00000000 0.79180001 0.61078043 +vn -0.00000000 0.79180001 0.61078043 +vn -0.00000000 0.79180001 0.61078043 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -0.99979182 0.02040391 +vn -0.00000000 -0.99979182 0.02040391 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +s 1 +usemtl Dark_Red_Roof +f 269/1074/1070 270/1075/1071 271/1076/1072 +f 276/1978/1868 278/1977/1859 270/1976/1071 +f 282/1965/1860 278/1963/1859 279/1962/1858 +f 282/1965/1860 283/1964/1851 278/1963/1859 +f 500/1953/1770 283/1954/1851 284/1955/1852 +f 500/1953/1770 284/1955/1852 287/1956/1853 +f 498/1858/1772 500/1856/1770 499/1855/1769 +f 498/1858/1772 492/1857/1771 500/1856/1770 +f 274/1979/1869 276/1978/1868 270/1976/1071 +f 269/1074/1070 271/1076/1072 272/1077/1073 +s 2 +f 273/1078/1074 270/1079/1075 269/1080/1076 +s 3 +f 274/1083/1079 270/1082/1078 273/1081/1077 +s 4 +f 275/1084/1080 277/1088/1084 276/1087/1083 +f 279/1098/1093 277/1097/1084 281/1096/1092 +f 281/1096/1092 280/1095/1091 282/1099/1094 +f 284/1109/1104 280/1110/1091 285/1106/1101 +f 285/1106/1101 286/1107/1102 287/1108/1103 +f 499/1944/1843 286/1945/1102 290/1946/1844 +f 290/1946/1844 491/1942/1841 498/1943/1842 +f 498/1943/1842 499/1944/1843 290/1946/1844 +f 285/1106/1101 287/1108/1103 284/1109/1104 +f 282/1099/1094 279/1098/1093 281/1096/1092 +f 276/1087/1083 274/1086/1082 275/1084/1080 +f 274/1086/1082 273/1085/1081 275/1084/1080 +f 466/1984/1873 273/1983/1081 269/1982/1872 +f 466/1984/1873 269/1982/1872 272/1981/1871 +f 272/1981/1871 381/1980/1870 466/1984/1873 +s 5 +f 277/1089/1085 278/1090/1086 276/1091/1087 +s 6 +f 279/1094/1090 278/1093/1089 277/1092/1088 +s 7 +f 280/1100/1095 283/1101/1096 282/1102/1097 +s 8 +f 284/1105/1100 283/1104/1099 280/1103/1098 +s 9 +usemtl Red_Roof +f 289/1112/1106 290/1113/1107 286/1114/1108 +f 286/1114/1108 285/1115/1109 288/1111/1105 +f 393/1971/1865 275/1975/1863 273/1974/1867 +f 273/1974/1867 466/1973/1572 392/1972/1866 +f 273/1974/1867 392/1972/1866 393/1971/1865 +f 288/1111/1105 289/1112/1106 286/1114/1108 +usemtl Dark_Red_Roof +f 401/1958/1855 285/1959/1109 280/1960/1856 +f 280/1960/1856 281/1961/1857 399/1957/1854 +f 397/1966/1861 281/1970/1857 277/1969/1864 +f 277/1969/1864 275/1968/1863 395/1967/1862 +f 388/1616/1571 466/1617/1572 381/1618/1573 +f 386/1615/1570 388/1616/1571 381/1618/1573 +f 277/1969/1864 395/1967/1862 397/1966/1861 +f 399/1957/1854 401/1958/1855 280/1960/1856 +f 491/1848/1763 290/1847/1107 497/1846/1762 +f 491/1848/1763 497/1846/1762 496/1845/1761 +s 10 +usemtl Red_Roof +f 291/1116/1110 292/1117/1111 289/1118/1112 +f 291/1116/1110 289/1118/1112 288/1119/1113 +f 288/1119/1113 293/1120/1114 291/1116/1110 +f 391/1316/1305 394/1320/1308 393/1319/1307 +f 393/1319/1307 392/1318/1306 391/1316/1305 +f 392/1318/1306 387/1317/1297 391/1316/1305 +usemtl Dark_Red_Roof +f 496/1925/1829 292/1923/1111 294/1922/1827 +f 496/1925/1829 497/1924/1828 292/1923/1111 +f 400/1338/1325 293/1339/1114 401/1340/1326 +f 400/1338/1325 401/1340/1326 399/1341/1327 +f 399/1341/1327 398/1342/1318 400/1338/1325 +f 396/1327/1315 398/1331/1318 397/1330/1317 +f 397/1330/1317 395/1329/1316 396/1327/1315 +f 395/1329/1316 394/1328/1308 396/1327/1315 +f 387/1308/1297 388/1309/1298 386/1310/1299 +f 385/1307/1296 387/1308/1297 386/1310/1299 +s 11 +usemtl Red_Roof +f 298/1935/1836 291/1936/1118 293/1937/1837 +f 293/1937/1837 400/1938/1334 300/1934/1835 +f 411/1596/1554 396/1600/1332 394/1599/1556 +f 394/1599/1556 391/1598/1303 410/1597/1555 +f 394/1599/1556 410/1597/1555 411/1596/1554 +f 300/1934/1835 298/1935/1836 293/1937/1837 +usemtl Dark_Red_Roof +f 295/1122/1116 294/1121/1115 292/1125/1119 +f 292/1125/1119 291/1124/1118 296/1123/1117 +f 403/1350/1335 400/1349/1334 398/1348/1333 +f 398/1348/1333 396/1347/1332 402/1346/1331 +f 390/1313/1302 391/1314/1303 387/1315/1304 +f 387/1315/1304 385/1311/1300 389/1312/1301 +f 389/1312/1301 390/1313/1302 387/1315/1304 +f 403/1350/1335 398/1348/1333 402/1346/1331 +f 292/1125/1119 296/1123/1117 295/1122/1116 +s 12 +f 296/1128/1122 291/1127/1121 297/1126/1120 +s 13 +usemtl Red_Roof +f 297/1129/1123 291/1130/1124 298/1131/1125 +s 14 +f 300/1135/1129 301/1136/1130 299/1132/1126 +f 299/1132/1126 297/1133/1127 298/1134/1128 +f 408/1364/1348 404/1368/1340 411/1367/1351 +f 411/1367/1351 410/1366/1350 408/1364/1348 +f 410/1366/1350 409/1365/1349 408/1364/1348 +f 299/1132/1126 298/1134/1128 300/1135/1129 +usemtl Dark_Red_Roof +f 296/1931/1833 297/1932/1127 304/1933/1834 +f 304/1933/1834 493/1929/1831 295/1930/1832 +f 295/1930/1832 296/1931/1833 304/1933/1834 +f 405/1356/1341 301/1357/1130 403/1358/1342 +f 402/1354/1339 405/1356/1341 403/1358/1342 +f 402/1354/1339 404/1355/1340 405/1356/1341 +f 414/1611/1566 409/1610/1349 390/1609/1565 +f 414/1611/1566 390/1609/1565 389/1608/1564 +f 389/1608/1564 384/1607/1563 414/1611/1566 +s 15 +usemtl Red_Roof +f 297/1140/1134 299/1141/1135 302/1137/1131 +f 412/1372/1355 408/1376/1345 409/1375/1358 +f 409/1375/1358 414/1374/1357 413/1373/1356 +f 409/1375/1358 413/1373/1356 412/1372/1355 +f 302/1137/1131 303/1138/1132 297/1140/1134 +f 303/1138/1132 304/1139/1133 297/1140/1134 +usemtl Dark_Red_Roof +f 422/1915/1822 299/1916/1135 301/1917/1823 +f 301/1917/1823 405/1918/1347 420/1914/1821 +f 406/1359/1343 405/1363/1347 404/1362/1346 +f 404/1362/1346 408/1361/1345 407/1360/1344 +f 417/1396/1377 414/1397/1357 384/1398/1378 +f 418/1395/1376 417/1396/1377 384/1398/1378 +f 404/1362/1346 407/1360/1344 406/1359/1343 +f 420/1914/1821 422/1915/1822 301/1917/1823 +f 493/1838/1754 304/1837/1133 495/1836/1753 +f 493/1838/1754 495/1836/1753 494/1835/1752 +s 16 +usemtl Red_Roof +f 302/1145/1139 307/1146/1140 305/1142/1136 +f 305/1142/1136 306/1143/1137 303/1144/1138 +f 376/1380/1362 415/1384/1366 412/1383/1365 +f 412/1383/1365 413/1382/1364 376/1380/1362 +f 413/1382/1364 416/1381/1363 376/1380/1362 +f 305/1142/1136 303/1144/1138 302/1145/1139 +usemtl Dark_Red_Roof +f 494/1895/1806 306/1893/1137 308/1892/1804 +f 494/1895/1806 495/1894/1805 306/1893/1137 +f 421/1408/1388 307/1409/1140 422/1410/1389 +f 421/1408/1388 422/1410/1389 420/1411/1390 +f 420/1411/1390 419/1412/1391 421/1408/1388 +f 423/1585/1545 419/1589/1391 406/1588/1547 +f 406/1588/1547 407/1587/1546 423/1585/1545 +f 407/1587/1546 415/1586/1366 423/1585/1545 +f 416/1392/1363 417/1393/1374 418/1394/1375 +f 380/1391/1373 416/1392/1363 418/1394/1375 +s 17 +usemtl Red_Roof +f 312/1904/1813 305/1905/1144 307/1906/1814 +f 307/1906/1814 421/1907/1396 313/1903/1812 +f 375/1708/1655 423/1712/1393 415/1711/1657 +f 415/1711/1657 376/1710/1653 374/1709/1656 +f 415/1711/1657 374/1709/1656 375/1708/1655 +f 313/1903/1812 312/1904/1813 307/1906/1814 +usemtl Dark_Red_Roof +f 309/1148/1142 308/1147/1141 306/1151/1145 +f 306/1151/1145 305/1150/1144 310/1149/1143 +f 425/1416/1395 421/1417/1396 419/1413/1392 +f 419/1413/1392 423/1414/1393 424/1415/1394 +f 377/1705/1652 376/1706/1653 416/1707/1654 +f 416/1707/1654 380/1703/1650 379/1704/1651 +f 379/1704/1651 377/1705/1652 416/1707/1654 +f 419/1413/1392 424/1415/1394 425/1416/1395 +f 306/1151/1145 310/1149/1143 309/1148/1142 +s 18 +f 310/1154/1148 305/1153/1147 311/1152/1146 +s 19 +usemtl Red_Roof +f 311/1155/1149 305/1156/1150 312/1157/1151 +s 20 +f 312/1158/1152 313/1159/1153 314/1160/1154 +f 373/1281/1271 375/1280/1270 374/1279/1269 +f 370/1282/1272 373/1281/1271 374/1279/1269 +f 312/1158/1152 314/1160/1154 311/1161/1155 +usemtl Dark_Red_Roof +f 314/1422/1154 425/1423/1400 424/1424/1401 +f 373/1421/1271 314/1422/1154 424/1424/1401 +f 378/1291/1280 370/1290/1272 377/1289/1279 +f 379/1292/1281 378/1291/1280 377/1289/1279 +f 311/1900/1155 319/1901/1810 309/1902/1811 +f 310/1899/1809 311/1900/1155 309/1902/1811 +s 21 +usemtl Red_Roof +f 311/1165/1159 314/1164/1158 316/1163/1157 +f 372/1276/1266 373/1277/1267 370/1278/1268 +f 371/1275/1265 372/1276/1266 370/1278/1268 +f 311/1165/1159 316/1163/1157 315/1162/1156 +usemtl Dark_Red_Roof +f 426/1425/1402 314/1426/1158 373/1427/1267 +f 369/1726/1669 370/1727/1268 378/1728/1670 +f 369/1726/1669 378/1728/1670 368/1729/1671 +f 426/1425/1402 373/1427/1267 427/1428/1403 +f 320/1175/1168 311/1173/1159 318/1172/1166 +f 320/1175/1168 319/1174/1167 311/1173/1159 +s 22 +usemtl Red_Roof +f 315/1168/1162 317/1167/1161 311/1166/1160 +s 23 +usemtl Dark_Red_Roof +f 311/1169/1163 317/1170/1164 318/1171/1165 +s 24 +f 320/1178/1171 321/1177/1170 319/1176/1169 +f 453/1823/1742 326/1822/1740 321/1821/1170 +f 448/1820/1741 327/1819/1738 326/1818/1740 +f 451/1817/1739 322/1816/1180 327/1815/1738 +f 328/1189/1182 323/1188/1181 322/1187/1180 +s 25 +f 325/1182/1175 319/1183/1176 321/1184/1177 +f 294/1831/1748 325/1827/1175 483/1826/1745 +f 483/1826/1745 492/1825/1744 491/1824/1743 +f 483/1826/1745 491/1824/1743 294/1831/1748 +f 294/1831/1748 493/1830/1747 325/1827/1175 +f 493/1830/1747 308/1829/1746 325/1827/1175 +f 308/1829/1746 319/1828/1176 325/1827/1175 +f 324/1181/1174 325/1182/1175 327/1186/1179 +f 327/1186/1179 322/1179/1172 324/1181/1174 +f 322/1179/1172 323/1180/1173 324/1181/1174 +f 325/1182/1175 326/1185/1178 327/1186/1179 +f 325/1182/1175 321/1184/1177 326/1185/1178 +s 26 +f 328/1191/1184 322/1190/1183 331/1194/1187 +f 331/1194/1187 330/1193/1186 329/1192/1185 +f 333/1203/1195 330/1202/1186 334/1201/1194 +f 334/1201/1194 336/1205/1197 335/1204/1196 +f 338/1214/1205 336/1213/1197 339/1212/1204 +f 339/1212/1204 341/1216/1207 340/1215/1206 +f 343/1226/1217 341/1227/1207 344/1223/1214 +f 344/1223/1214 345/1224/1215 346/1225/1216 +f 489/1771/1703 345/1772/1215 349/1773/1704 +f 349/1773/1704 467/1769/1701 488/1770/1702 +f 488/1770/1702 489/1771/1703 349/1773/1704 +f 344/1223/1214 346/1225/1216 343/1226/1217 +f 340/1215/1206 338/1214/1205 339/1212/1204 +f 335/1204/1196 333/1203/1195 334/1201/1194 +f 331/1194/1187 329/1192/1185 328/1191/1184 +s 27 +f 330/1195/1188 332/1196/1189 329/1197/1190 +s 28 +f 333/1200/1193 332/1199/1192 330/1198/1191 +s 29 +f 336/1206/1198 337/1207/1199 335/1208/1200 +s 30 +f 338/1211/1203 337/1210/1202 336/1209/1201 +s 31 +f 341/1217/1208 342/1218/1209 340/1219/1210 +s 32 +f 343/1222/1213 342/1221/1212 341/1220/1211 +s 33 +usemtl Red_Roof +f 348/1229/1219 349/1230/1220 345/1231/1221 +f 345/1231/1221 344/1232/1222 347/1228/1218 +f 446/1802/1728 334/1806/1723 330/1805/1731 +f 330/1805/1731 331/1804/1730 445/1803/1729 +f 330/1805/1731 445/1803/1729 446/1802/1728 +f 347/1228/1218 348/1229/1219 345/1231/1221 +usemtl Dark_Red_Roof +f 463/1785/1715 344/1786/1222 341/1787/1716 +f 341/1787/1716 339/1788/1717 461/1784/1714 +f 459/1793/1721 339/1797/1717 336/1796/1724 +f 336/1796/1724 334/1795/1723 447/1794/1722 +f 451/1807/1732 331/1809/1730 322/1810/1734 +f 451/1807/1732 450/1808/1733 331/1809/1730 +f 336/1796/1724 447/1794/1722 459/1793/1721 +f 461/1784/1714 463/1785/1715 341/1787/1716 +f 349/1684/1220 487/1683/1632 486/1682/1631 +f 467/1685/1633 349/1684/1220 486/1682/1631 +s 34 +usemtl Red_Roof +f 350/1233/1223 351/1234/1224 348/1235/1225 +f 350/1233/1223 348/1235/1225 347/1236/1226 +f 347/1236/1226 352/1237/1227 350/1233/1223 +f 441/1463/1437 442/1467/1441 446/1466/1440 +f 446/1466/1440 445/1465/1439 441/1463/1437 +f 445/1465/1439 444/1464/1438 441/1463/1437 +usemtl Dark_Red_Roof +f 487/1751/1688 351/1750/1224 353/1749/1687 +f 486/1752/1689 487/1751/1688 353/1749/1687 +f 462/1530/1499 352/1531/1227 463/1532/1500 +f 462/1530/1499 463/1532/1500 461/1533/1501 +f 461/1533/1501 460/1534/1492 462/1530/1499 +f 443/1519/1489 460/1523/1492 459/1522/1491 +f 459/1522/1491 447/1521/1490 443/1519/1489 +f 447/1521/1490 442/1520/1441 443/1519/1489 +f 327/1479/1452 444/1480/1438 450/1481/1453 +f 327/1479/1452 450/1481/1453 451/1482/1454 +s 35 +usemtl Red_Roof +f 357/1762/1696 350/1763/1231 352/1764/1697 +f 352/1764/1697 462/1765/1506 359/1761/1695 +f 440/1458/1432 443/1462/1436 442/1461/1435 +f 442/1461/1435 441/1460/1434 439/1459/1433 +f 442/1461/1435 439/1459/1433 440/1458/1432 +f 359/1761/1695 357/1762/1696 352/1764/1697 +usemtl Dark_Red_Roof +f 354/1239/1229 353/1238/1228 351/1242/1232 +f 351/1242/1232 350/1241/1231 355/1240/1230 +f 464/1540/1507 462/1539/1506 460/1538/1505 +f 460/1538/1505 443/1542/1436 465/1541/1508 +f 449/1476/1450 441/1477/1434 444/1478/1451 +f 444/1478/1451 327/1474/1448 448/1475/1449 +f 448/1475/1449 449/1476/1450 444/1478/1451 +f 465/1541/1508 464/1540/1507 460/1538/1505 +f 351/1242/1232 355/1240/1230 354/1239/1229 +s 36 +f 355/1245/1235 350/1244/1234 356/1243/1233 +s 37 +usemtl Red_Roof +f 356/1246/1236 350/1247/1237 357/1248/1238 +s 38 +f 359/1252/1242 360/1253/1243 358/1249/1239 +f 358/1249/1239 356/1250/1240 357/1251/1241 +f 436/1453/1427 437/1457/1431 440/1456/1430 +f 440/1456/1430 439/1455/1429 436/1453/1427 +f 439/1455/1429 438/1454/1428 436/1453/1427 +f 358/1249/1239 357/1251/1241 359/1252/1242 +usemtl Dark_Red_Roof +f 355/1758/1693 356/1759/1240 363/1760/1694 +f 363/1760/1694 470/1756/1691 354/1757/1692 +f 354/1757/1692 355/1758/1693 363/1760/1694 +f 433/1560/1524 360/1564/1243 464/1563/1526 +f 464/1563/1526 465/1562/1525 433/1560/1524 +f 465/1562/1525 437/1561/1431 433/1560/1524 +f 452/1493/1464 438/1492/1428 449/1491/1463 +f 452/1493/1464 449/1491/1463 448/1490/1462 +f 448/1490/1462 326/1489/1461 452/1493/1464 +s 39 +usemtl Red_Roof +f 356/1257/1247 358/1258/1248 361/1254/1244 +f 457/1552/1518 436/1556/1424 438/1555/1520 +f 438/1555/1520 452/1554/1467 456/1553/1519 +f 438/1555/1520 456/1553/1519 457/1552/1518 +f 361/1254/1244 362/1255/1245 356/1257/1247 +f 362/1255/1245 363/1256/1246 356/1257/1247 +usemtl Dark_Red_Roof +f 431/1742/1682 358/1743/1248 360/1744/1683 +f 360/1744/1683 433/1745/1426 432/1741/1681 +f 434/1448/1422 433/1452/1426 437/1451/1425 +f 437/1451/1425 436/1450/1424 435/1449/1423 +f 453/1494/1465 452/1496/1467 326/1497/1468 +f 453/1494/1465 454/1495/1466 452/1496/1467 +f 437/1451/1425 435/1449/1423 434/1448/1422 +f 432/1741/1681 431/1742/1682 360/1744/1683 +f 363/1674/1246 485/1673/1623 484/1672/1622 +f 470/1675/1624 363/1674/1246 484/1672/1622 +s 40 +usemtl Red_Roof +f 361/1262/1252 366/1263/1253 364/1259/1249 +f 364/1259/1249 365/1260/1250 362/1261/1251 +f 317/1508/1479 458/1512/1482 457/1511/1481 +f 457/1511/1481 456/1510/1480 317/1508/1479 +f 456/1510/1480 455/1509/1470 317/1508/1479 +f 364/1259/1249 362/1261/1251 361/1262/1252 +usemtl Dark_Red_Roof +f 485/1721/1665 365/1720/1250 367/1719/1664 +f 484/1722/1666 485/1721/1665 367/1719/1664 +f 430/1437/1412 366/1438/1253 431/1439/1413 +f 430/1437/1412 431/1439/1413 432/1440/1414 +f 432/1440/1414 429/1441/1415 430/1437/1412 +f 428/1571/1533 429/1575/1415 434/1574/1535 +f 434/1574/1535 435/1573/1534 428/1571/1533 +f 435/1573/1534 458/1572/1482 428/1571/1533 +f 321/1498/1469 455/1499/1470 454/1500/1471 +f 321/1498/1469 454/1500/1471 453/1501/1472 +s 41 +usemtl Red_Roof +f 371/1731/1673 364/1732/1257 366/1733/1674 +f 366/1733/1674 430/1734/1410 372/1730/1672 +f 316/1881/1795 428/1885/1408 458/1884/1797 +f 458/1884/1797 317/1883/1793 315/1882/1796 +f 458/1884/1797 315/1882/1796 316/1881/1795 +f 372/1730/1672 371/1731/1673 366/1733/1674 +usemtl Dark_Red_Roof +f 368/1265/1255 367/1264/1254 365/1268/1258 +f 365/1268/1258 364/1267/1257 369/1266/1256 +f 427/1436/1411 430/1435/1410 429/1434/1409 +f 429/1434/1409 428/1433/1408 426/1432/1407 +f 318/1878/1792 317/1879/1793 455/1880/1794 +f 455/1880/1794 321/1876/1790 320/1877/1791 +f 320/1877/1791 318/1878/1792 455/1880/1794 +f 427/1436/1411 429/1434/1409 426/1432/1407 +f 365/1268/1258 369/1266/1256 368/1265/1255 +s 42 +f 369/1271/1261 364/1270/1260 370/1269/1259 +s 43 +usemtl Red_Roof +f 370/1272/1262 364/1273/1263 371/1274/1264 +s 44 +f 374/1285/1275 376/1284/1274 370/1283/1273 +s 45 +usemtl Dark_Red_Roof +f 370/1286/1276 376/1287/1277 377/1288/1278 +s 46 +f 379/1295/1284 380/1294/1283 378/1293/1282 +f 418/1624/1576 384/1623/1574 380/1622/1283 +f 389/1621/1575 385/1620/1293 384/1619/1574 +f 386/1306/1295 381/1305/1294 385/1304/1293 +f 272/1987/1875 271/1986/1874 381/1985/1294 +s 47 +f 383/1299/1288 378/1300/1289 380/1301/1290 +f 353/1632/1582 383/1628/1288 469/1627/1579 +f 469/1627/1579 468/1626/1578 467/1625/1577 +f 469/1627/1579 467/1625/1577 353/1632/1582 +f 353/1632/1582 470/1631/1581 383/1628/1288 +f 470/1631/1581 367/1630/1580 383/1628/1288 +f 367/1630/1580 378/1629/1289 383/1628/1288 +f 382/1298/1287 383/1299/1288 385/1303/1292 +f 385/1303/1292 381/1296/1285 382/1298/1287 +f 381/1296/1285 271/1297/1286 382/1298/1287 +f 383/1299/1288 384/1302/1291 385/1303/1292 +f 383/1299/1288 380/1301/1290 384/1302/1291 +s 48 +usemtl Red_Roof +f 394/1321/1309 275/1322/1310 393/1323/1311 +s 49 +usemtl Dark_Red_Roof +f 395/1326/1314 275/1325/1313 394/1324/1312 +s 50 +f 398/1332/1319 281/1333/1320 397/1334/1321 +s 51 +f 399/1337/1324 281/1336/1323 398/1335/1322 +s 52 +f 293/1343/1328 285/1344/1329 401/1345/1330 +s 53 +f 301/1351/1336 400/1352/1337 403/1353/1338 +s 54 +usemtl Red_Roof +f 404/1369/1352 396/1370/1353 411/1371/1354 +s 55 +f 415/1377/1359 408/1378/1360 412/1379/1361 +s 56 +f 413/1387/1369 414/1386/1368 416/1385/1367 +s 57 +usemtl Dark_Red_Roof +f 416/1388/1370 414/1389/1371 417/1390/1372 +s 58 +usemtl Red_Roof +f 410/1401/1381 391/1400/1380 409/1399/1379 +s 59 +usemtl Dark_Red_Roof +f 419/1402/1382 405/1403/1383 406/1404/1384 +s 60 +f 420/1407/1387 405/1406/1386 419/1405/1385 +s 61 +f 314/1418/1397 421/1419/1398 425/1420/1399 +s 62 +f 426/1431/1406 428/1430/1405 314/1429/1404 +s 63 +f 432/1444/1418 433/1443/1417 429/1442/1416 +s 64 +f 429/1445/1419 433/1446/1420 434/1447/1421 +s 65 +usemtl Red_Roof +f 442/1468/1442 334/1469/1443 446/1470/1444 +s 66 +usemtl Dark_Red_Roof +f 447/1473/1447 334/1472/1446 442/1471/1445 +s 67 +f 444/1483/1455 331/1484/1456 450/1485/1457 +s 68 +f 438/1486/1458 441/1487/1459 449/1488/1460 +s 69 +f 455/1502/1473 452/1503/1474 454/1504/1475 +s 70 +usemtl Red_Roof +f 456/1507/1478 452/1506/1477 455/1505/1476 +s 71 +f 458/1513/1483 436/1514/1484 457/1515/1485 +s 72 +f 445/1518/1488 331/1517/1487 444/1516/1486 +s 73 +usemtl Dark_Red_Roof +f 460/1524/1493 339/1525/1494 459/1526/1495 +s 74 +f 461/1529/1498 339/1528/1497 460/1527/1496 +s 75 +f 352/1535/1502 344/1536/1503 463/1537/1504 +s 76 +f 465/1545/1511 443/1544/1510 437/1543/1509 +s 77 +f 360/1546/1512 462/1547/1513 464/1548/1514 +s 78 +usemtl Red_Roof +f 437/1549/1515 443/1550/1516 440/1551/1517 +s 79 +f 439/1559/1523 441/1558/1522 438/1557/1521 +s 80 +usemtl Dark_Red_Roof +f 435/1567/1529 436/1566/1528 458/1565/1527 +s 81 +f 366/1568/1530 358/1569/1531 431/1570/1532 +s 82 +f 373/1576/1536 430/1577/1537 427/1578/1538 +s 83 +f 424/1581/1541 423/1580/1540 373/1579/1539 +s 84 +f 307/1582/1542 299/1583/1543 422/1584/1544 +s 85 +f 407/1592/1550 408/1591/1549 415/1590/1548 +s 86 +f 402/1595/1553 396/1594/1552 404/1593/1551 +s 87 +usemtl Red_Roof +f 392/1603/1559 466/1602/1558 387/1601/1557 +s 88 +usemtl Dark_Red_Roof +f 409/1604/1560 391/1605/1561 390/1606/1562 +s 89 +f 387/1612/1567 466/1613/1568 388/1614/1569 +s 90 +f 383/1636/1586 472/1635/1585 469/1633/1583 +f 472/1635/1585 471/1634/1584 469/1633/1583 +s 91 +usemtl Wood_Darker +f 474/1638/1588 473/1637/1587 471/1642/1592 +f 472/1641/1591 476/1640/1590 474/1638/1588 +f 474/1638/1588 471/1642/1592 472/1641/1591 +f 476/1640/1590 475/1639/1589 474/1638/1588 +s 92 +f 478/1646/1596 477/1645/1595 475/1644/1594 +f 478/1646/1596 475/1644/1594 476/1643/1593 +s 93 +f 482/1652/1602 481/1651/1601 480/1650/1600 +f 480/1650/1600 478/1648/1598 479/1647/1597 +f 480/1650/1600 477/1649/1599 478/1648/1598 +f 479/1647/1597 482/1652/1602 480/1650/1600 +s 94 +usemtl Dark_Red_Roof +f 479/1653/1603 478/1654/1604 325/1656/1606 +f 478/1654/1604 483/1655/1605 325/1656/1606 +s 95 +usemtl Wood_Darker +f 471/1657/1607 473/1658/1608 482/1660/1610 +f 473/1658/1608 481/1659/1609 482/1660/1610 +s 96 +f 480/1664/1614 481/1663/1613 473/1662/1612 +f 480/1664/1614 473/1662/1612 474/1661/1611 +s 97 +f 474/1665/1615 475/1666/1616 480/1668/1618 +f 475/1666/1616 477/1667/1617 480/1668/1618 +s 98 +usemtl Dark_Red_Roof +f 367/1669/1619 470/1670/1620 484/1671/1621 +f 470/1753/1620 353/1754/1628 354/1755/1690 +f 353/1679/1628 467/1680/1629 486/1681/1630 +f 467/1689/1629 468/1690/1637 488/1691/1638 +f 378/1723/1667 367/1724/1619 368/1725/1668 +s 99 +f 485/1678/1627 363/1677/1626 365/1676/1625 +s 100 +f 487/1688/1636 349/1687/1635 351/1686/1634 +s 101 +f 488/1695/1642 468/1694/1641 489/1692/1639 +f 468/1694/1641 490/1693/1640 489/1692/1639 +f 490/1780/1640 342/1781/1711 346/1783/1713 +f 342/1791/1711 337/1790/1719 338/1789/1718 +f 333/1801/1727 337/1799/1719 332/1798/1725 +f 332/1812/1725 323/1813/1736 328/1814/1737 +f 329/1811/1735 332/1812/1725 328/1814/1737 +f 333/1801/1727 335/1800/1726 337/1799/1719 +f 340/1792/1720 342/1791/1711 338/1789/1718 +f 342/1781/1711 343/1782/1712 346/1783/1713 +s 102 +f 489/1698/1645 490/1697/1644 345/1696/1643 +s 103 +f 382/1699/1646 476/1700/1647 383/1702/1649 +f 476/1700/1647 472/1701/1648 383/1702/1649 +s 104 +usemtl Red_Roof +f 373/1713/1658 423/1714/1659 375/1715/1660 +s 105 +f 372/1718/1663 430/1717/1662 373/1716/1661 +s 106 +f 361/1737/1677 358/1736/1676 366/1735/1675 +s 107 +f 365/1738/1678 363/1739/1679 362/1740/1680 +s 108 +f 359/1748/1686 462/1747/1685 360/1746/1684 +s 109 +f 347/1768/1700 344/1767/1699 352/1766/1698 +s 110 +f 351/1774/1705 349/1775/1706 348/1776/1707 +s 111 +usemtl Dark_Red_Roof +f 345/1777/1708 490/1778/1709 346/1779/1710 +s 112 +f 308/1832/1749 493/1833/1750 494/1834/1751 +f 493/1926/1750 294/1927/1758 295/1928/1830 +f 294/1842/1758 491/1843/1759 496/1844/1760 +f 491/1852/1759 492/1853/1767 498/1854/1768 +f 319/1896/1807 308/1897/1749 309/1898/1808 +s 113 +f 495/1841/1757 304/1840/1756 306/1839/1755 +s 114 +f 497/1851/1766 290/1850/1765 292/1849/1764 +s 115 +f 499/1861/1775 500/1860/1774 286/1859/1773 +s 116 +f 325/1865/1779 324/1864/1778 482/1863/1777 +f 325/1865/1779 482/1863/1777 479/1862/1776 +s 117 +f 468/1875/1789 469/1874/1788 471/1873/1787 +f 471/1873/1787 482/1872/1786 337/1868/1782 +f 482/1872/1786 332/1869/1783 337/1868/1782 +f 482/1872/1786 323/1870/1784 332/1869/1783 +f 482/1872/1786 324/1871/1785 323/1870/1784 +f 337/1868/1782 342/1867/1781 471/1873/1787 +f 342/1867/1781 490/1866/1780 471/1873/1787 +f 490/1866/1780 468/1875/1789 471/1873/1787 +s 118 +usemtl Red_Roof +f 314/1886/1798 428/1887/1799 316/1888/1800 +s 119 +f 313/1891/1803 421/1890/1802 314/1889/1801 +s 120 +f 302/1910/1817 299/1909/1816 307/1908/1815 +s 121 +f 306/1911/1818 304/1912/1819 303/1913/1820 +s 122 +f 300/1921/1826 400/1920/1825 301/1919/1824 +s 123 +f 288/1941/1840 285/1940/1839 293/1939/1838 +s 124 +f 292/1947/1845 290/1948/1846 289/1949/1847 +s 125 +usemtl Dark_Red_Roof +f 286/1950/1848 500/1951/1849 287/1952/1850 +s 126 +f 476/1994/1882 382/1993/1881 271/1992/1880 +f 478/1995/1883 476/1994/1882 283/1989/1877 +f 283/1989/1877 500/1988/1876 478/1995/1883 +f 500/1988/1876 492/1997/1885 478/1995/1883 +f 492/1997/1885 483/1996/1884 478/1995/1883 +f 476/1994/1882 278/1990/1878 283/1989/1877 +f 476/1994/1882 270/1991/1879 278/1990/1878 +f 476/1994/1882 271/1992/1880 270/1991/1879 +o object2 +g object2 +v -0.52619048 0.90516376 0.48759427 +v -0.52619048 1.00476190 0.40476190 +v -0.44523810 1.00476190 0.40476190 +v -0.44523810 0.90516376 0.48759427 +v -0.52619048 0.96240573 0.54483624 +v -0.52619048 1.08571429 0.40476190 +v -0.44523810 0.96240573 0.54483624 +v -0.44523810 1.08571429 0.40476190 +v -0.52619048 1.49047619 -0.00000000 +v -0.44523810 1.49047619 -0.00000000 +v -0.44523810 1.40952381 -0.00000000 +v -0.52619048 1.40952381 -0.00000000 +vt 0.00000000 0.00000000 +vt 0.99999999 0.00000000 +vt 0.99999999 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.99999999 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 0.99999999 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vn -0.00000000 -0.63942740 -0.76885148 +vn -0.00000000 -0.63942740 -0.76885148 +vn -0.00000000 -0.63942740 -0.76885148 +vn -0.00000000 -0.63942740 -0.76885148 +vn -1.00000000 -0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn 0.00000000 0.75059887 0.66075815 +vn 0.00000000 0.75059887 0.66075815 +vn 0.00000000 0.75059887 0.66075815 +vn 0.00000000 0.75059887 0.66075815 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 1.00000000 0.00000000 -0.00000000 +vn 1.00000000 0.00000000 -0.00000000 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +s 1 +usemtl Wood_Darker +f 501/1998/1886 502/1999/1887 503/2000/1888 +f 501/1998/1886 503/2000/1888 504/2001/1889 +s 2 +f 505/2002/1890 506/2003/1891 502/2004/1892 +f 506/2024/1891 509/2023/1911 512/2022/1910 +f 502/2025/1892 506/2024/1891 512/2022/1910 +f 505/2002/1890 502/2004/1892 501/2005/1893 +s 3 +f 507/2006/1894 508/2007/1895 505/2009/1897 +f 508/2007/1895 506/2008/1896 505/2009/1897 +s 4 +f 506/2013/1901 508/2012/1900 510/2011/1899 +f 506/2013/1901 510/2011/1899 509/2010/1898 +s 5 +f 511/2014/1902 512/2015/1903 510/2017/1905 +f 512/2015/1903 509/2016/1904 510/2017/1905 +s 6 +f 508/2021/1909 503/2020/1908 511/2019/1907 +f 504/2030/1916 503/2031/1908 507/2033/1917 +f 503/2031/1908 508/2032/1909 507/2033/1917 +f 508/2021/1909 511/2019/1907 510/2018/1906 +s 7 +f 504/2026/1912 507/2027/1913 501/2029/1915 +f 507/2027/1913 505/2028/1914 501/2029/1915 +s 8 +f 503/2037/1921 502/2036/1920 511/2034/1918 +f 502/2036/1920 512/2035/1919 511/2034/1918 +o object3 +g object3 +v 0.52619048 0.90516376 0.48759427 +v 0.52619048 1.00476190 0.40476190 +v 0.44523810 1.00476190 0.40476190 +v 0.44523810 0.90516376 0.48759427 +v 0.44523810 1.08571429 0.40476190 +v 0.44523810 0.96240573 0.54483624 +v 0.52619048 1.08571429 0.40476190 +v 0.52619048 0.96240573 0.54483624 +v 0.52619048 1.49047619 -0.00000000 +v 0.44523810 1.49047619 -0.00000000 +v 0.44523810 1.40952381 -0.00000000 +v 0.52619048 1.40952381 -0.00000000 +vt 0.00000000 0.00000000 +vt 0.99999999 0.00000000 +vt 0.99999999 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.99999999 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.99999999 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vn 0.00000000 -0.63942740 -0.76885148 +vn 0.00000000 -0.63942740 -0.76885148 +vn 0.00000000 -0.63942740 -0.76885148 +vn 0.00000000 -0.63942740 -0.76885148 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -0.00000000 0.75059887 0.66075815 +vn -0.00000000 0.75059887 0.66075815 +vn -0.00000000 0.75059887 0.66075815 +vn -0.00000000 0.75059887 0.66075815 +vn -0.00000000 0.70710678 0.70710678 +vn -0.00000000 0.70710678 0.70710678 +vn -0.00000000 0.70710678 0.70710678 +vn -0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +s 1 +usemtl Wood_Darker +f 516/2041/1925 515/2040/1924 514/2039/1923 +f 516/2041/1925 514/2039/1923 513/2038/1922 +s 2 +f 518/2045/1929 517/2044/1928 515/2043/1927 +f 523/2063/1947 515/2064/1927 517/2065/1928 +f 522/2062/1946 523/2063/1947 517/2065/1928 +f 518/2045/1929 515/2043/1927 516/2042/1926 +s 3 +f 520/2049/1933 519/2048/1932 518/2046/1930 +f 519/2048/1932 517/2047/1931 518/2046/1930 +s 4 +f 521/2050/1934 522/2051/1935 519/2053/1937 +f 522/2051/1935 517/2052/1936 519/2053/1937 +s 5 +f 522/2057/1941 521/2056/1940 523/2054/1938 +f 521/2056/1940 524/2055/1939 523/2054/1938 +s 6 +f 524/2058/1942 521/2059/1943 519/2060/1944 +f 514/2072/1945 519/2071/1944 520/2070/1952 +f 513/2073/1953 514/2072/1945 520/2070/1952 +f 524/2058/1942 519/2060/1944 514/2061/1945 +s 7 +f 513/2069/1951 520/2068/1950 516/2066/1948 +f 520/2068/1950 518/2067/1949 516/2066/1948 +s 8 +f 523/2074/1954 524/2075/1955 514/2076/1956 +f 523/2074/1954 514/2076/1956 515/2077/1957 +o object4 +g object4 +v -0.58571429 0.98214286 0.02142857 +v -0.58571429 0.98214286 -0.02142857 +v -0.55000000 0.98214286 -0.02142857 +v -0.55000000 0.98214286 0.02142857 +v -0.55000000 0.69642857 -0.02142857 +v -0.55000000 0.69642857 0.02142857 +v -0.55000000 0.72857143 0.05357143 +v -0.55000000 0.95000000 0.05357143 +v -0.55000000 0.95000000 -0.05357143 +v -0.55000000 0.72857143 -0.05357143 +v -0.58571429 0.72857143 0.05357143 +v -0.58571429 0.95000000 0.05357143 +v -0.58571429 0.95000000 -0.05357143 +v -0.58571429 0.72857143 -0.05357143 +v -0.58571429 0.69642857 -0.02142857 +v -0.58571429 0.69642857 0.02142857 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.30000000 0.00000000 +vt 0.70000000 0.00000000 +vt 1.00000000 0.11250000 +vt 1.00000000 0.88750000 +vt 0.70000000 1.00000000 +vt 0.30000000 1.00000000 +vt 0.00000000 0.88750000 +vt 0.00000000 0.11250000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.30000000 0.00000000 +vt 0.70000000 0.00000000 +vt 1.00000000 0.11250000 +vt 1.00000000 0.88750000 +vt 0.70000000 1.00000000 +vt 0.30000000 1.00000000 +vt 0.00000000 0.88750000 +vt 0.00000000 0.11250000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn -0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +s 1 +usemtl Wood_Darker +f 528/2081/1961 527/2080/1960 525/2078/1958 +f 527/2080/1960 526/2079/1959 525/2078/1958 +s 2 +f 527/2087/1967 528/2086/1966 532/2085/1965 +f 534/2089/1969 533/2088/1968 532/2085/1965 +f 531/2084/1964 530/2083/1963 534/2089/1969 +f 530/2083/1963 529/2082/1962 534/2089/1969 +f 532/2085/1965 531/2084/1964 534/2089/1969 +f 533/2088/1968 527/2087/1967 532/2085/1965 +s 3 +f 536/2093/1973 535/2092/1972 532/2090/1970 +f 535/2092/1972 531/2091/1971 532/2090/1970 +s 4 +f 538/2097/1977 537/2096/1976 534/2094/1974 +f 537/2096/1976 533/2095/1975 534/2094/1974 +s 5 +f 534/2098/1978 529/2099/1979 538/2101/1981 +f 529/2099/1979 539/2100/1980 538/2101/1981 +s 6 +f 540/2105/1985 539/2104/1984 529/2103/1983 +f 540/2105/1985 529/2103/1983 530/2102/1982 +s 7 +f 530/2106/1986 531/2107/1987 540/2109/1989 +f 531/2107/1987 535/2108/1988 540/2109/1989 +s 8 +f 532/2110/1990 528/2111/1991 536/2113/1993 +f 528/2111/1991 525/2112/1992 536/2113/1993 +s 9 +f 536/2117/1997 525/2118/1998 526/2119/1999 +f 536/2117/1997 537/2120/2000 538/2121/2001 +f 538/2121/2001 540/2115/1995 535/2116/1996 +f 538/2121/2001 539/2114/1994 540/2115/1995 +f 538/2121/2001 535/2116/1996 536/2117/1997 +f 536/2117/1997 526/2119/1999 537/2120/2000 +s 10 +f 527/2122/2002 533/2123/2003 526/2125/2005 +f 533/2123/2003 537/2124/2004 526/2125/2005 +o object5 +g object5 +v -0.58498601 1.38561358 -0.06438642 +v -0.58498601 1.38561358 0.06438642 +v -0.60714286 1.45000000 0.00000000 +v -0.36428571 1.40085034 0.04914966 +v -0.36428571 1.40085034 -0.04914966 +v -0.58498601 1.51438642 0.06438642 +v -0.36428571 1.49914966 0.04914966 +v -0.58498601 1.51438642 -0.06438642 +v -0.36428571 1.49914966 -0.04914966 +v -0.00000000 1.49047619 0.04047619 +v -0.00000000 1.49047619 -0.04047619 +v -0.00000000 1.40952381 -0.04047619 +v 0.36428571 1.40085034 -0.04914966 +v 0.36428571 1.49914966 -0.04914966 +v 0.36428571 1.40085034 0.04914966 +v -0.00000000 1.40952381 0.04047619 +v 0.36428571 1.49914966 0.04914966 +v 0.58498601 1.38561358 0.06438642 +v 0.58498601 1.51438642 0.06438642 +v 0.58498601 1.51438642 -0.06438642 +v 0.60714286 1.45000000 0.00000000 +v 0.58498601 1.38561358 -0.06438642 +vt 1.00000000 1.00000000 +vt 0.00000000 0.05590045 +vt 1.00000000 0.00000000 +vt 0.23664558 1.00000000 +vt 0.00000000 0.03872221 +vt 1.00000000 0.00000000 +vt 1.00000000 0.97044123 +vt 0.23664558 1.00000000 +vt 0.00000000 0.03872221 +vt 1.00000000 0.00000000 +vt 1.00000000 0.97044123 +vt 0.00000000 0.97044123 +vt 0.00000000 0.00000000 +vt 1.00000000 0.03872221 +vt 0.76335442 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00525723 +vt 0.82352941 0.00000000 +vt 1.00000000 0.99361622 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00525723 +vt 0.82352941 0.00000000 +vt 1.00000000 0.99361622 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00525723 +vt 0.82352941 0.00000000 +vt 1.00000000 0.99361622 +vt 0.00000000 0.99361622 +vt 0.17647059 0.00000000 +vt 1.00000000 0.00525723 +vt 1.00000000 1.00000000 +vt 0.00000000 0.99361622 +vt 0.17647059 0.00000000 +vt 1.00000000 0.00525723 +vt 1.00000000 1.00000000 +vt 0.23664558 1.00000000 +vt 0.00000000 0.03872221 +vt 1.00000000 0.00000000 +vt 1.00000000 0.97044123 +vt 0.00000000 0.97044123 +vt 0.00000000 0.00000000 +vt 1.00000000 0.03872221 +vt 0.76335442 1.00000000 +vt 1.00000000 0.05590045 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.05590045 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.05590045 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.05590045 +vt 1.00000000 0.00000000 +vt 0.23664558 1.00000000 +vt 0.00000000 0.03872221 +vt 1.00000000 0.00000000 +vt 1.00000000 0.97044123 +vt 0.00000000 0.97044123 +vt 0.00000000 0.00000000 +vt 1.00000000 0.03872221 +vt 0.76335442 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00525723 +vt 0.82352941 0.00000000 +vt 1.00000000 0.99361622 +vt 1.00000000 0.05590045 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.99361622 +vt 0.17647059 0.00000000 +vt 1.00000000 0.00525723 +vt 1.00000000 1.00000000 +vt 0.00000000 0.99361622 +vt 0.17647059 0.00000000 +vt 1.00000000 0.00525723 +vt 1.00000000 1.00000000 +vt 0.00000000 0.97044123 +vt 0.00000000 0.00000000 +vt 1.00000000 0.03872221 +vt 0.76335442 1.00000000 +vt 1.00000000 0.05590045 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.05590045 +vt 1.00000000 0.00000000 +vn -0.94557811 -0.32539520 -0.00000000 +vn -0.94557811 -0.32539520 -0.00000000 +vn -0.94557811 -0.32539520 -0.00000000 +vn 0.06887431 -0.99762535 0.00000000 +vn 0.06887431 -0.99762535 0.00000000 +vn 0.06887431 -0.99762535 0.00000000 +vn 0.06887431 -0.99762535 0.00000000 +vn 0.06887431 -0.00000000 0.99762535 +vn 0.06887431 -0.00000000 0.99762535 +vn 0.06887431 -0.00000000 0.99762535 +vn 0.06887431 -0.00000000 0.99762535 +vn 0.06887431 0.99762535 0.00000000 +vn 0.06887431 0.99762535 0.00000000 +vn 0.06887431 0.99762535 0.00000000 +vn 0.06887431 0.99762535 0.00000000 +vn 0.02380278 0.99971667 0.00000000 +vn 0.02380278 0.99971667 0.00000000 +vn 0.02380278 -0.00000000 -0.99971667 +vn 0.02380278 -0.00000000 -0.99971667 +vn 0.02380278 -0.00000000 -0.99971667 +vn 0.02380278 -0.00000000 -0.99971667 +vn -0.02380278 -0.00000000 -0.99971667 +vn -0.02380278 -0.00000000 -0.99971667 +vn -0.02380278 -0.99971667 0.00000000 +vn -0.02380278 -0.99971667 0.00000000 +vn -0.02380278 -0.99971667 0.00000000 +vn -0.02380278 -0.99971667 0.00000000 +vn -0.02380278 -0.00000000 0.99971667 +vn -0.02380278 -0.00000000 0.99971667 +vn -0.02380278 -0.00000000 0.99971667 +vn -0.02380278 -0.00000000 0.99971667 +vn -0.06887431 -0.00000000 0.99762535 +vn -0.06887431 -0.00000000 0.99762535 +vn -0.06887431 0.99762535 0.00000000 +vn -0.06887431 0.99762535 0.00000000 +vn -0.06887431 0.99762535 0.00000000 +vn -0.06887431 0.99762535 0.00000000 +vn 0.94557811 0.32539520 -0.00000000 +vn 0.94557811 0.32539520 -0.00000000 +vn 0.94557811 0.32539520 -0.00000000 +vn 0.94557811 0.00000000 -0.32539520 +vn 0.94557811 0.00000000 -0.32539520 +vn 0.94557811 0.00000000 -0.32539520 +vn 0.94557811 -0.32539520 -0.00000000 +vn 0.94557811 -0.32539520 -0.00000000 +vn 0.94557811 -0.32539520 -0.00000000 +vn 0.94557811 0.00000000 0.32539520 +vn 0.94557811 0.00000000 0.32539520 +vn 0.94557811 0.00000000 0.32539520 +vn -0.06887431 -0.99762535 0.00000000 +vn -0.06887431 -0.99762535 0.00000000 +vn -0.06887431 -0.00000000 -0.99762535 +vn -0.06887431 -0.00000000 -0.99762535 +vn -0.94557811 0.32539520 -0.00000000 +vn -0.94557811 0.32539520 -0.00000000 +vn -0.94557811 0.32539520 -0.00000000 +vn 0.06887431 -0.00000000 -0.99762535 +vn 0.06887431 -0.00000000 -0.99762535 +vn -0.94557811 0.00000000 -0.32539520 +vn -0.94557811 0.00000000 -0.32539520 +vn -0.94557811 0.00000000 -0.32539520 +vn -0.94557811 0.00000000 0.32539520 +vn -0.94557811 0.00000000 0.32539520 +vn -0.94557811 0.00000000 0.32539520 +s 1 +usemtl Wood_Dark +f 541/2126/2006 542/2127/2007 543/2128/2008 +s 2 +usemtl Wood_Darker +f 545/2132/2012 544/2129/2009 542/2130/2010 +f 552/2202/2031 556/2201/2030 544/2200/2009 +f 555/2153/2029 552/2155/2031 553/2156/2032 +f 553/2184/2032 562/2183/2056 558/2182/2055 +f 558/2182/2055 555/2181/2029 553/2184/2032 +f 555/2153/2029 556/2154/2030 552/2155/2031 +f 545/2203/2012 552/2202/2031 544/2200/2009 +f 542/2130/2010 541/2131/2011 545/2132/2012 +s 3 +f 542/2134/2014 544/2133/2013 547/2136/2016 +f 556/2197/2034 550/2198/2035 547/2199/2016 +f 557/2160/2036 556/2158/2034 555/2157/2033 +f 558/2162/2037 559/2163/2038 557/2164/2036 +f 557/2164/2036 555/2161/2033 558/2162/2037 +f 557/2160/2036 550/2159/2035 556/2158/2034 +f 544/2196/2013 556/2197/2034 547/2199/2016 +f 547/2136/2016 546/2135/2015 542/2134/2014 +s 4 +f 547/2137/2017 549/2140/2020 548/2139/2019 +f 547/2141/2017 551/2143/2022 549/2144/2020 +f 554/2192/2042 551/2191/2022 557/2189/2039 +f 557/2165/2039 559/2166/2040 560/2167/2041 +f 560/2167/2041 554/2168/2042 557/2165/2039 +f 551/2191/2022 550/2190/2021 557/2189/2039 +f 547/2141/2017 550/2142/2021 551/2143/2022 +f 548/2139/2019 546/2138/2018 547/2137/2017 +s 5 +f 549/2148/2026 551/2147/2025 552/2146/2024 +f 553/2149/2027 552/2150/2024 554/2152/2028 +f 560/2187/2058 562/2186/2057 553/2185/2027 +f 553/2185/2027 554/2188/2028 560/2187/2058 +f 552/2150/2024 551/2151/2025 554/2152/2028 +f 548/2206/2063 549/2207/2026 545/2204/2023 +f 545/2204/2023 541/2205/2062 548/2206/2063 +f 549/2148/2026 552/2146/2024 545/2145/2023 +s 6 +usemtl Wood_Dark +f 560/2169/2043 559/2170/2044 561/2171/2045 +s 7 +f 561/2174/2048 562/2173/2047 560/2172/2046 +s 8 +f 561/2177/2051 558/2176/2050 562/2175/2049 +s 9 +f 559/2178/2052 558/2179/2053 561/2180/2054 +s 10 +f 543/2195/2061 546/2194/2060 548/2193/2059 +s 11 +f 548/2208/2064 541/2209/2065 543/2210/2066 +s 12 +f 543/2213/2069 542/2212/2068 546/2211/2067 +o object6 +g object6 +v -0.42857143 1.26805573 -0.07142857 +v -0.42857143 -0.00000000 -0.07142857 +v -0.42857143 0.00000000 0.07142857 +v -0.42857143 1.26805573 0.07142857 +v -0.32142857 1.26805573 -0.07142857 +v -0.32142857 -0.00000000 -0.07142857 +v -0.32142857 0.00000000 0.07142857 +v -0.32142857 1.26805573 0.07142857 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.00000000 1.00000000 -0.00000000 +vn 0.00000000 1.00000000 -0.00000000 +vn 0.00000000 1.00000000 -0.00000000 +vn 0.00000000 1.00000000 -0.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +s 1 +usemtl Wood_Darker +f 563/2214/2070 564/2215/2071 566/2217/2073 +f 564/2215/2071 565/2216/2072 566/2217/2073 +s 2 +f 567/2218/2074 568/2219/2075 563/2221/2077 +f 568/2219/2075 564/2220/2076 563/2221/2077 +s 3 +f 570/2225/2081 569/2224/2080 567/2222/2078 +f 569/2224/2080 568/2223/2079 567/2222/2078 +s 4 +f 563/2229/2085 566/2228/2084 570/2227/2083 +f 563/2229/2085 570/2227/2083 567/2226/2082 +s 5 +f 566/2233/2089 565/2232/2088 570/2230/2086 +f 565/2232/2088 569/2231/2087 570/2230/2086 +s 6 +f 568/2234/2090 569/2235/2091 564/2237/2093 +f 569/2235/2091 565/2236/2092 564/2237/2093 +o object7 +g object7 +v -0.44523810 0.90516376 -0.48759427 +v -0.44523810 0.96240573 -0.54483624 +v -0.52619048 0.96240573 -0.54483624 +v -0.52619048 0.90516376 -0.48759427 +v -0.52619048 1.08571429 -0.40476190 +v -0.52619048 1.00476190 -0.40476190 +v -0.52619048 1.40952381 0.00000000 +v -0.52619048 1.49047619 0.00000000 +v -0.44523810 1.40952381 0.00000000 +v -0.44523810 1.49047619 0.00000000 +v -0.44523810 1.00476190 -0.40476190 +v -0.44523810 1.08571429 -0.40476190 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.99999999 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 0.99999999 0.00000000 +vt 0.99999999 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.99999999 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 1.00000000 0.00000000 -0.00000000 +vn 1.00000000 0.00000000 -0.00000000 +vn 1.00000000 0.00000000 -0.00000000 +vn 1.00000000 0.00000000 -0.00000000 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn -0.00000000 -0.63942740 0.76885148 +vn -0.00000000 -0.63942740 0.76885148 +vn -0.00000000 -0.63942740 0.76885148 +vn -0.00000000 -0.63942740 0.76885148 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.00000000 0.75059887 -0.66075815 +vn 0.00000000 0.75059887 -0.66075815 +vn 0.00000000 0.75059887 -0.66075815 +vn 0.00000000 0.75059887 -0.66075815 +s 1 +usemtl Wood_Darker +f 574/2241/2097 573/2240/2096 572/2239/2095 +f 574/2241/2097 572/2239/2095 571/2238/2094 +s 2 +f 574/2245/2101 576/2244/2100 573/2242/2098 +f 577/2246/2102 575/2248/2099 576/2249/2100 +f 577/2246/2102 578/2247/2103 575/2248/2099 +f 576/2244/2100 575/2243/2099 573/2242/2098 +s 3 +f 580/2253/2107 578/2252/2106 579/2250/2104 +f 578/2252/2106 577/2251/2105 579/2250/2104 +s 4 +f 580/2254/2108 579/2255/2109 582/2257/2111 +f 572/2273/2125 582/2272/2111 581/2271/2110 +f 572/2273/2125 581/2271/2110 571/2270/2124 +f 579/2255/2109 581/2256/2110 582/2257/2111 +s 5 +f 579/2258/2112 577/2259/2113 581/2261/2115 +f 577/2259/2113 576/2260/2114 581/2261/2115 +s 6 +f 578/2262/2116 580/2263/2117 582/2264/2118 +f 578/2262/2116 582/2264/2118 575/2265/2119 +s 7 +f 571/2269/2123 581/2268/2122 574/2266/2120 +f 581/2268/2122 576/2267/2121 574/2266/2120 +s 8 +f 573/2277/2129 575/2276/2128 582/2275/2127 +f 573/2277/2129 582/2275/2127 572/2274/2126 +o object8 +g object8 +v -0.00000000 0.00000000 -0.22530922 +v -0.09332614 0.00000000 -0.22530922 +v -0.22530922 0.00000000 -0.09332614 +v -0.22530922 0.00000000 0.09332614 +v -0.09332614 0.00000000 0.22530922 +v -0.00000000 0.00000000 0.22530922 +v -0.00000000 0.00000000 0.33245208 +v -0.13770616 0.00000000 0.33245208 +v -0.33245208 0.00000000 0.13770616 +v -0.33245208 0.00000000 -0.13770616 +v -0.13770616 0.00000000 -0.33245208 +v -0.00000000 0.00000000 -0.33245208 +v 0.09332614 0.00000000 0.22530922 +v 0.22530922 0.00000000 0.09332614 +v 0.22530922 0.00000000 -0.09332614 +v 0.09332614 0.00000000 -0.22530922 +v 0.13770616 0.00000000 -0.33245208 +v 0.33245208 0.00000000 -0.13770616 +v 0.33245208 0.00000000 0.13770616 +v 0.13770616 0.00000000 0.33245208 +v -0.22530922 0.44642857 -0.09332614 +v -0.22530922 0.44642857 0.09332614 +v -0.00000000 0.44642857 -0.22530922 +v -0.09332614 0.44642857 -0.22530922 +v -0.09332614 0.44642857 0.22530922 +v -0.00000000 0.44642857 0.22530922 +v -0.00000000 0.44642857 0.33245208 +v -0.13770616 0.44642857 0.33245208 +v -0.33245208 0.44642857 0.13770616 +v -0.33245208 0.44642857 -0.13770616 +v -0.13770616 0.44642857 -0.33245208 +v -0.00000000 0.44642857 -0.33245208 +v 0.09332614 0.44642857 0.22530922 +v 0.22530922 0.44642857 0.09332614 +v 0.22530922 0.44642857 -0.09332614 +v 0.09332614 0.44642857 -0.22530922 +v 0.13770616 0.44642857 -0.33245208 +v 0.33245208 0.44642857 -0.13770616 +v 0.33245208 0.44642857 0.13770616 +v 0.13770616 0.44642857 0.33245208 +vt 0.83885970 0.50000000 +vt 0.83885970 0.35963972 +vt 0.64036028 0.16114030 +vt 0.35963972 0.16114030 +vt 0.16114030 0.35963972 +vt 0.16114030 0.50000000 +vt 0.00000000 0.50000000 +vt 0.00000000 0.29289322 +vt 0.29289322 0.00000000 +vt 0.70710678 0.00000000 +vt 1.00000000 0.29289322 +vt 1.00000000 0.50000000 +vt 0.16114030 0.64036028 +vt 0.35963972 0.83885970 +vt 0.64036028 0.83885970 +vt 0.83885970 0.64036028 +vt 1.00000000 0.70710678 +vt 0.70710678 1.00000000 +vt 0.29289322 1.00000000 +vt 0.00000000 0.70710678 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.83885970 0.50000000 +vt 0.83885970 0.35963972 +vt 0.64036028 0.16114030 +vt 0.35963972 0.16114030 +vt 0.16114030 0.35963972 +vt 0.16114030 0.50000000 +vt 0.00000000 0.50000000 +vt 0.00000000 0.29289322 +vt 0.29289322 0.00000000 +vt 0.70710678 0.00000000 +vt 1.00000000 0.29289322 +vt 1.00000000 0.50000000 +vt 0.16114030 0.64036028 +vt 0.35963972 0.83885970 +vt 0.64036028 0.83885970 +vt 0.83885970 0.64036028 +vt 1.00000000 0.70710678 +vt 0.70710678 1.00000000 +vt 0.29289322 1.00000000 +vt 0.00000000 0.70710678 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 0.50000000 +vt 1.00000000 0.50000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 0.50000000 +vt 1.00000000 0.50000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 0.50000000 +vt 1.00000000 0.50000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 0.50000000 +vt 1.00000000 0.50000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +s 1 +usemtl Stone_Brit_Gray +f 594/2289/2141 583/2278/2130 584/2279/2131 +f 593/2288/2140 584/2279/2131 585/2280/2132 +f 592/2287/2139 585/2280/2132 586/2281/2133 +f 586/2281/2133 590/2285/2137 591/2286/2138 +f 587/2282/2134 589/2284/2136 590/2285/2137 +f 602/2297/2149 589/2284/2136 595/2290/2142 +f 601/2296/2148 602/2297/2149 596/2291/2143 +f 600/2295/2147 601/2296/2148 597/2292/2144 +f 597/2292/2144 598/2293/2145 599/2294/2146 +f 597/2292/2144 599/2294/2146 600/2295/2147 +f 601/2296/2148 596/2291/2143 597/2292/2144 +f 602/2297/2149 595/2290/2142 596/2291/2143 +f 589/2284/2136 588/2283/2135 595/2290/2142 +f 587/2282/2134 588/2283/2135 589/2284/2136 +f 586/2281/2133 587/2282/2134 590/2285/2137 +f 591/2286/2138 592/2287/2139 586/2281/2133 +f 592/2287/2139 593/2288/2140 585/2280/2132 +f 598/2293/2145 594/2289/2141 599/2294/2146 +f 598/2293/2145 583/2278/2130 594/2289/2141 +f 593/2288/2140 594/2289/2141 584/2279/2131 +s 2 +f 585/2298/2150 603/2299/2151 586/2301/2153 +f 603/2299/2151 604/2300/2152 586/2301/2153 +s 3 +f 606/2303/2155 605/2302/2154 614/2313/2165 +f 619/2318/2170 614/2313/2165 618/2317/2169 +f 620/2319/2171 619/2318/2170 617/2316/2168 +f 617/2316/2168 616/2315/2167 621/2320/2172 +f 616/2315/2167 622/2321/2173 621/2320/2172 +f 615/2314/2166 609/2308/2160 622/2321/2173 +f 610/2309/2161 609/2308/2160 607/2306/2158 +f 611/2310/2162 610/2309/2161 604/2305/2157 +f 604/2305/2157 603/2304/2156 612/2311/2163 +f 603/2304/2156 613/2312/2164 612/2311/2163 +f 604/2305/2157 612/2311/2163 611/2310/2162 +f 610/2309/2161 607/2306/2158 604/2305/2157 +f 609/2308/2160 608/2307/2159 607/2306/2158 +f 615/2314/2166 608/2307/2159 609/2308/2160 +f 616/2315/2167 615/2314/2166 622/2321/2173 +f 617/2316/2168 621/2320/2172 620/2319/2171 +f 619/2318/2170 618/2317/2169 617/2316/2168 +f 614/2313/2165 605/2302/2154 618/2317/2169 +f 603/2304/2156 606/2303/2155 613/2312/2164 +f 606/2303/2155 614/2313/2165 613/2312/2164 +s 4 +f 584/2322/2174 606/2323/2175 585/2325/2177 +f 606/2323/2175 603/2324/2176 585/2325/2177 +s 5 +f 583/2331/2183 598/2326/2178 618/2327/2179 +f 605/2330/2182 606/2328/2180 583/2331/2183 +f 606/2328/2180 584/2329/2181 583/2331/2183 +f 583/2331/2183 618/2327/2179 605/2330/2182 +s 6 +f 597/2332/2184 617/2333/2185 598/2335/2187 +f 617/2333/2185 618/2334/2186 598/2335/2187 +s 7 +f 596/2336/2188 616/2337/2189 597/2339/2191 +f 616/2337/2189 617/2338/2190 597/2339/2191 +s 8 +f 595/2340/2192 615/2341/2193 596/2343/2195 +f 615/2341/2193 616/2342/2194 596/2343/2195 +s 9 +f 615/2346/2198 595/2347/2199 588/2349/2201 +f 607/2345/2197 608/2348/2200 588/2349/2201 +f 588/2349/2201 587/2344/2196 607/2345/2197 +f 608/2348/2200 615/2346/2198 588/2349/2201 +s 10 +f 586/2350/2202 604/2351/2203 587/2353/2205 +f 604/2351/2203 607/2352/2204 587/2353/2205 +s 11 +f 590/2357/2209 610/2356/2208 591/2354/2206 +f 610/2356/2208 611/2355/2207 591/2354/2206 +s 12 +f 589/2363/2215 602/2361/2213 622/2360/2212 +f 609/2362/2214 610/2359/2211 589/2363/2215 +f 610/2359/2211 590/2358/2210 589/2363/2215 +f 589/2363/2215 622/2360/2212 609/2362/2214 +s 13 +f 601/2367/2219 621/2366/2218 602/2364/2216 +f 621/2366/2218 622/2365/2217 602/2364/2216 +s 14 +f 600/2371/2223 620/2370/2222 601/2368/2220 +f 620/2370/2222 621/2369/2221 601/2368/2220 +s 15 +f 599/2375/2227 619/2374/2226 600/2372/2224 +f 619/2374/2226 620/2373/2225 600/2372/2224 +s 16 +f 619/2377/2229 599/2376/2228 594/2381/2233 +f 613/2378/2230 614/2380/2232 594/2381/2233 +f 594/2381/2233 593/2379/2231 613/2378/2230 +f 614/2380/2232 619/2377/2229 594/2381/2233 +s 17 +f 592/2385/2237 612/2384/2236 593/2382/2234 +f 612/2384/2236 613/2383/2235 593/2382/2234 +s 18 +f 591/2389/2241 611/2388/2240 592/2386/2238 +f 611/2388/2240 612/2387/2239 592/2386/2238 +o object9 +g object9 +v 0.07142857 0.89764195 -0.05357143 +v 0.07142857 0.89764195 -0.03571429 +v 0.05357143 0.89764195 -0.03571429 +v 0.05357143 0.89764195 -0.05357143 +v 0.05357143 0.00000000 -0.05357143 +v 0.05357143 0.00000000 -0.03571429 +v 0.07142857 0.00000000 -0.05357143 +v 0.07142857 0.00000000 -0.03571429 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +s 1 +usemtl Burlap +f 626/2393/2245 625/2392/2244 624/2391/2243 +f 626/2393/2245 624/2391/2243 623/2390/2242 +s 2 +f 626/2394/2246 627/2395/2247 625/2397/2249 +f 627/2395/2247 628/2396/2248 625/2397/2249 +s 3 +f 629/2398/2250 630/2399/2251 627/2401/2253 +f 630/2399/2251 628/2400/2252 627/2401/2253 +s 4 +f 623/2402/2254 629/2403/2255 626/2405/2257 +f 629/2403/2255 627/2404/2256 626/2405/2257 +s 5 +f 624/2409/2261 630/2408/2260 623/2406/2258 +f 630/2408/2260 629/2407/2259 623/2406/2258 +s 6 +f 625/2413/2265 628/2412/2264 624/2410/2262 +f 628/2412/2264 630/2411/2263 624/2410/2262 diff --git a/examples/opengl/opengl/utils.py b/examples/opengl/opengl/utils.py new file mode 100644 index 0000000000..99d24ec707 --- /dev/null +++ b/examples/opengl/opengl/utils.py @@ -0,0 +1,305 @@ +import os +from collections.abc import Callable +from contextlib import contextmanager +from dataclasses import dataclass +from enum import IntEnum +from functools import cached_property + +import toga + +from .matrix import Matrix + +if toga.backend in {"toga_cocoa", "toga_gtk", "toga_qt"}: + if os.environ.get("TOGA_OPENGL") == "pyglet": + from . import utils_pyglet as GL + else: + from . import utils_pyopengl as GL +elif toga.backend == "toga_android": + from . import utils_android as GL +elif toga.backend == "toga_iOS": + from . import utils_iOS as GL +else: + raise RuntimeError("No OpenGL for backend.") + + +VERSION_HEADER = GL.VERSION_HEADER + + +class ShaderType(IntEnum): + """Enum for the different OpenGL shader types.""" + + vertex = GL.GL_VERTEX_SHADER + fragment = GL.GL_FRAGMENT_SHADER + + +class BufferType(IntEnum): + """Enum for the different OpenGL buffer types.""" + + array = GL.GL_ARRAY_BUFFER + + +class BufferUsage(IntEnum): + """Enum for the different OpenGL buffer usage hints.""" + + dynamic_draw = GL.GL_DYNAMIC_DRAW + static_draw = GL.GL_STATIC_DRAW + + +class OpenGLError(RuntimeError): + """OpenGL-specific runtime errors.""" + + pass + + +@dataclass +class Buffer: + """A dataclass that encapsulates an OpenGL buffer. + + This can be used as a context manager to automatically bind and unbind + the buffer for use. + """ + + buffer_type: int + usage: int + + def create(self, data: bytes): + """Generate a new buffer and set the data into it.""" + self.id = GL.glGenBuffers(1) + self.set_data(data) + + def set_data(self, data: bytes): + """Set data into a buffer. + + This automatically binds the buffer.. + """ + with self: + GL.glBufferData(self.buffer_type, data, self.usage) + + def bind(self): + """Bind the buffer so that it is the current buffer.""" + GL.glBindBuffer(self.buffer_type, self.id) + + def unbind(self): + """Unbind the buffer so that it is no longer the current buffer.""" + GL.glBindBuffer(self.buffer_type, 0) + + def __enter__(self): + """Enter the context manager, binding the buffer.""" + self.bind() + + def __exit__(self, exc_type, exc_value, traceback): + """Exit the context manager, unbinding the buffer.""" + self.unbind() + return False + + +@dataclass +class Texture: + """A dataclass that encapsulates an OpenGL texture. + + This can be used as a context manager to automatically bind and unbind + the buffer for use. + """ + + texture_type: int + + def create(self, data: bytes): + """Generate a new buffer and set the data into it.""" + self.id = GL.glCreateTexture() + + def set_data(self, data: bytes): + """Set data into a buffer. + + This automatically binds the texture. + """ + with self: + GL.glTexture( + self.texture_type, + data, + ) + + def bind(self): + """Bind the texture so that it is the current texture.""" + GL.glBindTexture(self.texture_type, self.id) + + def unbind(self): + """Unbind the texture so that it is no longer the current buffer.""" + GL.glBindTexture(self.texture_type, 0) + + def __enter__(self): + """Enter the context manager, binding the buffer.""" + self.bind() + + def __exit__(self, exc_type, exc_value, traceback): + """Exit the context manager, unbinding the buffer.""" + self.unbind() + return False + + +@dataclass +class VertexArrayObject: + """A dataclass that encapsulates an OpenGL vertex array object. + + This can be used as a context manager to automatically bind and unbind + the vertex array object for use. + """ + + def create(self): + """Generate a new vertex array object.""" + self.id = GL.glGenVertexArrays(1) + + def bind(self): + """Make the vertex array object the current vertex array object.""" + GL.glBindVertexArray(self.id) + + def unbind(self): + """Make the vertex array object no longer the current vertex array object.""" + GL.glBindVertexArray(0) + + def __enter__(self): + """Enter the context manager, binding the vertex array object.""" + self.bind() + + def __exit__(self, exc_type, exc_value, traceback): + """Exit the context manager, unbinding the vertex array object.""" + self.unbind() + return False + + +@dataclass +class Shader: + """A dataclass that encapsulates an OpenGL shader.""" + + shader_type: int + source: str + + def create(self): + """Create and compile the shader.""" + self.id = GL.glCreateShader(self.shader_type) + GL.glShaderSource(self.id, self.source) + + GL.glCompileShader(self.id) + + # Handle errors + status = GL.glGetShaderiv(self.id, GL.GL_COMPILE_STATUS) + if status == GL.GL_FALSE: + info_log = GL.glGetShaderInfoLog(self.id) + raise OpenGLError( + f"Compilation failure for {self.shader_type} shader:\n{info_log}" + ) + + def delete(self): + """Delete the shader.""" + if hasattr(self, "id"): + GL.glDeleteShader(self.id) + del self.id + + +@dataclass +class Program: + """A dataclass that encapsulates an OpenGL program.""" + + shaders: list[Shader] + + def create(self): + """Create the program and its shaders and link them.""" + for shader in self.shaders: + shader.create() + + self.id = GL.glCreateProgram() + + for shader in self.shaders: + GL.glAttachShader(self.id, shader.id) + + GL.glLinkProgram(self.id) + + # Handle errors + status = GL.glGetProgramiv(self.id, GL.GL_LINK_STATUS) + if status == GL.GL_FALSE: + strInfoLog = GL.glGetProgramInfoLog(self.id) + raise OpenGLError(f"Linker failure: \n{strInfoLog}") + + # clean up shaders + for shader in self.shaders: + GL.glDetachShader(self.id, shader.id) + for shader in self.shaders: + shader.delete() + + def delete(self): + """Delete the program.""" + if hasattr(self, "id"): + GL.glDeleteProgram(self.id) + del self.id + + @contextmanager + def use(self): + """Context manager that sets the program for use and resets it when done.""" + GL.glUseProgram(self.id) + try: + yield + finally: + GL.glUseProgram(0) + + @cached_property + def active_uniforms(self) -> dict[str, tuple[int, int, int, Callable]]: + """Get information about the program's active uniforms. + + Returns a dictionary of uniform names mapping to the location, size, + type and setter function of the attribute. + """ + n_uniforms = GL.glGetProgramiv(self.id, GL.GL_ACTIVE_UNIFORMS) + data = [GL.glGetActiveUniform(self.id, i) for i in range(n_uniforms)] + uniforms = { + name: ( + GL.glGetUniformLocation(self.id, name), + int(size), + uniform_type, + ) + for i, (name, size, uniform_type) in enumerate(data) + } + return uniforms + + def attribute(self, item: str) -> int: + """Return the location of an active attribute.""" + return GL.glGetAttribLocation(self.id, item) + + def set_uniforms(self, uniforms: dict[str, tuple]): + """Set the values of active uniforms from a dictionary of values.""" + for uniform, (loc, size, type) in self.active_uniforms.items(): + if uniform in uniforms: + self.set_uniform(loc, size, type, uniforms[uniform]) + + def set_uniform(self, loc, size, type, value): + match [type, size]: + case [GL.GL_FLOAT, 1]: + GL.glUniform1f(loc, value) + case [GL.GL_FLOAT, n]: + GL.glUniform1fv(loc, n, value) + case [GL.GL_FLOAT_VEC2, 1]: + GL.glUniform2f(loc, *value) + case [GL.GL_FLOAT_VEC2, n]: + GL.glUniform2fv(loc, n, value) + case [GL.GL_FLOAT_VEC3, 1]: + GL.glUniform3f(loc, *value) + case [GL.GL_FLOAT_VEC3, n]: + GL.glUniform3fv(loc, n, value) + case [GL.GL_FLOAT_VEC4, 1]: + GL.glUniform4f(loc, *value) + case [GL.GL_FLOAT_VEC4, n]: + GL.glUniform4f(loc, n, value) + case [GL.GL_FLOAT_MAT4, n]: + if isinstance(value, Matrix): + value = value.data + GL.glUniformMatrix4fv(loc, n, False, value) + case _: + pass + + def bind_attribute_buffer(self, attribute: str, vbo: Buffer, *, size: int = 4): + """Bind an active attribute to a vertex buffer object.""" + loc = self.attribute(attribute) + if loc < 0: + print(f"No active attribute {attribute}") + return + with vbo: + GL.glEnableVertexAttribArray(loc) + GL.glVertexAttribPointer(loc, size, GL.GL_FLOAT, GL.GL_FALSE, 0, None) diff --git a/examples/opengl/opengl/utils_android.py b/examples/opengl/opengl/utils_android.py new file mode 100644 index 0000000000..c88ec738af --- /dev/null +++ b/examples/opengl/opengl/utils_android.py @@ -0,0 +1,87 @@ +"""Utility objects and methods for android.opengl""" + +from android.opengl import GLES32 as GL +from java import jarray, jbyte, jint +from java.nio import ByteBuffer, ByteOrder + +#: Shader version header: we want OpenGL ES GLSL 3 +VERSION_HEADER = """#version 300 es""" + +# Adapt some functions to common API + + +def v_func(v_size=1, jtype=jint): + """Decorator for functions expecting a pointer to an output vector. + + A number of OpenGL functions have a suffix of the form {l}{t}v, + which expect an argument of an array of length l of type t (and + an offset into it) which is used to return values by setting them + into the array. + + This wrapper wraps these functions to create a java array of the + correct type, call the function, and then unpack the values from + the array. + """ + + def wrapper(func): + def call_v_function(*args): + data = jarray(jtype)([0] * v_size) + func(*args, data, 0) + if v_size == 1: + return data[0] + else: + return tuple(data) + + return call_v_function + + return wrapper + + +glGetShaderiv = v_func()(GL.glGetShaderiv) +glGetProgramiv = v_func()(GL.glGetProgramiv) +glGenBuffers = v_func()(GL.glGenBuffers) +glGenVertexArrays = v_func()(GL.glGenVertexArrays) + + +def glVertexAttribPointer(loc, size, type, normalize, stride, offset): + offset = 0 if offset is None else offset + GL.glVertexAttribPointer(loc, size, type, bool(normalize), stride, offset) + + +def glGetActiveUniform(id, loc): + length = jarray(jint)([0]) + size = jarray(jint)([0]) + type = jarray(jint)([0]) + name = jarray(jbyte)(b"\0x00" * 255) + GL.glGetActiveUniform( + id, + loc, + 255, + length, + 0, + size, + 0, + type, + 0, + name, + 0, + ) + return bytes(name[: length[0]]).decode("utf-8"), size[0], type[0] + + +def glBufferData(buffer_type, data, usage): + nbytes = len(data) + buffer = ByteBuffer.allocateDirect(nbytes) + buffer.order(ByteOrder.nativeOrder()) + buffer.put(data) + buffer.position(0) + GL.glBufferData(buffer_type, nbytes, buffer, usage) + + +def __getattr__(name): + value = getattr(GL, name, None) + if value is not None: + globals()[name] = value + return value + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/examples/opengl/opengl/utils_iOS.py b/examples/opengl/opengl/utils_iOS.py new file mode 100644 index 0000000000..8b0ed2703a --- /dev/null +++ b/examples/opengl/opengl/utils_iOS.py @@ -0,0 +1,88 @@ +"""Utility objects and methods for iOS""" + +from ctypes import byref, c_char_p, c_int, c_uint, create_string_buffer + +from toga_iOS.libs import opengles as GL + +#: Shader version header: we want OpenGL ES GLSL 3 +VERSION_HEADER = """#version 300 es""" + + +# Adapt some functions to common API + + +def v_func(v_size=1, ctype=c_int): + """Decorator for functions expecting a pointer to an output vector. + + A number of OpenGL functions have a suffix of the form {l}{t}v, + which expect an argument of an array of length l of type t (and + an offset into it) which is used to return values by setting them + into the array. + + This wrapper wraps these functions to create a java array of the + correct type, call the function, and then unpack the values from + the array. + """ + + def wrapper(func): + def call_v_function(*args): + data = (ctype * v_size)(*([0] * v_size)) + func(*args, byref(data)) + if v_size == 1: + return data[0] + else: + return tuple(data) + + return call_v_function + + return wrapper + + +glGetShaderiv = v_func()(GL.glGetShaderiv) +glGetProgramiv = v_func()(GL.glGetProgramiv) +glGenBuffers = v_func()(GL.glGenBuffers) +glGenVertexArrays = v_func()(GL.glGenVertexArrays) + + +def glShaderSource(id, source): + source_bytes = c_char_p(source.encode("utf-8")) + GL.glShaderSource( + id, + 1, + source_bytes, + None, + ) + + +def glGetActiveUniform(id, loc): + size = c_int() + type = c_uint() + name = create_string_buffer(255) + GL.glGetActiveUniform( + id, + loc, + 255, + None, + byref(size), + byref(type), + name, + ) + return name.value.decode("utf-8"), size.value, type.value + + +def glGetAttribLocation(id, item): + buffer = create_string_buffer(item.encode("utf-8")) + return GL.glGetAttribLocation(id, buffer) + + +def glBufferData(buffer_type, data: bytes, usage): + GL.glBufferData(buffer_type, len(data), data, usage) + + +def __getattr__(name): + value = getattr(GL, name, None) + if value is not None: + globals()[name] = value + return value + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/examples/opengl/opengl/utils_pyglet.py b/examples/opengl/opengl/utils_pyglet.py new file mode 100644 index 0000000000..62e0671846 --- /dev/null +++ b/examples/opengl/opengl/utils_pyglet.py @@ -0,0 +1,100 @@ +"""Utility objects and methods for Pyglet""" + +from ctypes import ( + POINTER, + byref, + c_char, + c_char_p, + c_float, + c_int, + cast, + create_string_buffer, +) + +from pyglet.gl import gl as GL + +#: Shader version header: this should work on most modern desktops +VERSION_HEADER = "#version 330 core" + +# Adapt some functions to common API + + +def glShaderSource(id, source): + source_bytes = source.encode("utf-8") + GL.glShaderSource( + id, + 1, + cast(c_char_p(source_bytes), POINTER(c_char)), + c_int(len(source_bytes)), + ) + + +def glGetShaderiv(id, param): + status = c_int(0) + GL.glGetShaderiv(id, param, byref(status)) + return status.value + + +def glGetShaderInfoLog(id): + raise NotImplementedError() + + +def glGetProgramiv(id, param): + status = c_int(0) + GL.glGetProgramiv(id, param, byref(status)) + return status.value + + +def glGetProgramInfoLog(id): + raise NotImplementedError() + + +def glGetActiveUniform(id, loc): + size = GL.GLint() + uniform_type = GL.GLenum() + buf_size = 192 + name = create_string_buffer(buf_size) + GL.glGetActiveUniform(id, loc, buf_size, None, size, uniform_type, name) + return name.value.decode("utf-8"), size.value, uniform_type.value + + +def glGetAttribLocation(id, attrib): + buffer = create_string_buffer(attrib.encode("utf-8")) + return GL.glGetAttribLocation(id, buffer) + + +def glBufferData(buffer_type, data: bytes, usage): + GL.glBufferData(buffer_type, GL.GLsizeiptr(len(data)), data, usage) + + +def glGenBuffers(n): + if n == 1: + buffer_id = GL.GLuint() + GL.glGenBuffers(n, buffer_id) + return buffer_id.value + else: + raise NotImplementedError() + + +def glGenVertexArrays(n): + if n == 1: + id = GL.GLuint() + GL.glGenVertexArrays(1, id) + return id.value + else: + raise NotImplementedError() + + +def glUniformMatrix4fv(loc, size, transpose, value): + print(len(value)) + buffer = (c_float * (16 * size))(*value) + GL.glUniformMatrix4fv(loc, size, transpose, buffer) + + +def __getattr__(name): + value = getattr(GL, name, None) + if value is not None: + globals()[name] = value + return value + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/examples/opengl/opengl/utils_pyopengl.py b/examples/opengl/opengl/utils_pyopengl.py new file mode 100644 index 0000000000..f4273b7e62 --- /dev/null +++ b/examples/opengl/opengl/utils_pyopengl.py @@ -0,0 +1,35 @@ +"""Utility objects and methods for PyOpenGL""" + +import OpenGL + +# Get spurious errors with Qt backend +OpenGL.ERROR_CHECKING = False + +from OpenGL import GL # noqa: E402 + +#: Shader version header: this should work on most modern desktops +VERSION_HEADER = "#version 330 core" + +# Adapt some functions to common API + + +def glGetShaderInfoLog(id): + return GL.glGetShaderInfoLog(id).decode("utf-8") + + +def glGetProgramInfoLog(id): + return GL.glGetProgramInfoLog(id).decode("utf-8") + + +def glGetActiveUniform(id, loc): + name, size, uniform_type = GL.glGetActiveUniform(id, loc) + return name.decode("utf-8"), size, uniform_type + + +def __getattr__(name): + value = getattr(GL, name) + if value is not None: + globals()[name] = value + return value + else: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") From 92ce539e96b719f45eb423afac3666b53e632591 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 8 Apr 2026 14:40:29 +0100 Subject: [PATCH 16/26] Rename obj file viewer example. --- .../{opengl => opengl_obj_viewer}/CHANGELOG | 0 .../{opengl => opengl_obj_viewer}/LICENSE | 0 .../{opengl => opengl_obj_viewer}/README.md | 2 +- .../obj_viewer}/__init__.py | 0 .../obj_viewer}/__main__.py | 2 +- .../obj_viewer}/app.py | 2 +- .../obj_viewer}/matrix.py | 0 .../obj_viewer}/obj_file.py | 0 .../obj_viewer}/obj_file_renderer.py | 0 .../obj_viewer}/resources/LICENSE_well.txt | 0 .../obj_viewer}/resources/__init__.py | 0 .../obj_viewer}/resources/well.mtl | 0 .../obj_viewer}/resources/well.obj | 0 .../obj_viewer}/utils.py | 0 .../obj_viewer}/utils_android.py | 0 .../obj_viewer}/utils_iOS.py | 0 .../obj_viewer}/utils_pyglet.py | 0 .../obj_viewer}/utils_pyopengl.py | 0 .../pyproject.toml | 48 +++++++++---------- 19 files changed, 27 insertions(+), 27 deletions(-) rename examples/{opengl => opengl_obj_viewer}/CHANGELOG (100%) rename examples/{opengl => opengl_obj_viewer}/LICENSE (100%) rename examples/{opengl => opengl_obj_viewer}/README.md (93%) rename examples/{opengl/opengl => opengl_obj_viewer/obj_viewer}/__init__.py (100%) rename examples/{opengl/opengl => opengl_obj_viewer/obj_viewer}/__main__.py (61%) rename examples/{opengl/opengl => opengl_obj_viewer/obj_viewer}/app.py (97%) rename examples/{opengl/opengl => opengl_obj_viewer/obj_viewer}/matrix.py (100%) rename examples/{opengl/opengl => opengl_obj_viewer/obj_viewer}/obj_file.py (100%) rename examples/{opengl/opengl => opengl_obj_viewer/obj_viewer}/obj_file_renderer.py (100%) rename examples/{opengl/opengl => opengl_obj_viewer/obj_viewer}/resources/LICENSE_well.txt (100%) rename examples/{opengl/opengl => opengl_obj_viewer/obj_viewer}/resources/__init__.py (100%) rename examples/{opengl/opengl => opengl_obj_viewer/obj_viewer}/resources/well.mtl (100%) rename examples/{opengl/opengl => opengl_obj_viewer/obj_viewer}/resources/well.obj (100%) rename examples/{opengl/opengl => opengl_obj_viewer/obj_viewer}/utils.py (100%) rename examples/{opengl/opengl => opengl_obj_viewer/obj_viewer}/utils_android.py (100%) rename examples/{opengl/opengl => opengl_obj_viewer/obj_viewer}/utils_iOS.py (100%) rename examples/{opengl/opengl => opengl_obj_viewer/obj_viewer}/utils_pyglet.py (100%) rename examples/{opengl/opengl => opengl_obj_viewer/obj_viewer}/utils_pyopengl.py (100%) rename examples/{opengl => opengl_obj_viewer}/pyproject.toml (52%) diff --git a/examples/opengl/CHANGELOG b/examples/opengl_obj_viewer/CHANGELOG similarity index 100% rename from examples/opengl/CHANGELOG rename to examples/opengl_obj_viewer/CHANGELOG diff --git a/examples/opengl/LICENSE b/examples/opengl_obj_viewer/LICENSE similarity index 100% rename from examples/opengl/LICENSE rename to examples/opengl_obj_viewer/LICENSE diff --git a/examples/opengl/README.md b/examples/opengl_obj_viewer/README.md similarity index 93% rename from examples/opengl/README.md rename to examples/opengl_obj_viewer/README.md index b1dd57f3cc..88175c865e 100644 --- a/examples/opengl/README.md +++ b/examples/opengl_obj_viewer/README.md @@ -12,5 +12,5 @@ To run this example: ```text $ python -m pip install toga PyOpenGL -$ python -m opengl +$ python -m obj_viewer ``` diff --git a/examples/opengl/opengl/__init__.py b/examples/opengl_obj_viewer/obj_viewer/__init__.py similarity index 100% rename from examples/opengl/opengl/__init__.py rename to examples/opengl_obj_viewer/obj_viewer/__init__.py diff --git a/examples/opengl/opengl/__main__.py b/examples/opengl_obj_viewer/obj_viewer/__main__.py similarity index 61% rename from examples/opengl/opengl/__main__.py rename to examples/opengl_obj_viewer/obj_viewer/__main__.py index fd87db935c..b6c89d93aa 100644 --- a/examples/opengl/opengl/__main__.py +++ b/examples/opengl_obj_viewer/obj_viewer/__main__.py @@ -1,4 +1,4 @@ -from opengl.app import main +from obj_viewer.app import main if __name__ == "__main__": main().main_loop() diff --git a/examples/opengl/opengl/app.py b/examples/opengl_obj_viewer/obj_viewer/app.py similarity index 97% rename from examples/opengl/opengl/app.py rename to examples/opengl_obj_viewer/obj_viewer/app.py index 9ac0893d65..25b6b6eb2b 100644 --- a/examples/opengl/opengl/app.py +++ b/examples/opengl_obj_viewer/obj_viewer/app.py @@ -70,7 +70,7 @@ def startup(self): def main(): return OpenGLApp( "Obj File Viewer Example", - "org.beeware.toga.examples.opengl", + "org.beeware.toga.examples.obj-viewer", document_types=[ObjFile], ) diff --git a/examples/opengl/opengl/matrix.py b/examples/opengl_obj_viewer/obj_viewer/matrix.py similarity index 100% rename from examples/opengl/opengl/matrix.py rename to examples/opengl_obj_viewer/obj_viewer/matrix.py diff --git a/examples/opengl/opengl/obj_file.py b/examples/opengl_obj_viewer/obj_viewer/obj_file.py similarity index 100% rename from examples/opengl/opengl/obj_file.py rename to examples/opengl_obj_viewer/obj_viewer/obj_file.py diff --git a/examples/opengl/opengl/obj_file_renderer.py b/examples/opengl_obj_viewer/obj_viewer/obj_file_renderer.py similarity index 100% rename from examples/opengl/opengl/obj_file_renderer.py rename to examples/opengl_obj_viewer/obj_viewer/obj_file_renderer.py diff --git a/examples/opengl/opengl/resources/LICENSE_well.txt b/examples/opengl_obj_viewer/obj_viewer/resources/LICENSE_well.txt similarity index 100% rename from examples/opengl/opengl/resources/LICENSE_well.txt rename to examples/opengl_obj_viewer/obj_viewer/resources/LICENSE_well.txt diff --git a/examples/opengl/opengl/resources/__init__.py b/examples/opengl_obj_viewer/obj_viewer/resources/__init__.py similarity index 100% rename from examples/opengl/opengl/resources/__init__.py rename to examples/opengl_obj_viewer/obj_viewer/resources/__init__.py diff --git a/examples/opengl/opengl/resources/well.mtl b/examples/opengl_obj_viewer/obj_viewer/resources/well.mtl similarity index 100% rename from examples/opengl/opengl/resources/well.mtl rename to examples/opengl_obj_viewer/obj_viewer/resources/well.mtl diff --git a/examples/opengl/opengl/resources/well.obj b/examples/opengl_obj_viewer/obj_viewer/resources/well.obj similarity index 100% rename from examples/opengl/opengl/resources/well.obj rename to examples/opengl_obj_viewer/obj_viewer/resources/well.obj diff --git a/examples/opengl/opengl/utils.py b/examples/opengl_obj_viewer/obj_viewer/utils.py similarity index 100% rename from examples/opengl/opengl/utils.py rename to examples/opengl_obj_viewer/obj_viewer/utils.py diff --git a/examples/opengl/opengl/utils_android.py b/examples/opengl_obj_viewer/obj_viewer/utils_android.py similarity index 100% rename from examples/opengl/opengl/utils_android.py rename to examples/opengl_obj_viewer/obj_viewer/utils_android.py diff --git a/examples/opengl/opengl/utils_iOS.py b/examples/opengl_obj_viewer/obj_viewer/utils_iOS.py similarity index 100% rename from examples/opengl/opengl/utils_iOS.py rename to examples/opengl_obj_viewer/obj_viewer/utils_iOS.py diff --git a/examples/opengl/opengl/utils_pyglet.py b/examples/opengl_obj_viewer/obj_viewer/utils_pyglet.py similarity index 100% rename from examples/opengl/opengl/utils_pyglet.py rename to examples/opengl_obj_viewer/obj_viewer/utils_pyglet.py diff --git a/examples/opengl/opengl/utils_pyopengl.py b/examples/opengl_obj_viewer/obj_viewer/utils_pyopengl.py similarity index 100% rename from examples/opengl/opengl/utils_pyopengl.py rename to examples/opengl_obj_viewer/obj_viewer/utils_pyopengl.py diff --git a/examples/opengl/pyproject.toml b/examples/opengl_obj_viewer/pyproject.toml similarity index 52% rename from examples/opengl/pyproject.toml rename to examples/opengl_obj_viewer/pyproject.toml index 9f4b705814..40643caafb 100644 --- a/examples/opengl/pyproject.toml +++ b/examples/opengl_obj_viewer/pyproject.toml @@ -20,56 +20,56 @@ requires = [ "../../core", ] -[tool.briefcase.app.opengl] -sources = ["opengl"] +[tool.briefcase.app.obj-viewer] +sources = ["obj_viewer"] -[tool.briefcase.app.opengl-qt] -sources = ["opengl", "opengl_qt"] +[tool.briefcase.app.obj-viewer-qt] +sources = ["obj_viewer", "obj_viewer_qt"] -[tool.briefcase.app.opengl.macOS] +[tool.briefcase.app.obj-viewer.macOS] requires = [ "../../cocoa", "std-nslog>=1.0.0", "PyOpenGL", ] -[tool.briefcase.app.opengl.linux] +[tool.briefcase.app.obj-viewer.linux] requires = [ "../../gtk", "PyOpenGL", ] -[tool.briefcase.app.opengl-qt.linux] +[tool.briefcase.app.obj-viewer-qt.linux] requires = [ "../../qt", "PyOpenGL", ] -#[tool.briefcase.app.opengl.windows] +#[tool.briefcase.app.obj-viewer.windows] #requires = [ # "../../winforms", #] # Mobile deployments -[tool.briefcase.app.opengl.iOS] -requires = [ - "../../iOS", - "std-nslog>=1.0.0", -] - -[tool.briefcase.app.opengl.android] -requires = [ - "../../android", -] +# [tool.briefcase.app.obj-viewer.iOS] +# requires = [ +# "../../iOS", +# "std-nslog>=1.0.0", +# ] -base_theme = "Theme.MaterialComponents.Light.DarkActionBar" - -build_gradle_dependencies = [ - "com.google.android.material:material:1.12.0", -] +# [tool.briefcase.app.obj-viewer.android] +# requires = [ +# "../../android", +# ] +# +# base_theme = "Theme.MaterialComponents.Light.DarkActionBar" +# +# build_gradle_dependencies = [ +# "com.google.android.material:material:1.12.0", +# ] # Web deployment -#[tool.briefcase.app.opengl.web] +#[tool.briefcase.app.obj-viewer.web] #requires = [ # "../../web", #] From 35328bf99ad5120bd8e704be5723c4fc14fb1847 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 8 Apr 2026 14:48:27 +0100 Subject: [PATCH 17/26] Update utilities in shadertoy and make pyglet work. --- .../opengl_obj_viewer/obj_viewer/utils.py | 43 ----- .../obj_viewer/utils_pyglet.py | 5 + examples/shadertoy/shadertoy/matrix.py | 163 ++++++++++++++++++ .../shadertoy/shadertoy/shadertoy_renderer.py | 16 +- examples/shadertoy/shadertoy/utils.py | 58 ++++--- examples/shadertoy/shadertoy/utils_pyglet.py | 22 ++- 6 files changed, 233 insertions(+), 74 deletions(-) create mode 100644 examples/shadertoy/shadertoy/matrix.py diff --git a/examples/opengl_obj_viewer/obj_viewer/utils.py b/examples/opengl_obj_viewer/obj_viewer/utils.py index 99d24ec707..4b9a977247 100644 --- a/examples/opengl_obj_viewer/obj_viewer/utils.py +++ b/examples/opengl_obj_viewer/obj_viewer/utils.py @@ -93,49 +93,6 @@ def __exit__(self, exc_type, exc_value, traceback): return False -@dataclass -class Texture: - """A dataclass that encapsulates an OpenGL texture. - - This can be used as a context manager to automatically bind and unbind - the buffer for use. - """ - - texture_type: int - - def create(self, data: bytes): - """Generate a new buffer and set the data into it.""" - self.id = GL.glCreateTexture() - - def set_data(self, data: bytes): - """Set data into a buffer. - - This automatically binds the texture. - """ - with self: - GL.glTexture( - self.texture_type, - data, - ) - - def bind(self): - """Bind the texture so that it is the current texture.""" - GL.glBindTexture(self.texture_type, self.id) - - def unbind(self): - """Unbind the texture so that it is no longer the current buffer.""" - GL.glBindTexture(self.texture_type, 0) - - def __enter__(self): - """Enter the context manager, binding the buffer.""" - self.bind() - - def __exit__(self, exc_type, exc_value, traceback): - """Exit the context manager, unbinding the buffer.""" - self.unbind() - return False - - @dataclass class VertexArrayObject: """A dataclass that encapsulates an OpenGL vertex array object. diff --git a/examples/opengl_obj_viewer/obj_viewer/utils_pyglet.py b/examples/opengl_obj_viewer/obj_viewer/utils_pyglet.py index 62e0671846..4783dd4f17 100644 --- a/examples/opengl_obj_viewer/obj_viewer/utils_pyglet.py +++ b/examples/opengl_obj_viewer/obj_viewer/utils_pyglet.py @@ -63,6 +63,11 @@ def glGetAttribLocation(id, attrib): return GL.glGetAttribLocation(id, buffer) +def glGetUniformLocation(id, uniform): + buffer = create_string_buffer(uniform.encode("utf-8")) + return GL.glGetUniformLocation(id, buffer) + + def glBufferData(buffer_type, data: bytes, usage): GL.glBufferData(buffer_type, GL.GLsizeiptr(len(data)), data, usage) diff --git a/examples/shadertoy/shadertoy/matrix.py b/examples/shadertoy/shadertoy/matrix.py new file mode 100644 index 0000000000..ba9a339da6 --- /dev/null +++ b/examples/shadertoy/shadertoy/matrix.py @@ -0,0 +1,163 @@ +from math import cos, pi, sin, tan + + +# try to avoid a NumPy dependency in an example +class Matrix: + data: list[list[int]] + shape: tuple[int, int] + strides: tuple[int, int] + + def __init__(self, data=None, shape=None, strides=None): + if shape is None: + rows = len(data) + cols = max(len(row) for row in data) + shape = (rows, cols) + self.shape = tuple(shape) + if strides is None: + strides = (shape[1], 1) + self.strides = tuple(strides) + self.data = [0.0] * ( + (shape[0] - 1) * strides[0] + (shape[1] - 1) * strides[1] + 1 + ) + if data is not None: + for i, row in enumerate(data): + if i >= shape[0]: + break + for j, value in enumerate(row): + if j >= shape[1]: + break + self[i, j] = value + + def __getitem__(self, index): + return self.data[index[0] * self.strides[0] + index[1] * self.strides[1]] + + def __setitem__(self, index, value): + self.data[index[0] * self.strides[0] + index[1] * self.strides[1]] = value + + def __mul__(self, other): + if isinstance(other, (int, float, complex)): + result = Matrix(shape=self.shape) + result.data = [other * value for value in self.data] + else: + return NotImplemented + + def __rmul__(self, other): + if isinstance(other, (int, float, complex)): + result = Matrix(shape=self.shape) + result.data = [other * value for value in self.data] + else: + return NotImplemented + + def __matmul__(self, other): + if isinstance(other, Matrix) and self.shape[1] == other.shape[0]: + result = Matrix(shape=(self.shape[0], other.shape[1])) + for i in range(self.shape[0]): + for j in range(other.shape[1]): + result[i, j] = sum( + self[i, k] * other[k, j] for k in range(self.shape[1]) + ) + return result + elif isinstance(other, (int, float, complex)): + return self * other + else: + return NotImplemented + + def __rmatmul__(self, other): + if isinstance(other, (int, float, complex)): + return self * other + else: + return NotImplemented + + def __iter__(self): + yield from self.data + + def __str__(self): + return "\n".join( + ", ".join(f"{self[i, j]:5.2f}" for j in range(self.shape[1])) + for i in range(self.shape[0]) + ) + + def transpose(self): + result = Matrix(shape=self.shape[::-1], strides=self.strides[::-1]) + result.data = self.data.copy() + return result + + @classmethod + def identity(cls, n): + result = cls(shape=(n, n)) + for i in range(n): + result[i, i] = 1.0 + return result + + @classmethod + def translation(cls, *v): + n = len(v) + result = cls.identity(n + 1) + for i in range(n): + result[n, i] = v[i] + return result + + @classmethod + def skewing(cls, *v): + n = len(v) + result = cls.identity(n + 1) + for i in range(n): + result[i, n] = v[i] + return result + + @classmethod + def scaling(cls, *v): + n = len(v) + result = cls.identity(n + 1) + for i in range(n): + result[i, i] = v[i] + return result + + @classmethod + def rotation(cls, angle, d0=0, d1=1, n=4): + c = cos(angle) + s = sin(angle) + result = cls.identity(n) + result[d0, d0] = c + result[d0, d1] = s + result[d1, d0] = -s + result[d1, d1] = c + return result + + @classmethod + def rotation_x(cls, angle): + return cls.rotation(angle, 1, 2) + + @classmethod + def rotation_y(cls, angle): + return cls.rotation(angle, 0, 2) + + @classmethod + def rotation_z(cls, angle): + return cls.rotation(angle, 0, 1) + + @classmethod + def perspective(self, fov, aspect, near_clip, far_clip): + f = tan((pi - fov) / 2) + scale = 1 / (near_clip - far_clip) + return Matrix( + [ + [f / aspect, 0.0, 0.0], + [0.0, f, 0.0, 0.0], + [0.0, 0.0, (near_clip + far_clip) * scale, -1.0], + [0.0, 0.0, near_clip * far_clip * scale / 2, 0.0], + ], + shape=(4, 4), + ) + + @classmethod + def projection(self, width, height, depth): + return Matrix( + [ + [2 / width, 0.0, 0.0], + [0.0, 2 / height, 0.0, 0.0], + [0.0, 0.0, 1 / depth, 0.0], + [0.0, 0.0, 0.0, 1.0], + ], + shape=(4, 4), + ) diff --git a/examples/shadertoy/shadertoy/shadertoy_renderer.py b/examples/shadertoy/shadertoy/shadertoy_renderer.py index 098009a394..25074f97b3 100644 --- a/examples/shadertoy/shadertoy/shadertoy_renderer.py +++ b/examples/shadertoy/shadertoy/shadertoy_renderer.py @@ -68,14 +68,14 @@ #: Message to display in side-panel giving info about #: values of uniforms. -UNIFORM_VALUES = """Frame: {iFrame[0]} -Frame rate: {iFrameRate[0]:.3f} fps +UNIFORM_VALUES = """Frame: {iFrame} +Frame rate: {iFrameRate:.3f} fps Resolution: {iResolution} Mouse: ({iMouse[0]:.1f}, {iMouse[1]:.1f}) ({iMouse[2]:.1f}, {iMouse[3]:.1f}) -Time: {iTime[0]:.3f} s -Time delta: {iTimeDelta[0]:.3f} s +Time: {iTime:.3f} s +Time delta: {iTimeDelta:.3f} s Date: {iDate[0]}-{iDate[1]}-{iDate[2]} {iDate[3]:.3f} """ @@ -232,10 +232,10 @@ def on_render( uniforms = { "iResolution": (*size, 1.0), "iMouse": (*self.pointer, *self.drag_start), - "iTime": (self.timestamp,), - "iTimeDelta": (self.deltas[-1],), - "iFrame": (self.frame,), - "iFrameRate": (frame_rate,), + "iTime": self.timestamp, + "iTimeDelta": self.deltas[-1], + "iFrame": self.frame, + "iFrameRate": frame_rate, "iDate": ( dt.year, dt.month, diff --git a/examples/shadertoy/shadertoy/utils.py b/examples/shadertoy/shadertoy/utils.py index c49bee0d28..4b9a977247 100644 --- a/examples/shadertoy/shadertoy/utils.py +++ b/examples/shadertoy/shadertoy/utils.py @@ -3,9 +3,12 @@ from contextlib import contextmanager from dataclasses import dataclass from enum import IntEnum +from functools import cached_property import toga +from .matrix import Matrix + if toga.backend in {"toga_cocoa", "toga_gtk", "toga_qt"}: if os.environ.get("TOGA_OPENGL") == "pyglet": from . import utils_pyglet as GL @@ -42,20 +45,6 @@ class BufferUsage(IntEnum): static_draw = GL.GL_STATIC_DRAW -#: Map from OpenGL data types to corresponding setter functions -#: This is far from complete. -uniform_setters = { - (GL.GL_FLOAT, False): GL.glUniform1f, - (GL.GL_FLOAT_VEC2, False): GL.glUniform2f, - (GL.GL_FLOAT_VEC3, False): GL.glUniform3f, - (GL.GL_FLOAT_VEC4, False): GL.glUniform4f, - (GL.GL_FLOAT, True): GL.glUniform1fv, - (GL.GL_FLOAT_VEC2, True): GL.glUniform2fv, - (GL.GL_FLOAT_VEC3, True): GL.glUniform3fv, - (GL.GL_FLOAT_VEC4, True): GL.glUniform4fv, -} - - class OpenGLError(RuntimeError): """OpenGL-specific runtime errors.""" @@ -208,6 +197,7 @@ def use(self): finally: GL.glUseProgram(0) + @cached_property def active_uniforms(self) -> dict[str, tuple[int, int, int, Callable]]: """Get information about the program's active uniforms. @@ -218,10 +208,9 @@ def active_uniforms(self) -> dict[str, tuple[int, int, int, Callable]]: data = [GL.glGetActiveUniform(self.id, i) for i in range(n_uniforms)] uniforms = { name: ( - i, - size, + GL.glGetUniformLocation(self.id, name), + int(size), uniform_type, - uniform_setters[uniform_type, size > 1], ) for i, (name, size, uniform_type) in enumerate(data) } @@ -233,16 +222,41 @@ def attribute(self, item: str) -> int: def set_uniforms(self, uniforms: dict[str, tuple]): """Set the values of active uniforms from a dictionary of values.""" - for uniform, (loc, size, _, setter) in self.active_uniforms().items(): + for uniform, (loc, size, type) in self.active_uniforms.items(): if uniform in uniforms: - if size == 1: - setter(loc, *uniforms[uniform]) - else: - setter(loc, size, uniforms[uniform]) + self.set_uniform(loc, size, type, uniforms[uniform]) + + def set_uniform(self, loc, size, type, value): + match [type, size]: + case [GL.GL_FLOAT, 1]: + GL.glUniform1f(loc, value) + case [GL.GL_FLOAT, n]: + GL.glUniform1fv(loc, n, value) + case [GL.GL_FLOAT_VEC2, 1]: + GL.glUniform2f(loc, *value) + case [GL.GL_FLOAT_VEC2, n]: + GL.glUniform2fv(loc, n, value) + case [GL.GL_FLOAT_VEC3, 1]: + GL.glUniform3f(loc, *value) + case [GL.GL_FLOAT_VEC3, n]: + GL.glUniform3fv(loc, n, value) + case [GL.GL_FLOAT_VEC4, 1]: + GL.glUniform4f(loc, *value) + case [GL.GL_FLOAT_VEC4, n]: + GL.glUniform4f(loc, n, value) + case [GL.GL_FLOAT_MAT4, n]: + if isinstance(value, Matrix): + value = value.data + GL.glUniformMatrix4fv(loc, n, False, value) + case _: + pass def bind_attribute_buffer(self, attribute: str, vbo: Buffer, *, size: int = 4): """Bind an active attribute to a vertex buffer object.""" loc = self.attribute(attribute) + if loc < 0: + print(f"No active attribute {attribute}") + return with vbo: GL.glEnableVertexAttribArray(loc) GL.glVertexAttribPointer(loc, size, GL.GL_FLOAT, GL.GL_FALSE, 0, None) diff --git a/examples/shadertoy/shadertoy/utils_pyglet.py b/examples/shadertoy/shadertoy/utils_pyglet.py index d333d2da71..4783dd4f17 100644 --- a/examples/shadertoy/shadertoy/utils_pyglet.py +++ b/examples/shadertoy/shadertoy/utils_pyglet.py @@ -1,6 +1,15 @@ """Utility objects and methods for Pyglet""" -from ctypes import POINTER, byref, c_char, c_char_p, c_int, cast, create_string_buffer +from ctypes import ( + POINTER, + byref, + c_char, + c_char_p, + c_float, + c_int, + cast, + create_string_buffer, +) from pyglet.gl import gl as GL @@ -54,6 +63,11 @@ def glGetAttribLocation(id, attrib): return GL.glGetAttribLocation(id, buffer) +def glGetUniformLocation(id, uniform): + buffer = create_string_buffer(uniform.encode("utf-8")) + return GL.glGetUniformLocation(id, buffer) + + def glBufferData(buffer_type, data: bytes, usage): GL.glBufferData(buffer_type, GL.GLsizeiptr(len(data)), data, usage) @@ -76,6 +90,12 @@ def glGenVertexArrays(n): raise NotImplementedError() +def glUniformMatrix4fv(loc, size, transpose, value): + print(len(value)) + buffer = (c_float * (16 * size))(*value) + GL.glUniformMatrix4fv(loc, size, transpose, buffer) + + def __getattr__(name): value = getattr(GL, name, None) if value is not None: From 87107c3fc55dd91aaf3afcf28d7e7e2e0f8172a2 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 8 Apr 2026 15:24:03 +0100 Subject: [PATCH 18/26] Additional fixes for shadertoy renderer. --- examples/shadertoy/shadertoy/shadertoy_renderer.py | 2 +- examples/shadertoy/shadertoy/utils_iOS.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/shadertoy/shadertoy/shadertoy_renderer.py b/examples/shadertoy/shadertoy/shadertoy_renderer.py index 25074f97b3..68bb466dd6 100644 --- a/examples/shadertoy/shadertoy/shadertoy_renderer.py +++ b/examples/shadertoy/shadertoy/shadertoy_renderer.py @@ -192,7 +192,7 @@ def on_render( """Render a frame using OpenGL.""" self.message = "Rendering...\n\n" GL.glClearColor(0.0, 0.0, 1.0, 1.0) - GL.glClear(GL.GL_COLOR_BUFFER_BIT) + GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) # Handle a pending source update. if self.source_updated: diff --git a/examples/shadertoy/shadertoy/utils_iOS.py b/examples/shadertoy/shadertoy/utils_iOS.py index 8b0ed2703a..0c5b956982 100644 --- a/examples/shadertoy/shadertoy/utils_iOS.py +++ b/examples/shadertoy/shadertoy/utils_iOS.py @@ -75,6 +75,11 @@ def glGetAttribLocation(id, item): return GL.glGetAttribLocation(id, buffer) +def glGetUnifromLocation(id, item): + buffer = create_string_buffer(item.encode("utf-8")) + return GL.glGetUniformLocation(id, buffer) + + def glBufferData(buffer_type, data: bytes, usage): GL.glBufferData(buffer_type, len(data), data, usage) From 1bc16de8771ed065f0f80651943eaada2ba14430 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 9 Apr 2026 11:36:56 +0100 Subject: [PATCH 19/26] Fixes for unit tests. --- android/tests_backend/widgets/openglview.py | 2 +- cocoa/tests_backend/widgets/openglview.py | 9 ++++++++- gtk/src/toga_gtk/widgets/openglview.py | 2 +- iOS/src/toga_iOS/libs/opengles.py | 4 ++++ iOS/tests_backend/widgets/canvas.py | 11 ++--------- iOS/tests_backend/widgets/openglview.py | 11 ++--------- iOS/tests_backend/widgets/utils.py | 9 +++++++++ testbed/tests/widgets/test_openglview.py | 3 ++- 8 files changed, 29 insertions(+), 22 deletions(-) create mode 100644 iOS/tests_backend/widgets/utils.py diff --git a/android/tests_backend/widgets/openglview.py b/android/tests_backend/widgets/openglview.py index 275153c88c..38a79d725f 100644 --- a/android/tests_backend/widgets/openglview.py +++ b/android/tests_backend/widgets/openglview.py @@ -15,7 +15,7 @@ async def button_state(self, buttons: frozenset, x=0, y=0): if TOUCH in buttons: await self.touch_down(x, y) - async def reset_buttons(self, buttons: frozenset, x=0, y=0): + async def reset_buttons(self, x=0, y=0): await self.touch_up(x, y) await self.redraw("Touch cleared") diff --git a/cocoa/tests_backend/widgets/openglview.py b/cocoa/tests_backend/widgets/openglview.py index a3b8fd014f..e5055ef8a7 100644 --- a/cocoa/tests_backend/widgets/openglview.py +++ b/cocoa/tests_backend/widgets/openglview.py @@ -19,7 +19,14 @@ async def button_state(self, buttons: frozenset, x=0, y=0): await method(x, y) async def reset_buttons(self, x=0, y=0): - self.native.buttons.clear() + methods = [ + self.left_mouse_up, + self.middle_mouse_up, + self.right_mouse_up, + ] + for button in self.native.buttons: + method = methods[button] + await method(x, y) await self.redraw("Buttons cleared") async def left_mouse_down(self, x=0, y=0): diff --git a/gtk/src/toga_gtk/widgets/openglview.py b/gtk/src/toga_gtk/widgets/openglview.py index 36c7ff6e91..2a0bd906a0 100644 --- a/gtk/src/toga_gtk/widgets/openglview.py +++ b/gtk/src/toga_gtk/widgets/openglview.py @@ -41,7 +41,7 @@ def gtk_render(self, native, context): self.interface.renderer.on_render( self.interface, size=self._size(), - buttons=self.buttons, + buttons=frozenset(self.buttons), position=self.position, ) return True diff --git a/iOS/src/toga_iOS/libs/opengles.py b/iOS/src/toga_iOS/libs/opengles.py index 2aec6f2710..2f10f89ce0 100644 --- a/iOS/src/toga_iOS/libs/opengles.py +++ b/iOS/src/toga_iOS/libs/opengles.py @@ -52,6 +52,9 @@ # Constants opengles.GL_FALSE = False +opengles.GL_CULL_FACE = 0xB44 +opengles.GL_DEPTH_TEST = 0xB71 + opengles.GL_DEPTH_BUFFER_BIT = 0x200 opengles.GL_COLOR_BUFFER_BIT = 0x4000 opengles.GL_STENCIL_BUFFER_BIT = 0x400 @@ -73,6 +76,7 @@ opengles.GL_FLOAT_VEC2 = 0x8B50 opengles.GL_FLOAT_VEC3 = 0x8B51 opengles.GL_FLOAT_VEC4 = 0x8B52 +opengles.GL_FLOAT_MAT4 = 0x8B5C opengles.GL_COMPILE_STATUS = 0x8B81 diff --git a/iOS/tests_backend/widgets/canvas.py b/iOS/tests_backend/widgets/canvas.py index b6167fa974..4aa891d78c 100644 --- a/iOS/tests_backend/widgets/canvas.py +++ b/iOS/tests_backend/widgets/canvas.py @@ -2,24 +2,17 @@ import pytest from PIL import Image -from rubicon.objc import NSObject, NSPoint, ObjCClass, objc_method +from rubicon.objc import NSPoint, ObjCClass from toga_iOS.libs import UIView from .base import SimpleProbe +from .utils import MockTouch # Touch events generate a Set of 1 event. NSSet = ObjCClass("NSSet") -# UITouch objects can't be instantiated; but we only care about 1 method, so -# create a mock that satisfies our needs. -class MockTouch(NSObject): - @objc_method - def locationInView(self, view) -> NSPoint: - return self.position - - class CanvasProbe(SimpleProbe): native_class = UIView diff --git a/iOS/tests_backend/widgets/openglview.py b/iOS/tests_backend/widgets/openglview.py index 4f996e1e36..7b1aca0a55 100644 --- a/iOS/tests_backend/widgets/openglview.py +++ b/iOS/tests_backend/widgets/openglview.py @@ -1,22 +1,15 @@ -from rubicon.objc import NSObject, NSPoint, ObjCClass, objc_method +from rubicon.objc import NSPoint, ObjCClass from toga.widgets.openglview import TOUCH from toga_iOS.widgets.openglview import TogaGLKView from .base import SimpleProbe +from .utils import MockTouch # Touch events generate a Set of 1 event. NSSet = ObjCClass("NSSet") -# UITouch objects can't be instantiated; but we only care about 1 method, so -# create a mock that satisfies our needs. -class MockTouch(NSObject): - @objc_method - def locationInView(self, view) -> NSPoint: - return self.position - - class OpenGLViewProbe(SimpleProbe): native_class = TogaGLKView buttons = frozenset({TOUCH}) diff --git a/iOS/tests_backend/widgets/utils.py b/iOS/tests_backend/widgets/utils.py new file mode 100644 index 0000000000..1a4364ab76 --- /dev/null +++ b/iOS/tests_backend/widgets/utils.py @@ -0,0 +1,9 @@ +from rubicon.objc import NSObject, NSPoint, objc_method + + +# UITouch objects can't be instantiated; but we only care about 1 method, so +# create a mock that satisfies our needs. +class MockTouch(NSObject): + @objc_method + def locationInView(self, view) -> NSPoint: + return self.position diff --git a/testbed/tests/widgets/test_openglview.py b/testbed/tests/widgets/test_openglview.py index 188a3b358b..d445904e6e 100644 --- a/testbed/tests/widgets/test_openglview.py +++ b/testbed/tests/widgets/test_openglview.py @@ -5,7 +5,7 @@ import toga -# from .probe import get_probe +from ..conftest import skip_on_platforms from .properties import ( # noqa: F401 test_enable_noop, test_flex_widget_size, @@ -23,6 +23,7 @@ def renderer(): # initialized on the main thread @pytest.fixture async def widget(renderer): + skip_on_platforms("windows") return toga.OpenGLView(renderer, flex=1) From 1ad968a57607b96206cea8990d7e8340fe5f847a Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 9 Apr 2026 11:58:38 +0100 Subject: [PATCH 20/26] More fixes for tests. --- cocoa/tests_backend/widgets/openglview.py | 2 +- gtk/src/toga_gtk/widgets/openglview.py | 7 ++++++- iOS/tests_backend/widgets/openglview.py | 5 +++-- testbed/tests/widgets/test_openglview.py | 1 + 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/cocoa/tests_backend/widgets/openglview.py b/cocoa/tests_backend/widgets/openglview.py index e5055ef8a7..e4de284f41 100644 --- a/cocoa/tests_backend/widgets/openglview.py +++ b/cocoa/tests_backend/widgets/openglview.py @@ -24,7 +24,7 @@ async def reset_buttons(self, x=0, y=0): self.middle_mouse_up, self.right_mouse_up, ] - for button in self.native.buttons: + for button in frozenset(self.native.buttons): method = methods[button] await method(x, y) await self.redraw("Buttons cleared") diff --git a/gtk/src/toga_gtk/widgets/openglview.py b/gtk/src/toga_gtk/widgets/openglview.py index 2a0bd906a0..61df741ea8 100644 --- a/gtk/src/toga_gtk/widgets/openglview.py +++ b/gtk/src/toga_gtk/widgets/openglview.py @@ -57,7 +57,12 @@ def gtk_button_release(self, obj, event): self.position = (event.x, event.y) def gtk_motion_notify(self, obj, event): - self.position = (event.x, event.y) + # Don't have tests for mouse movement as most backends get + # live position + self.position = (event.x, event.y) # pragma: no-cover + + else: # pragma: no-cover-if-gtk3 + pass def _size(self): if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 diff --git a/iOS/tests_backend/widgets/openglview.py b/iOS/tests_backend/widgets/openglview.py index 7b1aca0a55..2b7e0c35ad 100644 --- a/iOS/tests_backend/widgets/openglview.py +++ b/iOS/tests_backend/widgets/openglview.py @@ -18,8 +18,9 @@ async def button_state(self, buttons: frozenset, x=0, y=0): if TOUCH in buttons: await self.touch_down(x, y) - async def reset_buttons(self, buttons: frozenset, x=0, y=0): - await self.touch_up(x, y) + async def reset_buttons(self, x=0, y=0): + if TOUCH in self.native.buttons: + await self.touch_up(x, y) await self.redraw("Touch cleared") async def touch_down(self, x, y): diff --git a/testbed/tests/widgets/test_openglview.py b/testbed/tests/widgets/test_openglview.py index d445904e6e..4367994edb 100644 --- a/testbed/tests/widgets/test_openglview.py +++ b/testbed/tests/widgets/test_openglview.py @@ -28,6 +28,7 @@ async def widget(renderer): async def test_callbacks(probe, widget, renderer): + await probe.redraw("OpenGLView widget created", 0.1) renderer.on_init.assert_called_once_with(widget) await probe.redraw("OpenGLView widget initialized", 0.1) From 619e35f490ddb363e3b5734e78d6c565fd9697a7 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sun, 12 Apr 2026 10:23:51 +0100 Subject: [PATCH 21/26] WIP commit: screenshot app; work on obj_viewer; other fixes. --- examples/opengl_obj_viewer/README.md | 4 +- .../obj_viewer/obj_file_renderer.py | 7 +- .../obj_viewer/utils_android.py | 7 +- .../opengl_obj_viewer/obj_viewer/utils_iOS.py | 11 +- .../obj_viewer/utils_pyglet.py | 1 - .../obj_viewer_mobile/__init__.py | 9 + .../obj_viewer_mobile/__main__.py | 4 + .../obj_viewer_mobile/app.py | 48 + .../resources/LICENSE_well.txt | 121 + .../obj_viewer_mobile/resources/__init__.py | 0 .../obj_viewer_mobile/resources/well.mtl | 180 + .../obj_viewer_mobile/resources/well.obj | 7152 +++++++++++++++++ examples/opengl_obj_viewer/pyproject.toml | 33 +- examples/screenshot/pyproject.toml | 3 + examples/screenshot/screenshot/app.py | 11 +- examples/screenshot/screenshot/openglview.py | 380 + examples/shadertoy/shadertoy/utils_iOS.py | 2 +- gtk/src/toga_gtk/widgets/openglview.py | 4 +- iOS/src/toga_iOS/libs/__init__.py | 1 - 19 files changed, 7950 insertions(+), 28 deletions(-) create mode 100644 examples/opengl_obj_viewer/obj_viewer_mobile/__init__.py create mode 100644 examples/opengl_obj_viewer/obj_viewer_mobile/__main__.py create mode 100644 examples/opengl_obj_viewer/obj_viewer_mobile/app.py create mode 100644 examples/opengl_obj_viewer/obj_viewer_mobile/resources/LICENSE_well.txt create mode 100644 examples/opengl_obj_viewer/obj_viewer_mobile/resources/__init__.py create mode 100644 examples/opengl_obj_viewer/obj_viewer_mobile/resources/well.mtl create mode 100644 examples/opengl_obj_viewer/obj_viewer_mobile/resources/well.obj create mode 100644 examples/screenshot/screenshot/openglview.py diff --git a/examples/opengl_obj_viewer/README.md b/examples/opengl_obj_viewer/README.md index 88175c865e..c242443438 100644 --- a/examples/opengl_obj_viewer/README.md +++ b/examples/opengl_obj_viewer/README.md @@ -4,13 +4,13 @@ Test app for the [OpenGLView widget](https://toga.beeware.org/en/stable/referenc The following OpenGLView features are present in this example: -- rendering a simple OpenGL view using PyOpenGL +- rendering a simple OpenGL view ## Quickstart To run this example: ```text -$ python -m pip install toga PyOpenGL +$ python -m pip install toga pyopengl pyopengl-accelerate $ python -m obj_viewer ``` diff --git a/examples/opengl_obj_viewer/obj_viewer/obj_file_renderer.py b/examples/opengl_obj_viewer/obj_viewer/obj_file_renderer.py index 50a752e53f..17fea82845 100644 --- a/examples/opengl_obj_viewer/obj_viewer/obj_file_renderer.py +++ b/examples/opengl_obj_viewer/obj_viewer/obj_file_renderer.py @@ -99,7 +99,7 @@ opacity ); - if (shininess > 0) { + if (shininess > 0.0) { vec3 surface_to_view_direction = normalize(surface_to_view); vec3 half_vector = normalize(light_direction + surface_to_view_direction); float specular_light = clamp(dot(normal, half_vector), 0.0, 1.0); @@ -218,6 +218,7 @@ def on_render( self.on_init(widget) self.message = "Rendering...\n\n" + GL.glViewport(0, 0, int(size[0]), int(size[1])) GL.glClearColor(0.0, 0.0, 1.0, 1.0) GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) @@ -227,6 +228,7 @@ def on_render( t = time.time() aspect = size[0] / size[1] + print(size, aspect, self.radius, self.bbox) try: # Set the uniform values into a dictionary to be applied later @@ -242,6 +244,7 @@ def on_render( "light_direction": (3 / 5, 0, 3 / 5), "ambient_light": (0.0, 0.0, 0.0), } + print(uniforms["projection"]) # draw! with self.program.use(): @@ -266,8 +269,10 @@ def on_render( "opacity": (1.0,), } self.program.set_uniforms(material) + print("uniforms set") with vao: GL.glDrawArrays(GL.GL_TRIANGLES, 0, n_vertices) + print("drawing done") # Update frame and time delta information # self.frame += 1 diff --git a/examples/opengl_obj_viewer/obj_viewer/utils_android.py b/examples/opengl_obj_viewer/obj_viewer/utils_android.py index c88ec738af..eea8885138 100644 --- a/examples/opengl_obj_viewer/obj_viewer/utils_android.py +++ b/examples/opengl_obj_viewer/obj_viewer/utils_android.py @@ -1,7 +1,7 @@ """Utility objects and methods for android.opengl""" from android.opengl import GLES32 as GL -from java import jarray, jbyte, jint +from java import jarray, jbyte, jfloat, jint from java.nio import ByteBuffer, ByteOrder #: Shader version header: we want OpenGL ES GLSL 3 @@ -43,6 +43,11 @@ def call_v_function(*args): glGenVertexArrays = v_func()(GL.glGenVertexArrays) +def glUniformMatrix4fv(loc, size, transpose, value): + buffer = jarray(jfloat)(list(value)) + GL.glUniformMatrix4fv(loc, size, transpose, buffer, 0) + + def glVertexAttribPointer(loc, size, type, normalize, stride, offset): offset = 0 if offset is None else offset GL.glVertexAttribPointer(loc, size, type, bool(normalize), stride, offset) diff --git a/examples/opengl_obj_viewer/obj_viewer/utils_iOS.py b/examples/opengl_obj_viewer/obj_viewer/utils_iOS.py index 8b0ed2703a..d717282118 100644 --- a/examples/opengl_obj_viewer/obj_viewer/utils_iOS.py +++ b/examples/opengl_obj_viewer/obj_viewer/utils_iOS.py @@ -1,8 +1,8 @@ """Utility objects and methods for iOS""" -from ctypes import byref, c_char_p, c_int, c_uint, create_string_buffer +from ctypes import byref, c_char_p, c_float, c_int, c_uint, create_string_buffer -from toga_iOS.libs import opengles as GL +from toga_iOS.libs.opengles import opengles as GL #: Shader version header: we want OpenGL ES GLSL 3 VERSION_HEADER = """#version 300 es""" @@ -19,7 +19,7 @@ def v_func(v_size=1, ctype=c_int): an offset into it) which is used to return values by setting them into the array. - This wrapper wraps these functions to create a java array of the + This wrapper wraps these functions to create a ctypes array of the correct type, call the function, and then unpack the values from the array. """ @@ -75,6 +75,11 @@ def glGetAttribLocation(id, item): return GL.glGetAttribLocation(id, buffer) +def glUniformMatrix4fv(loc, size, transpose, value): + buffer = (c_float * (16 * size))(*value) + GL.glUniformMatrix4fv(loc, size, transpose, buffer) + + def glBufferData(buffer_type, data: bytes, usage): GL.glBufferData(buffer_type, len(data), data, usage) diff --git a/examples/opengl_obj_viewer/obj_viewer/utils_pyglet.py b/examples/opengl_obj_viewer/obj_viewer/utils_pyglet.py index 4783dd4f17..e34fe28dc3 100644 --- a/examples/opengl_obj_viewer/obj_viewer/utils_pyglet.py +++ b/examples/opengl_obj_viewer/obj_viewer/utils_pyglet.py @@ -91,7 +91,6 @@ def glGenVertexArrays(n): def glUniformMatrix4fv(loc, size, transpose, value): - print(len(value)) buffer = (c_float * (16 * size))(*value) GL.glUniformMatrix4fv(loc, size, transpose, buffer) diff --git a/examples/opengl_obj_viewer/obj_viewer_mobile/__init__.py b/examples/opengl_obj_viewer/obj_viewer_mobile/__init__.py new file mode 100644 index 0000000000..86826e638c --- /dev/null +++ b/examples/opengl_obj_viewer/obj_viewer_mobile/__init__.py @@ -0,0 +1,9 @@ +# Examples of valid version strings +# __version__ = '1.2.3.dev1' # Development release 1 +# __version__ = '1.2.3a1' # Alpha Release 1 +# __version__ = '1.2.3b1' # Beta Release 1 +# __version__ = '1.2.3rc1' # RC Release 1 +# __version__ = '1.2.3' # Final Release +# __version__ = '1.2.3.post1' # Post Release 1 + +__version__ = "0.0.1" diff --git a/examples/opengl_obj_viewer/obj_viewer_mobile/__main__.py b/examples/opengl_obj_viewer/obj_viewer_mobile/__main__.py new file mode 100644 index 0000000000..e5e74f02e0 --- /dev/null +++ b/examples/opengl_obj_viewer/obj_viewer_mobile/__main__.py @@ -0,0 +1,4 @@ +from obj_viewer_mobile.app import main + +if __name__ == "__main__": + main().main_loop() diff --git a/examples/opengl_obj_viewer/obj_viewer_mobile/app.py b/examples/opengl_obj_viewer/obj_viewer_mobile/app.py new file mode 100644 index 0000000000..6a6b5c041c --- /dev/null +++ b/examples/opengl_obj_viewer/obj_viewer_mobile/app.py @@ -0,0 +1,48 @@ +import asyncio + +from obj_viewer.obj_file import parse_obj_file +from obj_viewer.obj_file_renderer import ObjFileRenderer + +import toga +import toga.sources + + +class OpenGLApp(toga.App): + def startup(self): + self.main_window = toga.MainWindow() + + data = parse_obj_file(self.paths.app / "resources" / "well.obj") + self.renderer = ObjFileRenderer(data) + + opengl_view = toga.OpenGLView(self.renderer, flex=1) + + async def animate(): + while True: + await asyncio.sleep(0.01) + opengl_view.redraw() + + loop = asyncio.get_running_loop() + self.task = loop.create_task(animate()) + + outer_box = toga.Box(children=[opengl_view]) + + self.main_window.content = outer_box + self.main_window.on_close = self.stop_animation + + # Show the main window + self.main_window.show() + + def stop_animation(self, *args, **kwargs): + self.task.cancel() + return True + + +def main(): + return OpenGLApp( + "Obj File Viewer Example", + "org.beeware.toga.examples.obj-viewer-mobile", + ) + + +if __name__ == "__main__": + main().main_loop() diff --git a/examples/opengl_obj_viewer/obj_viewer_mobile/resources/LICENSE_well.txt b/examples/opengl_obj_viewer/obj_viewer_mobile/resources/LICENSE_well.txt new file mode 100644 index 0000000000..0e259d42c9 --- /dev/null +++ b/examples/opengl_obj_viewer/obj_viewer_mobile/resources/LICENSE_well.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/examples/opengl_obj_viewer/obj_viewer_mobile/resources/__init__.py b/examples/opengl_obj_viewer/obj_viewer_mobile/resources/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/opengl_obj_viewer/obj_viewer_mobile/resources/well.mtl b/examples/opengl_obj_viewer/obj_viewer_mobile/resources/well.mtl new file mode 100644 index 0000000000..e0a0e023c5 --- /dev/null +++ b/examples/opengl_obj_viewer/obj_viewer_mobile/resources/well.mtl @@ -0,0 +1,180 @@ +# Materials file from "Modular Village" +# https://opengameart.org/content/modular-village +# License: CC0 1.0 https://creativecommons.org/publicdomain/zero/1.0/ +# See LICENSE_well.txt + +newmtl Stucco +Ka 0.0000 0.0000 0.0000 +Kd 0.9373 0.8941 0.8196 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Wood_Light +Ka 0.0000 0.0000 0.0000 +Kd 0.7373 0.6314 0.4627 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Wood_Medium +Ka 0.0000 0.0000 0.0000 +Kd 0.6275 0.5255 0.3961 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Wood_Dark +Ka 0.0000 0.0000 0.0000 +Kd 0.5176 0.4196 0.3333 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Wood_Darker +Ka 0.0000 0.0000 0.0000 +Kd 0.3843 0.3137 0.2510 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Red_Roof +Ka 0.0000 0.0000 0.0000 +Kd 0.5765 0.2667 0.1922 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Dark_Red_Roof +Ka 0.0000 0.0000 0.0000 +Kd 0.4235 0.1804 0.1176 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Stone_Brit_Gray_Dark +Ka 0.0000 0.0000 0.0000 +Kd 0.4039 0.3529 0.3137 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Stone_Brit_Gray +Ka 0.0000 0.0000 0.0000 +Kd 0.4863 0.4235 0.3725 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Stone_Brit_Gray_Light +Ka 0.0000 0.0000 0.0000 +Kd 0.5725 0.4902 0.4275 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Stone_Bri_Gray_Lighter +Ka 0.0000 0.0000 0.0000 +Kd 0.6627 0.5961 0.5451 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Burlap +Ka 0.0000 0.0000 0.0000 +Kd 0.7255 0.6431 0.5216 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Dirt +Ka 0.0000 0.0000 0.0000 +Kd 0.7686 0.6863 0.5608 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Grass +Ka 0.0000 0.0000 0.0000 +Kd 0.1333 0.5451 0.1333 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Water +Ka 0.0000 0.0000 0.0000 +Kd 0.2000 0.5882 0.8196 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Window +Ka 0.0000 0.0000 0.0000 +Kd 0.6588 0.7059 0.7765 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Iron_Grey_-_Dark +Ka 0.0000 0.0000 0.0000 +Kd 0.3020 0.2863 0.2745 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Iron_Grey +Ka 0.0000 0.0000 0.0000 +Kd 0.4275 0.4196 0.3882 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Iron_Grey_-_Light +Ka 0.0000 0.0000 0.0000 +Kd 0.6196 0.6039 0.5804 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Straw +Ka 0.0000 0.0000 0.0000 +Kd 0.8824 0.7843 0.5255 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Canvas +Ka 0.0000 0.0000 0.0000 +Kd 0.8039 0.7412 0.5961 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 + +newmtl Candlelight +Ka 0.0000 0.0000 0.0000 +Kd 1.0000 0.8824 0.5255 +Ks 1.0000 1.0000 1.0000 +Tf 0.0000 0.0000 0.0000 +d 1.0000 +Ns 0 diff --git a/examples/opengl_obj_viewer/obj_viewer_mobile/resources/well.obj b/examples/opengl_obj_viewer/obj_viewer_mobile/resources/well.obj new file mode 100644 index 0000000000..9a306b1f19 --- /dev/null +++ b/examples/opengl_obj_viewer/obj_viewer_mobile/resources/well.obj @@ -0,0 +1,7152 @@ +# Well prop from "Modular Village" +# https://opengameart.org/content/modular-village +# License: CC0 1.0 https://creativecommons.org/publicdomain/zero/1.0/ +# See LICENSE_well.txt + +mtllib well.mtl +o object1 +g object1 +v 0.44523810 0.90516376 -0.48759427 +v 0.44523810 0.96240573 -0.54483624 +v 0.52619048 0.96240573 -0.54483624 +v 0.52619048 0.90516376 -0.48759427 +v 0.44523810 1.00476190 -0.40476190 +v 0.44523810 1.08571429 -0.40476190 +v 0.44523810 1.49047619 0.00000000 +v 0.44523810 1.40952381 0.00000000 +v 0.52619048 1.49047619 0.00000000 +v 0.52619048 1.08571429 -0.40476190 +v 0.52619048 1.40952381 0.00000000 +v 0.52619048 1.00476190 -0.40476190 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 0.99999999 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 0.99999999 0.00000000 +vt 0.99999999 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.99999999 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -0.00000000 0.70710678 -0.70710678 +vn -0.00000000 0.70710678 -0.70710678 +vn -0.00000000 0.70710678 -0.70710678 +vn -0.00000000 0.70710678 -0.70710678 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.63942740 0.76885148 +vn 0.00000000 -0.63942740 0.76885148 +vn 0.00000000 -0.63942740 0.76885148 +vn 0.00000000 -0.63942740 0.76885148 +vn 1.00000000 -0.00000000 -0.00000000 +vn 1.00000000 -0.00000000 -0.00000000 +vn -0.00000000 0.75059887 -0.66075815 +vn -0.00000000 0.75059887 -0.66075815 +vn -0.00000000 0.75059887 -0.66075815 +vn -0.00000000 0.75059887 -0.66075815 +s 1 +usemtl Wood_Darker +f 1/1/1 2/2/2 3/3/3 +f 1/1/1 3/3/3 4/4/4 +s 2 +f 1/5/5 5/6/6 2/8/8 +f 6/12/7 5/11/6 8/10/10 +f 6/12/7 8/10/10 7/9/9 +f 5/6/6 6/7/7 2/8/8 +s 3 +f 10/16/14 6/15/13 9/13/11 +f 6/15/13 7/14/12 9/13/11 +s 4 +f 12/20/18 10/19/17 11/17/15 +f 10/19/17 9/18/16 11/17/15 +f 3/33/31 12/35/18 4/36/32 +f 3/33/31 10/34/17 12/35/18 +s 5 +f 8/21/19 11/22/20 7/24/22 +f 11/22/20 9/23/21 7/24/22 +s 6 +f 5/28/26 12/27/25 11/26/24 +f 5/28/26 11/26/24 8/25/23 +s 7 +f 4/29/27 12/30/28 1/32/30 +f 12/30/28 5/31/29 1/32/30 +s 8 +f 2/37/33 6/38/34 10/39/35 +f 2/37/33 10/39/35 3/40/36 +o object10 +g object10 +v 0.48571429 0.94919108 -0.03571429 +v 0.50357143 0.92857143 0.00000000 +v 0.48571429 0.90795178 -0.03571429 +v 0.48571429 0.96981073 0.00000000 +v 0.48571429 0.94919108 0.03571429 +v 0.48571429 0.90795178 0.03571429 +v 0.48571429 0.88733212 0.00000000 +v -0.60357143 0.90795178 0.03571429 +v -0.60357143 0.88733212 0.00000000 +v -0.60357143 0.90795178 -0.03571429 +v -0.62142857 0.92857143 0.00000000 +v -0.60357143 0.94919108 -0.03571429 +v -0.60357143 0.96981073 0.00000000 +v -0.60357143 0.94919108 0.03571429 +vt 1.00000000 1.00000000 +vt 0.00000000 0.50000000 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.57894737 +vt 0.00000000 0.57894737 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.50000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.42105263 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.57894737 +vt 1.00000000 1.00000000 +vt 0.00000000 0.50000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.42105263 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.42105263 +vt 0.00000000 0.00000000 +vt 1.00000000 0.50000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.57894737 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.42105263 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vn 0.89442719 0.00000000 -0.44721360 +vn 0.89442719 0.00000000 -0.44721360 +vn 0.89442719 0.00000000 -0.44721360 +vn 0.89442719 0.38729833 -0.22360680 +vn 0.89442719 0.38729833 -0.22360680 +vn 0.89442719 0.38729833 -0.22360680 +vn 0.89442719 0.38729833 0.22360680 +vn 0.89442719 0.38729833 0.22360680 +vn 0.89442719 0.38729833 0.22360680 +vn 0.89442719 0.00000000 0.44721360 +vn 0.89442719 0.00000000 0.44721360 +vn 0.89442719 0.00000000 0.44721360 +vn 0.89442719 -0.38729833 0.22360680 +vn 0.89442719 -0.38729833 0.22360680 +vn 0.89442719 -0.38729833 0.22360680 +vn 0.00000000 -0.86602540 0.50000000 +vn 0.00000000 -0.86602540 0.50000000 +vn 0.00000000 -0.86602540 0.50000000 +vn 0.00000000 -0.86602540 0.50000000 +vn 0.00000000 -0.86602540 -0.50000000 +vn 0.00000000 -0.86602540 -0.50000000 +vn 0.00000000 -0.86602540 -0.50000000 +vn 0.00000000 -0.86602540 -0.50000000 +vn -0.89442719 -0.38729833 -0.22360680 +vn -0.89442719 -0.38729833 -0.22360680 +vn -0.89442719 -0.38729833 -0.22360680 +vn -0.89442719 0.00000000 -0.44721360 +vn -0.89442719 0.00000000 -0.44721360 +vn -0.89442719 0.00000000 -0.44721360 +vn -0.89442719 0.38729833 -0.22360680 +vn -0.89442719 0.38729833 -0.22360680 +vn -0.89442719 0.38729833 -0.22360680 +vn -0.89442719 0.38729833 0.22360680 +vn -0.89442719 0.38729833 0.22360680 +vn -0.89442719 0.38729833 0.22360680 +vn -0.89442719 -0.00000000 0.44721360 +vn -0.89442719 -0.00000000 0.44721360 +vn -0.89442719 -0.00000000 0.44721360 +vn -0.89442719 -0.38729833 0.22360680 +vn -0.89442719 -0.38729833 0.22360680 +vn -0.89442719 -0.38729833 0.22360680 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.86602540 0.50000000 +vn 0.00000000 0.86602540 0.50000000 +vn 0.00000000 0.86602540 0.50000000 +vn 0.00000000 0.86602540 0.50000000 +vn 0.00000000 0.86602540 -0.50000000 +vn 0.00000000 0.86602540 -0.50000000 +vn 0.00000000 0.86602540 -0.50000000 +vn 0.00000000 0.86602540 -0.50000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.89442719 -0.38729833 -0.22360680 +vn 0.89442719 -0.38729833 -0.22360680 +vn 0.89442719 -0.38729833 -0.22360680 +s 1 +usemtl Wood_Medium +f 13/41/37 14/42/38 15/43/39 +s 2 +f 16/44/40 14/45/41 13/46/42 +s 3 +f 17/47/43 14/48/44 16/49/45 +s 4 +f 18/50/46 14/51/47 17/52/48 +s 5 +f 19/53/49 14/54/50 18/55/51 +s 6 +usemtl Wood_Dark +f 18/56/52 20/57/53 19/59/55 +f 20/57/53 21/58/54 19/59/55 +s 7 +f 19/60/56 21/61/57 15/63/59 +f 21/61/57 22/62/58 15/63/59 +s 8 +usemtl Wood_Medium +f 21/64/60 23/65/61 22/66/62 +s 9 +f 22/67/63 23/68/64 24/69/65 +s 10 +f 24/70/66 23/71/67 25/72/68 +s 11 +f 25/73/69 23/74/70 26/75/71 +s 12 +f 26/76/72 23/77/73 20/78/74 +s 13 +f 20/79/75 23/80/76 21/81/77 +s 14 +usemtl Wood_Dark +f 17/82/78 26/83/79 18/85/81 +f 26/83/79 20/84/80 18/85/81 +s 15 +f 16/86/82 25/87/83 17/89/85 +f 25/87/83 26/88/84 17/89/85 +s 16 +f 13/90/86 24/91/87 16/93/89 +f 24/91/87 25/92/88 16/93/89 +s 17 +f 15/94/90 22/95/91 13/97/93 +f 22/95/91 24/96/92 13/97/93 +s 18 +usemtl Wood_Medium +f 15/98/94 14/99/95 19/100/96 +o object11 +g object11 +v 0.42857143 1.26805573 -0.07142857 +v 0.42857143 -0.00000000 -0.07142857 +v 0.42857143 0.00000000 0.07142857 +v 0.42857143 1.26805573 0.07142857 +v 0.32142857 0.00000000 0.07142857 +v 0.32142857 1.26805573 0.07142857 +v 0.32142857 1.26805573 -0.07142857 +v 0.32142857 -0.00000000 -0.07142857 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn 0.00000000 1.00000000 -0.00000000 +vn 0.00000000 1.00000000 -0.00000000 +vn 0.00000000 1.00000000 -0.00000000 +vn 0.00000000 1.00000000 -0.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +s 1 +usemtl Wood_Darker +f 30/104/100 29/103/99 27/101/97 +f 29/103/99 28/102/98 27/101/97 +s 2 +f 32/108/104 31/107/103 29/106/102 +f 32/108/104 29/106/102 30/105/101 +s 3 +f 33/109/105 34/110/106 32/112/108 +f 34/110/106 31/111/107 32/112/108 +s 4 +f 33/116/112 32/115/111 30/114/110 +f 33/116/112 30/114/110 27/113/109 +s 5 +f 27/117/113 28/118/114 33/120/116 +f 28/118/114 34/119/115 33/120/116 +s 6 +f 28/121/117 29/122/118 34/124/120 +f 29/122/118 31/123/119 34/124/120 +o object12 +g object12 +v 0.16683092 0.33928571 -0.33824536 +v 0.13770616 0.33928571 -0.33245208 +v 0.13770616 0.44642857 -0.33245208 +v 0.16683092 0.44642857 -0.33824536 +v 0.15420401 0.32142857 -0.32561845 +v 0.10982399 0.32142857 -0.21847559 +v 0.09332614 0.33928571 -0.22530922 +v 0.09719708 0.33928571 -0.20584868 +v 0.09719708 0.44642857 -0.20584868 +v 0.09332614 0.44642857 -0.22530922 +v 0.07682829 0.33928571 -0.21428571 +v 0.07682829 0.44642857 -0.21428571 +v 0.07682829 0.32142857 -0.23214286 +v -0.07682829 0.33928571 -0.21428571 +v -0.07682829 0.32142857 -0.23214286 +v -0.12120831 0.32142857 -0.33928571 +v 0.12120831 0.32142857 -0.33928571 +v -0.12120831 0.33928571 -0.35714286 +v 0.12120831 0.33928571 -0.35714286 +v -0.13770616 0.33928571 -0.33245208 +v -0.13770616 0.44642857 -0.33245208 +v -0.12120831 0.44642857 -0.35714286 +v -0.12120831 0.46428571 -0.33928571 +v 0.12120831 0.46428571 -0.33928571 +v 0.12120831 0.44642857 -0.35714286 +v 0.07682829 0.46428571 -0.23214286 +v 0.10982399 0.46428571 -0.21847559 +v 0.15420401 0.46428571 -0.32561845 +v 0.21847559 0.46428571 -0.10982399 +v 0.32561845 0.46428571 -0.15420401 +v 0.33824536 0.44642857 -0.16683092 +v 0.33245208 0.44642857 -0.13770616 +v 0.33245208 0.33928571 -0.13770616 +v 0.33824536 0.33928571 -0.16683092 +v 0.32561845 0.32142857 -0.15420401 +v 0.22530922 0.33928571 -0.09332614 +v 0.21847559 0.32142857 -0.10982399 +v 0.20584868 0.33928571 -0.09719708 +v 0.22530922 0.44642857 -0.09332614 +v 0.20584868 0.44642857 -0.09719708 +v 0.21428571 0.33928571 -0.07682829 +v 0.21428571 0.44642857 -0.07682829 +v 0.23214286 0.32142857 -0.07682829 +v 0.23214286 0.32142857 0.07682829 +v 0.21428571 0.33928571 0.07682829 +v 0.22530922 0.33928571 0.09332614 +v 0.33928571 0.32142857 0.12120831 +v 0.33245208 0.33928571 0.13770616 +v 0.35714286 0.33928571 0.12120831 +v 0.35714286 0.44642857 0.12120831 +v 0.33245208 0.44642857 0.13770616 +v 0.33824536 0.33928571 0.16683092 +v 0.33824536 0.44642857 0.16683092 +v 0.32561845 0.32142857 0.15420401 +v 0.16683092 0.33928571 0.33824536 +v 0.15420401 0.32142857 0.32561845 +v 0.10982399 0.32142857 0.21847559 +v 0.21847559 0.32142857 0.10982399 +v 0.09719708 0.33928571 0.20584868 +v 0.20584868 0.33928571 0.09719708 +v 0.09332614 0.33928571 0.22530922 +v 0.09332614 0.44642857 0.22530922 +v 0.09719708 0.44642857 0.20584868 +v 0.10982399 0.46428571 0.21847559 +v 0.13770616 0.44642857 0.33245208 +v 0.15420401 0.46428571 0.32561845 +v 0.16683092 0.44642857 0.33824536 +v 0.32561845 0.46428571 0.15420401 +v 0.13770616 0.33928571 0.33245208 +v 0.12120831 0.33928571 0.35714286 +v 0.12120831 0.44642857 0.35714286 +v 0.12120831 0.32142857 0.33928571 +v -0.12120831 0.33928571 0.35714286 +v -0.12120831 0.32142857 0.33928571 +v -0.07682829 0.32142857 0.23214286 +v 0.07682829 0.32142857 0.23214286 +v -0.13770616 0.33928571 0.33245208 +v -0.09332614 0.33928571 0.22530922 +v -0.07682829 0.33928571 0.21428571 +v -0.09332614 0.44642857 0.22530922 +v -0.07682829 0.44642857 0.21428571 +v 0.07682829 0.33928571 0.21428571 +v 0.07682829 0.44642857 0.21428571 +v -0.07682829 0.46428571 0.23214286 +v 0.07682829 0.46428571 0.23214286 +v 0.12120831 0.46428571 0.33928571 +v -0.12120831 0.46428571 0.33928571 +v -0.13770616 0.44642857 0.33245208 +v -0.12120831 0.44642857 0.35714286 +v -0.16683092 0.33928571 0.33824536 +v -0.16683092 0.44642857 0.33824536 +v -0.15420401 0.32142857 0.32561845 +v -0.33824536 0.33928571 0.16683092 +v -0.32561845 0.32142857 0.15420401 +v -0.21847559 0.32142857 0.10982399 +v -0.10982399 0.32142857 0.21847559 +v -0.20584868 0.33928571 0.09719708 +v -0.09719708 0.33928571 0.20584868 +v -0.22530922 0.33928571 0.09332614 +v -0.22530922 0.44642857 0.09332614 +v -0.20584868 0.44642857 0.09719708 +v -0.21847559 0.46428571 0.10982399 +v -0.10982399 0.46428571 0.21847559 +v -0.09719708 0.44642857 0.20584868 +v -0.15420401 0.46428571 0.32561845 +v -0.32561845 0.46428571 0.15420401 +v -0.33824536 0.44642857 0.16683092 +v -0.33245208 0.44642857 0.13770616 +v -0.33245208 0.33928571 0.13770616 +v -0.35714286 0.33928571 0.12120831 +v -0.35714286 0.44642857 0.12120831 +v -0.33928571 0.32142857 0.12120831 +v -0.35714286 0.33928571 -0.12120831 +v -0.33928571 0.32142857 -0.12120831 +v -0.23214286 0.32142857 -0.07682829 +v -0.23214286 0.32142857 0.07682829 +v -0.21428571 0.33928571 -0.07682829 +v -0.21428571 0.33928571 0.07682829 +v -0.22530922 0.33928571 -0.09332614 +v -0.22530922 0.44642857 -0.09332614 +v -0.21428571 0.44642857 -0.07682829 +v -0.23214286 0.46428571 -0.07682829 +v -0.23214286 0.46428571 0.07682829 +v -0.21428571 0.44642857 0.07682829 +v -0.33928571 0.46428571 0.12120831 +v -0.33928571 0.46428571 -0.12120831 +v -0.35714286 0.44642857 -0.12120831 +v -0.33245208 0.44642857 -0.13770616 +v -0.33245208 0.33928571 -0.13770616 +v -0.33824536 0.33928571 -0.16683092 +v -0.33824536 0.44642857 -0.16683092 +v -0.32561845 0.32142857 -0.15420401 +v -0.21847559 0.32142857 -0.10982399 +v -0.20584868 0.33928571 -0.09719708 +v -0.10982399 0.32142857 -0.21847559 +v -0.09719708 0.33928571 -0.20584868 +v -0.09332614 0.33928571 -0.22530922 +v -0.09332614 0.44642857 -0.22530922 +v -0.09719708 0.44642857 -0.20584868 +v -0.10982399 0.46428571 -0.21847559 +v -0.21847559 0.46428571 -0.10982399 +v -0.20584868 0.44642857 -0.09719708 +v -0.32561845 0.46428571 -0.15420401 +v -0.15420401 0.46428571 -0.32561845 +v -0.16683092 0.44642857 -0.33824536 +v -0.07682829 0.44642857 -0.21428571 +v -0.07682829 0.46428571 -0.23214286 +v -0.15420401 0.32142857 -0.32561845 +v -0.16683092 0.33928571 -0.33824536 +v 0.21847559 0.46428571 0.10982399 +v 0.22530922 0.44642857 0.09332614 +v 0.20584868 0.44642857 0.09719708 +v 0.33928571 0.46428571 0.12120831 +v 0.23214286 0.46428571 0.07682829 +v 0.21428571 0.44642857 0.07682829 +v 0.23214286 0.46428571 -0.07682829 +v 0.33928571 0.46428571 -0.12120831 +v 0.35714286 0.44642857 -0.12120831 +v 0.35714286 0.33928571 -0.12120831 +v 0.33928571 0.32142857 -0.12120831 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.54119610 1.00000000 +vt 0.00000000 0.21824431 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.89694467 +vt 0.64884670 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.58030514 +vt 0.61731657 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.27676865 0.00000000 +vt 1.00000000 0.61830814 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.38169186 +vt 0.27676865 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.61830814 +vt 0.00000000 1.00000000 +vt 0.27676865 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.56645450 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.86878592 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.56645450 1.00000000 +vt 0.00000000 0.13121408 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.23463314 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.13585211 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.86414789 +vt 0.00000000 1.00000000 +vt 0.23463314 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.69134172 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.69134172 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.69134172 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.13121408 +vt 0.43354550 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.89694467 +vt 0.00000000 1.00000000 +vt 0.35115330 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.10305533 +vt 0.35115330 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.78175569 +vt 0.00000000 1.00000000 +vt 0.45880390 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.38169186 +vt 1.00000000 0.00000000 +vt 0.72323135 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.41969486 +vt 0.38268343 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.41969486 +vt 0.38268343 1.00000000 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.72323135 1.00000000 +vt 0.00000000 0.38169186 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.45880390 0.00000000 +vt 1.00000000 0.78175569 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.76536686 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.86414789 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.76536686 1.00000000 +vt 0.00000000 0.13585211 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.35115330 0.00000000 +vt 1.00000000 0.89694467 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.13121408 +vt 0.43354550 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.30865828 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.30865828 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.69134172 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.69134172 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.69134172 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.56645450 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.86878592 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.23463314 0.00000000 +vt 1.00000000 0.86414789 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.10305533 +vt 1.00000000 0.00000000 +vt 0.64884670 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.89694467 +vt 0.64884670 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.13585211 +vt 0.23463314 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.54119610 1.00000000 +vt 0.00000000 0.21824431 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.61731657 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.58030514 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.54119610 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.78175569 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.13121408 +vt 1.00000000 0.00000000 +vt 0.56645450 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.30865828 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.30865828 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.69134172 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.10305533 +vt 0.35115330 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.43354550 0.00000000 +vt 1.00000000 0.86878592 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.21824431 +vt 0.45880390 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.38268343 0.00000000 +vt 1.00000000 0.58030514 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.38268343 0.00000000 +vt 1.00000000 0.58030514 +vt 0.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.61830814 +vt 0.72323135 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.61830814 +vt 0.72323135 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.86414789 +vt 0.76536686 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.30865828 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.30865828 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 0.76536686 1.00000000 +vt 0.00000000 0.13585211 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.21824431 +vt 0.45880390 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.43354550 0.00000000 +vt 1.00000000 0.86878592 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.30865828 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.69134172 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.30865828 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.27676865 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.38169186 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.61731657 1.00000000 +vt 0.00000000 0.41969486 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.41969486 +vt 1.00000000 0.00000000 +vt 0.61731657 1.00000000 +vt 1.00000000 0.00000000 +vt 0.64884670 1.00000000 +vt 0.00000000 0.10305533 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.18307335 +vt 1.00000000 0.81692665 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.78175569 +vt 0.54119610 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vn -0.19509032 0.00000000 -0.98078528 +vn -0.19509032 0.00000000 -0.98078528 +vn -0.19509032 0.00000000 -0.98078528 +vn -0.19509032 0.00000000 -0.98078528 +vn -0.17053856 -0.48565274 -0.85735525 +vn -0.17053856 -0.48565274 -0.85735525 +vn -0.17053856 -0.48565274 -0.85735525 +vn -0.65328148 -0.70710678 -0.27059805 +vn -0.65328148 -0.70710678 -0.27059805 +vn -0.65328148 -0.70710678 -0.27059805 +vn -0.65328148 -0.70710678 -0.27059805 +vn -0.75415091 -0.63933827 0.15000994 +vn -0.75415091 -0.63933827 0.15000994 +vn -0.75415091 -0.63933827 0.15000994 +vn -0.98078528 0.00000000 0.19509032 +vn -0.98078528 0.00000000 0.19509032 +vn -0.98078528 0.00000000 0.19509032 +vn -0.98078528 0.00000000 0.19509032 +vn 0.55557023 0.00000000 0.83146961 +vn 0.55557023 0.00000000 0.83146961 +vn 0.55557023 0.00000000 0.83146961 +vn 0.55557023 0.00000000 0.83146961 +vn 0.42719217 -0.63933827 0.63933827 +vn 0.42719217 -0.63933827 0.63933827 +vn 0.42719217 -0.63933827 0.63933827 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn -0.72683068 -0.48565274 -0.48565274 +vn -0.72683068 -0.48565274 -0.48565274 +vn -0.72683068 -0.48565274 -0.48565274 +vn -0.83146961 0.00000000 -0.55557023 +vn -0.83146961 0.00000000 -0.55557023 +vn -0.83146961 0.00000000 -0.55557023 +vn -0.83146961 0.00000000 -0.55557023 +vn -0.72683068 0.48565274 -0.48565274 +vn -0.72683068 0.48565274 -0.48565274 +vn -0.72683068 0.48565274 -0.48565274 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.72683068 0.48565274 -0.48565274 +vn 0.72683068 0.48565274 -0.48565274 +vn 0.72683068 0.48565274 -0.48565274 +vn 0.65328148 0.70710678 0.27059805 +vn 0.65328148 0.70710678 0.27059805 +vn 0.65328148 0.70710678 0.27059805 +vn 0.65328148 0.70710678 0.27059805 +vn -0.65328148 0.70710678 -0.27059805 +vn -0.65328148 0.70710678 -0.27059805 +vn -0.65328148 0.70710678 -0.27059805 +vn -0.65328148 0.70710678 -0.27059805 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.50000000 0.70710678 -0.50000000 +vn 0.50000000 0.70710678 -0.50000000 +vn 0.50000000 0.70710678 -0.50000000 +vn 0.50000000 0.70710678 -0.50000000 +vn 0.85735525 0.48565274 0.17053856 +vn 0.85735525 0.48565274 0.17053856 +vn 0.85735525 0.48565274 0.17053856 +vn 0.98078528 0.00000000 0.19509032 +vn 0.98078528 0.00000000 0.19509032 +vn 0.98078528 0.00000000 0.19509032 +vn 0.98078528 0.00000000 0.19509032 +vn 0.85735525 -0.48565274 0.17053856 +vn 0.85735525 -0.48565274 0.17053856 +vn 0.85735525 -0.48565274 0.17053856 +vn 0.27059805 -0.70710678 0.65328148 +vn 0.27059805 -0.70710678 0.65328148 +vn 0.27059805 -0.70710678 0.65328148 +vn 0.27059805 -0.70710678 0.65328148 +vn -0.15000994 -0.63933827 0.75415091 +vn -0.15000994 -0.63933827 0.75415091 +vn -0.15000994 -0.63933827 0.75415091 +vn -0.19509032 0.00000000 0.98078528 +vn -0.19509032 0.00000000 0.98078528 +vn -0.19509032 0.00000000 0.98078528 +vn -0.19509032 0.00000000 0.98078528 +vn -0.15000994 0.63933827 0.75415091 +vn -0.15000994 0.63933827 0.75415091 +vn -0.15000994 0.63933827 0.75415091 +vn -0.83146961 0.00000000 -0.55557023 +vn -0.83146961 0.00000000 -0.55557023 +vn -0.83146961 0.00000000 -0.55557023 +vn -0.83146961 0.00000000 -0.55557023 +vn -0.63933827 -0.63933827 -0.42719217 +vn -0.63933827 -0.63933827 -0.42719217 +vn -0.63933827 -0.63933827 -0.42719217 +vn -0.70710678 -0.70710678 0.00000000 +vn -0.70710678 -0.70710678 0.00000000 +vn -0.70710678 -0.70710678 0.00000000 +vn -0.70710678 -0.70710678 0.00000000 +vn -0.63933827 -0.63933827 0.42719217 +vn -0.63933827 -0.63933827 0.42719217 +vn -0.63933827 -0.63933827 0.42719217 +vn -0.27059805 -0.70710678 0.65328148 +vn -0.27059805 -0.70710678 0.65328148 +vn -0.27059805 -0.70710678 0.65328148 +vn -0.27059805 -0.70710678 0.65328148 +vn 0.48565274 -0.48565274 0.72683068 +vn 0.48565274 -0.48565274 0.72683068 +vn 0.48565274 -0.48565274 0.72683068 +vn 0.55557023 0.00000000 0.83146961 +vn 0.55557023 0.00000000 0.83146961 +vn 0.55557023 0.00000000 0.83146961 +vn 0.55557023 0.00000000 0.83146961 +vn 0.98078528 0.00000000 -0.19509032 +vn 0.98078528 0.00000000 -0.19509032 +vn 0.98078528 0.00000000 -0.19509032 +vn 0.98078528 0.00000000 -0.19509032 +vn 0.85735525 -0.48565274 -0.17053856 +vn 0.85735525 -0.48565274 -0.17053856 +vn 0.85735525 -0.48565274 -0.17053856 +vn 0.50000000 -0.70710678 0.50000000 +vn 0.50000000 -0.70710678 0.50000000 +vn 0.50000000 -0.70710678 0.50000000 +vn 0.50000000 -0.70710678 0.50000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.50000000 -0.70710678 -0.50000000 +vn -0.50000000 -0.70710678 -0.50000000 +vn -0.50000000 -0.70710678 -0.50000000 +vn -0.50000000 -0.70710678 -0.50000000 +vn -0.75415091 -0.63933827 -0.15000994 +vn -0.75415091 -0.63933827 -0.15000994 +vn -0.75415091 -0.63933827 -0.15000994 +vn -0.98078528 0.00000000 -0.19509032 +vn -0.98078528 0.00000000 -0.19509032 +vn -0.98078528 0.00000000 -0.19509032 +vn -0.98078528 0.00000000 -0.19509032 +vn -0.75415091 0.63933827 -0.15000994 +vn -0.75415091 0.63933827 -0.15000994 +vn -0.75415091 0.63933827 -0.15000994 +vn -0.65328148 0.70710678 0.27059805 +vn -0.65328148 0.70710678 0.27059805 +vn -0.65328148 0.70710678 0.27059805 +vn -0.65328148 0.70710678 0.27059805 +vn -0.17053856 0.48565274 0.85735525 +vn -0.17053856 0.48565274 0.85735525 +vn -0.17053856 0.48565274 0.85735525 +vn 0.50000000 0.70710678 0.50000000 +vn 0.50000000 0.70710678 0.50000000 +vn 0.50000000 0.70710678 0.50000000 +vn 0.50000000 0.70710678 0.50000000 +vn -0.19509032 0.00000000 0.98078528 +vn -0.19509032 0.00000000 0.98078528 +vn -0.19509032 0.00000000 0.98078528 +vn -0.19509032 0.00000000 0.98078528 +vn 0.83146961 -0.00000000 0.55557023 +vn 0.83146961 -0.00000000 0.55557023 +vn 0.83146961 -0.00000000 0.55557023 +vn 0.83146961 -0.00000000 0.55557023 +vn 0.72683068 -0.48565274 0.48565274 +vn 0.72683068 -0.48565274 0.48565274 +vn 0.72683068 -0.48565274 0.48565274 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.65328148 -0.70710678 -0.27059805 +vn -0.65328148 -0.70710678 -0.27059805 +vn -0.65328148 -0.70710678 -0.27059805 +vn -0.65328148 -0.70710678 -0.27059805 +vn -0.42719217 -0.63933827 -0.63933827 +vn -0.42719217 -0.63933827 -0.63933827 +vn -0.42719217 -0.63933827 -0.63933827 +vn -0.55557023 0.00000000 -0.83146961 +vn -0.55557023 0.00000000 -0.83146961 +vn -0.55557023 0.00000000 -0.83146961 +vn -0.55557023 0.00000000 -0.83146961 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.42719217 0.63933827 -0.63933827 +vn 0.42719217 0.63933827 -0.63933827 +vn 0.42719217 0.63933827 -0.63933827 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn -0.65328148 0.70710678 -0.27059805 +vn -0.65328148 0.70710678 -0.27059805 +vn -0.65328148 0.70710678 -0.27059805 +vn -0.65328148 0.70710678 -0.27059805 +vn -0.72683068 0.48565274 0.48565274 +vn -0.72683068 0.48565274 0.48565274 +vn -0.72683068 0.48565274 0.48565274 +vn -0.83146961 -0.00000000 0.55557023 +vn -0.83146961 -0.00000000 0.55557023 +vn -0.83146961 -0.00000000 0.55557023 +vn -0.83146961 -0.00000000 0.55557023 +vn 0.19509032 0.00000000 0.98078528 +vn 0.19509032 0.00000000 0.98078528 +vn 0.19509032 0.00000000 0.98078528 +vn 0.19509032 0.00000000 0.98078528 +vn 0.17053856 -0.48565274 0.85735525 +vn 0.17053856 -0.48565274 0.85735525 +vn 0.17053856 -0.48565274 0.85735525 +vn -0.50000000 -0.70710678 0.50000000 +vn -0.50000000 -0.70710678 0.50000000 +vn -0.50000000 -0.70710678 0.50000000 +vn -0.50000000 -0.70710678 0.50000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.50000000 -0.70710678 -0.50000000 +vn 0.50000000 -0.70710678 -0.50000000 +vn 0.50000000 -0.70710678 -0.50000000 +vn 0.50000000 -0.70710678 -0.50000000 +vn 0.15000994 -0.63933827 -0.75415091 +vn 0.15000994 -0.63933827 -0.75415091 +vn 0.15000994 -0.63933827 -0.75415091 +vn 0.19509032 0.00000000 -0.98078528 +vn 0.19509032 0.00000000 -0.98078528 +vn 0.19509032 0.00000000 -0.98078528 +vn 0.19509032 0.00000000 -0.98078528 +vn 0.15000994 0.63933827 -0.75415091 +vn 0.15000994 0.63933827 -0.75415091 +vn 0.15000994 0.63933827 -0.75415091 +vn 0.50000000 0.70710678 -0.50000000 +vn 0.50000000 0.70710678 -0.50000000 +vn 0.50000000 0.70710678 -0.50000000 +vn 0.50000000 0.70710678 -0.50000000 +vn 0.75415091 0.63933827 -0.15000994 +vn 0.75415091 0.63933827 -0.15000994 +vn 0.75415091 0.63933827 -0.15000994 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn -0.50000000 0.70710678 0.50000000 +vn -0.50000000 0.70710678 0.50000000 +vn -0.50000000 0.70710678 0.50000000 +vn -0.50000000 0.70710678 0.50000000 +vn -0.85735525 0.48565274 -0.17053856 +vn -0.85735525 0.48565274 -0.17053856 +vn -0.85735525 0.48565274 -0.17053856 +vn -0.98078528 -0.00000000 -0.19509032 +vn -0.98078528 -0.00000000 -0.19509032 +vn -0.98078528 -0.00000000 -0.19509032 +vn -0.98078528 -0.00000000 -0.19509032 +vn -0.55557023 0.00000000 0.83146961 +vn -0.55557023 0.00000000 0.83146961 +vn -0.55557023 0.00000000 0.83146961 +vn -0.55557023 0.00000000 0.83146961 +vn -0.48565274 -0.48565274 0.72683068 +vn -0.48565274 -0.48565274 0.72683068 +vn -0.48565274 -0.48565274 0.72683068 +vn -0.70710678 -0.70710678 0.00000000 +vn -0.70710678 -0.70710678 0.00000000 +vn -0.70710678 -0.70710678 0.00000000 +vn -0.70710678 -0.70710678 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.70710678 -0.70710678 0.00000000 +vn 0.70710678 -0.70710678 0.00000000 +vn 0.70710678 -0.70710678 0.00000000 +vn 0.70710678 -0.70710678 0.00000000 +vn 0.63933827 -0.63933827 -0.42719217 +vn 0.63933827 -0.63933827 -0.42719217 +vn 0.63933827 -0.63933827 -0.42719217 +vn 0.83146961 -0.00000000 -0.55557023 +vn 0.83146961 -0.00000000 -0.55557023 +vn 0.83146961 -0.00000000 -0.55557023 +vn 0.83146961 -0.00000000 -0.55557023 +vn 0.63933827 0.63933827 -0.42719217 +vn 0.63933827 0.63933827 -0.42719217 +vn 0.63933827 0.63933827 -0.42719217 +vn 0.70710678 0.70710678 0.00000000 +vn 0.70710678 0.70710678 0.00000000 +vn 0.70710678 0.70710678 0.00000000 +vn 0.70710678 0.70710678 0.00000000 +vn 0.63933827 0.63933827 0.42719217 +vn 0.63933827 0.63933827 0.42719217 +vn 0.63933827 0.63933827 0.42719217 +vn 0.27059805 0.70710678 0.65328148 +vn 0.27059805 0.70710678 0.65328148 +vn 0.27059805 0.70710678 0.65328148 +vn 0.27059805 0.70710678 0.65328148 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn -0.70710678 0.70710678 0.00000000 +vn -0.70710678 0.70710678 0.00000000 +vn -0.70710678 0.70710678 0.00000000 +vn -0.70710678 0.70710678 0.00000000 +vn -0.48565274 0.48565274 -0.72683068 +vn -0.48565274 0.48565274 -0.72683068 +vn -0.48565274 0.48565274 -0.72683068 +vn -0.55557023 0.00000000 -0.83146961 +vn -0.55557023 0.00000000 -0.83146961 +vn -0.55557023 0.00000000 -0.83146961 +vn -0.55557023 0.00000000 -0.83146961 +vn -0.98078528 0.00000000 0.19509032 +vn -0.98078528 0.00000000 0.19509032 +vn -0.98078528 0.00000000 0.19509032 +vn -0.98078528 0.00000000 0.19509032 +vn -0.85735525 -0.48565274 0.17053856 +vn -0.85735525 -0.48565274 0.17053856 +vn -0.85735525 -0.48565274 0.17053856 +vn -0.27059805 -0.70710678 0.65328148 +vn -0.27059805 -0.70710678 0.65328148 +vn -0.27059805 -0.70710678 0.65328148 +vn -0.27059805 -0.70710678 0.65328148 +vn 0.15000994 -0.63933827 0.75415091 +vn 0.15000994 -0.63933827 0.75415091 +vn 0.15000994 -0.63933827 0.75415091 +vn 0.50000000 -0.70710678 0.50000000 +vn 0.50000000 -0.70710678 0.50000000 +vn 0.50000000 -0.70710678 0.50000000 +vn 0.50000000 -0.70710678 0.50000000 +vn 0.75415091 -0.63933827 0.15000994 +vn 0.75415091 -0.63933827 0.15000994 +vn 0.75415091 -0.63933827 0.15000994 +vn 0.98078528 0.00000000 0.19509032 +vn 0.98078528 0.00000000 0.19509032 +vn 0.98078528 0.00000000 0.19509032 +vn 0.98078528 0.00000000 0.19509032 +vn 0.75415091 0.63933827 0.15000994 +vn 0.75415091 0.63933827 0.15000994 +vn 0.75415091 0.63933827 0.15000994 +vn 0.50000000 0.70710678 0.50000000 +vn 0.50000000 0.70710678 0.50000000 +vn 0.50000000 0.70710678 0.50000000 +vn 0.50000000 0.70710678 0.50000000 +vn 0.15000994 0.63933827 0.75415091 +vn 0.15000994 0.63933827 0.75415091 +vn 0.15000994 0.63933827 0.75415091 +vn -0.27059805 0.70710678 0.65328148 +vn -0.27059805 0.70710678 0.65328148 +vn -0.27059805 0.70710678 0.65328148 +vn -0.27059805 0.70710678 0.65328148 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn -0.50000000 0.70710678 -0.50000000 +vn -0.50000000 0.70710678 -0.50000000 +vn -0.50000000 0.70710678 -0.50000000 +vn -0.50000000 0.70710678 -0.50000000 +vn 0.17053856 0.48565274 -0.85735525 +vn 0.17053856 0.48565274 -0.85735525 +vn 0.17053856 0.48565274 -0.85735525 +vn 0.65328148 0.70710678 -0.27059805 +vn 0.65328148 0.70710678 -0.27059805 +vn 0.65328148 0.70710678 -0.27059805 +vn 0.65328148 0.70710678 -0.27059805 +vn -0.55557023 0.00000000 0.83146961 +vn -0.55557023 0.00000000 0.83146961 +vn -0.55557023 0.00000000 0.83146961 +vn -0.55557023 0.00000000 0.83146961 +vn -0.42719217 0.63933827 0.63933827 +vn -0.42719217 0.63933827 0.63933827 +vn -0.42719217 0.63933827 0.63933827 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.65328148 -0.70710678 -0.27059805 +vn 0.65328148 -0.70710678 -0.27059805 +vn 0.65328148 -0.70710678 -0.27059805 +vn 0.65328148 -0.70710678 -0.27059805 +vn 0.17053856 -0.48565274 -0.85735525 +vn 0.17053856 -0.48565274 -0.85735525 +vn 0.17053856 -0.48565274 -0.85735525 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.50000000 -0.70710678 -0.50000000 +vn -0.50000000 -0.70710678 -0.50000000 +vn -0.50000000 -0.70710678 -0.50000000 +vn -0.50000000 -0.70710678 -0.50000000 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.85735525 0.48565274 0.17053856 +vn -0.85735525 0.48565274 0.17053856 +vn -0.85735525 0.48565274 0.17053856 +vn 0.27059805 0.70710678 -0.65328148 +vn 0.27059805 0.70710678 -0.65328148 +vn 0.27059805 0.70710678 -0.65328148 +vn 0.27059805 0.70710678 -0.65328148 +vn 0.19509032 0.00000000 0.98078528 +vn 0.19509032 0.00000000 0.98078528 +vn 0.19509032 0.00000000 0.98078528 +vn 0.19509032 0.00000000 0.98078528 +vn 0.63933827 -0.63933827 0.42719217 +vn 0.63933827 -0.63933827 0.42719217 +vn 0.63933827 -0.63933827 0.42719217 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.27059805 -0.70710678 -0.65328148 +vn 0.27059805 -0.70710678 -0.65328148 +vn 0.27059805 -0.70710678 -0.65328148 +vn 0.27059805 -0.70710678 -0.65328148 +vn -0.48565274 -0.48565274 -0.72683068 +vn -0.48565274 -0.48565274 -0.72683068 +vn -0.48565274 -0.48565274 -0.72683068 +vn 0.27059805 -0.70710678 0.65328148 +vn 0.27059805 -0.70710678 0.65328148 +vn 0.27059805 -0.70710678 0.65328148 +vn 0.27059805 -0.70710678 0.65328148 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -0.48565274 0.48565274 0.72683068 +vn -0.48565274 0.48565274 0.72683068 +vn -0.48565274 0.48565274 0.72683068 +vn -0.27059805 0.70710678 -0.65328148 +vn -0.27059805 0.70710678 -0.65328148 +vn -0.27059805 0.70710678 -0.65328148 +vn -0.27059805 0.70710678 -0.65328148 +vn 0.83146961 0.00000000 0.55557023 +vn 0.83146961 0.00000000 0.55557023 +vn 0.83146961 0.00000000 0.55557023 +vn 0.83146961 0.00000000 0.55557023 +vn 0.75415091 -0.63933827 -0.15000994 +vn 0.75415091 -0.63933827 -0.15000994 +vn 0.75415091 -0.63933827 -0.15000994 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn -0.27059805 -0.70710678 -0.65328148 +vn -0.27059805 -0.70710678 -0.65328148 +vn -0.27059805 -0.70710678 -0.65328148 +vn -0.27059805 -0.70710678 -0.65328148 +vn -0.85735525 -0.48565274 -0.17053856 +vn -0.85735525 -0.48565274 -0.17053856 +vn -0.85735525 -0.48565274 -0.17053856 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn 0.17053856 0.48565274 0.85735525 +vn 0.17053856 0.48565274 0.85735525 +vn 0.17053856 0.48565274 0.85735525 +vn 0.65328148 0.70710678 0.27059805 +vn 0.65328148 0.70710678 0.27059805 +vn 0.65328148 0.70710678 0.27059805 +vn 0.65328148 0.70710678 0.27059805 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn -0.42719217 0.63933827 -0.63933827 +vn -0.42719217 0.63933827 -0.63933827 +vn -0.42719217 0.63933827 -0.63933827 +vn 0.98078528 -0.00000000 -0.19509032 +vn 0.98078528 -0.00000000 -0.19509032 +vn 0.98078528 -0.00000000 -0.19509032 +vn 0.98078528 -0.00000000 -0.19509032 +vn 0.65328148 -0.70710678 0.27059805 +vn 0.65328148 -0.70710678 0.27059805 +vn 0.65328148 -0.70710678 0.27059805 +vn 0.65328148 -0.70710678 0.27059805 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.42719217 -0.63933827 -0.63933827 +vn 0.42719217 -0.63933827 -0.63933827 +vn 0.42719217 -0.63933827 -0.63933827 +vn -0.72683068 -0.48565274 0.48565274 +vn -0.72683068 -0.48565274 0.48565274 +vn -0.72683068 -0.48565274 0.48565274 +vn 0.65328148 -0.70710678 -0.27059805 +vn 0.65328148 -0.70710678 -0.27059805 +vn 0.65328148 -0.70710678 -0.27059805 +vn 0.65328148 -0.70710678 -0.27059805 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.72683068 0.48565274 0.48565274 +vn 0.72683068 0.48565274 0.48565274 +vn 0.72683068 0.48565274 0.48565274 +vn 0.65328148 0.70710678 -0.27059805 +vn 0.65328148 0.70710678 -0.27059805 +vn 0.65328148 0.70710678 -0.27059805 +vn 0.65328148 0.70710678 -0.27059805 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.27059805 0.70710678 -0.65328148 +vn 0.27059805 0.70710678 -0.65328148 +vn 0.27059805 0.70710678 -0.65328148 +vn 0.27059805 0.70710678 -0.65328148 +vn -0.15000994 0.63933827 -0.75415091 +vn -0.15000994 0.63933827 -0.75415091 +vn -0.15000994 0.63933827 -0.75415091 +vn -0.19509032 0.00000000 -0.98078528 +vn -0.19509032 0.00000000 -0.98078528 +vn -0.19509032 0.00000000 -0.98078528 +vn -0.19509032 0.00000000 -0.98078528 +vn -0.27059805 0.70710678 0.65328148 +vn -0.27059805 0.70710678 0.65328148 +vn -0.27059805 0.70710678 0.65328148 +vn -0.27059805 0.70710678 0.65328148 +vn -0.63933827 0.63933827 0.42719217 +vn -0.63933827 0.63933827 0.42719217 +vn -0.63933827 0.63933827 0.42719217 +vn -0.70710678 0.70710678 -0.00000000 +vn -0.70710678 0.70710678 -0.00000000 +vn -0.70710678 0.70710678 -0.00000000 +vn -0.70710678 0.70710678 -0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn -0.27059805 0.70710678 -0.65328148 +vn -0.27059805 0.70710678 -0.65328148 +vn -0.27059805 0.70710678 -0.65328148 +vn -0.27059805 0.70710678 -0.65328148 +vn 0.48565274 0.48565274 -0.72683068 +vn 0.48565274 0.48565274 -0.72683068 +vn 0.48565274 0.48565274 -0.72683068 +vn 0.70710678 0.70710678 -0.00000000 +vn 0.70710678 0.70710678 -0.00000000 +vn 0.70710678 0.70710678 -0.00000000 +vn 0.70710678 0.70710678 -0.00000000 +vn -0.50000000 0.70710678 -0.50000000 +vn -0.50000000 0.70710678 -0.50000000 +vn -0.50000000 0.70710678 -0.50000000 +vn -0.50000000 0.70710678 -0.50000000 +vn 0.55557023 0.00000000 -0.83146961 +vn 0.55557023 0.00000000 -0.83146961 +vn 0.55557023 0.00000000 -0.83146961 +vn 0.55557023 0.00000000 -0.83146961 +vn -0.15000994 -0.63933827 -0.75415091 +vn -0.15000994 -0.63933827 -0.75415091 +vn -0.15000994 -0.63933827 -0.75415091 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.65328148 -0.70710678 0.27059805 +vn -0.65328148 -0.70710678 0.27059805 +vn -0.65328148 -0.70710678 0.27059805 +vn -0.65328148 -0.70710678 0.27059805 +vn -0.17053856 -0.48565274 0.85735525 +vn -0.17053856 -0.48565274 0.85735525 +vn -0.17053856 -0.48565274 0.85735525 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.85735525 0.48565274 -0.17053856 +vn 0.85735525 0.48565274 -0.17053856 +vn 0.85735525 0.48565274 -0.17053856 +vn 0.48565274 0.48565274 0.72683068 +vn 0.48565274 0.48565274 0.72683068 +vn 0.48565274 0.48565274 0.72683068 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.70710678 -0.70710678 -0.00000000 +vn 0.70710678 -0.70710678 -0.00000000 +vn 0.70710678 -0.70710678 -0.00000000 +vn 0.70710678 -0.70710678 -0.00000000 +vn 0.48565274 -0.48565274 -0.72683068 +vn 0.48565274 -0.48565274 -0.72683068 +vn 0.48565274 -0.48565274 -0.72683068 +vn 0.27059805 -0.70710678 -0.65328148 +vn 0.27059805 -0.70710678 -0.65328148 +vn 0.27059805 -0.70710678 -0.65328148 +vn 0.27059805 -0.70710678 -0.65328148 +vn -0.83146961 0.00000000 0.55557023 +vn -0.83146961 0.00000000 0.55557023 +vn -0.83146961 0.00000000 0.55557023 +vn -0.83146961 0.00000000 0.55557023 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.63933827 0.63933827 -0.42719217 +vn -0.63933827 0.63933827 -0.42719217 +vn -0.63933827 0.63933827 -0.42719217 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -0.27059805 -0.70710678 -0.65328148 +vn -0.27059805 -0.70710678 -0.65328148 +vn -0.27059805 -0.70710678 -0.65328148 +vn -0.27059805 -0.70710678 -0.65328148 +vn 0.55557023 0.00000000 -0.83146961 +vn 0.55557023 0.00000000 -0.83146961 +vn 0.55557023 0.00000000 -0.83146961 +vn 0.55557023 0.00000000 -0.83146961 +vn 0.27059805 0.70710678 0.65328148 +vn 0.27059805 0.70710678 0.65328148 +vn 0.27059805 0.70710678 0.65328148 +vn 0.27059805 0.70710678 0.65328148 +vn -0.50000000 0.70710678 0.50000000 +vn -0.50000000 0.70710678 0.50000000 +vn -0.50000000 0.70710678 0.50000000 +vn -0.50000000 0.70710678 0.50000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn -0.65328148 0.70710678 0.27059805 +vn -0.65328148 0.70710678 0.27059805 +vn -0.65328148 0.70710678 0.27059805 +vn -0.65328148 0.70710678 0.27059805 +vn 0.19509032 0.00000000 -0.98078528 +vn 0.19509032 0.00000000 -0.98078528 +vn 0.19509032 0.00000000 -0.98078528 +vn 0.19509032 0.00000000 -0.98078528 +vn 0.72683068 -0.48565274 -0.48565274 +vn 0.72683068 -0.48565274 -0.48565274 +vn 0.72683068 -0.48565274 -0.48565274 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn -0.65328148 -0.70710678 0.27059805 +vn -0.65328148 -0.70710678 0.27059805 +vn -0.65328148 -0.70710678 0.27059805 +vn -0.65328148 -0.70710678 0.27059805 +vn -0.42719217 -0.63933827 0.63933827 +vn -0.42719217 -0.63933827 0.63933827 +vn -0.42719217 -0.63933827 0.63933827 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.42719217 0.63933827 0.63933827 +vn 0.42719217 0.63933827 0.63933827 +vn 0.42719217 0.63933827 0.63933827 +vn -0.75415091 0.63933827 0.15000994 +vn -0.75415091 0.63933827 0.15000994 +vn -0.75415091 0.63933827 0.15000994 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.50000000 -0.70710678 0.50000000 +vn -0.50000000 -0.70710678 0.50000000 +vn -0.50000000 -0.70710678 0.50000000 +vn -0.50000000 -0.70710678 0.50000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.65328148 -0.70710678 0.27059805 +vn 0.65328148 -0.70710678 0.27059805 +vn 0.65328148 -0.70710678 0.27059805 +vn 0.65328148 -0.70710678 0.27059805 +vn 0.50000000 -0.70710678 -0.50000000 +vn 0.50000000 -0.70710678 -0.50000000 +vn 0.50000000 -0.70710678 -0.50000000 +vn 0.50000000 -0.70710678 -0.50000000 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn -0.17053856 0.48565274 -0.85735525 +vn -0.17053856 0.48565274 -0.85735525 +vn -0.17053856 0.48565274 -0.85735525 +vn 0.83146961 0.00000000 -0.55557023 +vn 0.83146961 0.00000000 -0.55557023 +vn 0.83146961 0.00000000 -0.55557023 +vn 0.83146961 0.00000000 -0.55557023 +s 1 +usemtl Stone_Brit_Gray_Light +f 35/125/121 36/126/122 38/128/124 +f 36/126/122 37/127/123 38/128/124 +s 2 +f 35/131/127 39/130/126 36/129/125 +s 3 +f 40/132/128 41/133/129 39/135/131 +f 41/133/129 36/134/130 39/135/131 +s 4 +f 41/138/134 40/137/133 42/136/132 +s 5 +f 41/139/135 42/140/136 43/141/137 +f 41/139/135 43/141/137 44/142/138 +s 6 +usemtl Stone_Bri_Gray_Lighter +f 45/143/139 41/144/140 46/146/142 +f 41/144/140 44/145/141 46/146/142 +s 7 +f 47/149/145 41/148/144 45/147/143 +s 8 +f 47/150/146 45/151/147 49/153/149 +f 45/151/147 48/152/148 49/153/149 +s 9 +f 50/154/150 51/155/151 47/156/152 +f 50/154/150 47/156/152 49/157/153 +s 10 +f 50/158/154 52/159/155 53/160/156 +f 50/158/154 53/160/156 51/161/157 +s 11 +f 52/164/160 50/163/159 54/162/158 +s 12 +f 52/165/161 54/166/162 56/168/164 +f 54/166/162 55/167/163 56/168/164 +s 13 +f 55/171/167 57/170/166 56/169/165 +s 14 +f 56/172/168 57/173/169 58/174/170 +f 56/172/168 58/174/170 59/175/171 +s 15 +f 58/178/174 37/177/173 59/176/172 +s 16 +f 37/179/175 58/180/176 44/182/178 +f 58/180/176 60/181/177 44/182/178 +s 17 +usemtl Stone_Brit_Gray_Light +f 44/183/179 61/184/180 62/185/181 +f 44/183/179 62/185/181 37/186/182 +s 18 +f 64/190/186 62/189/185 63/187/183 +f 62/189/185 61/188/184 63/187/183 +s 19 +f 38/191/187 62/192/188 65/194/190 +f 62/192/188 64/193/189 65/194/190 +s 20 +f 66/197/193 65/196/192 64/195/191 +s 21 +f 67/198/194 68/199/195 66/201/197 +f 68/199/195 65/200/196 66/201/197 +s 22 +f 68/204/200 67/203/199 69/202/198 +s 23 +f 69/205/201 67/206/202 70/207/203 +f 69/205/201 70/207/203 71/208/204 +s 24 +f 71/211/207 70/210/206 72/209/205 +s 25 +f 72/212/208 70/213/209 73/214/210 +f 72/212/208 73/214/210 74/215/211 +s 26 +f 74/218/214 73/217/213 63/216/212 +s 27 +usemtl Stone_Bri_Gray_Lighter +f 70/219/215 75/220/216 73/222/218 +f 75/220/216 76/221/217 73/222/218 +s 28 +f 77/225/221 75/224/220 70/223/219 +s 29 +f 78/226/222 79/227/223 77/229/225 +f 79/227/223 75/228/224 77/229/225 +s 30 +f 80/232/228 79/231/227 78/230/226 +s 31 +f 81/233/229 82/234/230 78/236/232 +f 82/234/230 80/235/231 78/236/232 +s 32 +f 81/239/235 83/238/234 82/237/233 +s 33 +f 82/240/236 83/241/237 84/242/238 +f 82/240/236 84/242/238 85/243/239 +s 34 +usemtl Stone_Brit_Gray +f 86/244/240 82/245/241 87/247/243 +f 82/245/241 85/246/242 87/247/243 +s 35 +f 88/250/246 82/249/245 86/248/244 +s 36 +f 88/251/247 86/252/248 89/253/249 +f 88/251/247 89/253/249 90/254/250 +s 37 +f 88/255/251 90/256/252 92/258/254 +f 90/256/252 91/257/253 92/258/254 +s 38 +f 91/259/255 93/260/256 92/262/258 +f 93/260/256 94/261/257 92/262/258 +s 39 +f 91/265/261 95/264/260 93/263/259 +s 40 +f 93/266/262 95/267/263 97/269/265 +f 95/267/263 96/268/264 97/269/265 +s 41 +f 98/272/268 97/271/267 96/270/266 +s 42 +f 99/273/269 100/274/270 98/275/271 +f 99/273/269 98/275/271 96/276/272 +s 43 +f 99/279/275 101/278/274 100/277/273 +s 44 +f 87/280/276 102/281/277 101/283/279 +f 102/281/277 100/282/278 101/283/279 +s 45 +f 103/284/280 89/285/281 99/287/283 +f 89/285/281 101/286/282 99/287/283 +s 46 +usemtl Stone_Brit_Gray_Light +f 104/288/284 103/289/285 105/291/287 +f 103/289/285 99/290/286 105/291/287 +s 47 +f 106/294/290 103/293/289 104/292/288 +s 48 +f 106/295/291 104/296/292 108/298/294 +f 104/296/292 107/297/293 108/298/294 +s 49 +f 108/299/295 109/300/296 110/301/297 +f 108/299/295 110/301/297 106/302/298 +s 50 +f 108/303/299 111/304/300 109/306/302 +f 111/304/300 112/305/301 109/306/302 +s 51 +f 112/309/305 113/308/304 109/307/303 +s 52 +f 113/310/306 112/311/307 114/312/308 +f 113/310/306 114/312/308 115/313/309 +s 53 +f 116/314/310 113/315/311 117/317/313 +f 113/315/311 115/316/312 117/317/313 +s 54 +f 115/318/314 118/319/315 119/320/316 +f 115/318/314 119/320/316 117/321/317 +s 55 +f 96/324/320 117/323/319 119/322/318 +s 56 +f 118/328/324 121/327/323 120/326/322 +f 118/328/324 120/326/322 119/325/321 +s 57 +f 122/329/325 121/330/326 118/331/327 +f 122/329/325 118/331/327 114/332/328 +s 58 +f 122/335/331 123/334/330 121/333/329 +s 59 +f 111/336/332 107/337/333 122/339/335 +f 107/337/333 123/338/334 122/339/335 +s 60 +usemtl Stone_Bri_Gray_Lighter +f 124/340/336 111/341/337 125/343/339 +f 111/341/337 122/342/338 125/343/339 +s 61 +f 126/346/342 111/345/341 124/344/340 +s 62 +f 126/347/343 124/348/344 128/350/346 +f 124/348/344 127/349/345 128/350/346 +s 63 +f 126/351/347 128/352/348 129/353/349 +f 126/351/347 129/353/349 130/354/350 +s 64 +f 129/355/351 131/356/352 132/357/353 +f 129/355/351 132/357/353 130/358/354 +s 65 +f 129/361/357 133/360/356 131/359/355 +s 66 +f 131/362/358 133/363/359 134/364/360 +f 131/362/358 134/364/360 135/365/361 +s 67 +f 134/368/364 136/367/363 135/366/362 +s 68 +f 135/369/365 136/370/366 138/372/368 +f 136/370/366 137/371/367 138/372/368 +s 69 +f 114/375/371 138/374/370 137/373/369 +s 70 +f 137/379/375 136/378/374 139/376/372 +f 136/378/374 140/377/373 139/376/372 +s 71 +f 125/380/376 139/381/377 140/382/378 +f 125/380/376 140/382/378 141/383/379 +s 72 +f 140/386/382 142/385/381 141/384/380 +s 73 +f 143/387/383 127/388/384 142/390/386 +f 127/388/384 141/389/385 142/390/386 +s 74 +usemtl Stone_Brit_Gray_Light +f 144/391/387 143/392/388 145/394/390 +f 143/392/388 142/393/389 145/394/390 +s 75 +f 146/397/393 143/396/392 144/395/391 +s 76 +f 146/398/394 144/399/395 148/401/397 +f 144/399/395 147/400/396 148/401/397 +s 77 +f 146/402/398 148/403/399 149/404/400 +f 146/402/398 149/404/400 150/405/401 +s 78 +f 149/406/402 151/407/403 152/408/404 +f 149/406/402 152/408/404 150/409/405 +s 79 +f 149/412/408 153/411/407 151/410/406 +s 80 +f 151/413/409 153/414/410 155/416/412 +f 153/414/410 154/415/411 155/416/412 +s 81 +f 155/419/415 154/418/414 156/417/413 +s 82 +f 155/420/416 156/421/417 158/423/419 +f 156/421/417 157/422/418 158/423/419 +s 83 +f 134/426/422 158/425/421 157/424/420 +s 84 +f 134/427/423 157/428/424 142/430/426 +f 157/428/424 159/429/425 142/430/426 +s 85 +f 156/434/430 160/433/429 157/431/427 +f 160/433/429 159/432/428 157/431/427 +s 86 +f 145/435/431 159/436/432 160/437/433 +f 145/435/431 160/437/433 161/438/434 +s 87 +f 161/441/437 160/440/436 162/439/435 +s 88 +f 163/442/438 147/443/439 162/445/441 +f 147/443/439 161/444/440 162/445/441 +s 89 +usemtl Stone_Brit_Gray +f 164/446/442 163/447/443 165/449/445 +f 163/447/443 162/448/444 165/449/445 +s 90 +f 163/452/448 164/451/447 166/450/446 +s 91 +f 167/453/449 153/454/450 166/456/452 +f 153/454/450 163/455/451 166/456/452 +s 92 +f 167/459/455 168/458/454 153/457/453 +s 93 +f 169/460/456 170/461/457 168/462/458 +f 169/460/456 168/462/458 167/463/459 +s 94 +f 169/466/462 171/465/461 170/464/460 +s 95 +f 170/467/463 171/468/464 173/470/466 +f 171/468/464 172/469/465 173/470/466 +s 96 +f 172/473/469 174/472/468 173/471/467 +s 97 +f 173/474/470 174/475/471 176/477/473 +f 174/475/471 175/476/472 176/477/473 +s 98 +f 154/480/476 176/479/475 175/478/474 +s 99 +f 154/481/477 175/482/478 177/483/479 +f 154/481/477 177/483/479 162/484/480 +s 100 +f 174/488/484 178/487/483 175/485/481 +f 178/487/483 177/486/482 175/485/481 +s 101 +f 165/489/485 177/490/486 178/491/487 +f 165/489/485 178/491/487 179/492/488 +s 102 +f 179/495/491 178/494/490 55/493/489 +s 103 +f 55/496/492 178/497/493 172/499/495 +f 178/497/493 174/498/494 172/499/495 +s 104 +usemtl Stone_Bri_Gray_Lighter +f 171/500/496 48/501/497 180/502/498 +f 171/500/496 180/502/498 172/503/499 +s 105 +f 180/506/502 181/505/501 172/504/500 +s 106 +f 46/507/503 60/508/504 180/510/506 +f 60/508/504 181/509/505 180/510/506 +s 107 +usemtl Stone_Brit_Gray +f 182/511/507 54/512/508 171/513/509 +f 182/511/507 171/513/509 169/514/510 +s 108 +f 182/517/513 183/516/512 54/515/511 +s 109 +f 173/518/514 176/519/515 170/521/517 +f 176/519/515 168/520/516 170/521/517 +s 110 +f 167/522/518 166/523/519 169/525/521 +f 166/523/519 182/524/520 169/525/521 +s 111 +f 166/526/522 164/527/523 182/529/525 +f 164/527/523 183/528/524 182/529/525 +s 112 +f 183/530/526 164/531/527 179/533/529 +f 164/531/527 165/532/528 179/533/529 +s 113 +f 177/536/532 165/535/531 162/534/530 +s 114 +usemtl Stone_Brit_Gray_Light +f 162/537/533 160/538/534 154/540/536 +f 160/538/534 156/539/535 154/540/536 +s 115 +usemtl Stone_Brit_Gray +f 153/541/537 168/542/538 154/544/540 +f 168/542/538 176/543/539 154/544/540 +s 116 +usemtl Stone_Brit_Gray_Light +f 150/547/543 152/546/542 133/545/541 +s 117 +f 152/548/544 151/549/545 158/551/547 +f 151/549/545 155/550/546 158/551/547 +s 118 +f 148/552/548 163/553/549 153/554/550 +f 148/552/548 153/554/550 149/555/551 +s 119 +f 147/558/554 163/557/553 148/556/552 +s 120 +f 150/559/555 133/560/556 143/561/557 +f 150/559/555 143/561/557 146/562/558 +s 121 +f 147/563/559 144/564/560 161/566/562 +f 144/564/560 145/565/561 161/566/562 +s 122 +f 159/569/565 145/568/564 142/567/563 +s 123 +usemtl Stone_Bri_Gray_Lighter +f 142/570/566 140/571/567 136/572/568 +f 142/570/566 136/572/568 134/573/569 +s 124 +usemtl Stone_Brit_Gray_Light +f 133/574/570 152/575/571 158/576/572 +f 133/574/570 158/576/572 134/577/573 +s 125 +usemtl Stone_Bri_Gray_Lighter +f 130/580/576 132/579/575 112/578/574 +s 126 +f 135/581/577 138/582/578 131/584/580 +f 138/582/578 132/583/579 131/584/580 +s 127 +f 128/585/581 143/586/582 129/588/584 +f 143/586/582 133/587/583 129/588/584 +s 128 +f 127/591/587 143/590/586 128/589/585 +s 129 +f 127/592/588 124/593/589 141/595/591 +f 124/593/589 125/594/590 141/595/591 +s 130 +f 125/598/594 122/597/593 139/596/592 +s 131 +f 114/599/595 137/600/596 122/602/598 +f 137/600/596 139/601/597 122/602/598 +s 132 +usemtl Stone_Brit_Gray_Light +f 105/603/599 120/604/600 123/606/602 +f 120/604/600 121/605/601 123/606/602 +s 133 +f 115/609/605 114/608/604 118/607/603 +s 134 +usemtl Stone_Bri_Gray_Lighter +f 112/610/606 132/611/607 138/612/608 +f 112/610/606 138/612/608 114/613/609 +s 135 +f 130/614/610 112/615/611 111/616/612 +f 130/614/610 111/616/612 126/617/613 +s 136 +usemtl Stone_Brit_Gray_Light +f 109/618/614 113/619/615 116/620/616 +f 109/618/614 116/620/616 110/621/617 +s 137 +f 110/624/620 116/623/619 95/622/618 +s 138 +f 108/627/623 107/626/622 111/625/621 +s 139 +f 110/628/624 95/629/625 103/630/626 +f 110/628/624 103/630/626 106/631/627 +s 140 +f 105/632/628 123/633/629 104/635/631 +f 123/633/629 107/634/630 104/635/631 +s 141 +f 120/638/634 105/637/633 99/636/632 +s 142 +f 96/639/635 119/640/636 99/642/638 +f 119/640/636 120/641/637 99/642/638 +s 143 +usemtl Stone_Brit_Gray +f 102/646/642 184/645/641 100/643/639 +f 184/645/641 98/644/640 100/643/639 +s 144 +f 185/647/643 184/648/644 85/650/646 +f 184/648/644 102/649/645 85/650/646 +s 145 +f 186/653/649 184/652/648 185/651/647 +s 146 +f 80/654/650 94/655/651 186/656/652 +f 80/654/650 186/656/652 185/657/653 +s 147 +usemtl Stone_Bri_Gray_Lighter +f 85/658/654 187/659/655 188/660/656 +f 85/658/654 188/660/656 185/661/657 +s 148 +f 189/664/660 185/663/659 188/662/658 +s 149 +f 189/665/661 188/666/662 190/667/663 +f 189/665/661 190/667/663 76/668/664 +s 150 +f 188/672/668 187/671/667 191/670/666 +f 188/672/668 191/670/666 190/669/665 +s 151 +f 73/673/669 190/674/670 191/675/671 +f 73/673/669 191/675/671 66/676/672 +s 152 +f 191/679/675 192/678/674 66/677/673 +s 153 +f 192/680/676 191/681/677 84/683/679 +f 191/681/677 187/682/678 84/683/679 +s 154 +usemtl Stone_Brit_Gray +f 97/684/680 98/685/681 184/686/682 +f 97/684/680 184/686/682 186/687/683 +s 155 +usemtl Stone_Brit_Gray_Light +f 95/688/684 116/689/685 117/690/686 +f 95/688/684 117/690/686 96/691/687 +s 156 +usemtl Stone_Brit_Gray +f 92/694/690 94/693/689 80/692/688 +s 157 +f 93/695/691 97/696/692 186/697/693 +f 93/695/691 186/697/693 94/698/694 +s 158 +f 90/699/695 103/700/696 91/702/698 +f 103/700/696 95/701/697 91/702/698 +s 159 +f 89/705/701 103/704/700 90/703/699 +s 160 +f 89/706/702 86/707/703 101/709/705 +f 86/707/703 87/708/704 101/709/705 +s 161 +f 87/712/708 85/711/707 102/710/706 +s 162 +usemtl Stone_Bri_Gray_Lighter +f 85/715/711 84/714/710 187/713/709 +s 163 +f 192/716/712 84/717/713 193/719/715 +f 84/717/713 83/718/714 193/719/715 +s 164 +f 194/720/716 193/721/717 83/722/718 +f 194/720/716 83/722/718 81/723/719 +s 165 +f 194/726/722 67/725/721 193/724/720 +s 166 +usemtl Stone_Brit_Gray +f 92/727/723 80/728/724 82/729/725 +f 92/727/723 82/729/725 88/730/726 +s 167 +usemtl Stone_Bri_Gray_Lighter +f 79/731/727 80/732/728 185/733/729 +f 79/731/727 185/733/729 189/734/730 +s 168 +f 81/735/731 78/736/732 194/738/734 +f 78/736/732 77/737/733 194/738/734 +s 169 +f 73/741/737 76/740/736 190/739/735 +s 170 +f 75/742/738 79/743/739 76/745/741 +f 79/743/739 189/744/740 76/745/741 +s 171 +f 77/746/742 70/747/743 194/749/745 +f 70/747/743 67/748/744 194/749/745 +s 172 +f 193/750/746 67/751/747 192/753/749 +f 67/751/747 66/752/748 192/753/749 +s 173 +usemtl Stone_Brit_Gray_Light +f 66/754/750 64/755/751 73/757/753 +f 64/755/751 63/756/752 73/757/753 +s 174 +f 74/758/754 63/759/755 61/760/756 +f 74/758/754 61/760/756 43/761/757 +s 175 +usemtl Stone_Bri_Gray_Lighter +f 181/765/761 60/764/760 58/763/759 +f 181/765/761 58/763/759 57/762/758 +s 176 +f 172/766/762 181/767/763 57/768/764 +f 172/766/762 57/768/764 55/769/765 +s 177 +usemtl Stone_Brit_Gray +f 54/770/766 183/771/767 55/773/769 +f 183/771/767 179/772/768 55/773/769 +s 178 +usemtl Stone_Bri_Gray_Lighter +f 53/776/772 36/775/771 51/774/770 +s 179 +f 56/777/773 59/778/774 52/780/776 +f 59/778/774 53/779/775 52/780/776 +s 180 +f 49/781/777 171/782/778 50/784/780 +f 171/782/778 54/783/779 50/784/780 +s 181 +f 49/787/783 48/786/782 171/785/781 +s 182 +f 45/788/784 46/789/785 48/791/787 +f 46/789/785 180/790/786 48/791/787 +s 183 +f 44/794/790 60/793/789 46/792/788 +s 184 +usemtl Stone_Brit_Gray_Light +f 43/797/793 61/796/792 44/795/791 +s 185 +f 42/798/794 72/799/795 74/800/796 +f 42/798/794 74/800/796 43/801/797 +s 186 +f 71/802/798 72/803/799 40/805/801 +f 72/803/799 42/804/800 40/805/801 +s 187 +f 39/806/802 69/807/803 40/809/805 +f 69/807/803 71/808/804 40/809/805 +s 188 +usemtl Stone_Bri_Gray_Lighter +f 51/810/806 36/811/807 41/812/808 +f 51/810/806 41/812/808 47/813/809 +s 189 +usemtl Stone_Brit_Gray_Light +f 39/814/810 35/815/811 68/816/812 +f 39/814/810 68/816/812 69/817/813 +s 190 +f 38/818/814 65/819/815 35/821/817 +f 65/819/815 68/820/816 35/821/817 +s 191 +f 37/824/820 62/823/819 38/822/818 +s 192 +usemtl Stone_Bri_Gray_Lighter +f 36/825/821 53/826/822 37/828/824 +f 53/826/822 59/827/823 37/828/824 +o object13 +g object13 +v -0.08785714 0.95950091 0.05357143 +v -0.08785714 0.99043039 0.00000000 +v -0.14285714 0.92857143 0.00000000 +v 0.08785714 0.95950091 0.05357143 +v 0.08785714 0.99043039 0.00000000 +v -0.08785714 0.95950091 -0.05357143 +v 0.08785714 0.95950091 -0.05357143 +v -0.08785714 0.89764195 -0.05357143 +v 0.08785714 0.89764195 -0.05357143 +v -0.08785714 0.86671247 0.00000000 +v 0.08785714 0.86671247 0.00000000 +v -0.08785714 0.89764195 0.05357143 +v 0.08785714 0.89764195 0.05357143 +v 0.14285714 0.92857143 0.00000000 +vt 0.00000000 0.24993517 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 0.24993517 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.50000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.75006483 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.24993517 +vt 0.00000000 1.00000000 +vt 1.00000000 0.75006483 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.50000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.50000000 +vt 1.00000000 0.75006483 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.75006483 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.50000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.24993517 +vt 0.00000000 1.00000000 +vn -0.69774235 0.62037627 0.35817440 +vn -0.69774235 0.62037627 0.35817440 +vn -0.69774235 0.62037627 0.35817440 +vn 0.00000000 0.86602540 0.50000000 +vn 0.00000000 0.86602540 0.50000000 +vn 0.00000000 0.86602540 0.50000000 +vn 0.00000000 0.86602540 0.50000000 +vn 0.00000000 0.86602540 -0.50000000 +vn 0.00000000 0.86602540 -0.50000000 +vn 0.00000000 0.86602540 -0.50000000 +vn 0.00000000 0.86602540 -0.50000000 +vn 0.00000000 -0.00000000 -1.00000000 +vn 0.00000000 -0.00000000 -1.00000000 +vn 0.00000000 -0.00000000 -1.00000000 +vn 0.00000000 -0.00000000 -1.00000000 +vn -0.00000000 -0.86602540 -0.50000000 +vn -0.00000000 -0.86602540 -0.50000000 +vn -0.00000000 -0.86602540 -0.50000000 +vn -0.00000000 -0.86602540 -0.50000000 +vn -0.00000000 -0.86602540 0.50000000 +vn -0.00000000 -0.86602540 0.50000000 +vn -0.00000000 -0.86602540 0.50000000 +vn -0.00000000 -0.86602540 0.50000000 +vn 0.69774235 -0.62037627 0.35817440 +vn 0.69774235 -0.62037627 0.35817440 +vn 0.69774235 -0.62037627 0.35817440 +vn 0.69774235 -0.00000000 0.71634881 +vn 0.69774235 -0.00000000 0.71634881 +vn 0.69774235 -0.00000000 0.71634881 +vn -0.69774235 -0.62037627 0.35817440 +vn -0.69774235 -0.62037627 0.35817440 +vn -0.69774235 -0.62037627 0.35817440 +vn 0.69774235 -0.62037627 -0.35817440 +vn 0.69774235 -0.62037627 -0.35817440 +vn 0.69774235 -0.62037627 -0.35817440 +vn -0.69774235 -0.62037627 -0.35817440 +vn -0.69774235 -0.62037627 -0.35817440 +vn -0.69774235 -0.62037627 -0.35817440 +vn 0.69774235 0.00000000 -0.71634881 +vn 0.69774235 0.00000000 -0.71634881 +vn 0.69774235 0.00000000 -0.71634881 +vn -0.69774235 0.00000000 -0.71634881 +vn -0.69774235 0.00000000 -0.71634881 +vn -0.69774235 0.00000000 -0.71634881 +vn 0.69774235 0.62037627 -0.35817440 +vn 0.69774235 0.62037627 -0.35817440 +vn 0.69774235 0.62037627 -0.35817440 +vn 0.69774235 0.62037627 0.35817440 +vn 0.69774235 0.62037627 0.35817440 +vn 0.69774235 0.62037627 0.35817440 +vn -0.00000000 -0.00000000 1.00000000 +vn -0.00000000 -0.00000000 1.00000000 +vn -0.00000000 -0.00000000 1.00000000 +vn -0.00000000 -0.00000000 1.00000000 +vn -0.69774235 0.00000000 0.71634881 +vn -0.69774235 0.00000000 0.71634881 +vn -0.69774235 0.00000000 0.71634881 +vn -0.69774235 0.62037627 -0.35817440 +vn -0.69774235 0.62037627 -0.35817440 +vn -0.69774235 0.62037627 -0.35817440 +s 1 +usemtl Burlap +f 195/829/825 196/830/826 197/831/827 +s 2 +f 199/835/831 196/834/830 198/832/828 +f 196/834/830 195/833/829 198/832/828 +s 3 +f 201/839/835 200/838/834 199/836/832 +f 200/838/834 196/837/833 199/836/832 +s 4 +f 203/843/839 202/842/838 201/840/836 +f 202/842/838 200/841/837 201/840/836 +s 5 +f 205/847/843 204/846/842 203/844/840 +f 204/846/842 202/845/841 203/844/840 +s 6 +f 207/851/847 206/850/846 205/848/844 +f 206/850/846 204/849/845 205/848/844 +s 7 +f 207/852/848 205/853/849 208/854/850 +s 8 +f 198/855/851 207/856/852 208/857/853 +s 9 +f 204/858/854 206/859/855 197/860/856 +s 10 +f 205/861/857 203/862/858 208/863/859 +s 11 +f 202/864/860 204/865/861 197/866/862 +s 12 +f 203/867/863 201/868/864 208/869/865 +s 13 +f 200/870/866 202/871/867 197/872/868 +s 14 +f 201/873/869 199/874/870 208/875/871 +s 15 +f 199/876/872 198/877/873 208/878/874 +s 16 +f 198/882/878 195/881/877 206/880/876 +f 198/882/878 206/880/876 207/879/875 +s 17 +f 206/883/879 195/884/880 197/885/881 +s 18 +f 196/886/882 200/887/883 197/888/884 +o object14 +g object14 +v -0.54027069 0.76648296 -0.02033718 +v -0.72394416 0.76943367 -0.02544795 +v -0.72394416 0.74004891 -0.02544795 +v -0.54027069 0.74299962 -0.02033718 +v -0.72394416 0.72535653 0.00000000 +v -0.54027069 0.73125794 0.00000000 +v -0.72394416 0.74004891 0.02544795 +v -0.54027069 0.74299962 0.02033718 +v -0.72394416 0.76943367 0.02544795 +v -0.54027069 0.76648296 0.02033718 +v -0.72394416 0.78412605 0.00000000 +v -0.54027069 0.77822464 0.00000000 +v -0.74637327 0.76727174 0.02170337 +v -0.74637327 0.77980218 0.00000000 +v -0.74637327 0.76727174 -0.02170337 +v -0.75455641 0.75474129 0.00000000 +v -0.74637327 0.74221084 -0.02170337 +v -0.74637327 0.72968040 0.00000000 +v -0.74637327 0.74221084 0.02170337 +v -0.54027069 0.75474129 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.45182546 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.50000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.54817454 +vt 1.00000000 0.00000000 +vt 1.00000000 0.54817454 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.50000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.45182546 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.25000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.50000000 1.00000000 +vt 0.00000000 0.75000000 +vt 0.50000000 0.50000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vn 0.02781457 0.00000000 -0.99961310 +vn 0.02781457 0.00000000 -0.99961310 +vn 0.02781457 0.00000000 -0.99961310 +vn 0.02781457 0.00000000 -0.99961310 +vn 0.02781457 -0.86569034 -0.49980655 +vn 0.02781457 -0.86569034 -0.49980655 +vn 0.02781457 -0.86569034 -0.49980655 +vn 0.02781457 -0.86569034 -0.49980655 +vn 0.02781457 -0.86569034 0.49980655 +vn 0.02781457 -0.86569034 0.49980655 +vn 0.02781457 -0.86569034 0.49980655 +vn 0.02781457 -0.86569034 0.49980655 +vn 0.02781457 0.00000000 0.99961310 +vn 0.02781457 -0.00000000 0.99961310 +vn 0.02781457 -0.00000000 0.99961310 +vn 0.02781457 0.00000000 0.99961310 +vn 0.02781457 0.86569034 0.49980655 +vn 0.02781457 0.86569034 0.49980655 +vn 0.02781457 0.86569034 0.49980655 +vn 0.02781457 0.86569034 0.49980655 +vn -0.16467281 0.85420264 0.49317412 +vn -0.16467281 0.85420264 0.49317412 +vn -0.16467281 0.85420264 0.49317412 +vn -0.16467281 0.85420264 0.49317412 +vn -0.16467281 0.85420264 -0.49317412 +vn -0.16467281 0.85420264 -0.49317412 +vn -0.16467281 0.85420264 -0.49317412 +vn -0.16467281 0.85420264 -0.49317412 +vn -0.93569868 0.30553393 -0.17640010 +vn -0.93569868 0.30553393 -0.17640010 +vn -0.93569868 0.30553393 -0.17640010 +vn -0.93569868 0.00000000 -0.35280019 +vn -0.93569868 0.00000000 -0.35280019 +vn -0.93569868 0.00000000 -0.35280019 +vn -0.93569868 -0.30553393 -0.17640010 +vn -0.93569868 -0.30553393 -0.17640010 +vn -0.93569868 -0.30553393 -0.17640010 +vn -0.93569868 -0.30553393 0.17640010 +vn -0.93569868 -0.30553393 0.17640010 +vn -0.93569868 -0.30553393 0.17640010 +vn -0.93569868 0.00000000 0.35280019 +vn -0.93569868 0.00000000 0.35280019 +vn -0.93569868 0.00000000 0.35280019 +vn -0.93569868 0.30553393 0.17640010 +vn -0.93569868 0.30553393 0.17640010 +vn -0.93569868 0.30553393 0.17640010 +vn -0.16467281 -0.00000000 0.98634825 +vn -0.16467281 0.00000000 0.98634825 +vn -0.16467281 0.00000000 0.98634825 +vn -0.16467281 -0.00000000 0.98634825 +vn -0.16467281 -0.85420264 0.49317412 +vn -0.16467281 -0.85420264 0.49317412 +vn -0.16467281 -0.85420264 0.49317412 +vn -0.16467281 -0.85420264 0.49317412 +vn -0.16467281 -0.85420264 -0.49317412 +vn -0.16467281 -0.85420264 -0.49317412 +vn -0.16467281 -0.85420264 -0.49317412 +vn -0.16467281 -0.85420264 -0.49317412 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.02781457 0.86569034 -0.49980655 +vn 0.02781457 0.86569034 -0.49980655 +vn 0.02781457 0.86569034 -0.49980655 +vn 0.02781457 0.86569034 -0.49980655 +vn -0.16467281 0.00000000 -0.98634825 +vn -0.16467281 0.00000000 -0.98634825 +vn -0.16467281 0.00000000 -0.98634825 +vn -0.16467281 0.00000000 -0.98634825 +s 1 +usemtl Wood_Dark +f 212/892/888 211/891/887 209/889/885 +f 211/891/887 210/890/886 209/889/885 +s 2 +f 214/896/892 213/895/891 212/893/889 +f 213/895/891 211/894/890 212/893/889 +s 3 +f 216/900/896 215/899/895 214/897/893 +f 215/899/895 213/898/894 214/897/893 +s 4 +f 218/904/900 217/903/899 216/901/897 +f 217/903/899 215/902/898 216/901/897 +s 5 +f 220/908/904 219/907/903 218/905/901 +f 219/907/903 217/906/902 218/905/901 +s 6 +f 219/912/908 222/911/907 217/909/905 +f 222/911/907 221/910/906 217/909/905 +s 7 +f 210/916/912 223/915/911 219/913/909 +f 223/915/911 222/914/910 219/913/909 +s 8 +usemtl Wood_Medium +f 224/919/915 222/918/914 223/917/913 +s 9 +f 224/922/918 223/921/917 225/920/916 +s 10 +f 224/925/921 225/924/920 226/923/919 +s 11 +f 224/928/924 226/927/923 227/926/922 +s 12 +f 224/931/927 227/930/926 221/929/925 +s 13 +f 224/934/930 221/933/929 222/932/928 +s 14 +usemtl Wood_Dark +f 217/938/934 221/937/933 227/936/932 +f 217/938/934 227/936/932 215/935/931 +s 15 +f 215/942/938 227/941/937 213/939/935 +f 227/941/937 226/940/936 213/939/935 +s 16 +f 213/946/942 226/945/941 211/943/939 +f 226/945/941 225/944/940 211/943/939 +s 17 +f 209/952/948 220/951/947 228/953/949 +f 214/948/944 212/947/943 228/953/949 +f 216/949/945 214/948/944 228/953/949 +f 218/950/946 216/949/945 228/953/949 +f 220/951/947 218/950/946 228/953/949 +f 212/947/943 209/952/948 228/953/949 +s 18 +f 209/957/953 210/956/952 220/954/950 +f 210/956/952 219/955/951 220/954/950 +s 19 +f 211/961/957 225/960/956 210/958/954 +f 225/960/956 223/959/955 210/958/954 +o object15 +g object15 +v 0.25414477 0.00000000 -0.10527021 +v 0.25414477 0.15000000 -0.10527021 +v 0.25414477 0.15000000 0.10527021 +v 0.25414477 0.00000000 0.10527021 +v 0.10527021 0.15000000 0.25414477 +v 0.10527021 0.00000000 0.25414477 +v -0.10527021 0.15000000 0.25414477 +v -0.10527021 0.00000000 0.25414477 +v -0.00000000 0.15000000 0.25414477 +v -0.00000000 0.00000000 0.25414477 +v -0.25414477 0.15000000 0.10527021 +v -0.25414477 0.00000000 0.10527021 +v -0.25414477 0.15000000 -0.10527021 +v -0.25414477 0.00000000 -0.10527021 +v -0.10527021 0.15000000 -0.25414477 +v -0.10527021 0.00000000 -0.25414477 +v 0.10527021 0.15000000 -0.25414477 +v 0.10527021 0.00000000 -0.25414477 +v -0.00000000 0.15000000 -0.25414477 +v -0.00000000 0.00000000 -0.25414477 +v -0.00000000 0.00000000 0.37500000 +v -0.15533009 0.00000000 0.37500000 +v -0.37500000 0.00000000 0.15533009 +v -0.37500000 0.00000000 -0.15533009 +v -0.15533009 0.00000000 -0.37500000 +v -0.00000000 0.00000000 -0.37500000 +v 0.15533009 0.00000000 -0.37500000 +v 0.37500000 0.00000000 -0.15533009 +v 0.37500000 0.00000000 0.15533009 +v 0.15533009 0.00000000 0.37500000 +v -0.15533009 0.15000000 0.37500000 +v -0.37500000 0.15000000 0.15533009 +v 0.15533009 0.15000000 0.37500000 +v -0.00000000 0.15000000 0.37500000 +v 0.37500000 0.15000000 0.15533009 +v 0.37500000 0.15000000 -0.15533009 +v 0.15533009 0.15000000 -0.37500000 +v -0.15533009 0.15000000 -0.37500000 +v -0.00000000 0.15000000 -0.37500000 +v -0.37500000 0.15000000 -0.15533009 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.50000000 +vt 0.00000000 0.50000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.50000000 +vt 0.00000000 0.50000000 +vt 0.83885970 0.50000000 +vt 0.83885970 0.35963972 +vt 0.64036028 0.16114030 +vt 0.35963972 0.16114030 +vt 0.16114030 0.35963972 +vt 0.16114030 0.50000000 +vt 0.00000000 0.50000000 +vt 0.00000000 0.29289322 +vt 0.29289322 0.00000000 +vt 0.70710678 0.00000000 +vt 1.00000000 0.29289322 +vt 1.00000000 0.50000000 +vt 0.16114030 0.64036028 +vt 0.35963972 0.83885970 +vt 0.64036028 0.83885970 +vt 0.83885970 0.64036028 +vt 1.00000000 0.70710678 +vt 0.70710678 1.00000000 +vt 0.29289322 1.00000000 +vt 0.00000000 0.70710678 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.50000000 +vt 0.00000000 0.50000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.50000000 +vt 0.00000000 0.50000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.83885970 0.50000000 +vt 0.83885970 0.35963972 +vt 0.64036028 0.16114030 +vt 0.35963972 0.16114030 +vt 0.16114030 0.35963972 +vt 0.16114030 0.50000000 +vt 0.00000000 0.50000000 +vt 0.00000000 0.29289322 +vt 0.29289322 0.00000000 +vt 0.70710678 0.00000000 +vt 1.00000000 0.29289322 +vt 1.00000000 0.50000000 +vt 0.16114030 0.64036028 +vt 0.35963972 0.83885970 +vt 0.64036028 0.83885970 +vt 0.83885970 0.64036028 +vt 1.00000000 0.70710678 +vt 0.70710678 1.00000000 +vt 0.29289322 1.00000000 +vt 0.00000000 0.70710678 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +vn -0.00000000 1.00000000 0.00000000 +s 1 +usemtl Wood_Darker +f 232/965/961 231/964/960 230/963/959 +f 232/965/961 230/963/959 229/962/958 +s 2 +f 234/969/965 233/968/964 231/967/963 +f 234/969/965 231/967/963 232/966/962 +s 3 +f 233/971/967 234/970/966 238/975/971 +f 236/973/969 235/972/968 238/975/971 +f 235/972/968 237/974/970 238/975/971 +f 237/974/970 233/971/967 238/975/971 +s 4 +f 240/979/975 239/978/974 236/976/972 +f 239/978/974 235/977/973 236/976/972 +s 5 +f 242/983/979 241/982/978 239/981/977 +f 242/983/979 239/981/977 240/980/976 +s 6 +f 244/987/983 243/986/982 241/985/981 +f 244/987/983 241/985/981 242/984/980 +s 7 +f 248/993/989 246/991/987 245/990/986 +f 243/989/985 244/988/984 248/993/989 +f 248/993/989 247/992/988 243/989/985 +f 248/993/989 245/990/986 247/992/988 +s 8 +f 254/1005/1001 248/994/990 244/995/991 +f 253/1004/1000 244/995/991 242/996/992 +f 252/1003/999 242/996/992 240/997/993 +f 240/997/993 250/1001/997 251/1002/998 +f 236/998/994 249/1000/996 250/1001/997 +f 258/1013/1009 249/1000/996 234/1006/1002 +f 232/1007/1003 257/1012/1008 258/1013/1009 +f 232/1007/1003 256/1011/1007 257/1012/1008 +f 229/1008/1004 255/1010/1006 256/1011/1007 +f 229/1008/1004 246/1009/1005 255/1010/1006 +f 232/1007/1003 229/1008/1004 256/1011/1007 +f 258/1013/1009 234/1006/1002 232/1007/1003 +f 249/1000/996 238/999/995 234/1006/1002 +f 236/998/994 238/999/995 249/1000/996 +f 240/997/993 236/998/994 250/1001/997 +f 251/1002/998 252/1003/999 240/997/993 +f 252/1003/999 253/1004/1000 242/996/992 +f 246/1009/1005 254/1005/1001 255/1010/1006 +f 246/1009/1005 248/994/990 254/1005/1001 +f 253/1004/1000 254/1005/1001 244/995/991 +s 9 +f 250/1014/1010 259/1015/1011 251/1017/1013 +f 259/1015/1011 260/1016/1012 251/1017/1013 +s 10 +f 249/1023/1019 258/1018/1014 261/1019/1015 +f 259/1020/1016 250/1021/1017 249/1023/1019 +f 249/1023/1019 262/1022/1018 259/1020/1016 +f 249/1023/1019 261/1019/1015 262/1022/1018 +s 11 +f 257/1024/1020 263/1025/1021 261/1026/1022 +f 257/1024/1020 261/1026/1022 258/1027/1023 +s 12 +f 256/1028/1024 264/1029/1025 263/1030/1026 +f 256/1028/1024 263/1030/1026 257/1031/1027 +s 13 +f 255/1032/1028 265/1033/1029 264/1034/1030 +f 255/1032/1028 264/1034/1030 256/1035/1031 +s 14 +f 265/1038/1034 255/1039/1035 254/1041/1037 +f 253/1036/1032 266/1037/1033 254/1041/1037 +f 266/1037/1033 267/1040/1036 254/1041/1037 +f 267/1040/1036 265/1038/1034 254/1041/1037 +s 15 +f 252/1042/1038 268/1043/1039 266/1044/1040 +f 252/1042/1038 266/1044/1040 253/1045/1041 +s 16 +f 251/1046/1042 260/1047/1043 268/1048/1044 +f 251/1046/1042 268/1048/1044 252/1049/1045 +s 17 +f 229/1053/1049 230/1052/1048 246/1050/1046 +f 230/1052/1048 245/1051/1047 246/1050/1046 +s 18 +f 243/1055/1051 247/1054/1050 267/1065/1061 +f 265/1070/1066 267/1065/1061 245/1069/1065 +f 264/1071/1067 265/1070/1066 230/1068/1064 +f 230/1068/1064 231/1067/1063 263/1072/1068 +f 231/1067/1063 261/1073/1069 263/1072/1068 +f 233/1066/1062 262/1060/1056 261/1073/1069 +f 259/1061/1057 262/1060/1056 235/1058/1054 +f 260/1062/1058 259/1061/1057 239/1057/1053 +f 239/1057/1053 241/1056/1052 268/1063/1059 +f 241/1056/1052 266/1064/1060 268/1063/1059 +f 239/1057/1053 268/1063/1059 260/1062/1058 +f 259/1061/1057 235/1058/1054 239/1057/1053 +f 262/1060/1056 237/1059/1055 235/1058/1054 +f 233/1066/1062 237/1059/1055 262/1060/1056 +f 231/1067/1063 233/1066/1062 261/1073/1069 +f 230/1068/1064 263/1072/1068 264/1071/1067 +f 265/1070/1066 245/1069/1065 230/1068/1064 +f 267/1065/1061 247/1054/1050 245/1069/1065 +f 241/1056/1052 243/1055/1051 266/1064/1060 +f 243/1055/1051 267/1065/1061 266/1064/1060 +o object16 +g object16 +v 0.32380952 0.94866766 0.52995333 +v 0.30357143 0.94809524 0.50190476 +v 0.48571429 0.94809524 0.50190476 +v 0.46547619 0.94866766 0.52995333 +v 0.30357143 1.04523810 0.40476190 +v 0.28333333 0.94866766 0.52995333 +v 0.20238095 1.04523810 0.40476190 +v 0.12142857 0.94866766 0.52995333 +v 0.10119048 1.04523810 0.40476190 +v 0.10119048 0.94809524 0.50190476 +v 0.08095238 0.94866766 0.52995333 +v -0.10119048 1.04523810 0.40476190 +v -0.00000000 1.04523810 0.40476190 +v -0.08095238 0.94866766 0.52995333 +v -0.10119048 0.94809524 0.50190476 +v -0.12142857 0.94866766 0.52995333 +v -0.20238095 1.04523810 0.40476190 +v -0.30357143 1.04523810 0.40476190 +v -0.28333333 0.94866766 0.52995333 +v -0.22261905 1.04523810 0.43338289 +v -0.38452381 1.04523810 0.43338289 +v -0.40476190 1.04523810 0.40476190 +v -0.30357143 1.14642857 0.30357143 +v -0.40476190 1.14642857 0.30357143 +v -0.20238095 1.14642857 0.30357143 +v -0.48571429 1.14642857 0.30357143 +v -0.46547619 1.14642857 0.33219242 +v -0.32380952 1.14642857 0.33219242 +v -0.30357143 1.24761905 0.20238095 +v -0.28333333 1.14642857 0.33219242 +v -0.20238095 1.24761905 0.20238095 +v -0.12142857 1.14642857 0.33219242 +v -0.10119048 1.24761905 0.20238095 +v -0.22261905 1.24761905 0.23100194 +v -0.38452381 1.24761905 0.23100194 +v -0.40476190 1.24761905 0.20238095 +v -0.30357143 1.34880952 0.10119048 +v -0.40476190 1.34880952 0.10119048 +v -0.20238095 1.34880952 0.10119048 +v -0.48571429 1.34880952 0.10119048 +v -0.46547619 1.34880952 0.12981146 +v -0.32380952 1.34880952 0.12981146 +v -0.30357143 1.45000000 -0.00000000 +v -0.28333333 1.34880952 0.12981146 +v -0.12142857 1.34880952 0.12981146 +v -0.10119048 1.45000000 -0.00000000 +v -0.28333333 1.34880952 -0.12981146 +v -0.12142857 1.34880952 -0.12981146 +v -0.30357143 1.34880952 -0.10119048 +v -0.32380952 1.34880952 -0.12981146 +v -0.48571429 1.45000000 -0.00000000 +v -0.46547619 1.34880952 -0.12981146 +v -0.48571429 1.34880952 -0.10119048 +v -0.48571429 1.04523810 -0.40476190 +v -0.48571429 0.94809524 -0.50190476 +v -0.48571429 0.91436508 -0.46817460 +v -0.48571429 1.38253968 -0.00000000 +v -0.48571429 1.24761905 -0.20238095 +v -0.48571429 1.14642857 -0.30357143 +v -0.46547619 0.94866766 -0.52995333 +v -0.32380952 0.94866766 -0.52995333 +v -0.30357143 1.04523810 -0.40476190 +v -0.40476190 1.04523810 -0.40476190 +v -0.30357143 0.94809524 -0.50190476 +v -0.28333333 0.94866766 -0.52995333 +v -0.20238095 1.04523810 -0.40476190 +v -0.12142857 0.94866766 -0.52995333 +v -0.10119048 1.04523810 -0.40476190 +v -0.10119048 0.94809524 -0.50190476 +v -0.08095238 0.94866766 -0.52995333 +v -0.00000000 1.04523810 -0.40476190 +v 0.08095238 0.94866766 -0.52995333 +v 0.10119048 1.04523810 -0.40476190 +v 0.10119048 0.94809524 -0.50190476 +v 0.12142857 0.94866766 -0.52995333 +v 0.20238095 1.04523810 -0.40476190 +v 0.30357143 1.04523810 -0.40476190 +v 0.28333333 0.94866766 -0.52995333 +v 0.22261905 1.04523810 -0.43338289 +v 0.38452381 1.04523810 -0.43338289 +v 0.40476190 1.04523810 -0.40476190 +v 0.30357143 1.14642857 -0.30357143 +v 0.40476190 1.14642857 -0.30357143 +v 0.20238095 1.14642857 -0.30357143 +v 0.48571429 1.14642857 -0.30357143 +v 0.46547619 1.14642857 -0.33219242 +v 0.32380952 1.14642857 -0.33219242 +v 0.30357143 1.24761905 -0.20238095 +v 0.28333333 1.14642857 -0.33219242 +v 0.20238095 1.24761905 -0.20238095 +v 0.12142857 1.14642857 -0.33219242 +v 0.10119048 1.24761905 -0.20238095 +v 0.22261905 1.24761905 -0.23100194 +v 0.38452381 1.24761905 -0.23100194 +v 0.40476190 1.24761905 -0.20238095 +v 0.30357143 1.34880952 -0.10119048 +v 0.40476190 1.34880952 -0.10119048 +v 0.20238095 1.34880952 -0.10119048 +v 0.48571429 1.34880952 -0.10119048 +v 0.46547619 1.34880952 -0.12981146 +v 0.32380952 1.34880952 -0.12981146 +v 0.30357143 1.45000000 -0.00000000 +v 0.28333333 1.34880952 -0.12981146 +v 0.12142857 1.34880952 -0.12981146 +v 0.10119048 1.45000000 -0.00000000 +v 0.28333333 1.34880952 0.12981146 +v 0.12142857 1.34880952 0.12981146 +v 0.30357143 1.34880952 0.10119048 +v 0.32380952 1.34880952 0.12981146 +v 0.48571429 1.45000000 -0.00000000 +v 0.46547619 1.34880952 0.12981146 +v 0.48571429 1.34880952 0.10119048 +v 0.48571429 1.04523810 0.40476190 +v 0.48571429 0.91436508 0.46817460 +v 0.48571429 1.38253968 -0.00000000 +v 0.48571429 1.24761905 0.20238095 +v 0.48571429 1.14642857 0.30357143 +v 0.46547619 1.04523810 0.43338289 +v 0.40476190 1.14642857 0.30357143 +v 0.42500000 1.04523810 0.43338289 +v 0.46547619 1.14642857 0.33219242 +v 0.32380952 1.14642857 0.33219242 +v 0.30357143 1.14642857 0.30357143 +v 0.38452381 1.04523810 0.43338289 +v 0.22261905 1.04523810 0.43338289 +v 0.20238095 1.14642857 0.30357143 +v 0.18214286 1.04523810 0.43338289 +v 0.10119048 1.14642857 0.30357143 +v 0.02023810 1.04523810 0.43338289 +v -0.00000000 1.14642857 0.30357143 +v -0.02023810 1.04523810 0.43338289 +v -0.10119048 1.14642857 0.30357143 +v -0.18214286 1.04523810 0.43338289 +v 0.08095238 1.14642857 0.33219242 +v -0.08095238 1.14642857 0.33219242 +v 0.10119048 1.24761905 0.20238095 +v -0.00000000 1.24761905 0.20238095 +v 0.02023810 1.24761905 0.23100194 +v 0.18214286 1.24761905 0.23100194 +v 0.20238095 1.24761905 0.20238095 +v 0.30357143 1.24761905 0.20238095 +v 0.28333333 1.14642857 0.33219242 +v 0.12142857 1.14642857 0.33219242 +v 0.22261905 1.24761905 0.23100194 +v 0.38452381 1.24761905 0.23100194 +v 0.40476190 1.24761905 0.20238095 +v 0.20238095 1.34880952 0.10119048 +v 0.40476190 1.34880952 0.10119048 +v 0.42500000 1.24761905 0.23100194 +v 0.46547619 1.24761905 0.23100194 +v -0.00000000 1.34880952 0.10119048 +v -0.02023810 1.24761905 0.23100194 +v -0.10119048 1.34880952 0.10119048 +v -0.18214286 1.24761905 0.23100194 +v 0.10119048 1.34880952 0.10119048 +v 0.08095238 1.34880952 0.12981146 +v -0.08095238 1.34880952 0.12981146 +v -0.08095238 1.34880952 -0.12981146 +v 0.08095238 1.34880952 -0.12981146 +v -0.10119048 1.34880952 -0.10119048 +v -0.00000000 1.34880952 -0.10119048 +v 0.10119048 1.34880952 -0.10119048 +v 0.18214286 1.24761905 -0.23100194 +v 0.02023810 1.24761905 -0.23100194 +v -0.00000000 1.24761905 -0.20238095 +v -0.02023810 1.24761905 -0.23100194 +v -0.18214286 1.24761905 -0.23100194 +v -0.20238095 1.24761905 -0.20238095 +v -0.10119048 1.24761905 -0.20238095 +v -0.30357143 1.24761905 -0.20238095 +v -0.28333333 1.14642857 -0.33219242 +v -0.12142857 1.14642857 -0.33219242 +v -0.30357143 1.14642857 -0.30357143 +v -0.20238095 1.14642857 -0.30357143 +v -0.10119048 1.14642857 -0.30357143 +v -0.40476190 1.14642857 -0.30357143 +v -0.38452381 1.04523810 -0.43338289 +v -0.22261905 1.04523810 -0.43338289 +v -0.18214286 1.04523810 -0.43338289 +v -0.46547619 1.14642857 -0.33219242 +v -0.32380952 1.14642857 -0.33219242 +v -0.42500000 1.04523810 -0.43338289 +v -0.46547619 1.04523810 -0.43338289 +v -0.40476190 1.24761905 -0.20238095 +v -0.46547619 1.24761905 -0.23100194 +v -0.42500000 1.24761905 -0.23100194 +v -0.40476190 1.34880952 -0.10119048 +v -0.38452381 1.24761905 -0.23100194 +v -0.22261905 1.24761905 -0.23100194 +v -0.20238095 1.34880952 -0.10119048 +v -0.02023810 1.04523810 -0.43338289 +v -0.00000000 1.14642857 -0.30357143 +v 0.02023810 1.04523810 -0.43338289 +v 0.10119048 1.14642857 -0.30357143 +v 0.18214286 1.04523810 -0.43338289 +v 0.08095238 1.14642857 -0.33219242 +v -0.08095238 1.14642857 -0.33219242 +v 0.40476190 1.04523810 0.40476190 +v 0.48571429 1.04523810 -0.40476190 +v 0.48571429 0.94809524 -0.50190476 +v 0.48571429 0.91436508 -0.46817460 +v 0.48571429 1.24761905 -0.20238095 +v 0.44523810 0.91436508 -0.46817460 +v 0.44523810 1.38253968 -0.00000000 +v 0.44523810 0.86329382 -0.40476190 +v 0.44523810 1.26805573 -0.00000000 +v 0.44523810 0.86329382 0.40476190 +v 0.44523810 0.91436508 0.46817460 +v -0.44523810 0.86329382 0.40476190 +v -0.44523810 0.91436508 0.46817460 +v -0.44523810 1.38253968 -0.00000000 +v -0.44523810 1.26805573 -0.00000000 +v -0.44523810 0.86329382 -0.40476190 +v -0.44523810 0.91436508 -0.46817460 +v -0.48571429 0.91436508 0.46817460 +v 0.46547619 1.24761905 -0.23100194 +v 0.42500000 1.24761905 -0.23100194 +v 0.46547619 1.04523810 -0.43338289 +v 0.42500000 1.04523810 -0.43338289 +v 0.46547619 0.94866766 -0.52995333 +v 0.32380952 0.94866766 -0.52995333 +v 0.30357143 0.94809524 -0.50190476 +v -0.48571429 1.04523810 0.40476190 +v -0.48571429 0.94809524 0.50190476 +v -0.48571429 1.24761905 0.20238095 +v -0.46547619 1.24761905 0.23100194 +v -0.42500000 1.24761905 0.23100194 +v -0.46547619 1.04523810 0.43338289 +v -0.42500000 1.04523810 0.43338289 +v -0.46547619 0.94866766 0.52995333 +v -0.32380952 0.94866766 0.52995333 +v -0.30357143 0.94809524 0.50190476 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.90000000 1.00000000 +vt 0.10000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.19354839 1.00000000 +vt 0.00000000 1.00000000 +vt 0.01881720 0.00000000 +vt 0.95161290 0.00000000 +vt 1.00000000 1.00000000 +vt 0.79838710 1.00000000 +vt 0.59677419 1.00000000 +vt 0.39516129 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.10000000 0.00000000 +vt 0.90000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.19354839 1.00000000 +vt 0.00000000 1.00000000 +vt 0.01881720 0.00000000 +vt 0.95161290 0.00000000 +vt 1.00000000 1.00000000 +vt 0.79838710 1.00000000 +vt 0.59677419 1.00000000 +vt 0.39516129 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.90000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.10000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.90000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.10000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.90000000 1.00000000 +vt 0.10000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.10000000 0.00000000 +vt 0.90000000 0.00000000 +vt 0.10000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.90000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.10000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.90000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.90000000 1.00000000 +vt 0.10000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.10000000 0.00000000 +vt 0.90000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.50000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.19354839 1.00000000 +vt 0.00000000 1.00000000 +vt 0.01881720 0.00000000 +vt 0.95161290 0.00000000 +vt 1.00000000 1.00000000 +vt 0.79838710 1.00000000 +vt 0.59677419 1.00000000 +vt 0.39516129 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.93227666 +vt 0.77951879 0.50000000 +vt 0.00000000 0.06772334 +vt 0.09835660 0.00000000 +vt 1.00000000 0.50000000 +vt 0.09835660 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.50000000 +vt 0.09835660 1.00000000 +vt 0.00000000 0.93227666 +vt 0.77951879 0.50000000 +vt 0.00000000 0.06772334 +vt 0.09835660 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.10000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.90000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.19354839 1.00000000 +vt 0.00000000 1.00000000 +vt 0.01881720 0.00000000 +vt 0.95161290 0.00000000 +vt 1.00000000 1.00000000 +vt 0.79838710 1.00000000 +vt 0.59677419 1.00000000 +vt 0.39516129 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.18750000 +vt 1.00000000 0.39583333 +vt 1.00000000 0.60416667 +vt 1.00000000 0.81250000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.95833333 +vt 0.00000000 0.04166667 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.25000000 +vt 1.00000000 0.75000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 0.87610066 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.90000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.10000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.50000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.11111111 +vt 1.00000000 0.88888889 +vt 0.00000000 1.00000000 +vt 0.00000000 0.44444444 +vt 0.00000000 1.00000000 +vt 0.00000000 0.12389934 +vt 1.00000000 0.00000000 +vt 1.00000000 0.18750000 +vt 1.00000000 0.39583333 +vt 1.00000000 0.60416667 +vt 1.00000000 0.81250000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.95833333 +vt 0.00000000 0.04166667 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vn -0.00000000 -0.99979182 0.02040391 +vn -0.00000000 -0.99979182 0.02040391 +vn -0.00000000 -0.99979182 0.02040391 +vn -0.00000000 -0.99979182 0.02040391 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.00000000 0.79180001 0.61078043 +vn -0.00000000 0.79180001 0.61078043 +vn -0.00000000 0.79180001 0.61078043 +vn -0.00000000 0.79180001 0.61078043 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn -0.00000000 0.79180001 -0.61078043 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.79180001 -0.61078043 +vn 0.00000000 0.79180001 -0.61078043 +vn 0.00000000 0.79180001 -0.61078043 +vn 0.00000000 0.79180001 -0.61078043 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn -0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn 0.00000000 0.78868632 0.61479581 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 1.00000000 -0.00000000 -0.00000000 +vn 1.00000000 -0.00000000 -0.00000000 +vn 1.00000000 -0.00000000 -0.00000000 +vn 1.00000000 -0.00000000 -0.00000000 +vn 1.00000000 -0.00000000 -0.00000000 +vn 1.00000000 -0.00000000 -0.00000000 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn -0.00000000 -0.77882107 0.62724615 +vn -0.00000000 -0.77882107 0.62724615 +vn -0.00000000 -0.77882107 0.62724615 +vn -0.00000000 -0.77882107 0.62724615 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.77882107 -0.62724615 +vn -0.00000000 -0.77882107 -0.62724615 +vn -0.00000000 -0.77882107 -0.62724615 +vn -0.00000000 -0.77882107 -0.62724615 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.00000000 -0.99979182 -0.02040391 +vn -0.00000000 -0.99979182 -0.02040391 +vn -0.00000000 -0.99979182 -0.02040391 +vn -0.00000000 -0.99979182 -0.02040391 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 0.78868632 -0.61479581 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn 0.00000000 0.79180001 -0.61078043 +vn 0.00000000 0.79180001 -0.61078043 +vn 0.00000000 0.79180001 -0.61078043 +vn 0.00000000 0.79180001 -0.61078043 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn -0.00000000 -0.99979182 -0.02040391 +vn -0.00000000 -0.99979182 -0.02040391 +vn -0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -0.99979182 -0.02040391 +vn 0.00000000 -0.99979182 -0.02040391 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -0.70710678 0.50000000 -0.50000000 +vn -1.00000000 -0.00000000 0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 -0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn -0.70710678 0.50000000 0.50000000 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn -0.00000000 0.78868632 0.61479581 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.00000000 0.79180001 0.61078043 +vn -0.00000000 0.79180001 0.61078043 +vn -0.00000000 0.79180001 0.61078043 +vn -0.00000000 0.79180001 0.61078043 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn -0.70710678 0.50000000 0.50000000 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -0.99979182 0.02040391 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn 0.00000000 -1.00000000 -0.00000000 +vn -0.00000000 -0.99979182 0.02040391 +vn -0.00000000 -0.99979182 0.02040391 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn 0.00000000 0.79180001 0.61078043 +vn 0.70710678 0.50000000 0.50000000 +vn 0.70710678 0.50000000 0.50000000 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +s 1 +usemtl Dark_Red_Roof +f 269/1074/1070 270/1075/1071 271/1076/1072 +f 276/1978/1868 278/1977/1859 270/1976/1071 +f 282/1965/1860 278/1963/1859 279/1962/1858 +f 282/1965/1860 283/1964/1851 278/1963/1859 +f 500/1953/1770 283/1954/1851 284/1955/1852 +f 500/1953/1770 284/1955/1852 287/1956/1853 +f 498/1858/1772 500/1856/1770 499/1855/1769 +f 498/1858/1772 492/1857/1771 500/1856/1770 +f 274/1979/1869 276/1978/1868 270/1976/1071 +f 269/1074/1070 271/1076/1072 272/1077/1073 +s 2 +f 273/1078/1074 270/1079/1075 269/1080/1076 +s 3 +f 274/1083/1079 270/1082/1078 273/1081/1077 +s 4 +f 275/1084/1080 277/1088/1084 276/1087/1083 +f 279/1098/1093 277/1097/1084 281/1096/1092 +f 281/1096/1092 280/1095/1091 282/1099/1094 +f 284/1109/1104 280/1110/1091 285/1106/1101 +f 285/1106/1101 286/1107/1102 287/1108/1103 +f 499/1944/1843 286/1945/1102 290/1946/1844 +f 290/1946/1844 491/1942/1841 498/1943/1842 +f 498/1943/1842 499/1944/1843 290/1946/1844 +f 285/1106/1101 287/1108/1103 284/1109/1104 +f 282/1099/1094 279/1098/1093 281/1096/1092 +f 276/1087/1083 274/1086/1082 275/1084/1080 +f 274/1086/1082 273/1085/1081 275/1084/1080 +f 466/1984/1873 273/1983/1081 269/1982/1872 +f 466/1984/1873 269/1982/1872 272/1981/1871 +f 272/1981/1871 381/1980/1870 466/1984/1873 +s 5 +f 277/1089/1085 278/1090/1086 276/1091/1087 +s 6 +f 279/1094/1090 278/1093/1089 277/1092/1088 +s 7 +f 280/1100/1095 283/1101/1096 282/1102/1097 +s 8 +f 284/1105/1100 283/1104/1099 280/1103/1098 +s 9 +usemtl Red_Roof +f 289/1112/1106 290/1113/1107 286/1114/1108 +f 286/1114/1108 285/1115/1109 288/1111/1105 +f 393/1971/1865 275/1975/1863 273/1974/1867 +f 273/1974/1867 466/1973/1572 392/1972/1866 +f 273/1974/1867 392/1972/1866 393/1971/1865 +f 288/1111/1105 289/1112/1106 286/1114/1108 +usemtl Dark_Red_Roof +f 401/1958/1855 285/1959/1109 280/1960/1856 +f 280/1960/1856 281/1961/1857 399/1957/1854 +f 397/1966/1861 281/1970/1857 277/1969/1864 +f 277/1969/1864 275/1968/1863 395/1967/1862 +f 388/1616/1571 466/1617/1572 381/1618/1573 +f 386/1615/1570 388/1616/1571 381/1618/1573 +f 277/1969/1864 395/1967/1862 397/1966/1861 +f 399/1957/1854 401/1958/1855 280/1960/1856 +f 491/1848/1763 290/1847/1107 497/1846/1762 +f 491/1848/1763 497/1846/1762 496/1845/1761 +s 10 +usemtl Red_Roof +f 291/1116/1110 292/1117/1111 289/1118/1112 +f 291/1116/1110 289/1118/1112 288/1119/1113 +f 288/1119/1113 293/1120/1114 291/1116/1110 +f 391/1316/1305 394/1320/1308 393/1319/1307 +f 393/1319/1307 392/1318/1306 391/1316/1305 +f 392/1318/1306 387/1317/1297 391/1316/1305 +usemtl Dark_Red_Roof +f 496/1925/1829 292/1923/1111 294/1922/1827 +f 496/1925/1829 497/1924/1828 292/1923/1111 +f 400/1338/1325 293/1339/1114 401/1340/1326 +f 400/1338/1325 401/1340/1326 399/1341/1327 +f 399/1341/1327 398/1342/1318 400/1338/1325 +f 396/1327/1315 398/1331/1318 397/1330/1317 +f 397/1330/1317 395/1329/1316 396/1327/1315 +f 395/1329/1316 394/1328/1308 396/1327/1315 +f 387/1308/1297 388/1309/1298 386/1310/1299 +f 385/1307/1296 387/1308/1297 386/1310/1299 +s 11 +usemtl Red_Roof +f 298/1935/1836 291/1936/1118 293/1937/1837 +f 293/1937/1837 400/1938/1334 300/1934/1835 +f 411/1596/1554 396/1600/1332 394/1599/1556 +f 394/1599/1556 391/1598/1303 410/1597/1555 +f 394/1599/1556 410/1597/1555 411/1596/1554 +f 300/1934/1835 298/1935/1836 293/1937/1837 +usemtl Dark_Red_Roof +f 295/1122/1116 294/1121/1115 292/1125/1119 +f 292/1125/1119 291/1124/1118 296/1123/1117 +f 403/1350/1335 400/1349/1334 398/1348/1333 +f 398/1348/1333 396/1347/1332 402/1346/1331 +f 390/1313/1302 391/1314/1303 387/1315/1304 +f 387/1315/1304 385/1311/1300 389/1312/1301 +f 389/1312/1301 390/1313/1302 387/1315/1304 +f 403/1350/1335 398/1348/1333 402/1346/1331 +f 292/1125/1119 296/1123/1117 295/1122/1116 +s 12 +f 296/1128/1122 291/1127/1121 297/1126/1120 +s 13 +usemtl Red_Roof +f 297/1129/1123 291/1130/1124 298/1131/1125 +s 14 +f 300/1135/1129 301/1136/1130 299/1132/1126 +f 299/1132/1126 297/1133/1127 298/1134/1128 +f 408/1364/1348 404/1368/1340 411/1367/1351 +f 411/1367/1351 410/1366/1350 408/1364/1348 +f 410/1366/1350 409/1365/1349 408/1364/1348 +f 299/1132/1126 298/1134/1128 300/1135/1129 +usemtl Dark_Red_Roof +f 296/1931/1833 297/1932/1127 304/1933/1834 +f 304/1933/1834 493/1929/1831 295/1930/1832 +f 295/1930/1832 296/1931/1833 304/1933/1834 +f 405/1356/1341 301/1357/1130 403/1358/1342 +f 402/1354/1339 405/1356/1341 403/1358/1342 +f 402/1354/1339 404/1355/1340 405/1356/1341 +f 414/1611/1566 409/1610/1349 390/1609/1565 +f 414/1611/1566 390/1609/1565 389/1608/1564 +f 389/1608/1564 384/1607/1563 414/1611/1566 +s 15 +usemtl Red_Roof +f 297/1140/1134 299/1141/1135 302/1137/1131 +f 412/1372/1355 408/1376/1345 409/1375/1358 +f 409/1375/1358 414/1374/1357 413/1373/1356 +f 409/1375/1358 413/1373/1356 412/1372/1355 +f 302/1137/1131 303/1138/1132 297/1140/1134 +f 303/1138/1132 304/1139/1133 297/1140/1134 +usemtl Dark_Red_Roof +f 422/1915/1822 299/1916/1135 301/1917/1823 +f 301/1917/1823 405/1918/1347 420/1914/1821 +f 406/1359/1343 405/1363/1347 404/1362/1346 +f 404/1362/1346 408/1361/1345 407/1360/1344 +f 417/1396/1377 414/1397/1357 384/1398/1378 +f 418/1395/1376 417/1396/1377 384/1398/1378 +f 404/1362/1346 407/1360/1344 406/1359/1343 +f 420/1914/1821 422/1915/1822 301/1917/1823 +f 493/1838/1754 304/1837/1133 495/1836/1753 +f 493/1838/1754 495/1836/1753 494/1835/1752 +s 16 +usemtl Red_Roof +f 302/1145/1139 307/1146/1140 305/1142/1136 +f 305/1142/1136 306/1143/1137 303/1144/1138 +f 376/1380/1362 415/1384/1366 412/1383/1365 +f 412/1383/1365 413/1382/1364 376/1380/1362 +f 413/1382/1364 416/1381/1363 376/1380/1362 +f 305/1142/1136 303/1144/1138 302/1145/1139 +usemtl Dark_Red_Roof +f 494/1895/1806 306/1893/1137 308/1892/1804 +f 494/1895/1806 495/1894/1805 306/1893/1137 +f 421/1408/1388 307/1409/1140 422/1410/1389 +f 421/1408/1388 422/1410/1389 420/1411/1390 +f 420/1411/1390 419/1412/1391 421/1408/1388 +f 423/1585/1545 419/1589/1391 406/1588/1547 +f 406/1588/1547 407/1587/1546 423/1585/1545 +f 407/1587/1546 415/1586/1366 423/1585/1545 +f 416/1392/1363 417/1393/1374 418/1394/1375 +f 380/1391/1373 416/1392/1363 418/1394/1375 +s 17 +usemtl Red_Roof +f 312/1904/1813 305/1905/1144 307/1906/1814 +f 307/1906/1814 421/1907/1396 313/1903/1812 +f 375/1708/1655 423/1712/1393 415/1711/1657 +f 415/1711/1657 376/1710/1653 374/1709/1656 +f 415/1711/1657 374/1709/1656 375/1708/1655 +f 313/1903/1812 312/1904/1813 307/1906/1814 +usemtl Dark_Red_Roof +f 309/1148/1142 308/1147/1141 306/1151/1145 +f 306/1151/1145 305/1150/1144 310/1149/1143 +f 425/1416/1395 421/1417/1396 419/1413/1392 +f 419/1413/1392 423/1414/1393 424/1415/1394 +f 377/1705/1652 376/1706/1653 416/1707/1654 +f 416/1707/1654 380/1703/1650 379/1704/1651 +f 379/1704/1651 377/1705/1652 416/1707/1654 +f 419/1413/1392 424/1415/1394 425/1416/1395 +f 306/1151/1145 310/1149/1143 309/1148/1142 +s 18 +f 310/1154/1148 305/1153/1147 311/1152/1146 +s 19 +usemtl Red_Roof +f 311/1155/1149 305/1156/1150 312/1157/1151 +s 20 +f 312/1158/1152 313/1159/1153 314/1160/1154 +f 373/1281/1271 375/1280/1270 374/1279/1269 +f 370/1282/1272 373/1281/1271 374/1279/1269 +f 312/1158/1152 314/1160/1154 311/1161/1155 +usemtl Dark_Red_Roof +f 314/1422/1154 425/1423/1400 424/1424/1401 +f 373/1421/1271 314/1422/1154 424/1424/1401 +f 378/1291/1280 370/1290/1272 377/1289/1279 +f 379/1292/1281 378/1291/1280 377/1289/1279 +f 311/1900/1155 319/1901/1810 309/1902/1811 +f 310/1899/1809 311/1900/1155 309/1902/1811 +s 21 +usemtl Red_Roof +f 311/1165/1159 314/1164/1158 316/1163/1157 +f 372/1276/1266 373/1277/1267 370/1278/1268 +f 371/1275/1265 372/1276/1266 370/1278/1268 +f 311/1165/1159 316/1163/1157 315/1162/1156 +usemtl Dark_Red_Roof +f 426/1425/1402 314/1426/1158 373/1427/1267 +f 369/1726/1669 370/1727/1268 378/1728/1670 +f 369/1726/1669 378/1728/1670 368/1729/1671 +f 426/1425/1402 373/1427/1267 427/1428/1403 +f 320/1175/1168 311/1173/1159 318/1172/1166 +f 320/1175/1168 319/1174/1167 311/1173/1159 +s 22 +usemtl Red_Roof +f 315/1168/1162 317/1167/1161 311/1166/1160 +s 23 +usemtl Dark_Red_Roof +f 311/1169/1163 317/1170/1164 318/1171/1165 +s 24 +f 320/1178/1171 321/1177/1170 319/1176/1169 +f 453/1823/1742 326/1822/1740 321/1821/1170 +f 448/1820/1741 327/1819/1738 326/1818/1740 +f 451/1817/1739 322/1816/1180 327/1815/1738 +f 328/1189/1182 323/1188/1181 322/1187/1180 +s 25 +f 325/1182/1175 319/1183/1176 321/1184/1177 +f 294/1831/1748 325/1827/1175 483/1826/1745 +f 483/1826/1745 492/1825/1744 491/1824/1743 +f 483/1826/1745 491/1824/1743 294/1831/1748 +f 294/1831/1748 493/1830/1747 325/1827/1175 +f 493/1830/1747 308/1829/1746 325/1827/1175 +f 308/1829/1746 319/1828/1176 325/1827/1175 +f 324/1181/1174 325/1182/1175 327/1186/1179 +f 327/1186/1179 322/1179/1172 324/1181/1174 +f 322/1179/1172 323/1180/1173 324/1181/1174 +f 325/1182/1175 326/1185/1178 327/1186/1179 +f 325/1182/1175 321/1184/1177 326/1185/1178 +s 26 +f 328/1191/1184 322/1190/1183 331/1194/1187 +f 331/1194/1187 330/1193/1186 329/1192/1185 +f 333/1203/1195 330/1202/1186 334/1201/1194 +f 334/1201/1194 336/1205/1197 335/1204/1196 +f 338/1214/1205 336/1213/1197 339/1212/1204 +f 339/1212/1204 341/1216/1207 340/1215/1206 +f 343/1226/1217 341/1227/1207 344/1223/1214 +f 344/1223/1214 345/1224/1215 346/1225/1216 +f 489/1771/1703 345/1772/1215 349/1773/1704 +f 349/1773/1704 467/1769/1701 488/1770/1702 +f 488/1770/1702 489/1771/1703 349/1773/1704 +f 344/1223/1214 346/1225/1216 343/1226/1217 +f 340/1215/1206 338/1214/1205 339/1212/1204 +f 335/1204/1196 333/1203/1195 334/1201/1194 +f 331/1194/1187 329/1192/1185 328/1191/1184 +s 27 +f 330/1195/1188 332/1196/1189 329/1197/1190 +s 28 +f 333/1200/1193 332/1199/1192 330/1198/1191 +s 29 +f 336/1206/1198 337/1207/1199 335/1208/1200 +s 30 +f 338/1211/1203 337/1210/1202 336/1209/1201 +s 31 +f 341/1217/1208 342/1218/1209 340/1219/1210 +s 32 +f 343/1222/1213 342/1221/1212 341/1220/1211 +s 33 +usemtl Red_Roof +f 348/1229/1219 349/1230/1220 345/1231/1221 +f 345/1231/1221 344/1232/1222 347/1228/1218 +f 446/1802/1728 334/1806/1723 330/1805/1731 +f 330/1805/1731 331/1804/1730 445/1803/1729 +f 330/1805/1731 445/1803/1729 446/1802/1728 +f 347/1228/1218 348/1229/1219 345/1231/1221 +usemtl Dark_Red_Roof +f 463/1785/1715 344/1786/1222 341/1787/1716 +f 341/1787/1716 339/1788/1717 461/1784/1714 +f 459/1793/1721 339/1797/1717 336/1796/1724 +f 336/1796/1724 334/1795/1723 447/1794/1722 +f 451/1807/1732 331/1809/1730 322/1810/1734 +f 451/1807/1732 450/1808/1733 331/1809/1730 +f 336/1796/1724 447/1794/1722 459/1793/1721 +f 461/1784/1714 463/1785/1715 341/1787/1716 +f 349/1684/1220 487/1683/1632 486/1682/1631 +f 467/1685/1633 349/1684/1220 486/1682/1631 +s 34 +usemtl Red_Roof +f 350/1233/1223 351/1234/1224 348/1235/1225 +f 350/1233/1223 348/1235/1225 347/1236/1226 +f 347/1236/1226 352/1237/1227 350/1233/1223 +f 441/1463/1437 442/1467/1441 446/1466/1440 +f 446/1466/1440 445/1465/1439 441/1463/1437 +f 445/1465/1439 444/1464/1438 441/1463/1437 +usemtl Dark_Red_Roof +f 487/1751/1688 351/1750/1224 353/1749/1687 +f 486/1752/1689 487/1751/1688 353/1749/1687 +f 462/1530/1499 352/1531/1227 463/1532/1500 +f 462/1530/1499 463/1532/1500 461/1533/1501 +f 461/1533/1501 460/1534/1492 462/1530/1499 +f 443/1519/1489 460/1523/1492 459/1522/1491 +f 459/1522/1491 447/1521/1490 443/1519/1489 +f 447/1521/1490 442/1520/1441 443/1519/1489 +f 327/1479/1452 444/1480/1438 450/1481/1453 +f 327/1479/1452 450/1481/1453 451/1482/1454 +s 35 +usemtl Red_Roof +f 357/1762/1696 350/1763/1231 352/1764/1697 +f 352/1764/1697 462/1765/1506 359/1761/1695 +f 440/1458/1432 443/1462/1436 442/1461/1435 +f 442/1461/1435 441/1460/1434 439/1459/1433 +f 442/1461/1435 439/1459/1433 440/1458/1432 +f 359/1761/1695 357/1762/1696 352/1764/1697 +usemtl Dark_Red_Roof +f 354/1239/1229 353/1238/1228 351/1242/1232 +f 351/1242/1232 350/1241/1231 355/1240/1230 +f 464/1540/1507 462/1539/1506 460/1538/1505 +f 460/1538/1505 443/1542/1436 465/1541/1508 +f 449/1476/1450 441/1477/1434 444/1478/1451 +f 444/1478/1451 327/1474/1448 448/1475/1449 +f 448/1475/1449 449/1476/1450 444/1478/1451 +f 465/1541/1508 464/1540/1507 460/1538/1505 +f 351/1242/1232 355/1240/1230 354/1239/1229 +s 36 +f 355/1245/1235 350/1244/1234 356/1243/1233 +s 37 +usemtl Red_Roof +f 356/1246/1236 350/1247/1237 357/1248/1238 +s 38 +f 359/1252/1242 360/1253/1243 358/1249/1239 +f 358/1249/1239 356/1250/1240 357/1251/1241 +f 436/1453/1427 437/1457/1431 440/1456/1430 +f 440/1456/1430 439/1455/1429 436/1453/1427 +f 439/1455/1429 438/1454/1428 436/1453/1427 +f 358/1249/1239 357/1251/1241 359/1252/1242 +usemtl Dark_Red_Roof +f 355/1758/1693 356/1759/1240 363/1760/1694 +f 363/1760/1694 470/1756/1691 354/1757/1692 +f 354/1757/1692 355/1758/1693 363/1760/1694 +f 433/1560/1524 360/1564/1243 464/1563/1526 +f 464/1563/1526 465/1562/1525 433/1560/1524 +f 465/1562/1525 437/1561/1431 433/1560/1524 +f 452/1493/1464 438/1492/1428 449/1491/1463 +f 452/1493/1464 449/1491/1463 448/1490/1462 +f 448/1490/1462 326/1489/1461 452/1493/1464 +s 39 +usemtl Red_Roof +f 356/1257/1247 358/1258/1248 361/1254/1244 +f 457/1552/1518 436/1556/1424 438/1555/1520 +f 438/1555/1520 452/1554/1467 456/1553/1519 +f 438/1555/1520 456/1553/1519 457/1552/1518 +f 361/1254/1244 362/1255/1245 356/1257/1247 +f 362/1255/1245 363/1256/1246 356/1257/1247 +usemtl Dark_Red_Roof +f 431/1742/1682 358/1743/1248 360/1744/1683 +f 360/1744/1683 433/1745/1426 432/1741/1681 +f 434/1448/1422 433/1452/1426 437/1451/1425 +f 437/1451/1425 436/1450/1424 435/1449/1423 +f 453/1494/1465 452/1496/1467 326/1497/1468 +f 453/1494/1465 454/1495/1466 452/1496/1467 +f 437/1451/1425 435/1449/1423 434/1448/1422 +f 432/1741/1681 431/1742/1682 360/1744/1683 +f 363/1674/1246 485/1673/1623 484/1672/1622 +f 470/1675/1624 363/1674/1246 484/1672/1622 +s 40 +usemtl Red_Roof +f 361/1262/1252 366/1263/1253 364/1259/1249 +f 364/1259/1249 365/1260/1250 362/1261/1251 +f 317/1508/1479 458/1512/1482 457/1511/1481 +f 457/1511/1481 456/1510/1480 317/1508/1479 +f 456/1510/1480 455/1509/1470 317/1508/1479 +f 364/1259/1249 362/1261/1251 361/1262/1252 +usemtl Dark_Red_Roof +f 485/1721/1665 365/1720/1250 367/1719/1664 +f 484/1722/1666 485/1721/1665 367/1719/1664 +f 430/1437/1412 366/1438/1253 431/1439/1413 +f 430/1437/1412 431/1439/1413 432/1440/1414 +f 432/1440/1414 429/1441/1415 430/1437/1412 +f 428/1571/1533 429/1575/1415 434/1574/1535 +f 434/1574/1535 435/1573/1534 428/1571/1533 +f 435/1573/1534 458/1572/1482 428/1571/1533 +f 321/1498/1469 455/1499/1470 454/1500/1471 +f 321/1498/1469 454/1500/1471 453/1501/1472 +s 41 +usemtl Red_Roof +f 371/1731/1673 364/1732/1257 366/1733/1674 +f 366/1733/1674 430/1734/1410 372/1730/1672 +f 316/1881/1795 428/1885/1408 458/1884/1797 +f 458/1884/1797 317/1883/1793 315/1882/1796 +f 458/1884/1797 315/1882/1796 316/1881/1795 +f 372/1730/1672 371/1731/1673 366/1733/1674 +usemtl Dark_Red_Roof +f 368/1265/1255 367/1264/1254 365/1268/1258 +f 365/1268/1258 364/1267/1257 369/1266/1256 +f 427/1436/1411 430/1435/1410 429/1434/1409 +f 429/1434/1409 428/1433/1408 426/1432/1407 +f 318/1878/1792 317/1879/1793 455/1880/1794 +f 455/1880/1794 321/1876/1790 320/1877/1791 +f 320/1877/1791 318/1878/1792 455/1880/1794 +f 427/1436/1411 429/1434/1409 426/1432/1407 +f 365/1268/1258 369/1266/1256 368/1265/1255 +s 42 +f 369/1271/1261 364/1270/1260 370/1269/1259 +s 43 +usemtl Red_Roof +f 370/1272/1262 364/1273/1263 371/1274/1264 +s 44 +f 374/1285/1275 376/1284/1274 370/1283/1273 +s 45 +usemtl Dark_Red_Roof +f 370/1286/1276 376/1287/1277 377/1288/1278 +s 46 +f 379/1295/1284 380/1294/1283 378/1293/1282 +f 418/1624/1576 384/1623/1574 380/1622/1283 +f 389/1621/1575 385/1620/1293 384/1619/1574 +f 386/1306/1295 381/1305/1294 385/1304/1293 +f 272/1987/1875 271/1986/1874 381/1985/1294 +s 47 +f 383/1299/1288 378/1300/1289 380/1301/1290 +f 353/1632/1582 383/1628/1288 469/1627/1579 +f 469/1627/1579 468/1626/1578 467/1625/1577 +f 469/1627/1579 467/1625/1577 353/1632/1582 +f 353/1632/1582 470/1631/1581 383/1628/1288 +f 470/1631/1581 367/1630/1580 383/1628/1288 +f 367/1630/1580 378/1629/1289 383/1628/1288 +f 382/1298/1287 383/1299/1288 385/1303/1292 +f 385/1303/1292 381/1296/1285 382/1298/1287 +f 381/1296/1285 271/1297/1286 382/1298/1287 +f 383/1299/1288 384/1302/1291 385/1303/1292 +f 383/1299/1288 380/1301/1290 384/1302/1291 +s 48 +usemtl Red_Roof +f 394/1321/1309 275/1322/1310 393/1323/1311 +s 49 +usemtl Dark_Red_Roof +f 395/1326/1314 275/1325/1313 394/1324/1312 +s 50 +f 398/1332/1319 281/1333/1320 397/1334/1321 +s 51 +f 399/1337/1324 281/1336/1323 398/1335/1322 +s 52 +f 293/1343/1328 285/1344/1329 401/1345/1330 +s 53 +f 301/1351/1336 400/1352/1337 403/1353/1338 +s 54 +usemtl Red_Roof +f 404/1369/1352 396/1370/1353 411/1371/1354 +s 55 +f 415/1377/1359 408/1378/1360 412/1379/1361 +s 56 +f 413/1387/1369 414/1386/1368 416/1385/1367 +s 57 +usemtl Dark_Red_Roof +f 416/1388/1370 414/1389/1371 417/1390/1372 +s 58 +usemtl Red_Roof +f 410/1401/1381 391/1400/1380 409/1399/1379 +s 59 +usemtl Dark_Red_Roof +f 419/1402/1382 405/1403/1383 406/1404/1384 +s 60 +f 420/1407/1387 405/1406/1386 419/1405/1385 +s 61 +f 314/1418/1397 421/1419/1398 425/1420/1399 +s 62 +f 426/1431/1406 428/1430/1405 314/1429/1404 +s 63 +f 432/1444/1418 433/1443/1417 429/1442/1416 +s 64 +f 429/1445/1419 433/1446/1420 434/1447/1421 +s 65 +usemtl Red_Roof +f 442/1468/1442 334/1469/1443 446/1470/1444 +s 66 +usemtl Dark_Red_Roof +f 447/1473/1447 334/1472/1446 442/1471/1445 +s 67 +f 444/1483/1455 331/1484/1456 450/1485/1457 +s 68 +f 438/1486/1458 441/1487/1459 449/1488/1460 +s 69 +f 455/1502/1473 452/1503/1474 454/1504/1475 +s 70 +usemtl Red_Roof +f 456/1507/1478 452/1506/1477 455/1505/1476 +s 71 +f 458/1513/1483 436/1514/1484 457/1515/1485 +s 72 +f 445/1518/1488 331/1517/1487 444/1516/1486 +s 73 +usemtl Dark_Red_Roof +f 460/1524/1493 339/1525/1494 459/1526/1495 +s 74 +f 461/1529/1498 339/1528/1497 460/1527/1496 +s 75 +f 352/1535/1502 344/1536/1503 463/1537/1504 +s 76 +f 465/1545/1511 443/1544/1510 437/1543/1509 +s 77 +f 360/1546/1512 462/1547/1513 464/1548/1514 +s 78 +usemtl Red_Roof +f 437/1549/1515 443/1550/1516 440/1551/1517 +s 79 +f 439/1559/1523 441/1558/1522 438/1557/1521 +s 80 +usemtl Dark_Red_Roof +f 435/1567/1529 436/1566/1528 458/1565/1527 +s 81 +f 366/1568/1530 358/1569/1531 431/1570/1532 +s 82 +f 373/1576/1536 430/1577/1537 427/1578/1538 +s 83 +f 424/1581/1541 423/1580/1540 373/1579/1539 +s 84 +f 307/1582/1542 299/1583/1543 422/1584/1544 +s 85 +f 407/1592/1550 408/1591/1549 415/1590/1548 +s 86 +f 402/1595/1553 396/1594/1552 404/1593/1551 +s 87 +usemtl Red_Roof +f 392/1603/1559 466/1602/1558 387/1601/1557 +s 88 +usemtl Dark_Red_Roof +f 409/1604/1560 391/1605/1561 390/1606/1562 +s 89 +f 387/1612/1567 466/1613/1568 388/1614/1569 +s 90 +f 383/1636/1586 472/1635/1585 469/1633/1583 +f 472/1635/1585 471/1634/1584 469/1633/1583 +s 91 +usemtl Wood_Darker +f 474/1638/1588 473/1637/1587 471/1642/1592 +f 472/1641/1591 476/1640/1590 474/1638/1588 +f 474/1638/1588 471/1642/1592 472/1641/1591 +f 476/1640/1590 475/1639/1589 474/1638/1588 +s 92 +f 478/1646/1596 477/1645/1595 475/1644/1594 +f 478/1646/1596 475/1644/1594 476/1643/1593 +s 93 +f 482/1652/1602 481/1651/1601 480/1650/1600 +f 480/1650/1600 478/1648/1598 479/1647/1597 +f 480/1650/1600 477/1649/1599 478/1648/1598 +f 479/1647/1597 482/1652/1602 480/1650/1600 +s 94 +usemtl Dark_Red_Roof +f 479/1653/1603 478/1654/1604 325/1656/1606 +f 478/1654/1604 483/1655/1605 325/1656/1606 +s 95 +usemtl Wood_Darker +f 471/1657/1607 473/1658/1608 482/1660/1610 +f 473/1658/1608 481/1659/1609 482/1660/1610 +s 96 +f 480/1664/1614 481/1663/1613 473/1662/1612 +f 480/1664/1614 473/1662/1612 474/1661/1611 +s 97 +f 474/1665/1615 475/1666/1616 480/1668/1618 +f 475/1666/1616 477/1667/1617 480/1668/1618 +s 98 +usemtl Dark_Red_Roof +f 367/1669/1619 470/1670/1620 484/1671/1621 +f 470/1753/1620 353/1754/1628 354/1755/1690 +f 353/1679/1628 467/1680/1629 486/1681/1630 +f 467/1689/1629 468/1690/1637 488/1691/1638 +f 378/1723/1667 367/1724/1619 368/1725/1668 +s 99 +f 485/1678/1627 363/1677/1626 365/1676/1625 +s 100 +f 487/1688/1636 349/1687/1635 351/1686/1634 +s 101 +f 488/1695/1642 468/1694/1641 489/1692/1639 +f 468/1694/1641 490/1693/1640 489/1692/1639 +f 490/1780/1640 342/1781/1711 346/1783/1713 +f 342/1791/1711 337/1790/1719 338/1789/1718 +f 333/1801/1727 337/1799/1719 332/1798/1725 +f 332/1812/1725 323/1813/1736 328/1814/1737 +f 329/1811/1735 332/1812/1725 328/1814/1737 +f 333/1801/1727 335/1800/1726 337/1799/1719 +f 340/1792/1720 342/1791/1711 338/1789/1718 +f 342/1781/1711 343/1782/1712 346/1783/1713 +s 102 +f 489/1698/1645 490/1697/1644 345/1696/1643 +s 103 +f 382/1699/1646 476/1700/1647 383/1702/1649 +f 476/1700/1647 472/1701/1648 383/1702/1649 +s 104 +usemtl Red_Roof +f 373/1713/1658 423/1714/1659 375/1715/1660 +s 105 +f 372/1718/1663 430/1717/1662 373/1716/1661 +s 106 +f 361/1737/1677 358/1736/1676 366/1735/1675 +s 107 +f 365/1738/1678 363/1739/1679 362/1740/1680 +s 108 +f 359/1748/1686 462/1747/1685 360/1746/1684 +s 109 +f 347/1768/1700 344/1767/1699 352/1766/1698 +s 110 +f 351/1774/1705 349/1775/1706 348/1776/1707 +s 111 +usemtl Dark_Red_Roof +f 345/1777/1708 490/1778/1709 346/1779/1710 +s 112 +f 308/1832/1749 493/1833/1750 494/1834/1751 +f 493/1926/1750 294/1927/1758 295/1928/1830 +f 294/1842/1758 491/1843/1759 496/1844/1760 +f 491/1852/1759 492/1853/1767 498/1854/1768 +f 319/1896/1807 308/1897/1749 309/1898/1808 +s 113 +f 495/1841/1757 304/1840/1756 306/1839/1755 +s 114 +f 497/1851/1766 290/1850/1765 292/1849/1764 +s 115 +f 499/1861/1775 500/1860/1774 286/1859/1773 +s 116 +f 325/1865/1779 324/1864/1778 482/1863/1777 +f 325/1865/1779 482/1863/1777 479/1862/1776 +s 117 +f 468/1875/1789 469/1874/1788 471/1873/1787 +f 471/1873/1787 482/1872/1786 337/1868/1782 +f 482/1872/1786 332/1869/1783 337/1868/1782 +f 482/1872/1786 323/1870/1784 332/1869/1783 +f 482/1872/1786 324/1871/1785 323/1870/1784 +f 337/1868/1782 342/1867/1781 471/1873/1787 +f 342/1867/1781 490/1866/1780 471/1873/1787 +f 490/1866/1780 468/1875/1789 471/1873/1787 +s 118 +usemtl Red_Roof +f 314/1886/1798 428/1887/1799 316/1888/1800 +s 119 +f 313/1891/1803 421/1890/1802 314/1889/1801 +s 120 +f 302/1910/1817 299/1909/1816 307/1908/1815 +s 121 +f 306/1911/1818 304/1912/1819 303/1913/1820 +s 122 +f 300/1921/1826 400/1920/1825 301/1919/1824 +s 123 +f 288/1941/1840 285/1940/1839 293/1939/1838 +s 124 +f 292/1947/1845 290/1948/1846 289/1949/1847 +s 125 +usemtl Dark_Red_Roof +f 286/1950/1848 500/1951/1849 287/1952/1850 +s 126 +f 476/1994/1882 382/1993/1881 271/1992/1880 +f 478/1995/1883 476/1994/1882 283/1989/1877 +f 283/1989/1877 500/1988/1876 478/1995/1883 +f 500/1988/1876 492/1997/1885 478/1995/1883 +f 492/1997/1885 483/1996/1884 478/1995/1883 +f 476/1994/1882 278/1990/1878 283/1989/1877 +f 476/1994/1882 270/1991/1879 278/1990/1878 +f 476/1994/1882 271/1992/1880 270/1991/1879 +o object2 +g object2 +v -0.52619048 0.90516376 0.48759427 +v -0.52619048 1.00476190 0.40476190 +v -0.44523810 1.00476190 0.40476190 +v -0.44523810 0.90516376 0.48759427 +v -0.52619048 0.96240573 0.54483624 +v -0.52619048 1.08571429 0.40476190 +v -0.44523810 0.96240573 0.54483624 +v -0.44523810 1.08571429 0.40476190 +v -0.52619048 1.49047619 -0.00000000 +v -0.44523810 1.49047619 -0.00000000 +v -0.44523810 1.40952381 -0.00000000 +v -0.52619048 1.40952381 -0.00000000 +vt 0.00000000 0.00000000 +vt 0.99999999 0.00000000 +vt 0.99999999 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.99999999 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 0.99999999 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vn -0.00000000 -0.63942740 -0.76885148 +vn -0.00000000 -0.63942740 -0.76885148 +vn -0.00000000 -0.63942740 -0.76885148 +vn -0.00000000 -0.63942740 -0.76885148 +vn -1.00000000 -0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn 0.00000000 0.75059887 0.66075815 +vn 0.00000000 0.75059887 0.66075815 +vn 0.00000000 0.75059887 0.66075815 +vn 0.00000000 0.75059887 0.66075815 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 1.00000000 0.00000000 -0.00000000 +vn 1.00000000 0.00000000 -0.00000000 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +s 1 +usemtl Wood_Darker +f 501/1998/1886 502/1999/1887 503/2000/1888 +f 501/1998/1886 503/2000/1888 504/2001/1889 +s 2 +f 505/2002/1890 506/2003/1891 502/2004/1892 +f 506/2024/1891 509/2023/1911 512/2022/1910 +f 502/2025/1892 506/2024/1891 512/2022/1910 +f 505/2002/1890 502/2004/1892 501/2005/1893 +s 3 +f 507/2006/1894 508/2007/1895 505/2009/1897 +f 508/2007/1895 506/2008/1896 505/2009/1897 +s 4 +f 506/2013/1901 508/2012/1900 510/2011/1899 +f 506/2013/1901 510/2011/1899 509/2010/1898 +s 5 +f 511/2014/1902 512/2015/1903 510/2017/1905 +f 512/2015/1903 509/2016/1904 510/2017/1905 +s 6 +f 508/2021/1909 503/2020/1908 511/2019/1907 +f 504/2030/1916 503/2031/1908 507/2033/1917 +f 503/2031/1908 508/2032/1909 507/2033/1917 +f 508/2021/1909 511/2019/1907 510/2018/1906 +s 7 +f 504/2026/1912 507/2027/1913 501/2029/1915 +f 507/2027/1913 505/2028/1914 501/2029/1915 +s 8 +f 503/2037/1921 502/2036/1920 511/2034/1918 +f 502/2036/1920 512/2035/1919 511/2034/1918 +o object3 +g object3 +v 0.52619048 0.90516376 0.48759427 +v 0.52619048 1.00476190 0.40476190 +v 0.44523810 1.00476190 0.40476190 +v 0.44523810 0.90516376 0.48759427 +v 0.44523810 1.08571429 0.40476190 +v 0.44523810 0.96240573 0.54483624 +v 0.52619048 1.08571429 0.40476190 +v 0.52619048 0.96240573 0.54483624 +v 0.52619048 1.49047619 -0.00000000 +v 0.44523810 1.49047619 -0.00000000 +v 0.44523810 1.40952381 -0.00000000 +v 0.52619048 1.40952381 -0.00000000 +vt 0.00000000 0.00000000 +vt 0.99999999 0.00000000 +vt 0.99999999 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.99999999 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.99999999 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vn 0.00000000 -0.63942740 -0.76885148 +vn 0.00000000 -0.63942740 -0.76885148 +vn 0.00000000 -0.63942740 -0.76885148 +vn 0.00000000 -0.63942740 -0.76885148 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -0.00000000 0.75059887 0.66075815 +vn -0.00000000 0.75059887 0.66075815 +vn -0.00000000 0.75059887 0.66075815 +vn -0.00000000 0.75059887 0.66075815 +vn -0.00000000 0.70710678 0.70710678 +vn -0.00000000 0.70710678 0.70710678 +vn -0.00000000 0.70710678 0.70710678 +vn -0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn 1.00000000 -0.00000000 0.00000000 +vn 1.00000000 -0.00000000 0.00000000 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +s 1 +usemtl Wood_Darker +f 516/2041/1925 515/2040/1924 514/2039/1923 +f 516/2041/1925 514/2039/1923 513/2038/1922 +s 2 +f 518/2045/1929 517/2044/1928 515/2043/1927 +f 523/2063/1947 515/2064/1927 517/2065/1928 +f 522/2062/1946 523/2063/1947 517/2065/1928 +f 518/2045/1929 515/2043/1927 516/2042/1926 +s 3 +f 520/2049/1933 519/2048/1932 518/2046/1930 +f 519/2048/1932 517/2047/1931 518/2046/1930 +s 4 +f 521/2050/1934 522/2051/1935 519/2053/1937 +f 522/2051/1935 517/2052/1936 519/2053/1937 +s 5 +f 522/2057/1941 521/2056/1940 523/2054/1938 +f 521/2056/1940 524/2055/1939 523/2054/1938 +s 6 +f 524/2058/1942 521/2059/1943 519/2060/1944 +f 514/2072/1945 519/2071/1944 520/2070/1952 +f 513/2073/1953 514/2072/1945 520/2070/1952 +f 524/2058/1942 519/2060/1944 514/2061/1945 +s 7 +f 513/2069/1951 520/2068/1950 516/2066/1948 +f 520/2068/1950 518/2067/1949 516/2066/1948 +s 8 +f 523/2074/1954 524/2075/1955 514/2076/1956 +f 523/2074/1954 514/2076/1956 515/2077/1957 +o object4 +g object4 +v -0.58571429 0.98214286 0.02142857 +v -0.58571429 0.98214286 -0.02142857 +v -0.55000000 0.98214286 -0.02142857 +v -0.55000000 0.98214286 0.02142857 +v -0.55000000 0.69642857 -0.02142857 +v -0.55000000 0.69642857 0.02142857 +v -0.55000000 0.72857143 0.05357143 +v -0.55000000 0.95000000 0.05357143 +v -0.55000000 0.95000000 -0.05357143 +v -0.55000000 0.72857143 -0.05357143 +v -0.58571429 0.72857143 0.05357143 +v -0.58571429 0.95000000 0.05357143 +v -0.58571429 0.95000000 -0.05357143 +v -0.58571429 0.72857143 -0.05357143 +v -0.58571429 0.69642857 -0.02142857 +v -0.58571429 0.69642857 0.02142857 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.30000000 0.00000000 +vt 0.70000000 0.00000000 +vt 1.00000000 0.11250000 +vt 1.00000000 0.88750000 +vt 0.70000000 1.00000000 +vt 0.30000000 1.00000000 +vt 0.00000000 0.88750000 +vt 0.00000000 0.11250000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.30000000 0.00000000 +vt 0.70000000 0.00000000 +vt 1.00000000 0.11250000 +vt 1.00000000 0.88750000 +vt 0.70000000 1.00000000 +vt 0.30000000 1.00000000 +vt 0.00000000 0.88750000 +vt 0.00000000 0.11250000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn -0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn -0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn 0.00000000 -0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn 0.00000000 0.70710678 0.70710678 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +s 1 +usemtl Wood_Darker +f 528/2081/1961 527/2080/1960 525/2078/1958 +f 527/2080/1960 526/2079/1959 525/2078/1958 +s 2 +f 527/2087/1967 528/2086/1966 532/2085/1965 +f 534/2089/1969 533/2088/1968 532/2085/1965 +f 531/2084/1964 530/2083/1963 534/2089/1969 +f 530/2083/1963 529/2082/1962 534/2089/1969 +f 532/2085/1965 531/2084/1964 534/2089/1969 +f 533/2088/1968 527/2087/1967 532/2085/1965 +s 3 +f 536/2093/1973 535/2092/1972 532/2090/1970 +f 535/2092/1972 531/2091/1971 532/2090/1970 +s 4 +f 538/2097/1977 537/2096/1976 534/2094/1974 +f 537/2096/1976 533/2095/1975 534/2094/1974 +s 5 +f 534/2098/1978 529/2099/1979 538/2101/1981 +f 529/2099/1979 539/2100/1980 538/2101/1981 +s 6 +f 540/2105/1985 539/2104/1984 529/2103/1983 +f 540/2105/1985 529/2103/1983 530/2102/1982 +s 7 +f 530/2106/1986 531/2107/1987 540/2109/1989 +f 531/2107/1987 535/2108/1988 540/2109/1989 +s 8 +f 532/2110/1990 528/2111/1991 536/2113/1993 +f 528/2111/1991 525/2112/1992 536/2113/1993 +s 9 +f 536/2117/1997 525/2118/1998 526/2119/1999 +f 536/2117/1997 537/2120/2000 538/2121/2001 +f 538/2121/2001 540/2115/1995 535/2116/1996 +f 538/2121/2001 539/2114/1994 540/2115/1995 +f 538/2121/2001 535/2116/1996 536/2117/1997 +f 536/2117/1997 526/2119/1999 537/2120/2000 +s 10 +f 527/2122/2002 533/2123/2003 526/2125/2005 +f 533/2123/2003 537/2124/2004 526/2125/2005 +o object5 +g object5 +v -0.58498601 1.38561358 -0.06438642 +v -0.58498601 1.38561358 0.06438642 +v -0.60714286 1.45000000 0.00000000 +v -0.36428571 1.40085034 0.04914966 +v -0.36428571 1.40085034 -0.04914966 +v -0.58498601 1.51438642 0.06438642 +v -0.36428571 1.49914966 0.04914966 +v -0.58498601 1.51438642 -0.06438642 +v -0.36428571 1.49914966 -0.04914966 +v -0.00000000 1.49047619 0.04047619 +v -0.00000000 1.49047619 -0.04047619 +v -0.00000000 1.40952381 -0.04047619 +v 0.36428571 1.40085034 -0.04914966 +v 0.36428571 1.49914966 -0.04914966 +v 0.36428571 1.40085034 0.04914966 +v -0.00000000 1.40952381 0.04047619 +v 0.36428571 1.49914966 0.04914966 +v 0.58498601 1.38561358 0.06438642 +v 0.58498601 1.51438642 0.06438642 +v 0.58498601 1.51438642 -0.06438642 +v 0.60714286 1.45000000 0.00000000 +v 0.58498601 1.38561358 -0.06438642 +vt 1.00000000 1.00000000 +vt 0.00000000 0.05590045 +vt 1.00000000 0.00000000 +vt 0.23664558 1.00000000 +vt 0.00000000 0.03872221 +vt 1.00000000 0.00000000 +vt 1.00000000 0.97044123 +vt 0.23664558 1.00000000 +vt 0.00000000 0.03872221 +vt 1.00000000 0.00000000 +vt 1.00000000 0.97044123 +vt 0.00000000 0.97044123 +vt 0.00000000 0.00000000 +vt 1.00000000 0.03872221 +vt 0.76335442 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00525723 +vt 0.82352941 0.00000000 +vt 1.00000000 0.99361622 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00525723 +vt 0.82352941 0.00000000 +vt 1.00000000 0.99361622 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00525723 +vt 0.82352941 0.00000000 +vt 1.00000000 0.99361622 +vt 0.00000000 0.99361622 +vt 0.17647059 0.00000000 +vt 1.00000000 0.00525723 +vt 1.00000000 1.00000000 +vt 0.00000000 0.99361622 +vt 0.17647059 0.00000000 +vt 1.00000000 0.00525723 +vt 1.00000000 1.00000000 +vt 0.23664558 1.00000000 +vt 0.00000000 0.03872221 +vt 1.00000000 0.00000000 +vt 1.00000000 0.97044123 +vt 0.00000000 0.97044123 +vt 0.00000000 0.00000000 +vt 1.00000000 0.03872221 +vt 0.76335442 1.00000000 +vt 1.00000000 0.05590045 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.05590045 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.05590045 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.05590045 +vt 1.00000000 0.00000000 +vt 0.23664558 1.00000000 +vt 0.00000000 0.03872221 +vt 1.00000000 0.00000000 +vt 1.00000000 0.97044123 +vt 0.00000000 0.97044123 +vt 0.00000000 0.00000000 +vt 1.00000000 0.03872221 +vt 0.76335442 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00525723 +vt 0.82352941 0.00000000 +vt 1.00000000 0.99361622 +vt 1.00000000 0.05590045 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.99361622 +vt 0.17647059 0.00000000 +vt 1.00000000 0.00525723 +vt 1.00000000 1.00000000 +vt 0.00000000 0.99361622 +vt 0.17647059 0.00000000 +vt 1.00000000 0.00525723 +vt 1.00000000 1.00000000 +vt 0.00000000 0.97044123 +vt 0.00000000 0.00000000 +vt 1.00000000 0.03872221 +vt 0.76335442 1.00000000 +vt 1.00000000 0.05590045 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 0.05590045 +vt 1.00000000 0.00000000 +vn -0.94557811 -0.32539520 -0.00000000 +vn -0.94557811 -0.32539520 -0.00000000 +vn -0.94557811 -0.32539520 -0.00000000 +vn 0.06887431 -0.99762535 0.00000000 +vn 0.06887431 -0.99762535 0.00000000 +vn 0.06887431 -0.99762535 0.00000000 +vn 0.06887431 -0.99762535 0.00000000 +vn 0.06887431 -0.00000000 0.99762535 +vn 0.06887431 -0.00000000 0.99762535 +vn 0.06887431 -0.00000000 0.99762535 +vn 0.06887431 -0.00000000 0.99762535 +vn 0.06887431 0.99762535 0.00000000 +vn 0.06887431 0.99762535 0.00000000 +vn 0.06887431 0.99762535 0.00000000 +vn 0.06887431 0.99762535 0.00000000 +vn 0.02380278 0.99971667 0.00000000 +vn 0.02380278 0.99971667 0.00000000 +vn 0.02380278 -0.00000000 -0.99971667 +vn 0.02380278 -0.00000000 -0.99971667 +vn 0.02380278 -0.00000000 -0.99971667 +vn 0.02380278 -0.00000000 -0.99971667 +vn -0.02380278 -0.00000000 -0.99971667 +vn -0.02380278 -0.00000000 -0.99971667 +vn -0.02380278 -0.99971667 0.00000000 +vn -0.02380278 -0.99971667 0.00000000 +vn -0.02380278 -0.99971667 0.00000000 +vn -0.02380278 -0.99971667 0.00000000 +vn -0.02380278 -0.00000000 0.99971667 +vn -0.02380278 -0.00000000 0.99971667 +vn -0.02380278 -0.00000000 0.99971667 +vn -0.02380278 -0.00000000 0.99971667 +vn -0.06887431 -0.00000000 0.99762535 +vn -0.06887431 -0.00000000 0.99762535 +vn -0.06887431 0.99762535 0.00000000 +vn -0.06887431 0.99762535 0.00000000 +vn -0.06887431 0.99762535 0.00000000 +vn -0.06887431 0.99762535 0.00000000 +vn 0.94557811 0.32539520 -0.00000000 +vn 0.94557811 0.32539520 -0.00000000 +vn 0.94557811 0.32539520 -0.00000000 +vn 0.94557811 0.00000000 -0.32539520 +vn 0.94557811 0.00000000 -0.32539520 +vn 0.94557811 0.00000000 -0.32539520 +vn 0.94557811 -0.32539520 -0.00000000 +vn 0.94557811 -0.32539520 -0.00000000 +vn 0.94557811 -0.32539520 -0.00000000 +vn 0.94557811 0.00000000 0.32539520 +vn 0.94557811 0.00000000 0.32539520 +vn 0.94557811 0.00000000 0.32539520 +vn -0.06887431 -0.99762535 0.00000000 +vn -0.06887431 -0.99762535 0.00000000 +vn -0.06887431 -0.00000000 -0.99762535 +vn -0.06887431 -0.00000000 -0.99762535 +vn -0.94557811 0.32539520 -0.00000000 +vn -0.94557811 0.32539520 -0.00000000 +vn -0.94557811 0.32539520 -0.00000000 +vn 0.06887431 -0.00000000 -0.99762535 +vn 0.06887431 -0.00000000 -0.99762535 +vn -0.94557811 0.00000000 -0.32539520 +vn -0.94557811 0.00000000 -0.32539520 +vn -0.94557811 0.00000000 -0.32539520 +vn -0.94557811 0.00000000 0.32539520 +vn -0.94557811 0.00000000 0.32539520 +vn -0.94557811 0.00000000 0.32539520 +s 1 +usemtl Wood_Dark +f 541/2126/2006 542/2127/2007 543/2128/2008 +s 2 +usemtl Wood_Darker +f 545/2132/2012 544/2129/2009 542/2130/2010 +f 552/2202/2031 556/2201/2030 544/2200/2009 +f 555/2153/2029 552/2155/2031 553/2156/2032 +f 553/2184/2032 562/2183/2056 558/2182/2055 +f 558/2182/2055 555/2181/2029 553/2184/2032 +f 555/2153/2029 556/2154/2030 552/2155/2031 +f 545/2203/2012 552/2202/2031 544/2200/2009 +f 542/2130/2010 541/2131/2011 545/2132/2012 +s 3 +f 542/2134/2014 544/2133/2013 547/2136/2016 +f 556/2197/2034 550/2198/2035 547/2199/2016 +f 557/2160/2036 556/2158/2034 555/2157/2033 +f 558/2162/2037 559/2163/2038 557/2164/2036 +f 557/2164/2036 555/2161/2033 558/2162/2037 +f 557/2160/2036 550/2159/2035 556/2158/2034 +f 544/2196/2013 556/2197/2034 547/2199/2016 +f 547/2136/2016 546/2135/2015 542/2134/2014 +s 4 +f 547/2137/2017 549/2140/2020 548/2139/2019 +f 547/2141/2017 551/2143/2022 549/2144/2020 +f 554/2192/2042 551/2191/2022 557/2189/2039 +f 557/2165/2039 559/2166/2040 560/2167/2041 +f 560/2167/2041 554/2168/2042 557/2165/2039 +f 551/2191/2022 550/2190/2021 557/2189/2039 +f 547/2141/2017 550/2142/2021 551/2143/2022 +f 548/2139/2019 546/2138/2018 547/2137/2017 +s 5 +f 549/2148/2026 551/2147/2025 552/2146/2024 +f 553/2149/2027 552/2150/2024 554/2152/2028 +f 560/2187/2058 562/2186/2057 553/2185/2027 +f 553/2185/2027 554/2188/2028 560/2187/2058 +f 552/2150/2024 551/2151/2025 554/2152/2028 +f 548/2206/2063 549/2207/2026 545/2204/2023 +f 545/2204/2023 541/2205/2062 548/2206/2063 +f 549/2148/2026 552/2146/2024 545/2145/2023 +s 6 +usemtl Wood_Dark +f 560/2169/2043 559/2170/2044 561/2171/2045 +s 7 +f 561/2174/2048 562/2173/2047 560/2172/2046 +s 8 +f 561/2177/2051 558/2176/2050 562/2175/2049 +s 9 +f 559/2178/2052 558/2179/2053 561/2180/2054 +s 10 +f 543/2195/2061 546/2194/2060 548/2193/2059 +s 11 +f 548/2208/2064 541/2209/2065 543/2210/2066 +s 12 +f 543/2213/2069 542/2212/2068 546/2211/2067 +o object6 +g object6 +v -0.42857143 1.26805573 -0.07142857 +v -0.42857143 -0.00000000 -0.07142857 +v -0.42857143 0.00000000 0.07142857 +v -0.42857143 1.26805573 0.07142857 +v -0.32142857 1.26805573 -0.07142857 +v -0.32142857 -0.00000000 -0.07142857 +v -0.32142857 0.00000000 0.07142857 +v -0.32142857 1.26805573 0.07142857 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.00000000 1.00000000 -0.00000000 +vn 0.00000000 1.00000000 -0.00000000 +vn 0.00000000 1.00000000 -0.00000000 +vn 0.00000000 1.00000000 -0.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +s 1 +usemtl Wood_Darker +f 563/2214/2070 564/2215/2071 566/2217/2073 +f 564/2215/2071 565/2216/2072 566/2217/2073 +s 2 +f 567/2218/2074 568/2219/2075 563/2221/2077 +f 568/2219/2075 564/2220/2076 563/2221/2077 +s 3 +f 570/2225/2081 569/2224/2080 567/2222/2078 +f 569/2224/2080 568/2223/2079 567/2222/2078 +s 4 +f 563/2229/2085 566/2228/2084 570/2227/2083 +f 563/2229/2085 570/2227/2083 567/2226/2082 +s 5 +f 566/2233/2089 565/2232/2088 570/2230/2086 +f 565/2232/2088 569/2231/2087 570/2230/2086 +s 6 +f 568/2234/2090 569/2235/2091 564/2237/2093 +f 569/2235/2091 565/2236/2092 564/2237/2093 +o object7 +g object7 +v -0.44523810 0.90516376 -0.48759427 +v -0.44523810 0.96240573 -0.54483624 +v -0.52619048 0.96240573 -0.54483624 +v -0.52619048 0.90516376 -0.48759427 +v -0.52619048 1.08571429 -0.40476190 +v -0.52619048 1.00476190 -0.40476190 +v -0.52619048 1.40952381 0.00000000 +v -0.52619048 1.49047619 0.00000000 +v -0.44523810 1.40952381 0.00000000 +v -0.44523810 1.49047619 0.00000000 +v -0.44523810 1.00476190 -0.40476190 +v -0.44523810 1.08571429 -0.40476190 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.99999999 1.00000000 +vt 0.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.00000000 0.00000000 +vt 0.99999999 0.00000000 +vt 0.99999999 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 0.99999999 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn 0.00000000 -0.70710678 -0.70710678 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 -0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn -1.00000000 -0.00000000 0.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 1.00000000 0.00000000 -0.00000000 +vn 1.00000000 0.00000000 -0.00000000 +vn 1.00000000 0.00000000 -0.00000000 +vn 1.00000000 0.00000000 -0.00000000 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn -0.00000000 -0.70710678 0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn 0.00000000 0.70710678 -0.70710678 +vn -0.00000000 -0.63942740 0.76885148 +vn -0.00000000 -0.63942740 0.76885148 +vn -0.00000000 -0.63942740 0.76885148 +vn -0.00000000 -0.63942740 0.76885148 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.00000000 0.75059887 -0.66075815 +vn 0.00000000 0.75059887 -0.66075815 +vn 0.00000000 0.75059887 -0.66075815 +vn 0.00000000 0.75059887 -0.66075815 +s 1 +usemtl Wood_Darker +f 574/2241/2097 573/2240/2096 572/2239/2095 +f 574/2241/2097 572/2239/2095 571/2238/2094 +s 2 +f 574/2245/2101 576/2244/2100 573/2242/2098 +f 577/2246/2102 575/2248/2099 576/2249/2100 +f 577/2246/2102 578/2247/2103 575/2248/2099 +f 576/2244/2100 575/2243/2099 573/2242/2098 +s 3 +f 580/2253/2107 578/2252/2106 579/2250/2104 +f 578/2252/2106 577/2251/2105 579/2250/2104 +s 4 +f 580/2254/2108 579/2255/2109 582/2257/2111 +f 572/2273/2125 582/2272/2111 581/2271/2110 +f 572/2273/2125 581/2271/2110 571/2270/2124 +f 579/2255/2109 581/2256/2110 582/2257/2111 +s 5 +f 579/2258/2112 577/2259/2113 581/2261/2115 +f 577/2259/2113 576/2260/2114 581/2261/2115 +s 6 +f 578/2262/2116 580/2263/2117 582/2264/2118 +f 578/2262/2116 582/2264/2118 575/2265/2119 +s 7 +f 571/2269/2123 581/2268/2122 574/2266/2120 +f 581/2268/2122 576/2267/2121 574/2266/2120 +s 8 +f 573/2277/2129 575/2276/2128 582/2275/2127 +f 573/2277/2129 582/2275/2127 572/2274/2126 +o object8 +g object8 +v -0.00000000 0.00000000 -0.22530922 +v -0.09332614 0.00000000 -0.22530922 +v -0.22530922 0.00000000 -0.09332614 +v -0.22530922 0.00000000 0.09332614 +v -0.09332614 0.00000000 0.22530922 +v -0.00000000 0.00000000 0.22530922 +v -0.00000000 0.00000000 0.33245208 +v -0.13770616 0.00000000 0.33245208 +v -0.33245208 0.00000000 0.13770616 +v -0.33245208 0.00000000 -0.13770616 +v -0.13770616 0.00000000 -0.33245208 +v -0.00000000 0.00000000 -0.33245208 +v 0.09332614 0.00000000 0.22530922 +v 0.22530922 0.00000000 0.09332614 +v 0.22530922 0.00000000 -0.09332614 +v 0.09332614 0.00000000 -0.22530922 +v 0.13770616 0.00000000 -0.33245208 +v 0.33245208 0.00000000 -0.13770616 +v 0.33245208 0.00000000 0.13770616 +v 0.13770616 0.00000000 0.33245208 +v -0.22530922 0.44642857 -0.09332614 +v -0.22530922 0.44642857 0.09332614 +v -0.00000000 0.44642857 -0.22530922 +v -0.09332614 0.44642857 -0.22530922 +v -0.09332614 0.44642857 0.22530922 +v -0.00000000 0.44642857 0.22530922 +v -0.00000000 0.44642857 0.33245208 +v -0.13770616 0.44642857 0.33245208 +v -0.33245208 0.44642857 0.13770616 +v -0.33245208 0.44642857 -0.13770616 +v -0.13770616 0.44642857 -0.33245208 +v -0.00000000 0.44642857 -0.33245208 +v 0.09332614 0.44642857 0.22530922 +v 0.22530922 0.44642857 0.09332614 +v 0.22530922 0.44642857 -0.09332614 +v 0.09332614 0.44642857 -0.22530922 +v 0.13770616 0.44642857 -0.33245208 +v 0.33245208 0.44642857 -0.13770616 +v 0.33245208 0.44642857 0.13770616 +v 0.13770616 0.44642857 0.33245208 +vt 0.83885970 0.50000000 +vt 0.83885970 0.35963972 +vt 0.64036028 0.16114030 +vt 0.35963972 0.16114030 +vt 0.16114030 0.35963972 +vt 0.16114030 0.50000000 +vt 0.00000000 0.50000000 +vt 0.00000000 0.29289322 +vt 0.29289322 0.00000000 +vt 0.70710678 0.00000000 +vt 1.00000000 0.29289322 +vt 1.00000000 0.50000000 +vt 0.16114030 0.64036028 +vt 0.35963972 0.83885970 +vt 0.64036028 0.83885970 +vt 0.83885970 0.64036028 +vt 1.00000000 0.70710678 +vt 0.70710678 1.00000000 +vt 0.29289322 1.00000000 +vt 0.00000000 0.70710678 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.83885970 0.50000000 +vt 0.83885970 0.35963972 +vt 0.64036028 0.16114030 +vt 0.35963972 0.16114030 +vt 0.16114030 0.35963972 +vt 0.16114030 0.50000000 +vt 0.00000000 0.50000000 +vt 0.00000000 0.29289322 +vt 0.29289322 0.00000000 +vt 0.70710678 0.00000000 +vt 1.00000000 0.29289322 +vt 1.00000000 0.50000000 +vt 0.16114030 0.64036028 +vt 0.35963972 0.83885970 +vt 0.64036028 0.83885970 +vt 0.83885970 0.64036028 +vt 1.00000000 0.70710678 +vt 0.70710678 1.00000000 +vt 0.29289322 1.00000000 +vt 0.00000000 0.70710678 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 0.50000000 +vt 1.00000000 0.50000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 0.50000000 +vt 1.00000000 0.50000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 0.50000000 +vt 1.00000000 0.50000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 0.00000000 0.50000000 +vt 1.00000000 0.50000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn -0.70710678 0.00000000 0.70710678 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 0.70710678 0.00000000 0.70710678 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.70710678 0.00000000 -0.70710678 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -0.70710678 0.00000000 -0.70710678 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +vn -1.00000000 0.00000000 -0.00000000 +s 1 +usemtl Stone_Brit_Gray +f 594/2289/2141 583/2278/2130 584/2279/2131 +f 593/2288/2140 584/2279/2131 585/2280/2132 +f 592/2287/2139 585/2280/2132 586/2281/2133 +f 586/2281/2133 590/2285/2137 591/2286/2138 +f 587/2282/2134 589/2284/2136 590/2285/2137 +f 602/2297/2149 589/2284/2136 595/2290/2142 +f 601/2296/2148 602/2297/2149 596/2291/2143 +f 600/2295/2147 601/2296/2148 597/2292/2144 +f 597/2292/2144 598/2293/2145 599/2294/2146 +f 597/2292/2144 599/2294/2146 600/2295/2147 +f 601/2296/2148 596/2291/2143 597/2292/2144 +f 602/2297/2149 595/2290/2142 596/2291/2143 +f 589/2284/2136 588/2283/2135 595/2290/2142 +f 587/2282/2134 588/2283/2135 589/2284/2136 +f 586/2281/2133 587/2282/2134 590/2285/2137 +f 591/2286/2138 592/2287/2139 586/2281/2133 +f 592/2287/2139 593/2288/2140 585/2280/2132 +f 598/2293/2145 594/2289/2141 599/2294/2146 +f 598/2293/2145 583/2278/2130 594/2289/2141 +f 593/2288/2140 594/2289/2141 584/2279/2131 +s 2 +f 585/2298/2150 603/2299/2151 586/2301/2153 +f 603/2299/2151 604/2300/2152 586/2301/2153 +s 3 +f 606/2303/2155 605/2302/2154 614/2313/2165 +f 619/2318/2170 614/2313/2165 618/2317/2169 +f 620/2319/2171 619/2318/2170 617/2316/2168 +f 617/2316/2168 616/2315/2167 621/2320/2172 +f 616/2315/2167 622/2321/2173 621/2320/2172 +f 615/2314/2166 609/2308/2160 622/2321/2173 +f 610/2309/2161 609/2308/2160 607/2306/2158 +f 611/2310/2162 610/2309/2161 604/2305/2157 +f 604/2305/2157 603/2304/2156 612/2311/2163 +f 603/2304/2156 613/2312/2164 612/2311/2163 +f 604/2305/2157 612/2311/2163 611/2310/2162 +f 610/2309/2161 607/2306/2158 604/2305/2157 +f 609/2308/2160 608/2307/2159 607/2306/2158 +f 615/2314/2166 608/2307/2159 609/2308/2160 +f 616/2315/2167 615/2314/2166 622/2321/2173 +f 617/2316/2168 621/2320/2172 620/2319/2171 +f 619/2318/2170 618/2317/2169 617/2316/2168 +f 614/2313/2165 605/2302/2154 618/2317/2169 +f 603/2304/2156 606/2303/2155 613/2312/2164 +f 606/2303/2155 614/2313/2165 613/2312/2164 +s 4 +f 584/2322/2174 606/2323/2175 585/2325/2177 +f 606/2323/2175 603/2324/2176 585/2325/2177 +s 5 +f 583/2331/2183 598/2326/2178 618/2327/2179 +f 605/2330/2182 606/2328/2180 583/2331/2183 +f 606/2328/2180 584/2329/2181 583/2331/2183 +f 583/2331/2183 618/2327/2179 605/2330/2182 +s 6 +f 597/2332/2184 617/2333/2185 598/2335/2187 +f 617/2333/2185 618/2334/2186 598/2335/2187 +s 7 +f 596/2336/2188 616/2337/2189 597/2339/2191 +f 616/2337/2189 617/2338/2190 597/2339/2191 +s 8 +f 595/2340/2192 615/2341/2193 596/2343/2195 +f 615/2341/2193 616/2342/2194 596/2343/2195 +s 9 +f 615/2346/2198 595/2347/2199 588/2349/2201 +f 607/2345/2197 608/2348/2200 588/2349/2201 +f 588/2349/2201 587/2344/2196 607/2345/2197 +f 608/2348/2200 615/2346/2198 588/2349/2201 +s 10 +f 586/2350/2202 604/2351/2203 587/2353/2205 +f 604/2351/2203 607/2352/2204 587/2353/2205 +s 11 +f 590/2357/2209 610/2356/2208 591/2354/2206 +f 610/2356/2208 611/2355/2207 591/2354/2206 +s 12 +f 589/2363/2215 602/2361/2213 622/2360/2212 +f 609/2362/2214 610/2359/2211 589/2363/2215 +f 610/2359/2211 590/2358/2210 589/2363/2215 +f 589/2363/2215 622/2360/2212 609/2362/2214 +s 13 +f 601/2367/2219 621/2366/2218 602/2364/2216 +f 621/2366/2218 622/2365/2217 602/2364/2216 +s 14 +f 600/2371/2223 620/2370/2222 601/2368/2220 +f 620/2370/2222 621/2369/2221 601/2368/2220 +s 15 +f 599/2375/2227 619/2374/2226 600/2372/2224 +f 619/2374/2226 620/2373/2225 600/2372/2224 +s 16 +f 619/2377/2229 599/2376/2228 594/2381/2233 +f 613/2378/2230 614/2380/2232 594/2381/2233 +f 594/2381/2233 593/2379/2231 613/2378/2230 +f 614/2380/2232 619/2377/2229 594/2381/2233 +s 17 +f 592/2385/2237 612/2384/2236 593/2382/2234 +f 612/2384/2236 613/2383/2235 593/2382/2234 +s 18 +f 591/2389/2241 611/2388/2240 592/2386/2238 +f 611/2388/2240 612/2387/2239 592/2386/2238 +o object9 +g object9 +v 0.07142857 0.89764195 -0.05357143 +v 0.07142857 0.89764195 -0.03571429 +v 0.05357143 0.89764195 -0.03571429 +v 0.05357143 0.89764195 -0.05357143 +v 0.05357143 0.00000000 -0.05357143 +v 0.05357143 0.00000000 -0.03571429 +v 0.07142857 0.00000000 -0.05357143 +v 0.07142857 0.00000000 -0.03571429 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vt 1.00000000 1.00000000 +vt 0.00000000 1.00000000 +vt 0.00000000 0.00000000 +vt 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn 0.00000000 1.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn -1.00000000 0.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 -1.00000000 0.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 0.00000000 0.00000000 -1.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 1.00000000 0.00000000 0.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +vn 0.00000000 0.00000000 1.00000000 +s 1 +usemtl Burlap +f 626/2393/2245 625/2392/2244 624/2391/2243 +f 626/2393/2245 624/2391/2243 623/2390/2242 +s 2 +f 626/2394/2246 627/2395/2247 625/2397/2249 +f 627/2395/2247 628/2396/2248 625/2397/2249 +s 3 +f 629/2398/2250 630/2399/2251 627/2401/2253 +f 630/2399/2251 628/2400/2252 627/2401/2253 +s 4 +f 623/2402/2254 629/2403/2255 626/2405/2257 +f 629/2403/2255 627/2404/2256 626/2405/2257 +s 5 +f 624/2409/2261 630/2408/2260 623/2406/2258 +f 630/2408/2260 629/2407/2259 623/2406/2258 +s 6 +f 625/2413/2265 628/2412/2264 624/2410/2262 +f 628/2412/2264 630/2411/2263 624/2410/2262 diff --git a/examples/opengl_obj_viewer/pyproject.toml b/examples/opengl_obj_viewer/pyproject.toml index 40643caafb..bd115f7ef0 100644 --- a/examples/opengl_obj_viewer/pyproject.toml +++ b/examples/opengl_obj_viewer/pyproject.toml @@ -23,6 +23,9 @@ requires = [ [tool.briefcase.app.obj-viewer] sources = ["obj_viewer"] +[tool.briefcase.app.obj-viewer-mobile] +sources = ["obj_viewer", "obj_viewer_mobile"] + [tool.briefcase.app.obj-viewer-qt] sources = ["obj_viewer", "obj_viewer_qt"] @@ -51,22 +54,22 @@ requires = [ #] # Mobile deployments -# [tool.briefcase.app.obj-viewer.iOS] -# requires = [ -# "../../iOS", -# "std-nslog>=1.0.0", -# ] +[tool.briefcase.app.obj-viewer-mobile.iOS] +requires = [ + "../../iOS", + "std-nslog>=1.0.0", +] -# [tool.briefcase.app.obj-viewer.android] -# requires = [ -# "../../android", -# ] -# -# base_theme = "Theme.MaterialComponents.Light.DarkActionBar" -# -# build_gradle_dependencies = [ -# "com.google.android.material:material:1.12.0", -# ] +[tool.briefcase.app.obj-viewer-mobile.android] +requires = [ + "../../android", +] + +base_theme = "Theme.MaterialComponents.Light.DarkActionBar" + +build_gradle_dependencies = [ + "com.google.android.material:material:1.12.0", +] # Web deployment #[tool.briefcase.app.obj-viewer.web] diff --git a/examples/screenshot/pyproject.toml b/examples/screenshot/pyproject.toml index 2569b8d747..b0c01c0ac4 100644 --- a/examples/screenshot/pyproject.toml +++ b/examples/screenshot/pyproject.toml @@ -34,17 +34,20 @@ sources = ["screenshot", "screenshot_qt"] requires = [ "../../cocoa", "std-nslog>=1.0.0", + "pyopengl", ] [tool.briefcase.app.screenshot.linux] requires = [ "../../gtk", + "pyopengl", ] [tool.briefcase.app.screenshot-qt.linux] requires = [ "../../qt", "PySide6", + "pyopengl", ] [tool.briefcase.app.screenshot.windows] diff --git a/examples/screenshot/screenshot/app.py b/examples/screenshot/screenshot/app.py index 374fa0238a..d7f21f7473 100644 --- a/examples/screenshot/screenshot/app.py +++ b/examples/screenshot/screenshot/app.py @@ -150,6 +150,14 @@ def create_numberinput(self): width=300, ) + def create_openglview(self): + from .openglview import CubeRenderer + + renderer = CubeRenderer() + openglview = toga.OpenGLView(renderer, margin=10, width=280, height=290) + + return toga.Box(children=[openglview], width=280, height=290) + def create_passwordinput(self): return toga.Box( children=[ @@ -365,6 +373,7 @@ def proceed(button, **kwargs): async def sequence(self): print(f"Saving screenshots to {self.paths.data}") for content_type in [ + "openglview", "activityindicator", "button", "canvas", @@ -430,7 +439,7 @@ async def sequence(self): content.close() elif ( - content_type in {"webview", "mapview"} + content_type in {"webview", "mapview", "openglview"} and toga.platform.current_platform == "macOS" ): # Manual screenshot required on macOS because screenshots don't diff --git a/examples/screenshot/screenshot/openglview.py b/examples/screenshot/screenshot/openglview.py new file mode 100644 index 0000000000..6c645479f2 --- /dev/null +++ b/examples/screenshot/screenshot/openglview.py @@ -0,0 +1,380 @@ +import array + +import toga + +if toga.backend in {"toga_cocoa", "toga_gtk", "toga_qt"}: + import OpenGL + + # Get spurious errors with Qt backend + OpenGL.ERROR_CHECKING = True + + from OpenGL.GL import ( + GL_ARRAY_BUFFER, + GL_COLOR_BUFFER_BIT, + GL_DEPTH_BUFFER_BIT, + GL_DEPTH_TEST, + GL_FALSE, + GL_FLOAT, + GL_FRAGMENT_SHADER, + GL_STATIC_DRAW, + GL_TRIANGLES, + GL_VERTEX_SHADER, + glAttachShader, + glBindBuffer, + glBindVertexArray, + glBufferData, + glClear, + glClearColor, + glCompileShader, + glCreateProgram, + glCreateShader, + glDeleteShader, + glDetachShader, + glDrawArrays, + glEnable, + glEnableVertexAttribArray, + glGenBuffers, + glGenVertexArrays, + glGetAttribLocation, + glGetUniformLocation, + glLinkProgram, + glShaderSource, + glUniformMatrix4fv, + glUseProgram, + glVertexAttribPointer, + glViewport, + ) # noqa: E402 +elif toga.backend in {"toga_android"}: + from android.opengl import GLES32 as GL + from android.opengl.GLES32 import ( + GL_ARRAY_BUFFER, + GL_COLOR_BUFFER_BIT, + GL_DEPTH_BUFFER_BIT, + GL_DEPTH_TEST, + GL_FALSE, + GL_FLOAT, + GL_FRAGMENT_SHADER, + GL_STATIC_DRAW, + GL_TRIANGLES, + GL_VERTEX_SHADER, + glAttachShader, + glBindBuffer, + glBindVertexArray, + glClear, + glClearColor, + glCompileShader, + glCreateProgram, + glCreateShader, + glDeleteShader, + glDetachShader, + glDrawArrays, + glEnable, + glEnableVertexAttribArray, + glGetAttribLocation, + glGetUniformLocation, + glLinkProgram, + glShaderSource, + glUseProgram, + glViewport, + ) # noqa: E402 + from java import jarray, jfloat, jint + from java.nio import ByteBuffer, ByteOrder + + def glGenBuffers(count): + data = jarray(jint)([0] * count) + GL.glGenBuffers(count, data) + if count == 1: + return data[0] + else: + return tuple(data) + + def glGenVertexArrays(count): + data = jarray(jint)([0] * count) + GL.glGenVertexArrays(count, data) + if count == 1: + return data[0] + else: + return tuple(data) + + def glUniformMatrix4fv(loc, size, transpose, value): + buffer = jarray(jfloat)(list(value)) + GL.glUniformMatrix4fv(loc, size, transpose, buffer, 0) + + def glVertexAttribPointer(loc, size, type, normalize, stride, offset): + offset = 0 if offset is None else offset + GL.glVertexAttribPointer(loc, size, type, bool(normalize), stride, offset) + + def glBufferData(buffer_type, data, usage): + nbytes = len(data) + buffer = ByteBuffer.allocateDirect(nbytes) + buffer.order(ByteOrder.nativeOrder()) + buffer.put(data) + buffer.position(0) + GL.glBufferData(buffer_type, nbytes, buffer, usage) + +else: + raise NotImplementedError() + + +VERTEX_SHADER = """#version 330 core + +precision highp float; + +in vec4 position; +in vec3 normal; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 world; + +out vec3 vertex_normal; + +void main() { + gl_Position = projection * view * world * position; + vertex_normal = mat3(world) * normal; +} +""" +FRAGMENT_SHADER = """#version 330 core + +precision highp float; + +in vec3 vertex_normal; + +uniform vec3 light_direction = vec3(-1.0, 3.0, 5.0); +uniform vec3 diffuse = vec3(1.0, 1.0, 1.0); + +out vec4 output_color; + +void main() { + float light = dot( + normalize(vertex_normal), + normalize(light_direction) + ) * 0.5 + 0.5; + + output_color = vec4(light * diffuse, 1.0); +} +""" + + +# fmt: off +VERTICES = [ + -0.5, -0.5, -0.5, + -0.5, 0.5, -0.5, + 0.5, -0.5, -0.5, + -0.5, 0.5, -0.5, + 0.5, 0.5, -0.5, + 0.5, -0.5, -0.5, + + -0.5, -0.5, 0.5, + 0.5, -0.5, 0.5, + -0.5, 0.5, 0.5, + -0.5, 0.5, 0.5, + 0.5, -0.5, 0.5, + 0.5, 0.5, 0.5, + + -0.5, 0.5, -0.5, + -0.5, 0.5, 0.5, + 0.5, 0.5, -0.5, + -0.5, 0.5, 0.5, + 0.5, 0.5, 0.5, + 0.5, 0.5, -0.5, + + -0.5, -0.5, -0.5, + 0.5, -0.5, -0.5, + -0.5, -0.5, 0.5, + -0.5, -0.5, 0.5, + 0.5, -0.5, -0.5, + 0.5, -0.5, 0.5, + + -0.5, -0.5, -0.5, + -0.5, -0.5, 0.5, + -0.5, 0.5, -0.5, + -0.5, -0.5, 0.5, + -0.5, 0.5, 0.5, + -0.5, 0.5, -0.5, + + 0.5, -0.5, -0.5, + 0.5, 0.5, -0.5, + 0.5, -0.5, 0.5, + 0.5, -0.5, 0.5, + 0.5, 0.5, -0.5, + 0.5, 0.5, 0.5, +] + +NORMALS = ( + [0.0, 0.0, -1.0] * 6 + + [0.0, 0.0, 1.0] * 6 + + [0.0, 1.0, 0.0] * 6 + + [0.0, -1.0, 0.0] * 6 + + [-1.0, 0.0, 0.0] * 6 + + [1.0, 0.0, 0.0] * 6 +) +# fmt: on + + +class CubeRenderer: + def on_init(self, widget, **kwargs): + glEnable(GL_DEPTH_TEST) + self.init_buffers() + self.init_program() + self.init_vertex_array() + + def on_render(self, widget, size, **kwargs): + aspect = size[0] / size[1] + glViewport(0, 0, int(size[0]), int(size[1])) + glClearColor(0.5, 0.5, 1.0, 1.0) + glClear(int(GL_COLOR_BUFFER_BIT) | int(GL_DEPTH_BUFFER_BIT)) + + if not hasattr(self, "program"): + # Didn't initialize correctly. + return + + glUseProgram(self.program) + glBindVertexArray(self.vao) + # set uniforms + loc = glGetUniformLocation(self.program, "projection") + glUniformMatrix4fv( + loc, + 1, + GL_FALSE, + [ + 1.73 / aspect, + 0.00, + 0.00, + 0.00, + 0.00, + 1.73, + 0.00, + 0.00, + 0.00, + 0.00, + -1.00, + -1.00, + 0.00, + 0.00, + -0.01, + 0.00, + ], + ) + loc = glGetUniformLocation(self.program, "view") + glUniformMatrix4fv( + loc, + 1, + GL_FALSE, + [ + 1.00, + 0.00, + 0.00, + 0.00, + 0.00, + 1.00, + 0.00, + 0.00, + 0.00, + 0.00, + 1.00, + 0.00, + 0.00, + 0.00, + -2.00, + 1.00, + ], + ) + loc = glGetUniformLocation(self.program, "world") + glUniformMatrix4fv( + loc, + 1, + GL_FALSE, + [ + 0.50, + 0.61, + 0.61, + 0.00, + 0.00, + 0.71, + -0.71, + 0.00, + -0.87, + 0.35, + 0.35, + 0.00, + 0.00, + 0.00, + 0.00, + 1.00, + ], + ) + + # draw! + glDrawArrays(GL_TRIANGLES, 0, self.n_vertices) + + def init_buffers(self): + # Initialize buffers + self.n_vertices = len(VERTICES) // 3 + vertex_data = bytes(array.array("f", VERTICES)) + self.vertex_buffer = glGenBuffers(1) + glBindBuffer(GL_ARRAY_BUFFER, self.vertex_buffer) + glBufferData(GL_ARRAY_BUFFER, vertex_data, GL_STATIC_DRAW) + glBindBuffer(GL_ARRAY_BUFFER, 0) + + normal_data = bytes(array.array("f", NORMALS)) + self.normal_buffer = glGenBuffers(1) + glBindBuffer(GL_ARRAY_BUFFER, self.normal_buffer) + glBufferData(GL_ARRAY_BUFFER, normal_data, GL_STATIC_DRAW) + glBindBuffer(GL_ARRAY_BUFFER, 0) + + def init_program(self): + # Initialize shaders + vertex_shader = glCreateShader(GL_VERTEX_SHADER) + glShaderSource(vertex_shader, VERTEX_SHADER) + glCompileShader(vertex_shader) + # status = glGetShaderiv(vertex_shader, GL_COMPILE_STATUS) + # if status == GL_FALSE: + # info_log = glGetShaderInfoLog(vertex_shader) + # raise RuntimeError( + # f"Compilation failure shader:\n{info_log}" + # ) + + fragment_shader = glCreateShader(GL_FRAGMENT_SHADER) + glShaderSource(fragment_shader, FRAGMENT_SHADER) + glCompileShader(fragment_shader) + # status = glGetShaderiv(fragment_shader, GL_COMPILE_STATUS) + # if status == GL_FALSE: + # info_log = glGetShaderInfoLog(fragment_shader) + # raise RuntimeError( + # f"Compilation failure for shader:\n{info_log}" + # ) + + # Initialize program + self.program = glCreateProgram() + glAttachShader(self.program, vertex_shader) + glAttachShader(self.program, fragment_shader) + glLinkProgram(self.program) + + # status = glGetProgramiv(self.program, GL_LINK_STATUS) + # if status == GL_FALSE: + # strInfoLog = glGetProgramInfoLog(self.program) + # raise RuntimeError(f"Linker failure: \n{strInfoLog}") + + # Clean up shaders + glDetachShader(self.program, vertex_shader) + glDetachShader(self.program, fragment_shader) + glDeleteShader(vertex_shader) + glDeleteShader(fragment_shader) + + def init_vertex_array(self): + self.vao = glGenVertexArrays(1) + + glBindVertexArray(self.vao) + glBindBuffer(GL_ARRAY_BUFFER, self.vertex_buffer) + loc = glGetAttribLocation(self.program, "position") + glEnableVertexAttribArray(loc) + glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 0, None) + glBindBuffer(GL_ARRAY_BUFFER, 0) + + glBindBuffer(GL_ARRAY_BUFFER, self.normal_buffer) + loc = glGetAttribLocation(self.program, "normal") + glEnableVertexAttribArray(loc) + glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 0, None) + glBindBuffer(GL_ARRAY_BUFFER, 0) + glBindVertexArray(0) diff --git a/examples/shadertoy/shadertoy/utils_iOS.py b/examples/shadertoy/shadertoy/utils_iOS.py index 0c5b956982..b20dc32e3e 100644 --- a/examples/shadertoy/shadertoy/utils_iOS.py +++ b/examples/shadertoy/shadertoy/utils_iOS.py @@ -2,7 +2,7 @@ from ctypes import byref, c_char_p, c_int, c_uint, create_string_buffer -from toga_iOS.libs import opengles as GL +from toga_iOS.libs.opengles import opengles as GL #: Shader version header: we want OpenGL ES GLSL 3 VERSION_HEADER = """#version 300 es""" diff --git a/gtk/src/toga_gtk/widgets/openglview.py b/gtk/src/toga_gtk/widgets/openglview.py index 61df741ea8..6a1d88a42e 100644 --- a/gtk/src/toga_gtk/widgets/openglview.py +++ b/gtk/src/toga_gtk/widgets/openglview.py @@ -56,10 +56,10 @@ def gtk_button_release(self, obj, event): self.buttons.discard(BUTTONS[event.button]) self.position = (event.x, event.y) - def gtk_motion_notify(self, obj, event): + def gtk_motion_notify(self, obj, event): # pragma: no cover # Don't have tests for mouse movement as most backends get # live position - self.position = (event.x, event.y) # pragma: no-cover + self.position = (event.x, event.y) else: # pragma: no-cover-if-gtk3 pass diff --git a/iOS/src/toga_iOS/libs/__init__.py b/iOS/src/toga_iOS/libs/__init__.py index 1e05965682..7aece2646b 100644 --- a/iOS/src/toga_iOS/libs/__init__.py +++ b/iOS/src/toga_iOS/libs/__init__.py @@ -3,7 +3,6 @@ from .core_location import * # NOQA from .foundation import * # NOQA from .glkit import * # NOQA -from .opengles import * # NOQA from .mapkit import * # NOQA from .uikit import * # NOQA from .webkit import * # NOQA From d75e9fb8575dc7a8ba7563a1a11cd77bd141d96c Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 15 Apr 2026 16:28:05 +0100 Subject: [PATCH 22/26] Fix Android screenshots; start work on iOS screenshots. --- .../obj_viewer/utils_android.py | 2 +- .../opengl_obj_viewer/obj_viewer/utils_iOS.py | 5 + examples/screenshot/screenshot/openglview.py | 296 ++++++++++++------ 3 files changed, 198 insertions(+), 105 deletions(-) diff --git a/examples/opengl_obj_viewer/obj_viewer/utils_android.py b/examples/opengl_obj_viewer/obj_viewer/utils_android.py index eea8885138..3d0082a399 100644 --- a/examples/opengl_obj_viewer/obj_viewer/utils_android.py +++ b/examples/opengl_obj_viewer/obj_viewer/utils_android.py @@ -45,7 +45,7 @@ def call_v_function(*args): def glUniformMatrix4fv(loc, size, transpose, value): buffer = jarray(jfloat)(list(value)) - GL.glUniformMatrix4fv(loc, size, transpose, buffer, 0) + GL.glUniformMatrix4fv(loc, size, bool(transpose), buffer, 0) def glVertexAttribPointer(loc, size, type, normalize, stride, offset): diff --git a/examples/opengl_obj_viewer/obj_viewer/utils_iOS.py b/examples/opengl_obj_viewer/obj_viewer/utils_iOS.py index d717282118..333b02e37d 100644 --- a/examples/opengl_obj_viewer/obj_viewer/utils_iOS.py +++ b/examples/opengl_obj_viewer/obj_viewer/utils_iOS.py @@ -75,6 +75,11 @@ def glGetAttribLocation(id, item): return GL.glGetAttribLocation(id, buffer) +def glGetuniformLocation(id, item): + buffer = create_string_buffer(item.encode("utf-8")) + return GL.glGetUniformLocation(id, buffer) + + def glUniformMatrix4fv(loc, size, transpose, value): buffer = (c_float * (16 * size))(*value) GL.glUniformMatrix4fv(loc, size, transpose, buffer) diff --git a/examples/screenshot/screenshot/openglview.py b/examples/screenshot/screenshot/openglview.py index 6c645479f2..fd7d0f995f 100644 --- a/examples/screenshot/screenshot/openglview.py +++ b/examples/screenshot/screenshot/openglview.py @@ -5,17 +5,18 @@ if toga.backend in {"toga_cocoa", "toga_gtk", "toga_qt"}: import OpenGL - # Get spurious errors with Qt backend - OpenGL.ERROR_CHECKING = True + OpenGL.ERROR_CHECKING = False from OpenGL.GL import ( GL_ARRAY_BUFFER, GL_COLOR_BUFFER_BIT, + GL_COMPILE_STATUS, GL_DEPTH_BUFFER_BIT, GL_DEPTH_TEST, GL_FALSE, GL_FLOAT, GL_FRAGMENT_SHADER, + GL_LINK_STATUS, GL_STATIC_DRAW, GL_TRIANGLES, GL_VERTEX_SHADER, @@ -36,6 +37,10 @@ glGenBuffers, glGenVertexArrays, glGetAttribLocation, + glGetProgramInfoLog, + glGetProgramiv, + glGetShaderInfoLog, + glGetShaderiv, glGetUniformLocation, glLinkProgram, glShaderSource, @@ -44,45 +49,61 @@ glVertexAttribPointer, glViewport, ) # noqa: E402 + + SHADER_HEADER = "#version 330 core" + elif toga.backend in {"toga_android"}: from android.opengl import GLES32 as GL - from android.opengl.GLES32 import ( - GL_ARRAY_BUFFER, - GL_COLOR_BUFFER_BIT, - GL_DEPTH_BUFFER_BIT, - GL_DEPTH_TEST, - GL_FALSE, - GL_FLOAT, - GL_FRAGMENT_SHADER, - GL_STATIC_DRAW, - GL_TRIANGLES, - GL_VERTEX_SHADER, - glAttachShader, - glBindBuffer, - glBindVertexArray, - glClear, - glClearColor, - glCompileShader, - glCreateProgram, - glCreateShader, - glDeleteShader, - glDetachShader, - glDrawArrays, - glEnable, - glEnableVertexAttribArray, - glGetAttribLocation, - glGetUniformLocation, - glLinkProgram, - glShaderSource, - glUseProgram, - glViewport, - ) # noqa: E402 from java import jarray, jfloat, jint from java.nio import ByteBuffer, ByteOrder + GL_ARRAY_BUFFER = GL.GL_ARRAY_BUFFER + GL_COLOR_BUFFER_BIT = GL.GL_COLOR_BUFFER_BIT + GL_COMPILE_STATUS = GL.GL_COMPILE_STATUS + GL_DEPTH_BUFFER_BIT = GL.GL_DEPTH_BUFFER_BIT + GL_DEPTH_TEST = GL.GL_DEPTH_TEST + GL_FALSE = GL.GL_FALSE + GL_FLOAT = GL.GL_FLOAT + GL_FRAGMENT_SHADER = GL.GL_FRAGMENT_SHADER + GL_LINK_STATUS = GL.GL_LINK_STATUS + GL_STATIC_DRAW = GL.GL_STATIC_DRAW + GL_TRIANGLES = GL.GL_TRIANGLES + GL_VERTEX_SHADER = GL.GL_VERTEX_SHADER + glAttachShader = GL.glAttachShader + glBindBuffer = GL.glBindBuffer + glBindVertexArray = GL.glBindVertexArray + glClear = GL.glClear + glClearColor = GL.glClearColor + glCompileShader = GL.glCompileShader + glCreateProgram = GL.glCreateProgram + glCreateShader = GL.glCreateShader + glDeleteShader = GL.glDeleteShader + glDetachShader = GL.glDetachShader + glDrawArrays = GL.glDrawArrays + glEnable = GL.glEnable + glEnableVertexAttribArray = GL.glEnableVertexAttribArray + glGetAttribLocation = GL.glGetAttribLocation + glGetProgramInfoLog = GL.glGetProgramInfoLog + glGetShaderInfoLog = GL.glGetShaderInfoLog + glGetUniformLocation = GL.glGetUniformLocation + glLinkProgram = GL.glLinkProgram + glShaderSource = GL.glShaderSource + glUseProgram = GL.glUseProgram + glViewport = GL.glViewport + + def glGetShaderiv(id, param): + data = jarray(jint)([0]) + GL.glGetShaderiv(id, param, data, 0) + return data[0] + + def glGetProgramiv(id, param): + data = jarray(jint)([0]) + GL.glGetProgramiv(id, param, data, 0) + return data[0] + def glGenBuffers(count): data = jarray(jint)([0] * count) - GL.glGenBuffers(count, data) + GL.glGenBuffers(count, data, 0) if count == 1: return data[0] else: @@ -90,7 +111,7 @@ def glGenBuffers(count): def glGenVertexArrays(count): data = jarray(jint)([0] * count) - GL.glGenVertexArrays(count, data) + GL.glGenVertexArrays(count, data, 0) if count == 1: return data[0] else: @@ -98,7 +119,7 @@ def glGenVertexArrays(count): def glUniformMatrix4fv(loc, size, transpose, value): buffer = jarray(jfloat)(list(value)) - GL.glUniformMatrix4fv(loc, size, transpose, buffer, 0) + GL.glUniformMatrix4fv(loc, size, bool(transpose), buffer, 0) def glVertexAttribPointer(loc, size, type, normalize, stride, offset): offset = 0 if offset is None else offset @@ -112,11 +133,107 @@ def glBufferData(buffer_type, data, usage): buffer.position(0) GL.glBufferData(buffer_type, nbytes, buffer, usage) + SHADER_HEADER = "#version 300 es" + +elif toga.backend in {"toga_iOS"}: + from ctypes import byref, c_char_p, c_float, c_int, create_string_buffer + + from toga_iOS.libs.opengles import opengles as GL + + GL_ARRAY_BUFFER = GL.GL_ARRAY_BUFFER + GL_COLOR_BUFFER_BIT = GL.GL_COLOR_BUFFER_BIT + GL_COMPILE_STATUS = GL.GL_COMPILE_STATUS + GL_DEPTH_BUFFER_BIT = GL.GL_DEPTH_BUFFER_BIT + GL_DEPTH_TEST = GL.GL_DEPTH_TEST + GL_FALSE = GL.GL_FALSE + GL_FLOAT = GL.GL_FLOAT + GL_FRAGMENT_SHADER = GL.GL_FRAGMENT_SHADER + GL_LINK_STATUS = GL.GL_LINK_STATUS + GL_STATIC_DRAW = GL.GL_STATIC_DRAW + GL_TRIANGLES = GL.GL_TRIANGLES + GL_VERTEX_SHADER = GL.GL_VERTEX_SHADER + glAttachShader = GL.glAttachShader + glBindBuffer = GL.glBindBuffer + glBindVertexArray = GL.glBindVertexArray + glClear = GL.glClear + glClearColor = GL.glClearColor + glCompileShader = GL.glCompileShader + glCreateProgram = GL.glCreateProgram + glCreateShader = GL.glCreateShader + glDeleteShader = GL.glDeleteShader + glDetachShader = GL.glDetachShader + glDrawArrays = GL.glDrawArrays + glEnable = GL.glEnable + glEnableVertexAttribArray = GL.glEnableVertexAttribArray + glGetProgramInfoLog = GL.glGetProgramInfoLog + glGetShaderInfoLog = GL.glGetShaderInfoLog + glLinkProgram = GL.glLinkProgram + glUseProgram = GL.glUseProgram + glViewport = GL.glViewport + + def glGetShaderiv(id, param): + data = c_int(0) + GL.glGetShaderiv(id, param, byref(data)) + return data.value + + def glGetProgramiv(id, param): + data = c_int(0) + GL.glGetProgramiv(id, param, byref(data), 0) + return data.value + + def glGenBuffers(count): + data = (c_int * count)(*([0] * count)) + GL.glGenBuffers(count, byref(data)) + if count == 1: + return data[0] + else: + return tuple(data) + + def glGenVertexArrays(count): + data = (c_int * count)(*([0] * count)) + GL.glGenVertexArrays(count, byref(data)) + if count == 1: + return data[0] + else: + return tuple(data) + + def glShaderSource(id, source): + source_bytes = c_char_p(source.encode("utf-8")) + GL.glShaderSource( + id, + 1, + source_bytes, + None, + ) + + def glGetAttribLocation(id, item): + buffer = create_string_buffer(item.encode("utf-8")) + return GL.glGetAttribLocation(id, buffer) + + def glGetUniformLocation(id, item): + buffer = create_string_buffer(item.encode("utf-8")) + return GL.glGetUniformLocation(id, buffer) + + def glUniformMatrix4fv(loc, size, transpose, value): + buffer = (c_float * (16 * size))(*value) + GL.glUniformMatrix4fv(loc, size, transpose, buffer) + + def glBufferData(buffer_type, data: bytes, usage): + GL.glBufferData(buffer_type, len(data), data, usage) + + def glVertexAttribPointer(loc, size, type, normalize, stride, offset): + offset = 0 if offset is None else offset + GL.glVertexAttribPointer(loc, size, type, bool(normalize), stride, offset) + + SHADER_HEADER = "#version 300 es" + else: raise NotImplementedError() -VERTEX_SHADER = """#version 330 core +VERTEX_SHADER = ( + SHADER_HEADER + + """ precision highp float; @@ -134,14 +251,17 @@ def glBufferData(buffer_type, data, usage): vertex_normal = mat3(world) * normal; } """ -FRAGMENT_SHADER = """#version 330 core +) +FRAGMENT_SHADER = ( + SHADER_HEADER + + """ precision highp float; in vec3 vertex_normal; -uniform vec3 light_direction = vec3(-1.0, 3.0, 5.0); -uniform vec3 diffuse = vec3(1.0, 1.0, 1.0); +vec3 light_direction = vec3(-1.0, 3.0, 5.0); +vec3 diffuse = vec3(1.0, 1.0, 1.0); out vec4 output_color; @@ -154,6 +274,7 @@ def glBufferData(buffer_type, data, usage): output_color = vec4(light * diffuse, 1.0); } """ +) # fmt: off @@ -227,83 +348,54 @@ def on_render(self, widget, size, **kwargs): if not hasattr(self, "program"): # Didn't initialize correctly. + print("Failed!") return glUseProgram(self.program) glBindVertexArray(self.vao) # set uniforms loc = glGetUniformLocation(self.program, "projection") + # fmt: off glUniformMatrix4fv( loc, 1, GL_FALSE, [ - 1.73 / aspect, - 0.00, - 0.00, - 0.00, - 0.00, - 1.73, - 0.00, - 0.00, - 0.00, - 0.00, - -1.00, - -1.00, - 0.00, - 0.00, - -0.01, - 0.00, + 1.73 / aspect, 0.00, 0.00, 0.00, + 0.00, 1.73, 0.00, 0.00, + 0.00, 0.00, -1.00, -1.00, + 0.00, 0.00, -0.01, 0.00, ], ) + # fmt: on loc = glGetUniformLocation(self.program, "view") + # fmt: off glUniformMatrix4fv( loc, 1, GL_FALSE, [ - 1.00, - 0.00, - 0.00, - 0.00, - 0.00, - 1.00, - 0.00, - 0.00, - 0.00, - 0.00, - 1.00, - 0.00, - 0.00, - 0.00, - -2.00, - 1.00, + 1.00, 0.00, 0.00, 0.00, + 0.00, 1.00, 0.00, 0.00, + 0.00, 0.00, 1.00, 0.00, + 0.00, 0.00, -2.00, 1.00, ], ) + # fmt: on loc = glGetUniformLocation(self.program, "world") + # fmt: off glUniformMatrix4fv( loc, 1, GL_FALSE, [ - 0.50, - 0.61, - 0.61, - 0.00, - 0.00, - 0.71, - -0.71, - 0.00, - -0.87, - 0.35, - 0.35, - 0.00, - 0.00, - 0.00, - 0.00, - 1.00, + 0.50, 0.61, 0.61, 0.00, + 0.00, 0.71, -0.71, 0.00, + -0.87, 0.35, 0.35, 0.00, + 0.00, 0.00, 0.00, 1.00, ], ) + # fmt: on # draw! glDrawArrays(GL_TRIANGLES, 0, self.n_vertices) @@ -328,22 +420,18 @@ def init_program(self): vertex_shader = glCreateShader(GL_VERTEX_SHADER) glShaderSource(vertex_shader, VERTEX_SHADER) glCompileShader(vertex_shader) - # status = glGetShaderiv(vertex_shader, GL_COMPILE_STATUS) - # if status == GL_FALSE: - # info_log = glGetShaderInfoLog(vertex_shader) - # raise RuntimeError( - # f"Compilation failure shader:\n{info_log}" - # ) + status = glGetShaderiv(vertex_shader, GL_COMPILE_STATUS) + if status == GL_FALSE: + info_log = glGetShaderInfoLog(vertex_shader) + raise RuntimeError(f"Compilation failure shader:\n{info_log}") fragment_shader = glCreateShader(GL_FRAGMENT_SHADER) glShaderSource(fragment_shader, FRAGMENT_SHADER) glCompileShader(fragment_shader) - # status = glGetShaderiv(fragment_shader, GL_COMPILE_STATUS) - # if status == GL_FALSE: - # info_log = glGetShaderInfoLog(fragment_shader) - # raise RuntimeError( - # f"Compilation failure for shader:\n{info_log}" - # ) + status = glGetShaderiv(fragment_shader, GL_COMPILE_STATUS) + if status == GL_FALSE: + info_log = glGetShaderInfoLog(fragment_shader) + raise RuntimeError(f"Compilation failure for shader:\n{info_log}") # Initialize program self.program = glCreateProgram() @@ -351,10 +439,10 @@ def init_program(self): glAttachShader(self.program, fragment_shader) glLinkProgram(self.program) - # status = glGetProgramiv(self.program, GL_LINK_STATUS) - # if status == GL_FALSE: - # strInfoLog = glGetProgramInfoLog(self.program) - # raise RuntimeError(f"Linker failure: \n{strInfoLog}") + status = glGetProgramiv(self.program, GL_LINK_STATUS) + if status == GL_FALSE: + strInfoLog = glGetProgramInfoLog(self.program) + raise RuntimeError(f"Linker failure: \n{strInfoLog}") # Clean up shaders glDetachShader(self.program, vertex_shader) From c1d0ff7c1fa245b796a5338f8e30cb8b693be2b5 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 17 Apr 2026 09:39:19 +0100 Subject: [PATCH 23/26] More work on iOS backend for screenshot. --- examples/screenshot/screenshot/openglview.py | 13 +++++++++++-- iOS/src/toga_iOS/libs/opengles.py | 14 +++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/examples/screenshot/screenshot/openglview.py b/examples/screenshot/screenshot/openglview.py index fd7d0f995f..4a3d0b99dc 100644 --- a/examples/screenshot/screenshot/openglview.py +++ b/examples/screenshot/screenshot/openglview.py @@ -136,7 +136,15 @@ def glBufferData(buffer_type, data, usage): SHADER_HEADER = "#version 300 es" elif toga.backend in {"toga_iOS"}: - from ctypes import byref, c_char_p, c_float, c_int, create_string_buffer + from ctypes import ( + byref, + c_char_p, + c_float, + c_float_p, + c_int, + cast, + create_string_buffer, + ) from toga_iOS.libs.opengles import opengles as GL @@ -216,7 +224,7 @@ def glGetUniformLocation(id, item): def glUniformMatrix4fv(loc, size, transpose, value): buffer = (c_float * (16 * size))(*value) - GL.glUniformMatrix4fv(loc, size, transpose, buffer) + GL.glUniformMatrix4fv(loc, size, transpose, cast(buffer, c_float_p)) def glBufferData(buffer_type, data: bytes, usage): GL.glBufferData(buffer_type, len(data), data, usage) @@ -399,6 +407,7 @@ def on_render(self, widget, size, **kwargs): # draw! glDrawArrays(GL_TRIANGLES, 0, self.n_vertices) + print("Drawing done!") def init_buffers(self): # Initialize buffers diff --git a/iOS/src/toga_iOS/libs/opengles.py b/iOS/src/toga_iOS/libs/opengles.py index 2f10f89ce0..01939c8dd3 100644 --- a/iOS/src/toga_iOS/libs/opengles.py +++ b/iOS/src/toga_iOS/libs/opengles.py @@ -1,7 +1,17 @@ ########################################################################## # System/Library/Frameworks/UIKit.framework ########################################################################## -from ctypes import POINTER, c_char_p, c_float, c_int, c_uint, c_ulong, cdll, util +from ctypes import ( + POINTER, + c_bool, + c_char_p, + c_float, + c_int, + c_uint, + c_ulong, + cdll, + util, +) from rubicon.objc import ObjCClass @@ -49,6 +59,8 @@ opengles.glUniform3fv.argtypes = [GLint, GLsizei, POINTER(GLfloat)] opengles.glUniform4fv.argtypes = [GLint, GLsizei, POINTER(GLfloat)] +opengles.glUniformMatrix4fv.argtypes = [GLint, GLsizei, c_bool, POINTER(GLfloat)] + # Constants opengles.GL_FALSE = False From 923c9d3d8be6bfacd50fce987c87653723d9d86c Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 30 Apr 2026 11:08:14 +0100 Subject: [PATCH 24/26] Remove double defintions of EAGLContext in iOS. --- iOS/src/toga_iOS/libs/glkit.py | 6 ------ iOS/src/toga_iOS/widgets/openglview.py | 3 +-- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/iOS/src/toga_iOS/libs/glkit.py b/iOS/src/toga_iOS/libs/glkit.py index aa8c197017..22642bcbbe 100644 --- a/iOS/src/toga_iOS/libs/glkit.py +++ b/iOS/src/toga_iOS/libs/glkit.py @@ -17,9 +17,3 @@ GLKViewDrawableDepthFormat24 = 2 GLKViewDrawableStencilFormat8 = 1 GLKViewDrawableMultisample4X = 1 - -EAGLContext = ObjCClass("EAGLContext") - -kEAGLRenderingAPIOpenGLES1 = 1 -kEAGLRenderingAPIOpenGLES2 = 2 -kEAGLRenderingAPIOpenGLES3 = 3 diff --git a/iOS/src/toga_iOS/widgets/openglview.py b/iOS/src/toga_iOS/widgets/openglview.py index 12777f3231..6442cbc463 100644 --- a/iOS/src/toga_iOS/widgets/openglview.py +++ b/iOS/src/toga_iOS/widgets/openglview.py @@ -5,14 +5,13 @@ from toga_iOS.libs import ( CGRect, CGSize, - EAGLContext, GLKView, GLKViewDrawableColorFormatRGBA8888, GLKViewDrawableDepthFormat24, GLKViewDrawableMultisample4X, GLKViewDrawableStencilFormat8, - kEAGLRenderingAPIOpenGLES3, ) +from toga_iOS.libs.opengles import EAGLContext, kEAGLRenderingAPIOpenGLES3 from .base import Widget From 8266177448a9b3a2000b409b76830c14a531c25e Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 30 Apr 2026 11:52:13 +0100 Subject: [PATCH 25/26] Add tests of position; insist on position and buttons arguments. --- .../src/toga_android/widgets/openglview.py | 7 ++-- android/tests_backend/widgets/openglview.py | 3 ++ cocoa/tests_backend/widgets/openglview.py | 6 +++ gtk/src/toga_gtk/widgets/openglview.py | 2 +- gtk/tests_backend/widgets/openglview.py | 9 +++++ iOS/tests_backend/widgets/openglview.py | 8 ++-- qt/tests_backend/widgets/openglview.py | 8 ++++ testbed/tests/widgets/test_openglview.py | 38 ++++++++++++++++++- 8 files changed, 71 insertions(+), 10 deletions(-) diff --git a/android/src/toga_android/widgets/openglview.py b/android/src/toga_android/widgets/openglview.py index 1d997c235e..a1601ade71 100644 --- a/android/src/toga_android/widgets/openglview.py +++ b/android/src/toga_android/widgets/openglview.py @@ -69,11 +69,12 @@ def create(self): self.pointer = None self.buttons = frozenset() self.native = GLSurfaceView(self._native_activity) - self.renderer = TogaGLRenderer(self) - self.listener = TouchListener(self) - self.native.setEGLContextClientVersion(3) + + self.renderer = TogaGLRenderer(self) self.native.setRenderer(self.renderer) + + self.listener = TouchListener(self) self.native.setOnTouchListener(self.listener) def redraw(self): diff --git a/android/tests_backend/widgets/openglview.py b/android/tests_backend/widgets/openglview.py index 38a79d725f..d822e502c4 100644 --- a/android/tests_backend/widgets/openglview.py +++ b/android/tests_backend/widgets/openglview.py @@ -19,6 +19,9 @@ async def reset_buttons(self, x=0, y=0): await self.touch_up(x, y) await self.redraw("Touch cleared") + async def position_change(self, x=0, y=0): + self.motion_event(MotionEvent.ACTION_MOVE, x, y) + def motion_event(self, action, x, y): time = SystemClock.uptimeMillis() super().motion_event( diff --git a/cocoa/tests_backend/widgets/openglview.py b/cocoa/tests_backend/widgets/openglview.py index e4de284f41..67f3736811 100644 --- a/cocoa/tests_backend/widgets/openglview.py +++ b/cocoa/tests_backend/widgets/openglview.py @@ -29,6 +29,12 @@ async def reset_buttons(self, x=0, y=0): await method(x, y) await self.redraw("Buttons cleared") + async def position_change(self, x=0, y=0): + await self.mouse_event( + NSEventType.LeftMouseDragged, + self.native.convertPoint(NSPoint((x, y)), toView=None), + ) + async def left_mouse_down(self, x=0, y=0): event = self._button_event(NSEventType.LeftMouseDown) self.native.mouseDown_(event) diff --git a/gtk/src/toga_gtk/widgets/openglview.py b/gtk/src/toga_gtk/widgets/openglview.py index 6a1d88a42e..33b6c26b0d 100644 --- a/gtk/src/toga_gtk/widgets/openglview.py +++ b/gtk/src/toga_gtk/widgets/openglview.py @@ -42,7 +42,7 @@ def gtk_render(self, native, context): self.interface, size=self._size(), buttons=frozenset(self.buttons), - position=self.position, + pointer=self.position, ) return True diff --git a/gtk/tests_backend/widgets/openglview.py b/gtk/tests_backend/widgets/openglview.py index 9e6a80e6fd..cb973c8532 100644 --- a/gtk/tests_backend/widgets/openglview.py +++ b/gtk/tests_backend/widgets/openglview.py @@ -25,6 +25,15 @@ async def reset_buttons(self, x=0, y=0): await self.button_up(button, x, y) await self.redraw("Buttons cleared") + async def position_change(self, x=0, y=0): + self._emit_event( + Gdk.EventType.MOTION_NOTIFY, + x, + y, + button=1, + state=Gdk.ModifierType.MOD2_MASK, + ) + def _emit_event(self, event_type, x, y, button=1, state=0, emit_name=None): event = Gdk.Event.new(event_type) event.button = button diff --git a/iOS/tests_backend/widgets/openglview.py b/iOS/tests_backend/widgets/openglview.py index 2b7e0c35ad..19b22a12be 100644 --- a/iOS/tests_backend/widgets/openglview.py +++ b/iOS/tests_backend/widgets/openglview.py @@ -23,19 +23,19 @@ async def reset_buttons(self, x=0, y=0): await self.touch_up(x, y) await self.redraw("Touch cleared") - async def touch_down(self, x, y): + async def position_change(self, x=0, y=0): touch = MockTouch.alloc().init() touches = NSSet.setWithObject(touch) touch.position = NSPoint(x, y) - self.native.touchesBegan(touches, withEvent=None) + self.native.touchesMoved(touches, withEvent=None) - async def touch_move(self, x, y): + async def touch_down(self, x, y): touch = MockTouch.alloc().init() touches = NSSet.setWithObject(touch) touch.position = NSPoint(x, y) - self.native.touchesMoved(touches, withEvent=None) + self.native.touchesBegan(touches, withEvent=None) async def touch_up(self, x, y): touch = MockTouch.alloc().init() diff --git a/qt/tests_backend/widgets/openglview.py b/qt/tests_backend/widgets/openglview.py index 06e7d3b7a0..f55ea996a9 100644 --- a/qt/tests_backend/widgets/openglview.py +++ b/qt/tests_backend/widgets/openglview.py @@ -28,6 +28,14 @@ async def reset_buttons(self, x=0, y=0): await method(x, y) await self.redraw("Buttons cleared") + async def position_change(self, x=0, y=0): + self._emit_event( + QEvent.Type.MouseMove, + x, + y, + button=Qt.MouseButton.LeftButton, + ) + async def left_mouse_down(self, x=0, y=0): self._emit_event( QEvent.Type.MouseButtonPress, x, y, button=Qt.MouseButton.LeftButton diff --git a/testbed/tests/widgets/test_openglview.py b/testbed/tests/widgets/test_openglview.py index 4367994edb..6d07a4c86e 100644 --- a/testbed/tests/widgets/test_openglview.py +++ b/testbed/tests/widgets/test_openglview.py @@ -32,13 +32,18 @@ async def test_callbacks(probe, widget, renderer): renderer.on_init.assert_called_once_with(widget) await probe.redraw("OpenGLView widget initialized", 0.1) - # different backends render different numbers of times with - # different arguments + # different backends render different numbers of times renderer.on_render.assert_called() assert renderer.on_render.call_args[0] == (widget,) assert "size" in renderer.on_render.call_args[1] assert isinstance(renderer.on_render.call_args[1]["size"], tuple) assert len(renderer.on_render.call_args[1]["size"]) == 2 + assert "pointer" in renderer.on_render.call_args[1] + if renderer.on_render.call_args[1]["pointer"] is not None: + assert isinstance(renderer.on_render.call_args[1]["pointer"], tuple) + assert len(renderer.on_render.call_args[1]["pointer"]) == 2 + assert "buttons" in renderer.on_render.call_args[1] + assert renderer.on_render.call_args[1]["buttons"] == frozenset() renderer.reset_mock() @@ -49,6 +54,12 @@ async def test_callbacks(probe, widget, renderer): assert "size" in renderer.on_render.call_args[1] assert isinstance(renderer.on_render.call_args[1]["size"], tuple) assert len(renderer.on_render.call_args[1]["size"]) == 2 + assert "pointer" in renderer.on_render.call_args[1] + if renderer.on_render.call_args[1]["pointer"] is not None: + assert isinstance(renderer.on_render.call_args[1]["pointer"], tuple) + assert len(renderer.on_render.call_args[1]["pointer"]) == 2 + assert "buttons" in renderer.on_render.call_args[1] + assert renderer.on_render.call_args[1]["buttons"] == frozenset() async def test_buttons(probe, widget, renderer): @@ -81,3 +92,26 @@ async def test_buttons(probe, widget, renderer): assert "buttons" in renderer.on_render.call_args[1] assert isinstance(renderer.on_render.call_args[1]["buttons"], frozenset) assert renderer.on_render.call_args[1]["buttons"] == buttons + + assert "pointer" in renderer.on_render.call_args[1] + assert renderer.on_render.call_args[1]["pointer"] == (0, 0) + + +async def test_pointer(probe, widget, renderer): + if not probe.buttons: + pytest.skip("Backend does not support buttons.") + + await probe.redraw("OpenGLView widget initialized", 0.1) + + # generate a pointer move event + await probe.position_change(10, 10) + await probe.redraw("Pointer changed", 0.01) + + # redraw the view + widget.redraw() + await probe.redraw("OpenGLView widget redraw requested", 0.1) + + # pointer should reflect the new position + assert renderer.on_render.call_args[0] == (widget,) + assert "pointer" in renderer.on_render.call_args[1] + assert renderer.on_render.call_args[1]["pointer"] == (0, 0) From 3ed649102142299b574e205f9aa9d6d92a1e5d80 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 30 Apr 2026 12:08:35 +0100 Subject: [PATCH 26/26] Fix tests and cocoa NSPoint call. --- cocoa/tests_backend/widgets/openglview.py | 2 +- testbed/tests/widgets/test_openglview.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cocoa/tests_backend/widgets/openglview.py b/cocoa/tests_backend/widgets/openglview.py index 67f3736811..8d6fbeaa69 100644 --- a/cocoa/tests_backend/widgets/openglview.py +++ b/cocoa/tests_backend/widgets/openglview.py @@ -32,7 +32,7 @@ async def reset_buttons(self, x=0, y=0): async def position_change(self, x=0, y=0): await self.mouse_event( NSEventType.LeftMouseDragged, - self.native.convertPoint(NSPoint((x, y)), toView=None), + self.native.convertPoint(NSPoint(x, y), toView=None), ) async def left_mouse_down(self, x=0, y=0): diff --git a/testbed/tests/widgets/test_openglview.py b/testbed/tests/widgets/test_openglview.py index 6d07a4c86e..3463f5a703 100644 --- a/testbed/tests/widgets/test_openglview.py +++ b/testbed/tests/widgets/test_openglview.py @@ -94,7 +94,8 @@ async def test_buttons(probe, widget, renderer): assert renderer.on_render.call_args[1]["buttons"] == buttons assert "pointer" in renderer.on_render.call_args[1] - assert renderer.on_render.call_args[1]["pointer"] == (0, 0) + assert isinstance(renderer.on_render.call_args[1]["pointer"], tuple) + assert len(renderer.on_render.call_args[1]["pointer"]) == 2 async def test_pointer(probe, widget, renderer): @@ -114,4 +115,4 @@ async def test_pointer(probe, widget, renderer): # pointer should reflect the new position assert renderer.on_render.call_args[0] == (widget,) assert "pointer" in renderer.on_render.call_args[1] - assert renderer.on_render.call_args[1]["pointer"] == (0, 0) + assert renderer.on_render.call_args[1]["pointer"] == (10, 10)