From ebefcc885f74461cd0e3f19b5ae3622dc6cf6dbc Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 27 Jun 2013 11:25:32 -0800 Subject: SDK 0.2.2 --- 3rdParty/TinyXml/tinyxml2.cpp | 2101 +++++ 3rdParty/TinyXml/tinyxml2.h | 1911 ++++ LibOVR/Include/OVR.h | 33 + LibOVR/Include/OVRVersion.h | 22 + LibOVR/Projects/Win32/LibOVR_Msvc2010.sln | 20 + LibOVR/Projects/Win32/LibOVR_Msvc2010.vcxproj | 275 + .../Projects/Win32/LibOVR_Msvc2010.vcxproj.filters | 186 + LibOVR/Projects/libovr.txt | 83 + LibOVR/Src/Kernel/OVR_Alg.cpp | 46 + LibOVR/Src/Kernel/OVR_Alg.h | 953 ++ LibOVR/Src/Kernel/OVR_Allocator.cpp | 84 + LibOVR/Src/Kernel/OVR_Allocator.h | 336 + LibOVR/Src/Kernel/OVR_Array.h | 793 ++ LibOVR/Src/Kernel/OVR_Atomic.cpp | 82 + LibOVR/Src/Kernel/OVR_Atomic.h | 859 ++ LibOVR/Src/Kernel/OVR_Color.h | 55 + LibOVR/Src/Kernel/OVR_ContainerAllocator.h | 256 + LibOVR/Src/Kernel/OVR_File.cpp | 571 ++ LibOVR/Src/Kernel/OVR_File.h | 518 ++ LibOVR/Src/Kernel/OVR_FileFILE.cpp | 583 ++ LibOVR/Src/Kernel/OVR_Hash.h | 1291 +++ LibOVR/Src/Kernel/OVR_KeyCodes.h | 240 + LibOVR/Src/Kernel/OVR_List.h | 325 + LibOVR/Src/Kernel/OVR_Log.cpp | 173 + LibOVR/Src/Kernel/OVR_Log.h | 193 + LibOVR/Src/Kernel/OVR_Math.cpp | 153 + LibOVR/Src/Kernel/OVR_Math.h | 1070 +++ LibOVR/Src/Kernel/OVR_RefCount.cpp | 100 + LibOVR/Src/Kernel/OVR_RefCount.h | 522 ++ LibOVR/Src/Kernel/OVR_Std.cpp | 1025 +++ LibOVR/Src/Kernel/OVR_Std.h | 503 ++ LibOVR/Src/Kernel/OVR_String.cpp | 752 ++ LibOVR/Src/Kernel/OVR_String.h | 645 ++ LibOVR/Src/Kernel/OVR_StringHash.h | 89 + LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp | 42 + LibOVR/Src/Kernel/OVR_String_PathUtil.cpp | 200 + LibOVR/Src/Kernel/OVR_SysFile.cpp | 125 + LibOVR/Src/Kernel/OVR_SysFile.h | 93 + LibOVR/Src/Kernel/OVR_System.cpp | 70 + LibOVR/Src/Kernel/OVR_System.h | 67 + LibOVR/Src/Kernel/OVR_Threads.h | 396 + LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp | 795 ++ LibOVR/Src/Kernel/OVR_ThreadsWinAPI.cpp | 994 ++ LibOVR/Src/Kernel/OVR_Timer.cpp | 156 + LibOVR/Src/Kernel/OVR_Timer.h | 100 + LibOVR/Src/Kernel/OVR_Types.h | 456 + LibOVR/Src/Kernel/OVR_UTF8Util.cpp | 545 ++ LibOVR/Src/Kernel/OVR_UTF8Util.h | 88 + LibOVR/Src/OVR_Device.h | 627 ++ LibOVR/Src/OVR_DeviceConstants.h | 38 + LibOVR/Src/OVR_DeviceHandle.cpp | 174 + LibOVR/Src/OVR_DeviceHandle.h | 97 + LibOVR/Src/OVR_DeviceImpl.cpp | 784 ++ LibOVR/Src/OVR_DeviceImpl.h | 425 + LibOVR/Src/OVR_DeviceMessages.h | 162 + LibOVR/Src/OVR_HIDDevice.h | 143 + LibOVR/Src/OVR_HIDDeviceBase.h | 40 + LibOVR/Src/OVR_HIDDeviceImpl.h | 203 + LibOVR/Src/OVR_LatencyTestImpl.cpp | 798 ++ LibOVR/Src/OVR_LatencyTestImpl.h | 135 + LibOVR/Src/OVR_OSX_DeviceManager.cpp | 349 + LibOVR/Src/OVR_OSX_DeviceManager.h | 119 + LibOVR/Src/OVR_OSX_HIDDevice.cpp | 899 ++ LibOVR/Src/OVR_OSX_HIDDevice.h | 149 + LibOVR/Src/OVR_OSX_HMDDevice.cpp | 326 + LibOVR/Src/OVR_OSX_HMDDevice.h | 136 + LibOVR/Src/OVR_OSX_SensorDevice.cpp | 45 + LibOVR/Src/OVR_SensorFilter.cpp | 201 + LibOVR/Src/OVR_SensorFilter.h | 99 + LibOVR/Src/OVR_SensorFusion.cpp | 378 + LibOVR/Src/OVR_SensorFusion.h | 268 + LibOVR/Src/OVR_SensorImpl.cpp | 882 ++ LibOVR/Src/OVR_SensorImpl.h | 208 + LibOVR/Src/OVR_ThreadCommandQueue.cpp | 370 + LibOVR/Src/OVR_ThreadCommandQueue.h | 308 + LibOVR/Src/OVR_Win32_DeviceManager.cpp | 423 + LibOVR/Src/OVR_Win32_DeviceManager.h | 146 + LibOVR/Src/OVR_Win32_DeviceStatus.cpp | 350 + LibOVR/Src/OVR_Win32_DeviceStatus.h | 101 + LibOVR/Src/OVR_Win32_HIDDevice.cpp | 637 ++ LibOVR/Src/OVR_Win32_HIDDevice.h | 194 + LibOVR/Src/OVR_Win32_HMDDevice.cpp | 418 + LibOVR/Src/OVR_Win32_HMDDevice.h | 133 + LibOVR/Src/OVR_Win32_SensorDevice.cpp | 47 + LibOVR/Src/OVR_Win32_SensorDevice.h | 24 + LibOVR/Src/Util/Util_LatencyTest.cpp | 560 ++ LibOVR/Src/Util/Util_LatencyTest.h | 160 + LibOVR/Src/Util/Util_MagCalibration.cpp | 180 + LibOVR/Src/Util/Util_MagCalibration.h | 115 + LibOVR/Src/Util/Util_Render_Stereo.cpp | 311 + LibOVR/Src/Util/Util_Render_Stereo.h | 299 + Mac_OculusWorldDemo.app/Contents/Info.plist | 50 + .../Contents/MacOS/OculusWorldDemo | Bin 0 -> 881004 bytes Mac_OculusWorldDemo.app/Contents/PkgInfo | 1 + Samples/CommonSrc/Makefile | 7 + Samples/CommonSrc/Oculus.ico | Bin 0 -> 40177 bytes Samples/CommonSrc/Platform/Gamepad.h | 102 + Samples/CommonSrc/Platform/OSX_Gamepad.cpp | 424 + Samples/CommonSrc/Platform/OSX_Gamepad.h | 67 + Samples/CommonSrc/Platform/OSX_Platform.h | 80 + Samples/CommonSrc/Platform/OSX_Platform.mm | 514 ++ Samples/CommonSrc/Platform/OSX_PlatformObjc.h | 31 + Samples/CommonSrc/Platform/OSX_WavPlayer.cpp | 242 + Samples/CommonSrc/Platform/OSX_WavPlayer.h | 64 + Samples/CommonSrc/Platform/Platform.cpp | 75 + Samples/CommonSrc/Platform/Platform.h | 190 + Samples/CommonSrc/Platform/Platform_Default.h | 59 + Samples/CommonSrc/Platform/Win32_Gamepad.cpp | 101 + Samples/CommonSrc/Platform/Win32_Gamepad.h | 54 + Samples/CommonSrc/Platform/Win32_Platform.cpp | 600 ++ Samples/CommonSrc/Platform/Win32_Platform.h | 101 + Samples/CommonSrc/Render/Render_D3D10_Device.cpp | 26 + Samples/CommonSrc/Render/Render_D3D10_Device.h | 26 + Samples/CommonSrc/Render/Render_D3D11_Device.cpp | 26 + Samples/CommonSrc/Render/Render_D3D11_Device.h | 26 + Samples/CommonSrc/Render/Render_D3D1X_Device.cpp | 1831 ++++ Samples/CommonSrc/Render/Render_D3D1X_Device.h | 360 + Samples/CommonSrc/Render/Render_Device.cpp | 1033 +++ Samples/CommonSrc/Render/Render_Device.h | 897 ++ Samples/CommonSrc/Render/Render_Font.h | 51 + .../CommonSrc/Render/Render_FontEmbed_DejaVu48.h | 9463 ++++++++++++++++++++ Samples/CommonSrc/Render/Render_GL_Device.cpp | 849 ++ Samples/CommonSrc/Render/Render_GL_Device.h | 230 + .../CommonSrc/Render/Render_GL_Win32_Device.cpp | 90 + Samples/CommonSrc/Render/Render_GL_Win32_Device.h | 59 + Samples/CommonSrc/Render/Render_LoadTextureDDS.cpp | 121 + Samples/CommonSrc/Render/Render_LoadTextureTGA.cpp | 102 + Samples/CommonSrc/Render/Render_XmlSceneLoader.cpp | 390 + Samples/CommonSrc/Render/Render_XmlSceneLoader.h | 74 + Samples/LibOVR_Samples_Msvc2010.sln | 43 + .../OculusRoomTiny-Info.plist | 34 + .../OculusWorldDemo-Info.plist | 34 + .../SensorBoxTest-Info.plist | 34 + .../LibOVR_With_Samples.xcodeproj/project.pbxproj | 1092 +++ Samples/LibOVR_With_Samples_Msvc2010.sln | 67 + Samples/Oculus.icns | Bin 0 -> 144958 bytes Samples/OculusRoomTiny/OSX_OculusRoomTiny.h | 223 + Samples/OculusRoomTiny/OSX_OculusRoomTiny.mm | 861 ++ Samples/OculusRoomTiny/OculusRoomModel.cpp | 260 + Samples/OculusRoomTiny/OculusRoomTiny.rc | Bin 0 -> 144 bytes .../OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj | 187 + .../OculusRoomTiny_Msvc2010.vcxproj.filters | 17 + Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp | 1311 +++ Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.h | 273 + Samples/OculusRoomTiny/RenderTiny_Device.cpp | 442 + Samples/OculusRoomTiny/RenderTiny_Device.h | 724 ++ Samples/OculusRoomTiny/RenderTiny_GL_Device.cpp | 784 ++ Samples/OculusRoomTiny/RenderTiny_GL_Device.h | 228 + Samples/OculusRoomTiny/Win32_OculusRoomTiny.cpp | 712 ++ Samples/OculusRoomTiny/Win32_OculusRoomTiny.h | 189 + Samples/OculusWorldDemo/Makefile | 5 + Samples/OculusWorldDemo/OculusWorldDemo.cpp | 1836 ++++ Samples/OculusWorldDemo/OculusWorldDemo.rc | Bin 0 -> 144 bytes .../OculusWorldDemo_Msvc2010.vcxproj | 211 + .../OculusWorldDemo_Msvc2010.vcxproj.filters | 89 + Samples/OculusWorldDemo/Player.cpp | 166 + Samples/OculusWorldDemo/Player.h | 79 + Samples/SensorBox/SensorBoxTest.cpp | 506 ++ Samples/SensorBox/SensorBoxTest.rc | Bin 0 -> 144 bytes Samples/SensorBox/SensorBoxTest_Msvc2010.vcxproj | 126 + .../SensorBoxTest_Msvc2010.vcxproj.filters | 73 + Win_OculusWorldDemo.lnk | Bin 0 -> 2349 bytes readme.txt | 6 + 163 files changed, 62464 insertions(+) create mode 100644 3rdParty/TinyXml/tinyxml2.cpp create mode 100644 3rdParty/TinyXml/tinyxml2.h create mode 100644 LibOVR/Include/OVR.h create mode 100644 LibOVR/Include/OVRVersion.h create mode 100644 LibOVR/Projects/Win32/LibOVR_Msvc2010.sln create mode 100644 LibOVR/Projects/Win32/LibOVR_Msvc2010.vcxproj create mode 100644 LibOVR/Projects/Win32/LibOVR_Msvc2010.vcxproj.filters create mode 100644 LibOVR/Projects/libovr.txt create mode 100644 LibOVR/Src/Kernel/OVR_Alg.cpp create mode 100644 LibOVR/Src/Kernel/OVR_Alg.h create mode 100644 LibOVR/Src/Kernel/OVR_Allocator.cpp create mode 100644 LibOVR/Src/Kernel/OVR_Allocator.h create mode 100644 LibOVR/Src/Kernel/OVR_Array.h create mode 100644 LibOVR/Src/Kernel/OVR_Atomic.cpp create mode 100644 LibOVR/Src/Kernel/OVR_Atomic.h create mode 100644 LibOVR/Src/Kernel/OVR_Color.h create mode 100644 LibOVR/Src/Kernel/OVR_ContainerAllocator.h create mode 100644 LibOVR/Src/Kernel/OVR_File.cpp create mode 100644 LibOVR/Src/Kernel/OVR_File.h create mode 100644 LibOVR/Src/Kernel/OVR_FileFILE.cpp create mode 100644 LibOVR/Src/Kernel/OVR_Hash.h create mode 100644 LibOVR/Src/Kernel/OVR_KeyCodes.h create mode 100644 LibOVR/Src/Kernel/OVR_List.h create mode 100644 LibOVR/Src/Kernel/OVR_Log.cpp create mode 100644 LibOVR/Src/Kernel/OVR_Log.h create mode 100644 LibOVR/Src/Kernel/OVR_Math.cpp create mode 100644 LibOVR/Src/Kernel/OVR_Math.h create mode 100644 LibOVR/Src/Kernel/OVR_RefCount.cpp create mode 100644 LibOVR/Src/Kernel/OVR_RefCount.h create mode 100644 LibOVR/Src/Kernel/OVR_Std.cpp create mode 100644 LibOVR/Src/Kernel/OVR_Std.h create mode 100644 LibOVR/Src/Kernel/OVR_String.cpp create mode 100644 LibOVR/Src/Kernel/OVR_String.h create mode 100644 LibOVR/Src/Kernel/OVR_StringHash.h create mode 100644 LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp create mode 100644 LibOVR/Src/Kernel/OVR_String_PathUtil.cpp create mode 100644 LibOVR/Src/Kernel/OVR_SysFile.cpp create mode 100644 LibOVR/Src/Kernel/OVR_SysFile.h create mode 100644 LibOVR/Src/Kernel/OVR_System.cpp create mode 100644 LibOVR/Src/Kernel/OVR_System.h create mode 100644 LibOVR/Src/Kernel/OVR_Threads.h create mode 100644 LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp create mode 100644 LibOVR/Src/Kernel/OVR_ThreadsWinAPI.cpp create mode 100644 LibOVR/Src/Kernel/OVR_Timer.cpp create mode 100644 LibOVR/Src/Kernel/OVR_Timer.h create mode 100644 LibOVR/Src/Kernel/OVR_Types.h create mode 100644 LibOVR/Src/Kernel/OVR_UTF8Util.cpp create mode 100644 LibOVR/Src/Kernel/OVR_UTF8Util.h create mode 100644 LibOVR/Src/OVR_Device.h create mode 100644 LibOVR/Src/OVR_DeviceConstants.h create mode 100644 LibOVR/Src/OVR_DeviceHandle.cpp create mode 100644 LibOVR/Src/OVR_DeviceHandle.h create mode 100644 LibOVR/Src/OVR_DeviceImpl.cpp create mode 100644 LibOVR/Src/OVR_DeviceImpl.h create mode 100644 LibOVR/Src/OVR_DeviceMessages.h create mode 100644 LibOVR/Src/OVR_HIDDevice.h create mode 100644 LibOVR/Src/OVR_HIDDeviceBase.h create mode 100644 LibOVR/Src/OVR_HIDDeviceImpl.h create mode 100644 LibOVR/Src/OVR_LatencyTestImpl.cpp create mode 100644 LibOVR/Src/OVR_LatencyTestImpl.h create mode 100644 LibOVR/Src/OVR_OSX_DeviceManager.cpp create mode 100644 LibOVR/Src/OVR_OSX_DeviceManager.h create mode 100644 LibOVR/Src/OVR_OSX_HIDDevice.cpp create mode 100644 LibOVR/Src/OVR_OSX_HIDDevice.h create mode 100644 LibOVR/Src/OVR_OSX_HMDDevice.cpp create mode 100644 LibOVR/Src/OVR_OSX_HMDDevice.h create mode 100644 LibOVR/Src/OVR_OSX_SensorDevice.cpp create mode 100644 LibOVR/Src/OVR_SensorFilter.cpp create mode 100644 LibOVR/Src/OVR_SensorFilter.h create mode 100644 LibOVR/Src/OVR_SensorFusion.cpp create mode 100644 LibOVR/Src/OVR_SensorFusion.h create mode 100644 LibOVR/Src/OVR_SensorImpl.cpp create mode 100644 LibOVR/Src/OVR_SensorImpl.h create mode 100644 LibOVR/Src/OVR_ThreadCommandQueue.cpp create mode 100644 LibOVR/Src/OVR_ThreadCommandQueue.h create mode 100644 LibOVR/Src/OVR_Win32_DeviceManager.cpp create mode 100644 LibOVR/Src/OVR_Win32_DeviceManager.h create mode 100644 LibOVR/Src/OVR_Win32_DeviceStatus.cpp create mode 100644 LibOVR/Src/OVR_Win32_DeviceStatus.h create mode 100644 LibOVR/Src/OVR_Win32_HIDDevice.cpp create mode 100644 LibOVR/Src/OVR_Win32_HIDDevice.h create mode 100644 LibOVR/Src/OVR_Win32_HMDDevice.cpp create mode 100644 LibOVR/Src/OVR_Win32_HMDDevice.h create mode 100644 LibOVR/Src/OVR_Win32_SensorDevice.cpp create mode 100644 LibOVR/Src/OVR_Win32_SensorDevice.h create mode 100644 LibOVR/Src/Util/Util_LatencyTest.cpp create mode 100644 LibOVR/Src/Util/Util_LatencyTest.h create mode 100644 LibOVR/Src/Util/Util_MagCalibration.cpp create mode 100644 LibOVR/Src/Util/Util_MagCalibration.h create mode 100644 LibOVR/Src/Util/Util_Render_Stereo.cpp create mode 100644 LibOVR/Src/Util/Util_Render_Stereo.h create mode 100644 Mac_OculusWorldDemo.app/Contents/Info.plist create mode 100644 Mac_OculusWorldDemo.app/Contents/MacOS/OculusWorldDemo create mode 100644 Mac_OculusWorldDemo.app/Contents/PkgInfo create mode 100644 Samples/CommonSrc/Makefile create mode 100644 Samples/CommonSrc/Oculus.ico create mode 100644 Samples/CommonSrc/Platform/Gamepad.h create mode 100644 Samples/CommonSrc/Platform/OSX_Gamepad.cpp create mode 100644 Samples/CommonSrc/Platform/OSX_Gamepad.h create mode 100644 Samples/CommonSrc/Platform/OSX_Platform.h create mode 100644 Samples/CommonSrc/Platform/OSX_Platform.mm create mode 100644 Samples/CommonSrc/Platform/OSX_PlatformObjc.h create mode 100644 Samples/CommonSrc/Platform/OSX_WavPlayer.cpp create mode 100644 Samples/CommonSrc/Platform/OSX_WavPlayer.h create mode 100644 Samples/CommonSrc/Platform/Platform.cpp create mode 100644 Samples/CommonSrc/Platform/Platform.h create mode 100644 Samples/CommonSrc/Platform/Platform_Default.h create mode 100644 Samples/CommonSrc/Platform/Win32_Gamepad.cpp create mode 100644 Samples/CommonSrc/Platform/Win32_Gamepad.h create mode 100644 Samples/CommonSrc/Platform/Win32_Platform.cpp create mode 100644 Samples/CommonSrc/Platform/Win32_Platform.h create mode 100644 Samples/CommonSrc/Render/Render_D3D10_Device.cpp create mode 100644 Samples/CommonSrc/Render/Render_D3D10_Device.h create mode 100644 Samples/CommonSrc/Render/Render_D3D11_Device.cpp create mode 100644 Samples/CommonSrc/Render/Render_D3D11_Device.h create mode 100644 Samples/CommonSrc/Render/Render_D3D1X_Device.cpp create mode 100644 Samples/CommonSrc/Render/Render_D3D1X_Device.h create mode 100644 Samples/CommonSrc/Render/Render_Device.cpp create mode 100644 Samples/CommonSrc/Render/Render_Device.h create mode 100644 Samples/CommonSrc/Render/Render_Font.h create mode 100644 Samples/CommonSrc/Render/Render_FontEmbed_DejaVu48.h create mode 100644 Samples/CommonSrc/Render/Render_GL_Device.cpp create mode 100644 Samples/CommonSrc/Render/Render_GL_Device.h create mode 100644 Samples/CommonSrc/Render/Render_GL_Win32_Device.cpp create mode 100644 Samples/CommonSrc/Render/Render_GL_Win32_Device.h create mode 100644 Samples/CommonSrc/Render/Render_LoadTextureDDS.cpp create mode 100644 Samples/CommonSrc/Render/Render_LoadTextureTGA.cpp create mode 100644 Samples/CommonSrc/Render/Render_XmlSceneLoader.cpp create mode 100644 Samples/CommonSrc/Render/Render_XmlSceneLoader.h create mode 100644 Samples/LibOVR_Samples_Msvc2010.sln create mode 100644 Samples/LibOVR_With_Samples.xcodeproj/OculusRoomTiny-Info.plist create mode 100644 Samples/LibOVR_With_Samples.xcodeproj/OculusWorldDemo-Info.plist create mode 100644 Samples/LibOVR_With_Samples.xcodeproj/SensorBoxTest-Info.plist create mode 100644 Samples/LibOVR_With_Samples.xcodeproj/project.pbxproj create mode 100644 Samples/LibOVR_With_Samples_Msvc2010.sln create mode 100644 Samples/Oculus.icns create mode 100644 Samples/OculusRoomTiny/OSX_OculusRoomTiny.h create mode 100644 Samples/OculusRoomTiny/OSX_OculusRoomTiny.mm create mode 100644 Samples/OculusRoomTiny/OculusRoomModel.cpp create mode 100644 Samples/OculusRoomTiny/OculusRoomTiny.rc create mode 100644 Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj create mode 100644 Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj.filters create mode 100644 Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp create mode 100644 Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.h create mode 100644 Samples/OculusRoomTiny/RenderTiny_Device.cpp create mode 100644 Samples/OculusRoomTiny/RenderTiny_Device.h create mode 100644 Samples/OculusRoomTiny/RenderTiny_GL_Device.cpp create mode 100644 Samples/OculusRoomTiny/RenderTiny_GL_Device.h create mode 100644 Samples/OculusRoomTiny/Win32_OculusRoomTiny.cpp create mode 100644 Samples/OculusRoomTiny/Win32_OculusRoomTiny.h create mode 100644 Samples/OculusWorldDemo/Makefile create mode 100644 Samples/OculusWorldDemo/OculusWorldDemo.cpp create mode 100644 Samples/OculusWorldDemo/OculusWorldDemo.rc create mode 100644 Samples/OculusWorldDemo/OculusWorldDemo_Msvc2010.vcxproj create mode 100644 Samples/OculusWorldDemo/OculusWorldDemo_Msvc2010.vcxproj.filters create mode 100644 Samples/OculusWorldDemo/Player.cpp create mode 100644 Samples/OculusWorldDemo/Player.h create mode 100644 Samples/SensorBox/SensorBoxTest.cpp create mode 100644 Samples/SensorBox/SensorBoxTest.rc create mode 100644 Samples/SensorBox/SensorBoxTest_Msvc2010.vcxproj create mode 100644 Samples/SensorBox/SensorBoxTest_Msvc2010.vcxproj.filters create mode 100644 Win_OculusWorldDemo.lnk create mode 100644 readme.txt diff --git a/3rdParty/TinyXml/tinyxml2.cpp b/3rdParty/TinyXml/tinyxml2.cpp new file mode 100644 index 0000000..647901b --- /dev/null +++ b/3rdParty/TinyXml/tinyxml2.cpp @@ -0,0 +1,2101 @@ +/* +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "tinyxml2.h" + +#include // yes, this one new style header, is in the Android SDK. +# ifdef ANDROID_NDK +# include +#else +# include +#endif + +static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF +static const char LF = LINE_FEED; +static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out +static const char CR = CARRIAGE_RETURN; +static const char SINGLE_QUOTE = '\''; +static const char DOUBLE_QUOTE = '\"'; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// ef bb bf (Microsoft "lead bytes") - designates UTF-8 + +static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + +#define DELETE_NODE( node ) { \ + if ( node ) { \ + MemPool* pool = node->_memPool; \ + node->~XMLNode(); \ + pool->Free( node ); \ + } \ + } +#define DELETE_ATTRIBUTE( attrib ) { \ + if ( attrib ) { \ + MemPool* pool = attrib->_memPool; \ + attrib->~XMLAttribute(); \ + pool->Free( attrib ); \ + } \ + } + +namespace tinyxml2 +{ + +struct Entity { + const char* pattern; + int length; + char value; +}; + +static const int NUM_ENTITIES = 5; +static const Entity entities[NUM_ENTITIES] = { + { "quot", 4, DOUBLE_QUOTE }, + { "amp", 3, '&' }, + { "apos", 4, SINGLE_QUOTE }, + { "lt", 2, '<' }, + { "gt", 2, '>' } +}; + + +StrPair::~StrPair() +{ + Reset(); +} + + +void StrPair::Reset() +{ + if ( _flags & NEEDS_DELETE ) { + delete [] _start; + } + _flags = 0; + _start = 0; + _end = 0; +} + + +void StrPair::SetStr( const char* str, int flags ) +{ + Reset(); + size_t len = strlen( str ); + _start = new char[ len+1 ]; + memcpy( _start, str, len+1 ); + _end = _start + len; + _flags = flags | NEEDS_DELETE; +} + + +char* StrPair::ParseText( char* p, const char* endTag, int strFlags ) +{ + TIXMLASSERT( endTag && *endTag ); + + char* start = p; // fixme: hides a member + char endChar = *endTag; + size_t length = strlen( endTag ); + + // Inner loop of text parsing. + while ( *p ) { + if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) { + Set( start, p, strFlags ); + return p + length; + } + ++p; + } + return 0; +} + + +char* StrPair::ParseName( char* p ) +{ + char* start = p; + + if ( !start || !(*start) ) { + return 0; + } + + while( *p && ( + XMLUtil::IsAlphaNum( (unsigned char) *p ) + || *p == '_' + || *p == ':' + || (*p == '-' && p>start ) // can be in a name, but not lead it. + || (*p == '.' && p>start ) )) { // can be in a name, but not lead it. + ++p; + } + + if ( p > start ) { + Set( start, p, 0 ); + return p; + } + return 0; +} + + +void StrPair::CollapseWhitespace() +{ + // Trim leading space. + _start = XMLUtil::SkipWhiteSpace( _start ); + + if ( _start && *_start ) { + char* p = _start; // the read pointer + char* q = _start; // the write pointer + + while( *p ) { + if ( XMLUtil::IsWhiteSpace( *p )) { + p = XMLUtil::SkipWhiteSpace( p ); + if ( *p == 0 ) { + break; // don't write to q; this trims the trailing space. + } + *q = ' '; + ++q; + } + *q = *p; + ++q; + ++p; + } + *q = 0; + } +} + + +const char* StrPair::GetStr() +{ + if ( _flags & NEEDS_FLUSH ) { + *_end = 0; + _flags ^= NEEDS_FLUSH; + + if ( _flags ) { + char* p = _start; // the read pointer + char* q = _start; // the write pointer + + while( p < _end ) { + if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) { + // CR-LF pair becomes LF + // CR alone becomes LF + // LF-CR becomes LF + if ( *(p+1) == LF ) { + p += 2; + } + else { + ++p; + } + *q++ = LF; + } + else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { + if ( *(p+1) == CR ) { + p += 2; + } + else { + ++p; + } + *q++ = LF; + } + else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) { + // Entities handled by tinyXML2: + // - special entities in the entity table [in/out] + // - numeric character reference [in] + // 中 or 中 + + if ( *(p+1) == '#' ) { + char buf[10] = { 0 }; + int len; + p = const_cast( XMLUtil::GetCharacterRef( p, buf, &len ) ); + for( int i=0; i(p); + // Check for BOM: + if ( *(pu+0) == TIXML_UTF_LEAD_0 + && *(pu+1) == TIXML_UTF_LEAD_1 + && *(pu+2) == TIXML_UTF_LEAD_2 ) { + *bom = true; + p += 3; + } + return p; +} + + +void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) { + *length = 1; + } + else if ( input < 0x800 ) { + *length = 2; + } + else if ( input < 0x10000 ) { + *length = 3; + } + else if ( input < 0x200000 ) { + *length = 4; + } + else { + *length = 0; // This code won't covert this correctly anyway. + return; + } + + output += *length; + + // Scary scary fall throughs. + switch (*length) { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + default: + break; + } +} + + +const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) +{ + // Presume an entity, and pull it out. + *length = 0; + + if ( *(p+1) == '#' && *(p+2) ) { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) { + // Hexadecimal. + if ( !*(p+3) ) { + return 0; + } + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) { + return 0; + } + + delta = q-p; + --q; + + while ( *q != 'x' ) { + if ( *q >= '0' && *q <= '9' ) { + ucs += mult * (*q - '0'); + } + else if ( *q >= 'a' && *q <= 'f' ) { + ucs += mult * (*q - 'a' + 10); + } + else if ( *q >= 'A' && *q <= 'F' ) { + ucs += mult * (*q - 'A' + 10 ); + } + else { + return 0; + } + mult *= 16; + --q; + } + } + else { + // Decimal. + if ( !*(p+2) ) { + return 0; + } + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) { + return 0; + } + + delta = q-p; + --q; + + while ( *q != '#' ) { + if ( *q >= '0' && *q <= '9' ) { + ucs += mult * (*q - '0'); + } + else { + return 0; + } + mult *= 10; + --q; + } + } + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + return p + delta + 1; + } + return p+1; +} + + +void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); +} + + +void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%u", v ); +} + + +void XMLUtil::ToStr( bool v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 ); +} + + +void XMLUtil::ToStr( float v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%g", v ); +} + + +void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%g", v ); +} + + +bool XMLUtil::ToInt( const char* str, int* value ) +{ + if ( TIXML_SSCANF( str, "%d", value ) == 1 ) { + return true; + } + return false; +} + +bool XMLUtil::ToUnsigned( const char* str, unsigned *value ) +{ + if ( TIXML_SSCANF( str, "%u", value ) == 1 ) { + return true; + } + return false; +} + +bool XMLUtil::ToBool( const char* str, bool* value ) +{ + int ival = 0; + if ( ToInt( str, &ival )) { + *value = (ival==0) ? false : true; + return true; + } + if ( StringEqual( str, "true" ) ) { + *value = true; + return true; + } + else if ( StringEqual( str, "false" ) ) { + *value = false; + return true; + } + return false; +} + + +bool XMLUtil::ToFloat( const char* str, float* value ) +{ + if ( TIXML_SSCANF( str, "%f", value ) == 1 ) { + return true; + } + return false; +} + +bool XMLUtil::ToDouble( const char* str, double* value ) +{ + if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) { + return true; + } + return false; +} + + +char* XMLDocument::Identify( char* p, XMLNode** node ) +{ + XMLNode* returnNode = 0; + char* start = p; + p = XMLUtil::SkipWhiteSpace( p ); + if( !p || !*p ) { + return p; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: + // + // With a special case: + // + // + // + // + // Where the closing element (/foo) *must* be the next thing after the opening + // element, and the names must match. BUT the tricky bit is that the closing + // element will be read by the child. + // + // 'endTag' is the end tag for this node, it is returned by a call to a child. + // 'parentEnd' is the end tag for the parent, which is filled in and returned. + + while( p && *p ) { + XMLNode* node = 0; + + p = _document->Identify( p, &node ); + if ( p == 0 || node == 0 ) { + break; + } + + StrPair endTag; + p = node->ParseDeep( p, &endTag ); + if ( !p ) { + DELETE_NODE( node ); + node = 0; + if ( !_document->Error() ) { + _document->SetError( XML_ERROR_PARSING, 0, 0 ); + } + break; + } + + // We read the end tag. Return it to the parent. + if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) { + if ( parentEnd ) { + *parentEnd = static_cast(node)->_value; + } + node->_memPool->SetTracked(); // created and then immediately deleted. + DELETE_NODE( node ); + return p; + } + + // Handle an end tag returned to this level. + // And handle a bunch of annoying errors. + XMLElement* ele = node->ToElement(); + if ( ele ) { + if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) { + _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); + p = 0; + } + else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) { + _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); + p = 0; + } + else if ( !endTag.Empty() ) { + if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) { + _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); + p = 0; + } + } + } + if ( p == 0 ) { + DELETE_NODE( node ); + node = 0; + } + if ( node ) { + this->InsertEndChild( node ); + } + } + return 0; +} + +// --------- XMLText ---------- // +char* XMLText::ParseDeep( char* p, StrPair* ) +{ + const char* start = p; + if ( this->CData() ) { + p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION ); + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 ); + } + return p; + } + else { + int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; + if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) { + flags |= StrPair::COLLAPSE_WHITESPACE; + } + + p = _value.ParseText( p, "<", flags ); + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 ); + } + if ( p && *p ) { + return p-1; + } + } + return 0; +} + + +XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern? + text->SetCData( this->CData() ); + return text; +} + + +bool XMLText::ShallowEqual( const XMLNode* compare ) const +{ + return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() )); +} + + +bool XMLText::Accept( XMLVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +// --------- XMLComment ---------- // + +XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLComment::~XMLComment() +{ +} + + +char* XMLComment::ParseDeep( char* p, StrPair* ) +{ + // Comment parses as text. + const char* start = p; + p = _value.ParseText( p, "-->", StrPair::COMMENT ); + if ( p == 0 ) { + _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 ); + } + return p; +} + + +XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern? + return comment; +} + + +bool XMLComment::ShallowEqual( const XMLNode* compare ) const +{ + return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() )); +} + + +bool XMLComment::Accept( XMLVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +// --------- XMLDeclaration ---------- // + +XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLDeclaration::~XMLDeclaration() +{ + //printf( "~XMLDeclaration\n" ); +} + + +char* XMLDeclaration::ParseDeep( char* p, StrPair* ) +{ + // Declaration parses as text. + const char* start = p; + p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION ); + if ( p == 0 ) { + _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 ); + } + return p; +} + + +XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern? + return dec; +} + + +bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const +{ + return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() )); +} + + + +bool XMLDeclaration::Accept( XMLVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + +// --------- XMLUnknown ---------- // + +XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLUnknown::~XMLUnknown() +{ +} + + +char* XMLUnknown::ParseDeep( char* p, StrPair* ) +{ + // Unknown parses as text. + const char* start = p; + + p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION ); + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 ); + } + return p; +} + + +XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern? + return text; +} + + +bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const +{ + return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() )); +} + + +bool XMLUnknown::Accept( XMLVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + +// --------- XMLAttribute ---------- // +char* XMLAttribute::ParseDeep( char* p, bool processEntities ) +{ + // Parse using the name rules: bug fix, was using ParseText before + p = _name.ParseName( p ); + if ( !p || !*p ) { + return 0; + } + + // Skip white space before = + p = XMLUtil::SkipWhiteSpace( p ); + if ( !p || *p != '=' ) { + return 0; + } + + ++p; // move up to opening quote + p = XMLUtil::SkipWhiteSpace( p ); + if ( *p != '\"' && *p != '\'' ) { + return 0; + } + + char endTag[2] = { *p, 0 }; + ++p; // move past opening quote + + p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES ); + return p; +} + + +void XMLAttribute::SetName( const char* n ) +{ + _name.SetStr( n ); +} + + +XMLError XMLAttribute::QueryIntValue( int* value ) const +{ + if ( XMLUtil::ToInt( Value(), value )) { + return XML_NO_ERROR; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const +{ + if ( XMLUtil::ToUnsigned( Value(), value )) { + return XML_NO_ERROR; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryBoolValue( bool* value ) const +{ + if ( XMLUtil::ToBool( Value(), value )) { + return XML_NO_ERROR; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryFloatValue( float* value ) const +{ + if ( XMLUtil::ToFloat( Value(), value )) { + return XML_NO_ERROR; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryDoubleValue( double* value ) const +{ + if ( XMLUtil::ToDouble( Value(), value )) { + return XML_NO_ERROR; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +void XMLAttribute::SetAttribute( const char* v ) +{ + _value.SetStr( v ); +} + + +void XMLAttribute::SetAttribute( int v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +void XMLAttribute::SetAttribute( unsigned v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +void XMLAttribute::SetAttribute( bool v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + +void XMLAttribute::SetAttribute( double v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + +void XMLAttribute::SetAttribute( float v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +// --------- XMLElement ---------- // +XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ), + _closingType( 0 ), + _rootAttribute( 0 ) +{ +} + + +XMLElement::~XMLElement() +{ + while( _rootAttribute ) { + XMLAttribute* next = _rootAttribute->_next; + DELETE_ATTRIBUTE( _rootAttribute ); + _rootAttribute = next; + } +} + + +XMLAttribute* XMLElement::FindAttribute( const char* name ) +{ + XMLAttribute* a = 0; + for( a=_rootAttribute; a; a = a->_next ) { + if ( XMLUtil::StringEqual( a->Name(), name ) ) { + return a; + } + } + return 0; +} + + +const XMLAttribute* XMLElement::FindAttribute( const char* name ) const +{ + XMLAttribute* a = 0; + for( a=_rootAttribute; a; a = a->_next ) { + if ( XMLUtil::StringEqual( a->Name(), name ) ) { + return a; + } + } + return 0; +} + + +const char* XMLElement::Attribute( const char* name, const char* value ) const +{ + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return 0; + } + if ( !value || XMLUtil::StringEqual( a->Value(), value )) { + return a->Value(); + } + return 0; +} + + +const char* XMLElement::GetText() const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + return FirstChild()->ToText()->Value(); + } + return 0; +} + + +XMLError XMLElement::QueryIntText( int* ival ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->ToText()->Value(); + if ( XMLUtil::ToInt( t, ival ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->ToText()->Value(); + if ( XMLUtil::ToUnsigned( t, uval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryBoolText( bool* bval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->ToText()->Value(); + if ( XMLUtil::ToBool( t, bval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryDoubleText( double* dval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->ToText()->Value(); + if ( XMLUtil::ToDouble( t, dval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryFloatText( float* fval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->ToText()->Value(); + if ( XMLUtil::ToFloat( t, fval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + + +XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) +{ + XMLAttribute* last = 0; + XMLAttribute* attrib = 0; + for( attrib = _rootAttribute; + attrib; + last = attrib, attrib = attrib->_next ) { + if ( XMLUtil::StringEqual( attrib->Name(), name ) ) { + break; + } + } + if ( !attrib ) { + attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); + attrib->_memPool = &_document->_attributePool; + if ( last ) { + last->_next = attrib; + } + else { + _rootAttribute = attrib; + } + attrib->SetName( name ); + attrib->_memPool->SetTracked(); // always created and linked. + } + return attrib; +} + + +void XMLElement::DeleteAttribute( const char* name ) +{ + XMLAttribute* prev = 0; + for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) { + if ( XMLUtil::StringEqual( name, a->Name() ) ) { + if ( prev ) { + prev->_next = a->_next; + } + else { + _rootAttribute = a->_next; + } + DELETE_ATTRIBUTE( a ); + break; + } + prev = a; + } +} + + +char* XMLElement::ParseAttributes( char* p ) +{ + const char* start = p; + XMLAttribute* prevAttribute = 0; + + // Read the attributes. + while( p ) { + p = XMLUtil::SkipWhiteSpace( p ); + if ( !p || !(*p) ) { + _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() ); + return 0; + } + + // attribute. + if ( XMLUtil::IsAlpha( *p ) ) { + XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); + attrib->_memPool = &_document->_attributePool; + attrib->_memPool->SetTracked(); + + p = attrib->ParseDeep( p, _document->ProcessEntities() ); + if ( !p || Attribute( attrib->Name() ) ) { + DELETE_ATTRIBUTE( attrib ); + _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p ); + return 0; + } + // There is a minor bug here: if the attribute in the source xml + // document is duplicated, it will not be detected and the + // attribute will be doubly added. However, tracking the 'prevAttribute' + // avoids re-scanning the attribute list. Preferring performance for + // now, may reconsider in the future. + if ( prevAttribute ) { + prevAttribute->_next = attrib; + } + else { + _rootAttribute = attrib; + } + prevAttribute = attrib; + } + // end of the tag + else if ( *p == '/' && *(p+1) == '>' ) { + _closingType = CLOSED; + return p+2; // done; sealed element. + } + // end of the tag + else if ( *p == '>' ) { + ++p; + break; + } + else { + _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p ); + return 0; + } + } + return p; +} + + +// +// +// foobar +// +char* XMLElement::ParseDeep( char* p, StrPair* strPair ) +{ + // Read the element name. + p = XMLUtil::SkipWhiteSpace( p ); + if ( !p ) { + return 0; + } + + // The closing element is the form. It is + // parsed just like a regular element then deleted from + // the DOM. + if ( *p == '/' ) { + _closingType = CLOSING; + ++p; + } + + p = _value.ParseName( p ); + if ( _value.Empty() ) { + return 0; + } + + p = ParseAttributes( p ); + if ( !p || !*p || _closingType ) { + return p; + } + + p = XMLNode::ParseDeep( p, strPair ); + return p; +} + + + +XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern? + for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) { + element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern? + } + return element; +} + + +bool XMLElement::ShallowEqual( const XMLNode* compare ) const +{ + const XMLElement* other = compare->ToElement(); + if ( other && XMLUtil::StringEqual( other->Value(), Value() )) { + + const XMLAttribute* a=FirstAttribute(); + const XMLAttribute* b=other->FirstAttribute(); + + while ( a && b ) { + if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) { + return false; + } + a = a->Next(); + b = b->Next(); + } + if ( a || b ) { + // different count + return false; + } + return true; + } + return false; +} + + +bool XMLElement::Accept( XMLVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, _rootAttribute ) ) { + for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { + if ( !node->Accept( visitor ) ) { + break; + } + } + } + return visitor->VisitExit( *this ); +} + + +// --------- XMLDocument ----------- // +XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) : + XMLNode( 0 ), + _writeBOM( false ), + _processEntities( processEntities ), + _errorID( XML_NO_ERROR ), + _whitespace( whitespace ), + _errorStr1( 0 ), + _errorStr2( 0 ), + _charBuffer( 0 ) +{ + _document = this; // avoid warning about 'this' in initializer list +} + + +XMLDocument::~XMLDocument() +{ + DeleteChildren(); + delete [] _charBuffer; + +#if 0 + textPool.Trace( "text" ); + elementPool.Trace( "element" ); + commentPool.Trace( "comment" ); + attributePool.Trace( "attribute" ); +#endif + +#ifdef DEBUG + if ( Error() == false ) { + TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); + TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); + TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); + TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); + } +#endif +} + + +void XMLDocument::InitDocument() +{ + _errorID = XML_NO_ERROR; + _errorStr1 = 0; + _errorStr2 = 0; + + delete [] _charBuffer; + _charBuffer = 0; +} + + +XMLElement* XMLDocument::NewElement( const char* name ) +{ + XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this ); + ele->_memPool = &_elementPool; + ele->SetName( name ); + return ele; +} + + +XMLComment* XMLDocument::NewComment( const char* str ) +{ + XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this ); + comment->_memPool = &_commentPool; + comment->SetValue( str ); + return comment; +} + + +XMLText* XMLDocument::NewText( const char* str ) +{ + XMLText* text = new (_textPool.Alloc()) XMLText( this ); + text->_memPool = &_textPool; + text->SetValue( str ); + return text; +} + + +XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) +{ + XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this ); + dec->_memPool = &_commentPool; + dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); + return dec; +} + + +XMLUnknown* XMLDocument::NewUnknown( const char* str ) +{ + XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this ); + unk->_memPool = &_commentPool; + unk->SetValue( str ); + return unk; +} + + +XMLError XMLDocument::LoadFile( const char* filename ) +{ + DeleteChildren(); + InitDocument(); + FILE* fp = 0; + +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + errno_t err = fopen_s(&fp, filename, "rb" ); + if ( !fp || err) { +#else + fp = fopen( filename, "rb" ); + if ( !fp) { +#endif + SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 ); + return _errorID; + } + LoadFile( fp ); + fclose( fp ); + return _errorID; +} + + +XMLError XMLDocument::LoadFile( FILE* fp ) +{ + DeleteChildren(); + InitDocument(); + + fseek( fp, 0, SEEK_END ); + size_t size = ftell( fp ); + fseek( fp, 0, SEEK_SET ); + + if ( size == 0 ) { + return _errorID; + } + + _charBuffer = new char[size+1]; + size_t read = fread( _charBuffer, 1, size, fp ); + if ( read != size ) { + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + + _charBuffer[size] = 0; + + const char* p = _charBuffer; + p = XMLUtil::SkipWhiteSpace( p ); + p = XMLUtil::ReadBOM( p, &_writeBOM ); + if ( !p || !*p ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); + return _errorID; + } + + ParseDeep( _charBuffer + (p-_charBuffer), 0 ); + return _errorID; +} + + +XMLError XMLDocument::SaveFile( const char* filename, bool compact ) +{ + FILE* fp = 0; +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + errno_t err = fopen_s(&fp, filename, "w" ); + if ( !fp || err) { +#else + fp = fopen( filename, "w" ); + if ( !fp) { +#endif + SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 ); + return _errorID; + } + SaveFile(fp, compact); + fclose( fp ); + return _errorID; +} + + +XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) +{ + XMLPrinter stream( fp, compact ); + Print( &stream ); + return _errorID; +} + + +XMLError XMLDocument::Parse( const char* p, size_t len ) +{ + DeleteChildren(); + InitDocument(); + + if ( !p || !*p ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); + return _errorID; + } + if ( len == (size_t)(-1) ) { + len = strlen( p ); + } + _charBuffer = new char[ len+1 ]; + memcpy( _charBuffer, p, len ); + _charBuffer[len] = 0; + + p = XMLUtil::SkipWhiteSpace( p ); + p = XMLUtil::ReadBOM( p, &_writeBOM ); + if ( !p || !*p ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); + return _errorID; + } + + ParseDeep( _charBuffer, 0 ); + return _errorID; +} + + +void XMLDocument::Print( XMLPrinter* streamer ) +{ + XMLPrinter stdStreamer( stdout ); + if ( !streamer ) { + streamer = &stdStreamer; + } + Accept( streamer ); +} + + +void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 ) +{ + _errorID = error; + _errorStr1 = str1; + _errorStr2 = str2; +} + + +void XMLDocument::PrintError() const +{ + if ( _errorID ) { + static const int LEN = 20; + char buf1[LEN] = { 0 }; + char buf2[LEN] = { 0 }; + + if ( _errorStr1 ) { + TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 ); + } + if ( _errorStr2 ) { + TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 ); + } + + printf( "XMLDocument error id=%d str1=%s str2=%s\n", + _errorID, buf1, buf2 ); + } +} + + +XMLPrinter::XMLPrinter( FILE* file, bool compact ) : + _elementJustOpened( false ), + _firstElement( true ), + _fp( file ), + _depth( 0 ), + _textDepth( -1 ), + _processEntities( true ), + _compactMode( compact ) +{ + for( int i=0; i'] = true; // not required, but consistency is nice + _buffer.Push( 0 ); +} + + +void XMLPrinter::Print( const char* format, ... ) +{ + va_list va; + va_start( va, format ); + + if ( _fp ) { + vfprintf( _fp, format, va ); + } + else { + // This seems brutally complex. Haven't figured out a better + // way on windows. +#ifdef _MSC_VER + int len = -1; + int expand = 1000; + while ( len < 0 ) { + len = vsnprintf_s( _accumulator.Mem(), _accumulator.Capacity(), _TRUNCATE, format, va ); + if ( len < 0 ) { + expand *= 3/2; + _accumulator.PushArr( expand ); + } + } + char* p = _buffer.PushArr( len ) - 1; + memcpy( p, _accumulator.Mem(), len+1 ); +#else + int len = vsnprintf( 0, 0, format, va ); + // Close out and re-start the va-args + va_end( va ); + va_start( va, format ); + char* p = _buffer.PushArr( len ) - 1; + vsnprintf( p, len+1, format, va ); +#endif + } + va_end( va ); +} + + +void XMLPrinter::PrintSpace( int depth ) +{ + for( int i=0; i 0 && *q < ENTITY_RANGE ) { + // Check for entities. If one is found, flush + // the stream up until the entity, write the + // entity, and keep looking. + if ( flag[(unsigned)(*q)] ) { + while ( p < q ) { + Print( "%c", *p ); + ++p; + } + for( int i=0; i 0) ) { + Print( "%s", p ); + } +} + + +void XMLPrinter::PushHeader( bool writeBOM, bool writeDec ) +{ + static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 }; + if ( writeBOM ) { + Print( "%s", bom ); + } + if ( writeDec ) { + PushDeclaration( "xml version=\"1.0\"" ); + } +} + + +void XMLPrinter::OpenElement( const char* name ) +{ + if ( _elementJustOpened ) { + SealElement(); + } + _stack.Push( name ); + + if ( _textDepth < 0 && !_firstElement && !_compactMode ) { + Print( "\n" ); + PrintSpace( _depth ); + } + + Print( "<%s", name ); + _elementJustOpened = true; + _firstElement = false; + ++_depth; +} + + +void XMLPrinter::PushAttribute( const char* name, const char* value ) +{ + TIXMLASSERT( _elementJustOpened ); + Print( " %s=\"", name ); + PrintString( value, false ); + Print( "\"" ); +} + + +void XMLPrinter::PushAttribute( const char* name, int v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::PushAttribute( const char* name, unsigned v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::PushAttribute( const char* name, bool v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::PushAttribute( const char* name, double v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::CloseElement() +{ + --_depth; + const char* name = _stack.Pop(); + + if ( _elementJustOpened ) { + Print( "/>" ); + } + else { + if ( _textDepth < 0 && !_compactMode) { + Print( "\n" ); + PrintSpace( _depth ); + } + Print( "", name ); + } + + if ( _textDepth == _depth ) { + _textDepth = -1; + } + if ( _depth == 0 && !_compactMode) { + Print( "\n" ); + } + _elementJustOpened = false; +} + + +void XMLPrinter::SealElement() +{ + _elementJustOpened = false; + Print( ">" ); +} + + +void XMLPrinter::PushText( const char* text, bool cdata ) +{ + _textDepth = _depth-1; + + if ( _elementJustOpened ) { + SealElement(); + } + if ( cdata ) { + Print( "" ); + } + else { + PrintString( text, true ); + } +} + +void XMLPrinter::PushText( int value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( unsigned value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( bool value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( float value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( double value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushComment( const char* comment ) +{ + if ( _elementJustOpened ) { + SealElement(); + } + if ( _textDepth < 0 && !_firstElement && !_compactMode) { + Print( "\n" ); + PrintSpace( _depth ); + } + _firstElement = false; + Print( "", comment ); +} + + +void XMLPrinter::PushDeclaration( const char* value ) +{ + if ( _elementJustOpened ) { + SealElement(); + } + if ( _textDepth < 0 && !_firstElement && !_compactMode) { + Print( "\n" ); + PrintSpace( _depth ); + } + _firstElement = false; + Print( "", value ); +} + + +void XMLPrinter::PushUnknown( const char* value ) +{ + if ( _elementJustOpened ) { + SealElement(); + } + if ( _textDepth < 0 && !_firstElement && !_compactMode) { + Print( "\n" ); + PrintSpace( _depth ); + } + _firstElement = false; + Print( "", value ); +} + + +bool XMLPrinter::VisitEnter( const XMLDocument& doc ) +{ + _processEntities = doc.ProcessEntities(); + if ( doc.HasBOM() ) { + PushHeader( true, false ); + } + return true; +} + + +bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) +{ + OpenElement( element.Name() ); + while ( attribute ) { + PushAttribute( attribute->Name(), attribute->Value() ); + attribute = attribute->Next(); + } + return true; +} + + +bool XMLPrinter::VisitExit( const XMLElement& ) +{ + CloseElement(); + return true; +} + + +bool XMLPrinter::Visit( const XMLText& text ) +{ + PushText( text.Value(), text.CData() ); + return true; +} + + +bool XMLPrinter::Visit( const XMLComment& comment ) +{ + PushComment( comment.Value() ); + return true; +} + +bool XMLPrinter::Visit( const XMLDeclaration& declaration ) +{ + PushDeclaration( declaration.Value() ); + return true; +} + + +bool XMLPrinter::Visit( const XMLUnknown& unknown ) +{ + PushUnknown( unknown.Value() ); + return true; +} + +} // namespace tinyxml2 + diff --git a/3rdParty/TinyXml/tinyxml2.h b/3rdParty/TinyXml/tinyxml2.h new file mode 100644 index 0000000..0f8e9ad --- /dev/null +++ b/3rdParty/TinyXml/tinyxml2.h @@ -0,0 +1,1911 @@ +/* +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#ifndef TINYXML2_INCLUDED +#define TINYXML2_INCLUDED + +#if defined(ANDROID_NDK) || defined(__BORLANDC__) +# include +# include +# include +# include +# include +# include +#else +# include +# include +# include +# include +# include +# include +#endif + +/* + TODO: intern strings instead of allocation. +*/ +/* + gcc: + g++ -Wall -DDEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe + + Formatting, Artistic Style: + AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h +*/ + +#if defined( _DEBUG ) || defined( DEBUG ) || defined (__DEBUG__) +# ifndef DEBUG +# define DEBUG +# endif +#endif + + +#if defined(DEBUG) +# if defined(_MSC_VER) +# define TIXMLASSERT( x ) if ( !(x)) { __debugbreak(); } //if ( !(x)) WinDebugBreak() +# elif defined (ANDROID_NDK) +# include +# define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } +# else +# include +# define TIXMLASSERT assert +# endif +# else +# define TIXMLASSERT( x ) {} +#endif + + +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) +// Microsoft visual studio, version 2005 and higher. +/*int _snprintf_s( + char *buffer, + size_t sizeOfBuffer, + size_t count, + const char *format [, + argument] ... +);*/ +inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) +{ + va_list va; + va_start( va, format ); + int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); + va_end( va ); + return result; +} +#define TIXML_SSCANF sscanf_s +#else +// GCC version 3 and higher +//#warning( "Using sn* functions." ) +#define TIXML_SNPRINTF snprintf +#define TIXML_SSCANF sscanf +#endif + +static const int TIXML2_MAJOR_VERSION = 1; +static const int TIXML2_MINOR_VERSION = 0; +static const int TIXML2_PATCH_VERSION = 9; + +namespace tinyxml2 +{ +class XMLDocument; +class XMLElement; +class XMLAttribute; +class XMLComment; +class XMLNode; +class XMLText; +class XMLDeclaration; +class XMLUnknown; + +class XMLPrinter; + +/* + A class that wraps strings. Normally stores the start and end + pointers into the XML file itself, and will apply normalization + and entity translation if actually read. Can also store (and memory + manage) a traditional char[] +*/ +class StrPair +{ +public: + enum { + NEEDS_ENTITY_PROCESSING = 0x01, + NEEDS_NEWLINE_NORMALIZATION = 0x02, + COLLAPSE_WHITESPACE = 0x04, + + TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, + TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, + ATTRIBUTE_NAME = 0, + ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, + ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, + COMMENT = NEEDS_NEWLINE_NORMALIZATION + }; + + StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {} + ~StrPair(); + + void Set( char* start, char* end, int flags ) { + Reset(); + _start = start; + _end = end; + _flags = flags | NEEDS_FLUSH; + } + + const char* GetStr(); + + bool Empty() const { + return _start == _end; + } + + void SetInternedStr( const char* str ) { + Reset(); + _start = const_cast(str); + } + + void SetStr( const char* str, int flags=0 ); + + char* ParseText( char* in, const char* endTag, int strFlags ); + char* ParseName( char* in ); + +private: + void Reset(); + void CollapseWhitespace(); + + enum { + NEEDS_FLUSH = 0x100, + NEEDS_DELETE = 0x200 + }; + + // After parsing, if *end != 0, it can be set to zero. + int _flags; + char* _start; + char* _end; +}; + + +/* + A dynamic array of Plain Old Data. Doesn't support constructors, etc. + Has a small initial memory pool, so that low or no usage will not + cause a call to new/delete +*/ +template +class DynArray +{ +public: + DynArray< T, INIT >() { + _mem = _pool; + _allocated = INIT; + _size = 0; + } + + ~DynArray() { + if ( _mem != _pool ) { + delete [] _mem; + } + } + + void Push( T t ) { + EnsureCapacity( _size+1 ); + _mem[_size++] = t; + } + + T* PushArr( int count ) { + EnsureCapacity( _size+count ); + T* ret = &_mem[_size]; + _size += count; + return ret; + } + + T Pop() { + return _mem[--_size]; + } + + void PopArr( int count ) { + TIXMLASSERT( _size >= count ); + _size -= count; + } + + bool Empty() const { + return _size == 0; + } + + T& operator[](int i) { + TIXMLASSERT( i>= 0 && i < _size ); + return _mem[i]; + } + + const T& operator[](int i) const { + TIXMLASSERT( i>= 0 && i < _size ); + return _mem[i]; + } + + int Size() const { + return _size; + } + + int Capacity() const { + return _allocated; + } + + const T* Mem() const { + return _mem; + } + + T* Mem() { + return _mem; + } + +private: + void EnsureCapacity( int cap ) { + if ( cap > _allocated ) { + int newAllocated = cap * 2; + T* newMem = new T[newAllocated]; + memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs + if ( _mem != _pool ) { + delete [] _mem; + } + _mem = newMem; + _allocated = newAllocated; + } + } + + T* _mem; + T _pool[INIT]; + int _allocated; // objects allocated + int _size; // number objects in use +}; + + +/* + Parent virtual class of a pool for fast allocation + and deallocation of objects. +*/ +class MemPool +{ +public: + MemPool() {} + virtual ~MemPool() {} + + virtual int ItemSize() const = 0; + virtual void* Alloc() = 0; + virtual void Free( void* ) = 0; + virtual void SetTracked() = 0; +}; + + +/* + Template child class to create pools of the correct type. +*/ +template< int SIZE > +class MemPoolT : public MemPool +{ +public: + MemPoolT() : _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} + ~MemPoolT() { + // Delete the blocks. + for( int i=0; i<_blockPtrs.Size(); ++i ) { + delete _blockPtrs[i]; + } + } + + virtual int ItemSize() const { + return SIZE; + } + int CurrentAllocs() const { + return _currentAllocs; + } + + virtual void* Alloc() { + if ( !_root ) { + // Need a new block. + Block* block = new Block(); + _blockPtrs.Push( block ); + + for( int i=0; ichunk[i].next = &block->chunk[i+1]; + } + block->chunk[COUNT-1].next = 0; + _root = block->chunk; + } + void* result = _root; + _root = _root->next; + + ++_currentAllocs; + if ( _currentAllocs > _maxAllocs ) { + _maxAllocs = _currentAllocs; + } + _nAllocs++; + _nUntracked++; + return result; + } + virtual void Free( void* mem ) { + if ( !mem ) { + return; + } + --_currentAllocs; + Chunk* chunk = (Chunk*)mem; +#ifdef DEBUG + memset( chunk, 0xfe, sizeof(Chunk) ); +#endif + chunk->next = _root; + _root = chunk; + } + void Trace( const char* name ) { + printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", + name, _maxAllocs, _maxAllocs*SIZE/1024, _currentAllocs, SIZE, _nAllocs, _blockPtrs.Size() ); + } + + void SetTracked() { + _nUntracked--; + } + + int Untracked() const { + return _nUntracked; + } + + enum { COUNT = 1024/SIZE }; // Some compilers do not accept to use COUNT in private part if COUNT is private + +private: + union Chunk { + Chunk* next; + char mem[SIZE]; + }; + struct Block { + Chunk chunk[COUNT]; + }; + DynArray< Block*, 10 > _blockPtrs; + Chunk* _root; + + int _currentAllocs; + int _nAllocs; + int _maxAllocs; + int _nUntracked; +}; + + + +/** + Implements the interface to the "Visitor pattern" (see the Accept() method.) + If you call the Accept() method, it requires being passed a XMLVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs + are simply called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its sibilings will be visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes support visiting. + + You should never change the document from a callback. + + @sa XMLNode::Accept() +*/ +class XMLVisitor +{ +public: + virtual ~XMLVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const XMLDocument& /*doc*/ ) { + return true; + } + /// Visit a document. + virtual bool VisitExit( const XMLDocument& /*doc*/ ) { + return true; + } + + /// Visit an element. + virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) { + return true; + } + /// Visit an element. + virtual bool VisitExit( const XMLElement& /*element*/ ) { + return true; + } + + /// Visit a declaration. + virtual bool Visit( const XMLDeclaration& /*declaration*/ ) { + return true; + } + /// Visit a text node. + virtual bool Visit( const XMLText& /*text*/ ) { + return true; + } + /// Visit a comment node. + virtual bool Visit( const XMLComment& /*comment*/ ) { + return true; + } + /// Visit an unknown node. + virtual bool Visit( const XMLUnknown& /*unknown*/ ) { + return true; + } +}; + + +/* + Utility functionality. +*/ +class XMLUtil +{ +public: + // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't + // correct, but simple, and usually works. + static const char* SkipWhiteSpace( const char* p ) { + while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast(p) ) ) { + ++p; + } + return p; + } + static char* SkipWhiteSpace( char* p ) { + while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast(p) ) ) { + ++p; + } + return p; + } + static bool IsWhiteSpace( char p ) { + return !IsUTF8Continuation(p) && isspace( static_cast(p) ); + } + + inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { + int n = 0; + if ( p == q ) { + return true; + } + while( *p && *q && *p == *q && n(const_cast(this)->FirstChildElement( value )); + } + + /// Get the last child node, or null if none exists. + const XMLNode* LastChild() const { + return _lastChild; + } + + XMLNode* LastChild() { + return const_cast(const_cast(this)->LastChild() ); + } + + /** Get the last child element or optionally the last child + element with the specified name. + */ + const XMLElement* LastChildElement( const char* value=0 ) const; + + XMLElement* LastChildElement( const char* value=0 ) { + return const_cast(const_cast(this)->LastChildElement(value) ); + } + + /// Get the previous (left) sibling node of this node. + const XMLNode* PreviousSibling() const { + return _prev; + } + + XMLNode* PreviousSibling() { + return _prev; + } + + /// Get the previous (left) sibling element of this node, with an opitionally supplied name. + const XMLElement* PreviousSiblingElement( const char* value=0 ) const ; + + XMLElement* PreviousSiblingElement( const char* value=0 ) { + return const_cast(const_cast(this)->PreviousSiblingElement( value ) ); + } + + /// Get the next (right) sibling node of this node. + const XMLNode* NextSibling() const { + return _next; + } + + XMLNode* NextSibling() { + return _next; + } + + /// Get the next (right) sibling element of this node, with an opitionally supplied name. + const XMLElement* NextSiblingElement( const char* value=0 ) const; + + XMLElement* NextSiblingElement( const char* value=0 ) { + return const_cast(const_cast(this)->NextSiblingElement( value ) ); + } + + /** + Add a child node as the last (right) child. + */ + XMLNode* InsertEndChild( XMLNode* addThis ); + + XMLNode* LinkEndChild( XMLNode* addThis ) { + return InsertEndChild( addThis ); + } + /** + Add a child node as the first (left) child. + */ + XMLNode* InsertFirstChild( XMLNode* addThis ); + /** + Add a node after the specified child node. + */ + XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ); + + /** + Delete all the children of this node. + */ + void DeleteChildren(); + + /** + Delete a child of this node. + */ + void DeleteChild( XMLNode* node ); + + /** + Make a copy of this node, but not its children. + You may pass in a Document pointer that will be + the owner of the new Node. If the 'document' is + null, then the node returned will be allocated + from the current Document. (this->GetDocument()) + + Note: if called on a XMLDocument, this will return null. + */ + virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0; + + /** + Test if 2 nodes are the same, but don't test children. + The 2 nodes do not need to be in the same Document. + + Note: if called on a XMLDocument, this will return false. + */ + virtual bool ShallowEqual( const XMLNode* compare ) const = 0; + + /** Accept a hierarchical visit of the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( XMLVisitor* visitor ) const = 0; + + // internal + virtual char* ParseDeep( char*, StrPair* ); + +protected: + XMLNode( XMLDocument* ); + virtual ~XMLNode(); + XMLNode( const XMLNode& ); // not supported + XMLNode& operator=( const XMLNode& ); // not supported + + XMLDocument* _document; + XMLNode* _parent; + mutable StrPair _value; + + XMLNode* _firstChild; + XMLNode* _lastChild; + + XMLNode* _prev; + XMLNode* _next; + +private: + MemPool* _memPool; + void Unlink( XMLNode* child ); +}; + + +/** XML text. + + Note that a text node can have child element nodes, for example: + @verbatim + This is bold + @endverbatim + + A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class XMLText : public XMLNode +{ + friend class XMLBase; + friend class XMLDocument; +public: + virtual bool Accept( XMLVisitor* visitor ) const; + + virtual XMLText* ToText() { + return this; + } + virtual const XMLText* ToText() const { + return this; + } + + /// Declare whether this should be CDATA or standard text. + void SetCData( bool isCData ) { + _isCData = isCData; + } + /// Returns true if this is a CDATA text element. + bool CData() const { + return _isCData; + } + + char* ParseDeep( char*, StrPair* endTag ); + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} + virtual ~XMLText() {} + XMLText( const XMLText& ); // not supported + XMLText& operator=( const XMLText& ); // not supported + +private: + bool _isCData; +}; + + +/** An XML Comment. */ +class XMLComment : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLComment* ToComment() { + return this; + } + virtual const XMLComment* ToComment() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + char* ParseDeep( char*, StrPair* endTag ); + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + XMLComment( XMLDocument* doc ); + virtual ~XMLComment(); + XMLComment( const XMLComment& ); // not supported + XMLComment& operator=( const XMLComment& ); // not supported + +private: +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXML2 will happily read or write files without a declaration, + however. + + The text of the declaration isn't interpreted. It is parsed + and written as a string. +*/ +class XMLDeclaration : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLDeclaration* ToDeclaration() { + return this; + } + virtual const XMLDeclaration* ToDeclaration() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + char* ParseDeep( char*, StrPair* endTag ); + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + XMLDeclaration( XMLDocument* doc ); + virtual ~XMLDeclaration(); + XMLDeclaration( const XMLDeclaration& ); // not supported + XMLDeclaration& operator=( const XMLDeclaration& ); // not supported +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class XMLUnknown : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLUnknown* ToUnknown() { + return this; + } + virtual const XMLUnknown* ToUnknown() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + char* ParseDeep( char*, StrPair* endTag ); + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + XMLUnknown( XMLDocument* doc ); + virtual ~XMLUnknown(); + XMLUnknown( const XMLUnknown& ); // not supported + XMLUnknown& operator=( const XMLUnknown& ); // not supported +}; + + +enum XMLError { + XML_NO_ERROR = 0, + XML_SUCCESS = 0, + + XML_NO_ATTRIBUTE, + XML_WRONG_ATTRIBUTE_TYPE, + + XML_ERROR_FILE_NOT_FOUND, + XML_ERROR_FILE_COULD_NOT_BE_OPENED, + XML_ERROR_FILE_READ_ERROR, + XML_ERROR_ELEMENT_MISMATCH, + XML_ERROR_PARSING_ELEMENT, + XML_ERROR_PARSING_ATTRIBUTE, + XML_ERROR_IDENTIFYING_TAG, + XML_ERROR_PARSING_TEXT, + XML_ERROR_PARSING_CDATA, + XML_ERROR_PARSING_COMMENT, + XML_ERROR_PARSING_DECLARATION, + XML_ERROR_PARSING_UNKNOWN, + XML_ERROR_EMPTY_DOCUMENT, + XML_ERROR_MISMATCHED_ELEMENT, + XML_ERROR_PARSING, + + XML_CAN_NOT_CONVERT_TEXT, + XML_NO_TEXT_NODE +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not XMLNodes. You may only query the + Next() attribute in a list. +*/ +class XMLAttribute +{ + friend class XMLElement; +public: + /// The name of the attribute. + const char* Name() const { + return _name.GetStr(); + } + /// The value of the attribute. + const char* Value() const { + return _value.GetStr(); + } + /// The next attribute in the list. + const XMLAttribute* Next() const { + return _next; + } + + /** IntAttribute interprets the attribute as an integer, and returns the value. + If the value isn't an integer, 0 will be returned. There is no error checking; + use QueryIntAttribute() if you need error checking. + */ + int IntValue() const { + int i=0; + QueryIntValue( &i ); + return i; + } + /// Query as an unsigned integer. See IntAttribute() + unsigned UnsignedValue() const { + unsigned i=0; + QueryUnsignedValue( &i ); + return i; + } + /// Query as a boolean. See IntAttribute() + bool BoolValue() const { + bool b=false; + QueryBoolValue( &b ); + return b; + } + /// Query as a double. See IntAttribute() + double DoubleValue() const { + double d=0; + QueryDoubleValue( &d ); + return d; + } + /// Query as a float. See IntAttribute() + float FloatValue() const { + float f=0; + QueryFloatValue( &f ); + return f; + } + + /** QueryIntAttribute interprets the attribute as an integer, and returns the value + in the provided paremeter. The function will return XML_NO_ERROR on success, + and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful. + */ + XMLError QueryIntValue( int* value ) const; + /// See QueryIntAttribute + XMLError QueryUnsignedValue( unsigned int* value ) const; + /// See QueryIntAttribute + XMLError QueryBoolValue( bool* value ) const; + /// See QueryIntAttribute + XMLError QueryDoubleValue( double* value ) const; + /// See QueryIntAttribute + XMLError QueryFloatValue( float* value ) const; + + /// Set the attribute to a string value. + void SetAttribute( const char* value ); + /// Set the attribute to value. + void SetAttribute( int value ); + /// Set the attribute to value. + void SetAttribute( unsigned value ); + /// Set the attribute to value. + void SetAttribute( bool value ); + /// Set the attribute to value. + void SetAttribute( double value ); + /// Set the attribute to value. + void SetAttribute( float value ); + +private: + enum { BUF_SIZE = 200 }; + + XMLAttribute() : _next( 0 ) {} + virtual ~XMLAttribute() {} + + XMLAttribute( const XMLAttribute& ); // not supported + void operator=( const XMLAttribute& ); // not supported + void SetName( const char* name ); + + char* ParseDeep( char* p, bool processEntities ); + + mutable StrPair _name; + mutable StrPair _value; + XMLAttribute* _next; + MemPool* _memPool; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class XMLElement : public XMLNode +{ + friend class XMLBase; + friend class XMLDocument; +public: + /// Get the name of an element (which is the Value() of the node.) + const char* Name() const { + return Value(); + } + /// Set the name of the element. + void SetName( const char* str, bool staticMem=false ) { + SetValue( str, staticMem ); + } + + virtual XMLElement* ToElement() { + return this; + } + virtual const XMLElement* ToElement() const { + return this; + } + virtual bool Accept( XMLVisitor* visitor ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none + exists. For example: + + @verbatim + const char* value = ele->Attribute( "foo" ); + @endverbatim + + The 'value' parameter is normally null. However, if specified, + the attribute will only be returned if the 'name' and 'value' + match. This allow you to write code: + + @verbatim + if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar(); + @endverbatim + + rather than: + @verbatim + if ( ele->Attribute( "foo" ) ) { + if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar(); + } + @endverbatim + */ + const char* Attribute( const char* name, const char* value=0 ) const; + + /** Given an attribute name, IntAttribute() returns the value + of the attribute interpreted as an integer. 0 will be + returned if there is an error. For a method with error + checking, see QueryIntAttribute() + */ + int IntAttribute( const char* name ) const { + int i=0; + QueryIntAttribute( name, &i ); + return i; + } + /// See IntAttribute() + unsigned UnsignedAttribute( const char* name ) const { + unsigned i=0; + QueryUnsignedAttribute( name, &i ); + return i; + } + /// See IntAttribute() + bool BoolAttribute( const char* name ) const { + bool b=false; + QueryBoolAttribute( name, &b ); + return b; + } + /// See IntAttribute() + double DoubleAttribute( const char* name ) const { + double d=0; + QueryDoubleAttribute( name, &d ); + return d; + } + /// See IntAttribute() + float FloatAttribute( const char* name ) const { + float f=0; + QueryFloatAttribute( name, &f ); + return f; + } + + /** Given an attribute name, QueryIntAttribute() returns + XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion + can't be performed, or XML_NO_ATTRIBUTE if the attribute + doesn't exist. If successful, the result of the conversion + will be written to 'value'. If not successful, nothing will + be written to 'value'. This allows you to provide default + value: + + @verbatim + int value = 10; + QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 + @endverbatim + */ + XMLError QueryIntAttribute( const char* name, int* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryIntValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryUnsignedValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryBoolAttribute( const char* name, bool* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryBoolValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryDoubleAttribute( const char* name, double* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryDoubleValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryFloatAttribute( const char* name, float* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryFloatValue( value ); + } + + /// Sets the named attribute to value. + void SetAttribute( const char* name, const char* value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, int value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, unsigned value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, bool value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, double value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + + /** + Delete an attribute. + */ + void DeleteAttribute( const char* name ); + + /// Return the first attribute in the list. + const XMLAttribute* FirstAttribute() const { + return _rootAttribute; + } + /// Query a specific attribute in the list. + const XMLAttribute* FindAttribute( const char* name ) const; + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + */ + const char* GetText() const; + + /** + Convenience method to query the value of a child text node. This is probably best + shown by example. Given you have a document is this form: + @verbatim + + 1 + 1.4 + + @endverbatim + + The QueryIntText() and similar functions provide a safe and easier way to get to the + "value" of x and y. + + @verbatim + int x = 0; + float y = 0; // types of x and y are contrived for example + const XMLElement* xElement = pointElement->FirstChildElement( "x" ); + const XMLElement* yElement = pointElement->FirstChildElement( "y" ); + xElement->QueryIntText( &x ); + yElement->QueryFloatText( &y ); + @endverbatim + + @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted + to the requested type, and XML_NO_TEXT_NODE if there is no child text to query. + + */ + XMLError QueryIntText( int* ival ) const; + /// See QueryIntText() + XMLError QueryUnsignedText( unsigned* uval ) const; + /// See QueryIntText() + XMLError QueryBoolText( bool* bval ) const; + /// See QueryIntText() + XMLError QueryDoubleText( double* dval ) const; + /// See QueryIntText() + XMLError QueryFloatText( float* fval ) const; + + // internal: + enum { + OPEN, // + CLOSED, // + CLOSING // + }; + int ClosingType() const { + return _closingType; + } + char* ParseDeep( char* p, StrPair* endTag ); + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +private: + XMLElement( XMLDocument* doc ); + virtual ~XMLElement(); + XMLElement( const XMLElement& ); // not supported + void operator=( const XMLElement& ); // not supported + + XMLAttribute* FindAttribute( const char* name ); + XMLAttribute* FindOrCreateAttribute( const char* name ); + //void LinkAttribute( XMLAttribute* attrib ); + char* ParseAttributes( char* p ); + + int _closingType; + // The attribute list is ordered; there is no 'lastAttribute' + // because the list needs to be scanned for dupes before adding + // a new attribute. + XMLAttribute* _rootAttribute; +}; + + +enum Whitespace { + PRESERVE_WHITESPACE, + COLLAPSE_WHITESPACE +}; + + +/** A Document binds together all the functionality. + It can be saved, loaded, and printed to the screen. + All Nodes are connected and allocated to a Document. + If the Document is deleted, all its Nodes are also deleted. +*/ +class XMLDocument : public XMLNode +{ + friend class XMLElement; +public: + /// constructor + XMLDocument( bool processEntities = true, Whitespace = PRESERVE_WHITESPACE ); + ~XMLDocument(); + + virtual XMLDocument* ToDocument() { + return this; + } + virtual const XMLDocument* ToDocument() const { + return this; + } + + /** + Parse an XML file from a character string. + Returns XML_NO_ERROR (0) on success, or + an errorID. + + You may optionally pass in the 'nBytes', which is + the number of bytes which will be parsed. If not + specified, TinyXML will assume 'xml' points to a + null terminated string. + */ + XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) ); + + /** + Load an XML file from disk. + Returns XML_NO_ERROR (0) on success, or + an errorID. + */ + XMLError LoadFile( const char* filename ); + + /** + Load an XML file from disk. You are responsible + for providing and closing the FILE*. + + Returns XML_NO_ERROR (0) on success, or + an errorID. + */ + XMLError LoadFile( FILE* ); + + /** + Save the XML file to disk. + Returns XML_NO_ERROR (0) on success, or + an errorID. + */ + XMLError SaveFile( const char* filename, bool compact = false ); + + /** + Save the XML file to disk. You are responsible + for providing and closing the FILE*. + + Returns XML_NO_ERROR (0) on success, or + an errorID. + */ + XMLError SaveFile( FILE* fp, bool compact = false ); + + bool ProcessEntities() const { + return _processEntities; + } + Whitespace WhitespaceMode() const { + return _whitespace; + } + + /** + Returns true if this document has a leading Byte Order Mark of UTF8. + */ + bool HasBOM() const { + return _writeBOM; + } + /** Sets whether to write the BOM when writing the file. + */ + void SetBOM( bool useBOM ) { + _writeBOM = useBOM; + } + + /** Return the root element of DOM. Equivalent to FirstChildElement(). + To get the first node, use FirstChild(). + */ + XMLElement* RootElement() { + return FirstChildElement(); + } + const XMLElement* RootElement() const { + return FirstChildElement(); + } + + /** Print the Document. If the Printer is not provided, it will + print to stdout. If you provide Printer, this can print to a file: + @verbatim + XMLPrinter printer( fp ); + doc.Print( &printer ); + @endverbatim + + Or you can use a printer to print to memory: + @verbatim + XMLPrinter printer; + doc->Print( &printer ); + // printer.CStr() has a const char* to the XML + @endverbatim + */ + void Print( XMLPrinter* streamer=0 ); + virtual bool Accept( XMLVisitor* visitor ) const; + + /** + Create a new Element associated with + this Document. The memory for the Element + is managed by the Document. + */ + XMLElement* NewElement( const char* name ); + /** + Create a new Comment associated with + this Document. The memory for the Comment + is managed by the Document. + */ + XMLComment* NewComment( const char* comment ); + /** + Create a new Text associated with + this Document. The memory for the Text + is managed by the Document. + */ + XMLText* NewText( const char* text ); + /** + Create a new Declaration associated with + this Document. The memory for the object + is managed by the Document. + + If the 'text' param is null, the standard + declaration is used.: + @verbatim + + @endverbatim + */ + XMLDeclaration* NewDeclaration( const char* text=0 ); + /** + Create a new Unknown associated with + this Document. The memory forthe object + is managed by the Document. + */ + XMLUnknown* NewUnknown( const char* text ); + + /** + Delete a node associated with this document. + It will be unlinked from the DOM. + */ + void DeleteNode( XMLNode* node ) { + node->_parent->DeleteChild( node ); + } + + void SetError( XMLError error, const char* str1, const char* str2 ); + + /// Return true if there was an error parsing the document. + bool Error() const { + return _errorID != XML_NO_ERROR; + } + /// Return the errorID. + XMLError ErrorID() const { + return _errorID; + } + /// Return a possibly helpful diagnostic location or string. + const char* GetErrorStr1() const { + return _errorStr1; + } + /// Return a possibly helpful secondary diagnostic location or string. + const char* GetErrorStr2() const { + return _errorStr2; + } + /// If there is an error, print it to stdout. + void PrintError() const; + + // internal + char* Identify( char* p, XMLNode** node ); + + virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const { + return 0; + } + virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const { + return false; + } + +private: + XMLDocument( const XMLDocument& ); // not supported + void operator=( const XMLDocument& ); // not supported + void InitDocument(); + + bool _writeBOM; + bool _processEntities; + XMLError _errorID; + Whitespace _whitespace; + const char* _errorStr1; + const char* _errorStr2; + char* _charBuffer; + + MemPoolT< sizeof(XMLElement) > _elementPool; + MemPoolT< sizeof(XMLAttribute) > _attributePool; + MemPoolT< sizeof(XMLText) > _textPool; + MemPoolT< sizeof(XMLComment) > _commentPool; +}; + + +/** + A XMLHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that XMLHandle is not part of the TinyXML + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + XMLElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + XMLElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + XMLElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + XMLElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. XMLHandle addresses the verbosity + of such code. A XMLHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + XMLHandle docHandle( &document ); + XMLElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild().NextSibling().ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + XMLHandle handleCopy = handle; + @endverbatim + + See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects. +*/ +class XMLHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + XMLHandle( XMLNode* node ) { + _node = node; + } + /// Create a handle from a node. + XMLHandle( XMLNode& node ) { + _node = &node; + } + /// Copy constructor + XMLHandle( const XMLHandle& ref ) { + _node = ref._node; + } + /// Assignment + XMLHandle& operator=( const XMLHandle& ref ) { + _node = ref._node; + return *this; + } + + /// Get the first child of this handle. + XMLHandle FirstChild() { + return XMLHandle( _node ? _node->FirstChild() : 0 ); + } + /// Get the first child element of this handle. + XMLHandle FirstChildElement( const char* value=0 ) { + return XMLHandle( _node ? _node->FirstChildElement( value ) : 0 ); + } + /// Get the last child of this handle. + XMLHandle LastChild() { + return XMLHandle( _node ? _node->LastChild() : 0 ); + } + /// Get the last child element of this handle. + XMLHandle LastChildElement( const char* _value=0 ) { + return XMLHandle( _node ? _node->LastChildElement( _value ) : 0 ); + } + /// Get the previous sibling of this handle. + XMLHandle PreviousSibling() { + return XMLHandle( _node ? _node->PreviousSibling() : 0 ); + } + /// Get the previous sibling element of this handle. + XMLHandle PreviousSiblingElement( const char* _value=0 ) { + return XMLHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 ); + } + /// Get the next sibling of this handle. + XMLHandle NextSibling() { + return XMLHandle( _node ? _node->NextSibling() : 0 ); + } + /// Get the next sibling element of this handle. + XMLHandle NextSiblingElement( const char* _value=0 ) { + return XMLHandle( _node ? _node->NextSiblingElement( _value ) : 0 ); + } + + /// Safe cast to XMLNode. This can return null. + XMLNode* ToNode() { + return _node; + } + /// Safe cast to XMLElement. This can return null. + XMLElement* ToElement() { + return ( ( _node && _node->ToElement() ) ? _node->ToElement() : 0 ); + } + /// Safe cast to XMLText. This can return null. + XMLText* ToText() { + return ( ( _node && _node->ToText() ) ? _node->ToText() : 0 ); + } + /// Safe cast to XMLUnknown. This can return null. + XMLUnknown* ToUnknown() { + return ( ( _node && _node->ToUnknown() ) ? _node->ToUnknown() : 0 ); + } + /// Safe cast to XMLDeclaration. This can return null. + XMLDeclaration* ToDeclaration() { + return ( ( _node && _node->ToDeclaration() ) ? _node->ToDeclaration() : 0 ); + } + +private: + XMLNode* _node; +}; + + +/** + A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the + same in all regards, except for the 'const' qualifiers. See XMLHandle for API. +*/ +class XMLConstHandle +{ +public: + XMLConstHandle( const XMLNode* node ) { + _node = node; + } + XMLConstHandle( const XMLNode& node ) { + _node = &node; + } + XMLConstHandle( const XMLConstHandle& ref ) { + _node = ref._node; + } + + XMLConstHandle& operator=( const XMLConstHandle& ref ) { + _node = ref._node; + return *this; + } + + const XMLConstHandle FirstChild() const { + return XMLConstHandle( _node ? _node->FirstChild() : 0 ); + } + const XMLConstHandle FirstChildElement( const char* value=0 ) const { + return XMLConstHandle( _node ? _node->FirstChildElement( value ) : 0 ); + } + const XMLConstHandle LastChild() const { + return XMLConstHandle( _node ? _node->LastChild() : 0 ); + } + const XMLConstHandle LastChildElement( const char* _value=0 ) const { + return XMLConstHandle( _node ? _node->LastChildElement( _value ) : 0 ); + } + const XMLConstHandle PreviousSibling() const { + return XMLConstHandle( _node ? _node->PreviousSibling() : 0 ); + } + const XMLConstHandle PreviousSiblingElement( const char* _value=0 ) const { + return XMLConstHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 ); + } + const XMLConstHandle NextSibling() const { + return XMLConstHandle( _node ? _node->NextSibling() : 0 ); + } + const XMLConstHandle NextSiblingElement( const char* _value=0 ) const { + return XMLConstHandle( _node ? _node->NextSiblingElement( _value ) : 0 ); + } + + + const XMLNode* ToNode() const { + return _node; + } + const XMLElement* ToElement() const { + return ( ( _node && _node->ToElement() ) ? _node->ToElement() : 0 ); + } + const XMLText* ToText() const { + return ( ( _node && _node->ToText() ) ? _node->ToText() : 0 ); + } + const XMLUnknown* ToUnknown() const { + return ( ( _node && _node->ToUnknown() ) ? _node->ToUnknown() : 0 ); + } + const XMLDeclaration* ToDeclaration() const { + return ( ( _node && _node->ToDeclaration() ) ? _node->ToDeclaration() : 0 ); + } + +private: + const XMLNode* _node; +}; + + +/** + Printing functionality. The XMLPrinter gives you more + options than the XMLDocument::Print() method. + + It can: + -# Print to memory. + -# Print to a file you provide. + -# Print XML without a XMLDocument. + + Print to Memory + + @verbatim + XMLPrinter printer; + doc->Print( &printer ); + SomeFunction( printer.CStr() ); + @endverbatim + + Print to a File + + You provide the file pointer. + @verbatim + XMLPrinter printer( fp ); + doc.Print( &printer ); + @endverbatim + + Print without a XMLDocument + + When loading, an XML parser is very useful. However, sometimes + when saving, it just gets in the way. The code is often set up + for streaming, and constructing the DOM is just overhead. + + The Printer supports the streaming case. The following code + prints out a trivially simple XML file without ever creating + an XML document. + + @verbatim + XMLPrinter printer( fp ); + printer.OpenElement( "foo" ); + printer.PushAttribute( "foo", "bar" ); + printer.CloseElement(); + @endverbatim +*/ +class XMLPrinter : public XMLVisitor +{ +public: + /** Construct the printer. If the FILE* is specified, + this will print to the FILE. Else it will print + to memory, and the result is available in CStr(). + If 'compact' is set to true, then output is created + with only required whitespace and newlines. + */ + XMLPrinter( FILE* file=0, bool compact = false ); + ~XMLPrinter() {} + + /** If streaming, write the BOM and declaration. */ + void PushHeader( bool writeBOM, bool writeDeclaration ); + /** If streaming, start writing an element. + The element must be closed with CloseElement() + */ + void OpenElement( const char* name ); + /// If streaming, add an attribute to an open element. + void PushAttribute( const char* name, const char* value ); + void PushAttribute( const char* name, int value ); + void PushAttribute( const char* name, unsigned value ); + void PushAttribute( const char* name, bool value ); + void PushAttribute( const char* name, double value ); + /// If streaming, close the Element. + void CloseElement(); + + /// Add a text node. + void PushText( const char* text, bool cdata=false ); + /// Add a text node from an integer. + void PushText( int value ); + /// Add a text node from an unsigned. + void PushText( unsigned value ); + /// Add a text node from a bool. + void PushText( bool value ); + /// Add a text node from a float. + void PushText( float value ); + /// Add a text node from a double. + void PushText( double value ); + + /// Add a comment + void PushComment( const char* comment ); + + void PushDeclaration( const char* value ); + void PushUnknown( const char* value ); + + virtual bool VisitEnter( const XMLDocument& /*doc*/ ); + virtual bool VisitExit( const XMLDocument& /*doc*/ ) { + return true; + } + + virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ); + virtual bool VisitExit( const XMLElement& element ); + + virtual bool Visit( const XMLText& text ); + virtual bool Visit( const XMLComment& comment ); + virtual bool Visit( const XMLDeclaration& declaration ); + virtual bool Visit( const XMLUnknown& unknown ); + + /** + If in print to memory mode, return a pointer to + the XML file in memory. + */ + const char* CStr() const { + return _buffer.Mem(); + } + /** + If in print to memory mode, return the size + of the XML file in memory. (Note the size returned + includes the terminating null.) + */ + int CStrSize() const { + return _buffer.Size(); + } + +private: + void SealElement(); + void PrintSpace( int depth ); + void PrintString( const char*, bool restrictedEntitySet ); // prints out, after detecting entities. + void Print( const char* format, ... ); + + bool _elementJustOpened; + bool _firstElement; + FILE* _fp; + int _depth; + int _textDepth; + bool _processEntities; + bool _compactMode; + + enum { + ENTITY_RANGE = 64, + BUF_SIZE = 200 + }; + bool _entityFlag[ENTITY_RANGE]; + bool _restrictedEntityFlag[ENTITY_RANGE]; + + DynArray< const char*, 10 > _stack; + DynArray< char, 20 > _buffer; +#ifdef _MSC_VER + DynArray< char, 20 > _accumulator; +#endif +}; + + +} // tinyxml2 + + +#endif // TINYXML2_INCLUDED diff --git a/LibOVR/Include/OVR.h b/LibOVR/Include/OVR.h new file mode 100644 index 0000000..7c2b335 --- /dev/null +++ b/LibOVR/Include/OVR.h @@ -0,0 +1,33 @@ +/************************************************************************************ + +Filename : OVR.h +Content : This contains references to all OVR-specific headers in Src folder. + Should be generated automatically based on PublicHeader tags. + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_h +#define OVR_h + +#include "../Src/Kernel/OVR_Allocator.h" +#include "../Src/Kernel/OVR_Log.h" +#include "../Src/Kernel/OVR_Math.h" +#include "../Src/Kernel/OVR_System.h" +#include "../Src/Kernel/OVR_Types.h" +#include "../Src/OVR_Device.h" +#include "../Src/OVR_DeviceConstants.h" +#include "../Src/OVR_DeviceHandle.h" +#include "../Src/OVR_DeviceMessages.h" +#include "../Src/OVR_SensorFusion.h" +#include "../Src/Util/Util_LatencyTest.h" +#include "../Src/Util/Util_Render_Stereo.h" +#include "../Src/Util/Util_MagCalibration.h" + +#endif + diff --git a/LibOVR/Include/OVRVersion.h b/LibOVR/Include/OVRVersion.h new file mode 100644 index 0000000..ce4c30c --- /dev/null +++ b/LibOVR/Include/OVRVersion.h @@ -0,0 +1,22 @@ +/************************************************************************************ + +Filename : OVRVersion.h +Content : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef _OVR_VERSION_H +#define _OVR_VERSION_H + +#define OVR_MAJOR_VERSION 0 +#define OVR_MINOR_VERSION 2 +#define OVR_BUILD_VERSION 2 +#define OVR_VERSION_STRING "0.2.2" + +#endif diff --git a/LibOVR/Projects/Win32/LibOVR_Msvc2010.sln b/LibOVR/Projects/Win32/LibOVR_Msvc2010.sln new file mode 100644 index 0000000..305c332 --- /dev/null +++ b/LibOVR/Projects/Win32/LibOVR_Msvc2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibOVR", "LibOVR_Msvc2010.vcxproj", "{934B40C7-F40A-4E4C-97A7-B9659BE0A441}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Debug|Win32.ActiveCfg = Debug|Win32 + {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Debug|Win32.Build.0 = Debug|Win32 + {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Release|Win32.ActiveCfg = Release|Win32 + {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/LibOVR/Projects/Win32/LibOVR_Msvc2010.vcxproj b/LibOVR/Projects/Win32/LibOVR_Msvc2010.vcxproj new file mode 100644 index 0000000..90f7bdd --- /dev/null +++ b/LibOVR/Projects/Win32/LibOVR_Msvc2010.vcxproj @@ -0,0 +1,275 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {934B40C7-F40A-4E4C-97A7-B9659BE0A441} + Win32Proj + LibOVR + LibOVR + + + + StaticLibrary + true + Unicode + + + StaticLibrary + true + Unicode + + + StaticLibrary + false + true + Unicode + + + StaticLibrary + false + true + Unicode + + + + + + + + + + + + + + + + + + + ../../Lib/$(Platform)/ + + + ../../Lib/$(Platform)/ + + + ../../Obj/$(Platform)/$(Configuration)/ + + + ../../Obj/$(Platform)/$(Configuration)/ + + + libovrd + + + libovr64d + + + ../../Lib/$(Platform)/ + + + ../../Lib/$(Platform)/ + + + ../../Obj/$(Platform)/$(Configuration)/ + + + ../../Obj/$(Platform)/$(Configuration)/ + + + libovr + + + libovr64 + + + + + + Level4 + Disabled + OVR_BUILD_DEBUG;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + OldStyle + true + false + true + MultiThreadedDebug + + + Windows + true + + + Setupapi.lib + + + + + + + Level4 + Disabled + OVR_BUILD_DEBUG;WIN32;_WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions) + OldStyle + true + false + true + MultiThreadedDebug + + + Windows + true + + + Setupapi.lib + + + + + Level4 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreaded + true + true + OldStyle + + + Windows + true + true + true + + + Setupapi.lib + + + + + Level4 + + + MaxSpeed + true + true + WIN32;_WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreaded + true + true + OldStyle + + + Windows + true + true + true + + + Setupapi.lib + + + + + + \ No newline at end of file diff --git a/LibOVR/Projects/Win32/LibOVR_Msvc2010.vcxproj.filters b/LibOVR/Projects/Win32/LibOVR_Msvc2010.vcxproj.filters new file mode 100644 index 0000000..23f1686 --- /dev/null +++ b/LibOVR/Projects/Win32/LibOVR_Msvc2010.vcxproj.filters @@ -0,0 +1,186 @@ + + + + + + + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + + + + Kernel + + + Util + + + Util + + + + + + + Util + + + + + + + + + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Kernel + + + Include + + + + + + + + Kernel + + + Util + + + Util + + + + + + + + + + Util + + + + + + {ccc06f04-d013-483f-8471-bd25332e38bb} + + + {53c19267-8aec-48ad-a1a3-7ffd9090f075} + + + {6d4ac63a-dea8-4fdb-ad20-6768c33376d9} + + + \ No newline at end of file diff --git a/LibOVR/Projects/libovr.txt b/LibOVR/Projects/libovr.txt new file mode 100644 index 0000000..633db8f --- /dev/null +++ b/LibOVR/Projects/libovr.txt @@ -0,0 +1,83 @@ +LibOVR/Src/Kernel/OVR_Alg.cpp +LibOVR/Src/Kernel/OVR_Alg.h +LibOVR/Src/Kernel/OVR_Allocator.cpp +LibOVR/Src/Kernel/OVR_Allocator.h +LibOVR/Src/Kernel/OVR_Array.h +LibOVR/Src/Kernel/OVR_Atomic.cpp +LibOVR/Src/Kernel/OVR_Atomic.h +LibOVR/Src/Kernel/OVR_Color.h +LibOVR/Src/Kernel/OVR_ContainerAllocator.h +LibOVR/Src/Kernel/OVR_File.cpp +LibOVR/Src/Kernel/OVR_File.h +LibOVR/Src/Kernel/OVR_FileFILE.cpp +LibOVR/Src/Kernel/OVR_Hash.h +LibOVR/Src/Kernel/OVR_KeyCodes.h +LibOVR/Src/Kernel/OVR_List.h +LibOVR/Src/Kernel/OVR_Log.cpp +LibOVR/Src/Kernel/OVR_Log.h +LibOVR/Src/Kernel/OVR_Math.cpp +LibOVR/Src/Kernel/OVR_Math.h +LibOVR/Src/Kernel/OVR_RefCount.cpp +LibOVR/Src/Kernel/OVR_RefCount.h +LibOVR/Src/Kernel/OVR_Std.cpp +LibOVR/Src/Kernel/OVR_Std.h +LibOVR/Src/Kernel/OVR_String.cpp +LibOVR/Src/Kernel/OVR_String.h +LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp +LibOVR/Src/Kernel/OVR_String_PathUtil.cpp +LibOVR/Src/Kernel/OVR_StringHash.h +LibOVR/Src/Kernel/OVR_SysFile.cpp +LibOVR/Src/Kernel/OVR_SysFile.h +LibOVR/Src/Kernel/OVR_System.cpp +LibOVR/Src/Kernel/OVR_System.h +LibOVR/Src/Kernel/OVR_Threads.h +LibOVR/Src/Kernel/OVR_Timer.cpp +LibOVR/Src/Kernel/OVR_Timer.h +LibOVR/Src/Kernel/OVR_Types.h +LibOVR/Src/Kernel/OVR_UTF8Util.cpp +LibOVR/Src/Kernel/OVR_UTF8Util.h +LibOVR/Src/OVR_Device.h +LibOVR/Src/OVR_DeviceConstants.h +LibOVR/Src/OVR_DeviceHandle.cpp +LibOVR/Src/OVR_DeviceHandle.h +LibOVR/Src/OVR_DeviceImpl.cpp +LibOVR/Src/OVR_DeviceImpl.h +LibOVR/Src/OVR_DeviceMessages.h +LibOVR/Src/OVR_HIDDevice.h +LibOVR/Src/OVR_HIDDeviceBase.h +LibOVR/Src/OVR_HIDDeviceImpl.h +LibOVR/Src/OVR_LatencyTestImpl.cpp +LibOVR/Src/OVR_LatencyTestImpl.h +LibOVR/Src/OVR_SensorFusion.cpp +LibOVR/Src/OVR_SensorFusion.h +LibOVR/Src/OVR_SensorImpl.cpp +LibOVR/Src/OVR_SensorImpl.h +LibOVR/Src/OVR_ThreadCommandQueue.cpp +LibOVR/Src/OVR_ThreadCommandQueue.h +LibOVR/Src/Util/Util_LatencyTest.cpp +LibOVR/Src/Util/Util_LatencyTest.h +LibOVR/Src/Util/Util_Render_Stereo.cpp +LibOVR/Src/Util/Util_Render_Stereo.h + +[MacOS] +LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp +LibOVR/Src/OVR_OSX_DeviceManager.cpp +LibOVR/Src/OVR_OSX_DeviceManager.h +LibOVR/Src/OVR_OSX_HIDDevice.cpp +LibOVR/Src/OVR_OSX_HIDDevice.h +LibOVR/Src/OVR_OSX_HMDDevice.cpp +LibOVR/Src/OVR_OSX_HMDDevice.h +LibOVR/Src/OVR_OSX_SensorDevice.cpp + +[Win32] +LibOVR/Src/Kernel/OVR_ThreadsWinAPI.cpp +LibOVR/Src/OVR_Win32_DeviceManager.cpp +LibOVR/Src/OVR_Win32_DeviceManager.h +LibOVR/Src/OVR_Win32_DeviceStatus.cpp +LibOVR/Src/OVR_Win32_DeviceStatus.h +LibOVR/Src/OVR_Win32_HIDDevice.cpp +LibOVR/Src/OVR_Win32_HIDDevice.h +LibOVR/Src/OVR_Win32_HMDDevice.cpp +LibOVR/Src/OVR_Win32_HMDDevice.h +LibOVR/Src/OVR_Win32_SensorDevice.cpp +LibOVR/Src/OVR_Win32_SensorDevice.h diff --git a/LibOVR/Src/Kernel/OVR_Alg.cpp b/LibOVR/Src/Kernel/OVR_Alg.cpp new file mode 100644 index 0000000..ea82f2a --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Alg.cpp @@ -0,0 +1,46 @@ +/************************************************************************************ + +Filename : OVR_Alg.cpp +Content : Static lookup tables for Alg functions +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_Types.h" + +namespace OVR { namespace Alg { + +//------------------------------------------------------------------------ +extern const UByte UpperBitTable[256] = +{ + 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + +extern const UByte LowerBitTable[256] = +{ + 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, + 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, + 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, + 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, + 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, + 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, + 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, + 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 +}; + + +}} // OVE::Alg diff --git a/LibOVR/Src/Kernel/OVR_Alg.h b/LibOVR/Src/Kernel/OVR_Alg.h new file mode 100644 index 0000000..841e8f0 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Alg.h @@ -0,0 +1,953 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Alg.h +Content : Simple general purpose algorithms: Sort, Binary Search, etc. +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_Alg_h +#define OVR_Alg_h + +#include "OVR_Types.h" +#include + +namespace OVR { namespace Alg { + + +//----------------------------------------------------------------------------------- +// ***** Operator extensions + +template OVR_FORCE_INLINE void Swap(T &a, T &b) +{ T temp(a); a = b; b = temp; } + + +// ***** min/max are not implemented in Visual Studio 6 standard STL + +template OVR_FORCE_INLINE const T Min(const T a, const T b) +{ return (a < b) ? a : b; } + +template OVR_FORCE_INLINE const T Max(const T a, const T b) +{ return (b < a) ? a : b; } + +template OVR_FORCE_INLINE const T Clamp(const T v, const T minVal, const T maxVal) +{ return Max(minVal, Min(v, maxVal)); } + +template OVR_FORCE_INLINE int Chop(T f) +{ return (int)f; } + +template OVR_FORCE_INLINE T Lerp(T a, T b, T f) +{ return (b - a) * f + a; } + + +// These functions stand to fix a stupid VC++ warning (with /Wp64 on): +// "warning C4267: 'argument' : conversion from 'size_t' to 'const unsigned', possible loss of data" +// Use these functions instead of gmin/gmax if the argument has size +// of the pointer to avoid the warning. Though, functionally they are +// absolutelly the same as regular gmin/gmax. +template OVR_FORCE_INLINE const T PMin(const T a, const T b) +{ + OVR_COMPILER_ASSERT(sizeof(T) == sizeof(UPInt)); + return (a < b) ? a : b; +} +template OVR_FORCE_INLINE const T PMax(const T a, const T b) +{ + OVR_COMPILER_ASSERT(sizeof(T) == sizeof(UPInt)); + return (b < a) ? a : b; +} + + +template OVR_FORCE_INLINE const T Abs(const T v) +{ return (v>=0) ? v : -v; } + + +//----------------------------------------------------------------------------------- +// ***** OperatorLess +// +template struct OperatorLess +{ + static bool Compare(const T& a, const T& b) + { + return a < b; + } +}; + + +//----------------------------------------------------------------------------------- +// ***** QuickSortSliced +// +// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. +// The range is specified with start, end, where "end" is exclusive! +// The comparison predicate must be specified. +template +void QuickSortSliced(Array& arr, UPInt start, UPInt end, Less less) +{ + enum + { + Threshold = 9 + }; + + if(end - start < 2) return; + + SPInt stack[80]; + SPInt* top = stack; + SPInt base = (SPInt)start; + SPInt limit = (SPInt)end; + + for(;;) + { + SPInt len = limit - base; + SPInt i, j, pivot; + + if(len > Threshold) + { + // we use base + len/2 as the pivot + pivot = base + len / 2; + Swap(arr[base], arr[pivot]); + + i = base + 1; + j = limit - 1; + + // now ensure that *i <= *base <= *j + if(less(arr[j], arr[i])) Swap(arr[j], arr[i]); + if(less(arr[base], arr[i])) Swap(arr[base], arr[i]); + if(less(arr[j], arr[base])) Swap(arr[j], arr[base]); + + for(;;) + { + do i++; while( less(arr[i], arr[base]) ); + do j--; while( less(arr[base], arr[j]) ); + + if( i > j ) + { + break; + } + + Swap(arr[i], arr[j]); + } + + Swap(arr[base], arr[j]); + + // now, push the largest sub-array + if(j - base > limit - i) + { + top[0] = base; + top[1] = j; + base = i; + } + else + { + top[0] = i; + top[1] = limit; + limit = j; + } + top += 2; + } + else + { + // the sub-array is small, perform insertion sort + j = base; + i = j + 1; + + for(; i < limit; j = i, i++) + { + for(; less(arr[j + 1], arr[j]); j--) + { + Swap(arr[j + 1], arr[j]); + if(j == base) + { + break; + } + } + } + if(top > stack) + { + top -= 2; + base = top[0]; + limit = top[1]; + } + else + { + break; + } + } + } +} + + +//----------------------------------------------------------------------------------- +// ***** QuickSortSliced +// +// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. +// The range is specified with start, end, where "end" is exclusive! +// The data type must have a defined "<" operator. +template +void QuickSortSliced(Array& arr, UPInt start, UPInt end) +{ + typedef typename Array::ValueType ValueType; + QuickSortSliced(arr, start, end, OperatorLess::Compare); +} + +// Same as corresponding G_QuickSortSliced but with checking array limits to avoid +// crash in the case of wrong comparator functor. +template +bool QuickSortSlicedSafe(Array& arr, UPInt start, UPInt end, Less less) +{ + enum + { + Threshold = 9 + }; + + if(end - start < 2) return true; + + SPInt stack[80]; + SPInt* top = stack; + SPInt base = (SPInt)start; + SPInt limit = (SPInt)end; + + for(;;) + { + SPInt len = limit - base; + SPInt i, j, pivot; + + if(len > Threshold) + { + // we use base + len/2 as the pivot + pivot = base + len / 2; + Swap(arr[base], arr[pivot]); + + i = base + 1; + j = limit - 1; + + // now ensure that *i <= *base <= *j + if(less(arr[j], arr[i])) Swap(arr[j], arr[i]); + if(less(arr[base], arr[i])) Swap(arr[base], arr[i]); + if(less(arr[j], arr[base])) Swap(arr[j], arr[base]); + + for(;;) + { + do + { + i++; + if (i >= limit) + return false; + } while( less(arr[i], arr[base]) ); + do + { + j--; + if (j < 0) + return false; + } while( less(arr[base], arr[j]) ); + + if( i > j ) + { + break; + } + + Swap(arr[i], arr[j]); + } + + Swap(arr[base], arr[j]); + + // now, push the largest sub-array + if(j - base > limit - i) + { + top[0] = base; + top[1] = j; + base = i; + } + else + { + top[0] = i; + top[1] = limit; + limit = j; + } + top += 2; + } + else + { + // the sub-array is small, perform insertion sort + j = base; + i = j + 1; + + for(; i < limit; j = i, i++) + { + for(; less(arr[j + 1], arr[j]); j--) + { + Swap(arr[j + 1], arr[j]); + if(j == base) + { + break; + } + } + } + if(top > stack) + { + top -= 2; + base = top[0]; + limit = top[1]; + } + else + { + break; + } + } + } + return true; +} + +template +bool QuickSortSlicedSafe(Array& arr, UPInt start, UPInt end) +{ + typedef typename Array::ValueType ValueType; + return QuickSortSlicedSafe(arr, start, end, OperatorLess::Compare); +} + +//----------------------------------------------------------------------------------- +// ***** QuickSort +// +// Sort an array Array, ArrayPaged, ArrayUnsafe. +// The array must have GetSize() function. +// The comparison predicate must be specified. +template +void QuickSort(Array& arr, Less less) +{ + QuickSortSliced(arr, 0, arr.GetSize(), less); +} + +// checks for boundaries +template +bool QuickSortSafe(Array& arr, Less less) +{ + return QuickSortSlicedSafe(arr, 0, arr.GetSize(), less); +} + + +//----------------------------------------------------------------------------------- +// ***** QuickSort +// +// Sort an array Array, ArrayPaged, ArrayUnsafe. +// The array must have GetSize() function. +// The data type must have a defined "<" operator. +template +void QuickSort(Array& arr) +{ + typedef typename Array::ValueType ValueType; + QuickSortSliced(arr, 0, arr.GetSize(), OperatorLess::Compare); +} + +template +bool QuickSortSafe(Array& arr) +{ + typedef typename Array::ValueType ValueType; + return QuickSortSlicedSafe(arr, 0, arr.GetSize(), OperatorLess::Compare); +} + +//----------------------------------------------------------------------------------- +// ***** InsertionSortSliced +// +// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. +// The range is specified with start, end, where "end" is exclusive! +// The comparison predicate must be specified. +// Unlike Quick Sort, the Insertion Sort works much slower in average, +// but may be much faster on almost sorted arrays. Besides, it guarantees +// that the elements will not be swapped if not necessary. For example, +// an array with all equal elements will remain "untouched", while +// Quick Sort will considerably shuffle the elements in this case. +template +void InsertionSortSliced(Array& arr, UPInt start, UPInt end, Less less) +{ + UPInt j = start; + UPInt i = j + 1; + UPInt limit = end; + + for(; i < limit; j = i, i++) + { + for(; less(arr[j + 1], arr[j]); j--) + { + Swap(arr[j + 1], arr[j]); + if(j <= start) + { + break; + } + } + } +} + + +//----------------------------------------------------------------------------------- +// ***** InsertionSortSliced +// +// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. +// The range is specified with start, end, where "end" is exclusive! +// The data type must have a defined "<" operator. +template +void InsertionSortSliced(Array& arr, UPInt start, UPInt end) +{ + typedef typename Array::ValueType ValueType; + InsertionSortSliced(arr, start, end, OperatorLess::Compare); +} + +//----------------------------------------------------------------------------------- +// ***** InsertionSort +// +// Sort an array Array, ArrayPaged, ArrayUnsafe. +// The array must have GetSize() function. +// The comparison predicate must be specified. + +template +void InsertionSort(Array& arr, Less less) +{ + InsertionSortSliced(arr, 0, arr.GetSize(), less); +} + +//----------------------------------------------------------------------------------- +// ***** InsertionSort +// +// Sort an array Array, ArrayPaged, ArrayUnsafe. +// The array must have GetSize() function. +// The data type must have a defined "<" operator. +template +void InsertionSort(Array& arr) +{ + typedef typename Array::ValueType ValueType; + InsertionSortSliced(arr, 0, arr.GetSize(), OperatorLess::Compare); +} + + + +//----------------------------------------------------------------------------------- +// ***** LowerBoundSliced +// +template +UPInt LowerBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val, Less less) +{ + SPInt first = (SPInt)start; + SPInt len = (SPInt)(end - start); + SPInt half; + SPInt middle; + + while(len > 0) + { + half = len >> 1; + middle = first + half; + if(less(arr[middle], val)) + { + first = middle + 1; + len = len - half - 1; + } + else + { + len = half; + } + } + return (UPInt)first; +} + + +//----------------------------------------------------------------------------------- +// ***** LowerBoundSliced +// +template +UPInt LowerBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val) +{ + return LowerBoundSliced(arr, start, end, val, OperatorLess::Compare); +} + +//----------------------------------------------------------------------------------- +// ***** LowerBoundSized +// +template +UPInt LowerBoundSized(const Array& arr, UPInt size, const Value& val) +{ + return LowerBoundSliced(arr, 0, size, val, OperatorLess::Compare); +} + +//----------------------------------------------------------------------------------- +// ***** LowerBound +// +template +UPInt LowerBound(const Array& arr, const Value& val, Less less) +{ + return LowerBoundSliced(arr, 0, arr.GetSize(), val, less); +} + + +//----------------------------------------------------------------------------------- +// ***** LowerBound +// +template +UPInt LowerBound(const Array& arr, const Value& val) +{ + return LowerBoundSliced(arr, 0, arr.GetSize(), val, OperatorLess::Compare); +} + + + +//----------------------------------------------------------------------------------- +// ***** UpperBoundSliced +// +template +UPInt UpperBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val, Less less) +{ + SPInt first = (SPInt)start; + SPInt len = (SPInt)(end - start); + SPInt half; + SPInt middle; + + while(len > 0) + { + half = len >> 1; + middle = first + half; + if(less(val, arr[middle])) + { + len = half; + } + else + { + first = middle + 1; + len = len - half - 1; + } + } + return (UPInt)first; +} + + +//----------------------------------------------------------------------------------- +// ***** UpperBoundSliced +// +template +UPInt UpperBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val) +{ + return UpperBoundSliced(arr, start, end, val, OperatorLess::Compare); +} + + +//----------------------------------------------------------------------------------- +// ***** UpperBoundSized +// +template +UPInt UpperBoundSized(const Array& arr, UPInt size, const Value& val) +{ + return UpperBoundSliced(arr, 0, size, val, OperatorLess::Compare); +} + + +//----------------------------------------------------------------------------------- +// ***** UpperBound +// +template +UPInt UpperBound(const Array& arr, const Value& val, Less less) +{ + return UpperBoundSliced(arr, 0, arr.GetSize(), val, less); +} + + +//----------------------------------------------------------------------------------- +// ***** UpperBound +// +template +UPInt UpperBound(const Array& arr, const Value& val) +{ + return UpperBoundSliced(arr, 0, arr.GetSize(), val, OperatorLess::Compare); +} + + +//----------------------------------------------------------------------------------- +// ***** ReverseArray +// +template void ReverseArray(Array& arr) +{ + SPInt from = 0; + SPInt to = arr.GetSize() - 1; + while(from < to) + { + Swap(arr[from], arr[to]); + ++from; + --to; + } +} + + +// ***** AppendArray +// +template +void AppendArray(CDst& dst, const CSrc& src) +{ + UPInt i; + for(i = 0; i < src.GetSize(); i++) + dst.PushBack(src[i]); +} + +//----------------------------------------------------------------------------------- +// ***** ArrayAdaptor +// +// A simple adapter that provides the GetSize() method and overloads +// operator []. Used to wrap plain arrays in QuickSort and such. +template class ArrayAdaptor +{ +public: + typedef T ValueType; + ArrayAdaptor() : Data(0), Size(0) {} + ArrayAdaptor(T* ptr, UPInt size) : Data(ptr), Size(size) {} + UPInt GetSize() const { return Size; } + const T& operator [] (UPInt i) const { return Data[i]; } + T& operator [] (UPInt i) { return Data[i]; } +private: + T* Data; + UPInt Size; +}; + + +//----------------------------------------------------------------------------------- +// ***** GConstArrayAdaptor +// +// A simple const adapter that provides the GetSize() method and overloads +// operator []. Used to wrap plain arrays in LowerBound and such. +template class ConstArrayAdaptor +{ +public: + typedef T ValueType; + ConstArrayAdaptor() : Data(0), Size(0) {} + ConstArrayAdaptor(const T* ptr, UPInt size) : Data(ptr), Size(size) {} + UPInt GetSize() const { return Size; } + const T& operator [] (UPInt i) const { return Data[i]; } +private: + const T* Data; + UPInt Size; +}; + + + +//----------------------------------------------------------------------------------- +extern const UByte UpperBitTable[256]; +extern const UByte LowerBitTable[256]; + + + +//----------------------------------------------------------------------------------- +inline UByte UpperBit(UPInt val) +{ +#ifndef OVR_64BIT_POINTERS + + if (val & 0xFFFF0000) + { + return (val & 0xFF000000) ? + UpperBitTable[(val >> 24) ] + 24: + UpperBitTable[(val >> 16) & 0xFF] + 16; + } + return (val & 0xFF00) ? + UpperBitTable[(val >> 8) & 0xFF] + 8: + UpperBitTable[(val ) & 0xFF]; + +#else + + if (val & 0xFFFFFFFF00000000) + { + if (val & 0xFFFF000000000000) + { + return (val & 0xFF00000000000000) ? + UpperBitTable[(val >> 56) ] + 56: + UpperBitTable[(val >> 48) & 0xFF] + 48; + } + return (val & 0xFF0000000000) ? + UpperBitTable[(val >> 40) & 0xFF] + 40: + UpperBitTable[(val >> 32) & 0xFF] + 32; + } + else + { + if (val & 0xFFFF0000) + { + return (val & 0xFF000000) ? + UpperBitTable[(val >> 24) ] + 24: + UpperBitTable[(val >> 16) & 0xFF] + 16; + } + return (val & 0xFF00) ? + UpperBitTable[(val >> 8) & 0xFF] + 8: + UpperBitTable[(val ) & 0xFF]; + } + +#endif +} + +//----------------------------------------------------------------------------------- +inline UByte LowerBit(UPInt val) +{ +#ifndef OVR_64BIT_POINTERS + + if (val & 0xFFFF) + { + return (val & 0xFF) ? + LowerBitTable[ val & 0xFF]: + LowerBitTable[(val >> 8) & 0xFF] + 8; + } + return (val & 0xFF0000) ? + LowerBitTable[(val >> 16) & 0xFF] + 16: + LowerBitTable[(val >> 24) & 0xFF] + 24; + +#else + + if (val & 0xFFFFFFFF) + { + if (val & 0xFFFF) + { + return (val & 0xFF) ? + LowerBitTable[ val & 0xFF]: + LowerBitTable[(val >> 8) & 0xFF] + 8; + } + return (val & 0xFF0000) ? + LowerBitTable[(val >> 16) & 0xFF] + 16: + LowerBitTable[(val >> 24) & 0xFF] + 24; + } + else + { + if (val & 0xFFFF00000000) + { + return (val & 0xFF00000000) ? + LowerBitTable[(val >> 32) & 0xFF] + 32: + LowerBitTable[(val >> 40) & 0xFF] + 40; + } + return (val & 0xFF000000000000) ? + LowerBitTable[(val >> 48) & 0xFF] + 48: + LowerBitTable[(val >> 56) & 0xFF] + 56; + } + +#endif +} + + + +// ******* Special (optimized) memory routines +// Note: null (bad) pointer is not tested +class MemUtil +{ +public: + + // Memory compare + static int Cmp (const void* p1, const void* p2, UPInt byteCount) { return memcmp(p1, p2, byteCount); } + static int Cmp16(const void* p1, const void* p2, UPInt int16Count); + static int Cmp32(const void* p1, const void* p2, UPInt int32Count); + static int Cmp64(const void* p1, const void* p2, UPInt int64Count); +}; + +// ** Inline Implementation + +inline int MemUtil::Cmp16(const void* p1, const void* p2, UPInt int16Count) +{ + SInt16* pa = (SInt16*)p1; + SInt16* pb = (SInt16*)p2; + unsigned ic = 0; + if (int16Count == 0) + return 0; + while (pa[ic] == pb[ic]) + if (++ic==int16Count) + return 0; + return pa[ic] > pb[ic] ? 1 : -1; +} +inline int MemUtil::Cmp32(const void* p1, const void* p2, UPInt int32Count) +{ + SInt32* pa = (SInt32*)p1; + SInt32* pb = (SInt32*)p2; + unsigned ic = 0; + if (int32Count == 0) + return 0; + while (pa[ic] == pb[ic]) + if (++ic==int32Count) + return 0; + return pa[ic] > pb[ic] ? 1 : -1; +} +inline int MemUtil::Cmp64(const void* p1, const void* p2, UPInt int64Count) +{ + SInt64* pa = (SInt64*)p1; + SInt64* pb = (SInt64*)p2; + unsigned ic = 0; + if (int64Count == 0) + return 0; + while (pa[ic] == pb[ic]) + if (++ic==int64Count) + return 0; + return pa[ic] > pb[ic] ? 1 : -1; +} + +// ** End Inline Implementation + + +//----------------------------------------------------------------------------------- +// ******* Byte Order Conversions +namespace ByteUtil { + + // *** Swap Byte Order + + // Swap the byte order of a byte array + inline void SwapOrder(void* pv, int size) + { + UByte* pb = (UByte*)pv; + UByte temp; + for (int i = 0; i < size>>1; i++) + { + temp = pb[size-1-i]; + pb[size-1-i] = pb[i]; + pb[i] = temp; + } + } + + // Swap the byte order of primitive types + inline UByte SwapOrder(UByte v) { return v; } + inline SByte SwapOrder(SByte v) { return v; } + inline UInt16 SwapOrder(UInt16 v) { return UInt16(v>>8)|UInt16(v<<8); } + inline SInt16 SwapOrder(SInt16 v) { return SInt16((UInt16(v)>>8)|(v<<8)); } + inline UInt32 SwapOrder(UInt32 v) { return (v>>24)|((v&0x00FF0000)>>8)|((v&0x0000FF00)<<8)|(v<<24); } + inline SInt32 SwapOrder(SInt32 p) { return (SInt32)SwapOrder(UInt32(p)); } + inline UInt64 SwapOrder(UInt64 v) + { + return (v>>56) | + ((v&UInt64(0x00FF000000000000))>>40) | + ((v&UInt64(0x0000FF0000000000))>>24) | + ((v&UInt64(0x000000FF00000000))>>8) | + ((v&UInt64(0x00000000FF000000))<<8) | + ((v&UInt64(0x0000000000FF0000))<<24) | + ((v&UInt64(0x000000000000FF00))<<40) | + (v<<56); + } + inline SInt64 SwapOrder(SInt64 v) { return (SInt64)SwapOrder(UInt64(v)); } + inline float SwapOrder(float p) + { + union { + float p; + UInt32 v; + } u; + u.p = p; + u.v = SwapOrder(u.v); + return u.p; + } + + inline double SwapOrder(double p) + { + union { + double p; + UInt64 v; + } u; + u.p = p; + u.v = SwapOrder(u.v); + return u.p; + } + + // *** Byte-order conversion + +#if (OVR_BYTE_ORDER == OVR_LITTLE_ENDIAN) + // Little Endian to System (LE) + inline UByte LEToSystem(UByte v) { return v; } + inline SByte LEToSystem(SByte v) { return v; } + inline UInt16 LEToSystem(UInt16 v) { return v; } + inline SInt16 LEToSystem(SInt16 v) { return v; } + inline UInt32 LEToSystem(UInt32 v) { return v; } + inline SInt32 LEToSystem(SInt32 v) { return v; } + inline UInt64 LEToSystem(UInt64 v) { return v; } + inline SInt64 LEToSystem(SInt64 v) { return v; } + inline float LEToSystem(float v) { return v; } + inline double LEToSystem(double v) { return v; } + + // Big Endian to System (LE) + inline UByte BEToSystem(UByte v) { return SwapOrder(v); } + inline SByte BEToSystem(SByte v) { return SwapOrder(v); } + inline UInt16 BEToSystem(UInt16 v) { return SwapOrder(v); } + inline SInt16 BEToSystem(SInt16 v) { return SwapOrder(v); } + inline UInt32 BEToSystem(UInt32 v) { return SwapOrder(v); } + inline SInt32 BEToSystem(SInt32 v) { return SwapOrder(v); } + inline UInt64 BEToSystem(UInt64 v) { return SwapOrder(v); } + inline SInt64 BEToSystem(SInt64 v) { return SwapOrder(v); } + inline float BEToSystem(float v) { return SwapOrder(v); } + inline double BEToSystem(double v) { return SwapOrder(v); } + + // System (LE) to Little Endian + inline UByte SystemToLE(UByte v) { return v; } + inline SByte SystemToLE(SByte v) { return v; } + inline UInt16 SystemToLE(UInt16 v) { return v; } + inline SInt16 SystemToLE(SInt16 v) { return v; } + inline UInt32 SystemToLE(UInt32 v) { return v; } + inline SInt32 SystemToLE(SInt32 v) { return v; } + inline UInt64 SystemToLE(UInt64 v) { return v; } + inline SInt64 SystemToLE(SInt64 v) { return v; } + inline float SystemToLE(float v) { return v; } + inline double SystemToLE(double v) { return v; } + + // System (LE) to Big Endian + inline UByte SystemToBE(UByte v) { return SwapOrder(v); } + inline SByte SystemToBE(SByte v) { return SwapOrder(v); } + inline UInt16 SystemToBE(UInt16 v) { return SwapOrder(v); } + inline SInt16 SystemToBE(SInt16 v) { return SwapOrder(v); } + inline UInt32 SystemToBE(UInt32 v) { return SwapOrder(v); } + inline SInt32 SystemToBE(SInt32 v) { return SwapOrder(v); } + inline UInt64 SystemToBE(UInt64 v) { return SwapOrder(v); } + inline SInt64 SystemToBE(SInt64 v) { return SwapOrder(v); } + inline float SystemToBE(float v) { return SwapOrder(v); } + inline double SystemToBE(double v) { return SwapOrder(v); } + +#elif (OVR_BYTE_ORDER == OVR_BIG_ENDIAN) + // Little Endian to System (BE) + inline UByte LEToSystem(UByte v) { return SwapOrder(v); } + inline SByte LEToSystem(SByte v) { return SwapOrder(v); } + inline UInt16 LEToSystem(UInt16 v) { return SwapOrder(v); } + inline SInt16 LEToSystem(SInt16 v) { return SwapOrder(v); } + inline UInt32 LEToSystem(UInt32 v) { return SwapOrder(v); } + inline SInt32 LEToSystem(SInt32 v) { return SwapOrder(v); } + inline UInt64 LEToSystem(UInt64 v) { return SwapOrder(v); } + inline SInt64 LEToSystem(SInt64 v) { return SwapOrder(v); } + inline float LEToSystem(float v) { return SwapOrder(v); } + inline double LEToSystem(double v) { return SwapOrder(v); } + + // Big Endian to System (BE) + inline UByte BEToSystem(UByte v) { return v; } + inline SByte BEToSystem(SByte v) { return v; } + inline UInt16 BEToSystem(UInt16 v) { return v; } + inline SInt16 BEToSystem(SInt16 v) { return v; } + inline UInt32 BEToSystem(UInt32 v) { return v; } + inline SInt32 BEToSystem(SInt32 v) { return v; } + inline UInt64 BEToSystem(UInt64 v) { return v; } + inline SInt64 BEToSystem(SInt64 v) { return v; } + inline float BEToSystem(float v) { return v; } + inline double BEToSystem(double v) { return v; } + + // System (BE) to Little Endian + inline UByte SystemToLE(UByte v) { return SwapOrder(v); } + inline SByte SystemToLE(SByte v) { return SwapOrder(v); } + inline UInt16 SystemToLE(UInt16 v) { return SwapOrder(v); } + inline SInt16 SystemToLE(SInt16 v) { return SwapOrder(v); } + inline UInt32 SystemToLE(UInt32 v) { return SwapOrder(v); } + inline SInt32 SystemToLE(SInt32 v) { return SwapOrder(v); } + inline UInt64 SystemToLE(UInt64 v) { return SwapOrder(v); } + inline SInt64 SystemToLE(SInt64 v) { return SwapOrder(v); } + inline float SystemToLE(float v) { return SwapOrder(v); } + inline double SystemToLE(double v) { return SwapOrder(v); } + + // System (BE) to Big Endian + inline UByte SystemToBE(UByte v) { return v; } + inline SByte SystemToBE(SByte v) { return v; } + inline UInt16 SystemToBE(UInt16 v) { return v; } + inline SInt16 SystemToBE(SInt16 v) { return v; } + inline UInt32 SystemToBE(UInt32 v) { return v; } + inline SInt32 SystemToBE(SInt32 v) { return v; } + inline UInt64 SystemToBE(UInt64 v) { return v; } + inline SInt64 SystemToBE(SInt64 v) { return v; } + inline float SystemToBE(float v) { return v; } + inline double SystemToBE(double v) { return v; } + +#else + #error "OVR_BYTE_ORDER must be defined to OVR_LITTLE_ENDIAN or OVR_BIG_ENDIAN" +#endif + +} // namespace ByteUtil + + + +}} // OVR::Alg + +#endif diff --git a/LibOVR/Src/Kernel/OVR_Allocator.cpp b/LibOVR/Src/Kernel/OVR_Allocator.cpp new file mode 100644 index 0000000..1f17ffe --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Allocator.cpp @@ -0,0 +1,84 @@ +/************************************************************************************ + +Filename : OVR_Allocator.cpp +Content : Installable memory allocator implementation +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_Allocator.h" +#ifdef OVR_OS_MAC + #include +#else + #include +#endif + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** Allocator + +Allocator* Allocator::pInstance = 0; + +// Default AlignedAlloc implementation will delegate to Alloc/Free after doing rounding. +void* Allocator::AllocAligned(UPInt size, UPInt align) +{ + OVR_ASSERT((align & (align-1)) == 0); + align = (align > sizeof(UPInt)) ? align : sizeof(UPInt); + UPInt p = (UPInt)Alloc(size+align); + UPInt aligned = 0; + if (p) + { + aligned = (UPInt(p) + align-1) & ~(align-1); + if (aligned == p) + aligned += align; + *(((UPInt*)aligned)-1) = aligned-p; + } + return (void*)aligned; +} + +void Allocator::FreeAligned(void* p) +{ + UPInt src = UPInt(p) - *(((UPInt*)p)-1); + Free((void*)src); +} + + +//------------------------------------------------------------------------ +// ***** Default Allocator + +// This allocator is created and used if no other allocator is installed. +// Default allocator delegates to system malloc. + +void* DefaultAllocator::Alloc(UPInt size) +{ + return malloc(size); +} +void* DefaultAllocator::AllocDebug(UPInt size, const char* file, unsigned line) +{ +#if defined(OVR_CC_MSVC) && defined(_CRTDBG_MAP_ALLOC) + return _malloc_dbg(size, _NORMAL_BLOCK, file, line); +#else + OVR_UNUSED2(file, line); + return malloc(size); +#endif +} + +void* DefaultAllocator::Realloc(void* p, UPInt newSize) +{ + return realloc(p, newSize); +} +void DefaultAllocator::Free(void *p) +{ + return free(p); +} + + +} // OVR diff --git a/LibOVR/Src/Kernel/OVR_Allocator.h b/LibOVR/Src/Kernel/OVR_Allocator.h new file mode 100644 index 0000000..88cbb42 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Allocator.h @@ -0,0 +1,336 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Allocator.h +Content : Installable memory allocator +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_Allocator_h +#define OVR_Allocator_h + +#include "OVR_Types.h" + +//----------------------------------------------------------------------------------- + +// ***** Disable template-unfriendly MS VC++ warnings +#if defined(OVR_CC_MSVC) +// Pragma to prevent long name warnings in in VC++ +#pragma warning(disable : 4503) +#pragma warning(disable : 4786) +// In MSVC 7.1, warning about placement new POD default initializer +#pragma warning(disable : 4345) +#endif + +// Un-define new so that placement constructors work +#undef new + + +//----------------------------------------------------------------------------------- +// ***** Placement new overrides + +// Calls constructor on own memory created with "new(ptr) type" +#ifndef __PLACEMENT_NEW_INLINE +#define __PLACEMENT_NEW_INLINE + +# if defined(OVR_CC_MWERKS) || defined(OVR_CC_BORLAND) || defined(OVR_CC_GNU) +# include +# else + // Useful on MSVC + OVR_FORCE_INLINE void* operator new (OVR::UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; } + OVR_FORCE_INLINE void operator delete (void *, void *) { } +# endif + +#endif // __PLACEMENT_NEW_INLINE + + + +//------------------------------------------------------------------------ +// ***** Macros to redefine class new/delete operators + +// Types specifically declared to allow disambiguation of address in +// class member operator new. + +#define OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, check_delete) \ + void* operator new(UPInt sz) \ + { void *p = OVR_ALLOC_DEBUG(sz, __FILE__, __LINE__); return p; } \ + void* operator new(UPInt sz, const char* file, int line) \ + { void* p = OVR_ALLOC_DEBUG(sz, file, line); OVR_UNUSED2(file, line); return p; } \ + void operator delete(void *p) \ + { check_delete(class_name, p); OVR_FREE(p); } \ + void operator delete(void *p, const char*, int) \ + { check_delete(class_name, p); OVR_FREE(p); } + +#define OVR_MEMORY_DEFINE_PLACEMENT_NEW \ + void* operator new (UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; } \ + void operator delete (void *ptr, void *ptr2) { OVR_UNUSED2(ptr,ptr2); } + + +#define OVR_MEMORY_CHECK_DELETE_NONE(class_name, p) + +// Redefined all delete/new operators in a class without custom memory initialization +#define OVR_MEMORY_REDEFINE_NEW(class_name) \ + OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, OVR_MEMORY_CHECK_DELETE_NONE) + + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** Construct / Destruct + +// Construct/Destruct functions are useful when new is redefined, as they can +// be called instead of placement new constructors. + + +template +OVR_FORCE_INLINE T* Construct(void *p) +{ + return ::new(p) T; +} + +template +OVR_FORCE_INLINE T* Construct(void *p, const T& source) +{ + return ::new(p) T(source); +} + +// Same as above, but allows for a different type of constructor. +template +OVR_FORCE_INLINE T* ConstructAlt(void *p, const S& source) +{ + return ::new(p) T(source); +} + +template +OVR_FORCE_INLINE T* ConstructAlt(void *p, const S1& src1, const S2& src2) +{ + return ::new(p) T(src1, src2); +} + +template +OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count) +{ + UByte *pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + { + Construct(pdata); + } +} + +template +OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count, const T& source) +{ + UByte *pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + { + Construct(pdata, source); + } +} + +template +OVR_FORCE_INLINE void Destruct(T *pobj) +{ + pobj->~T(); + OVR_UNUSED1(pobj); // Fix incorrect 'unused variable' MSVC warning. +} + +template +OVR_FORCE_INLINE void DestructArray(T *pobj, UPInt count) +{ + for (UPInt i=0; i~T(); +} + + +//----------------------------------------------------------------------------------- +// ***** Allocator + +// Allocator defines a memory allocation interface that developers can override +// to to provide memory for OVR; an instance of this class is typically created on +// application startup and passed into System or OVR::System constructor. +// +// +// Users implementing this interface must provide three functions: Alloc, Free, +// and Realloc. Implementations of these functions must honor the requested alignment. +// Although arbitrary alignment requests are possible, requested alignment will +// typically be small, such as 16 bytes or less. + +class Allocator +{ + friend class System; +public: + + // *** Standard Alignment Alloc/Free + + // Allocate memory of specified size with default alignment. + // Alloc of size==0 will allocate a tiny block & return a valid pointer; + // this makes it suitable for new operator. + virtual void* Alloc(UPInt size) = 0; + // Same as Alloc, but provides an option of passing debug data. + virtual void* AllocDebug(UPInt size, const char* file, unsigned line) + { OVR_UNUSED2(file, line); return Alloc(size); } + + // Reallocate memory block to a new size, copying data if necessary. Returns the pointer to + // new memory block, which may be the same as original pointer. Will return 0 if reallocation + // failed, in which case previous memory is still valid. + // Realloc to decrease size will never fail. + // Realloc of pointer == 0 is equivalent to Alloc + // Realloc to size == 0, shrinks to the minimal size, pointer remains valid and requires Free(). + virtual void* Realloc(void* p, UPInt newSize) = 0; + + // Frees memory allocated by Alloc/Realloc. + // Free of null pointer is valid and will do nothing. + virtual void Free(void *p) = 0; + + + // *** Standard Alignment Alloc/Free + + // Allocate memory of specified alignment. + // Memory allocated with AllocAligned MUST be freed with FreeAligned. + // Default implementation will delegate to Alloc/Free after doing rounding. + virtual void* AllocAligned(UPInt size, UPInt align); + // Frees memory allocated with AllocAligned. + virtual void FreeAligned(void* p); + + // Returns the pointer to the current globally installed Allocator instance. + // This pointer is used for most of the memory allocations. + static Allocator* GetInstance() { return pInstance; } + + +protected: + // onSystemShutdown is called on the allocator during System::Shutdown. + // At this point, all allocations should've been freed. + virtual void onSystemShutdown() { } + +public: + static void setInstance(Allocator* palloc) + { + OVR_ASSERT((pInstance == 0) || (palloc == 0)); + pInstance = palloc; + } + +private: + + static Allocator* pInstance; +}; + + + +//------------------------------------------------------------------------ +// ***** Allocator_SingletonSupport + +// Allocator_SingletonSupport is a Allocator wrapper class that implements +// the InitSystemSingleton static function, used to create a global singleton +// used for the OVR::System default argument initialization. +// +// End users implementing custom Allocator interface don't need to make use of this base +// class; they can just create an instance of their own class on stack and pass it to System. + +template +class Allocator_SingletonSupport : public Allocator +{ + struct AllocContainer + { + UPInt Data[(sizeof(D) + sizeof(UPInt)-1) / sizeof(UPInt)]; + bool Initialized; + AllocContainer() : Initialized(0) { } + }; + + AllocContainer* pContainer; + +public: + Allocator_SingletonSupport() : pContainer(0) { } + + // Creates a singleton instance of this Allocator class used + // on OVR_DEFAULT_ALLOCATOR during System initialization. + static D* InitSystemSingleton() + { + static AllocContainer Container; + OVR_ASSERT(Container.Initialized == false); + + Allocator_SingletonSupport *presult = Construct((void*)Container.Data); + presult->pContainer = &Container; + Container.Initialized = true; + return (D*)presult; + } + +protected: + virtual void onSystemShutdown() + { + Allocator::onSystemShutdown(); + if (pContainer) + { + pContainer->Initialized = false; + Destruct((D*)this); + pContainer = 0; + } + } +}; + +//------------------------------------------------------------------------ +// ***** Default Allocator + +// This allocator is created and used if no other allocator is installed. +// Default allocator delegates to system malloc. + +class DefaultAllocator : public Allocator_SingletonSupport +{ +public: + virtual void* Alloc(UPInt size); + virtual void* AllocDebug(UPInt size, const char* file, unsigned line); + virtual void* Realloc(void* p, UPInt newSize); + virtual void Free(void *p); +}; + + +//------------------------------------------------------------------------ +// ***** Memory Allocation Macros + +// These macros should be used for global allocation. In the future, these +// macros will allows allocation to be extended with debug file/line information +// if necessary. + +#define OVR_REALLOC(p,s) OVR::Allocator::GetInstance()->Realloc((p),(s)) +#define OVR_FREE(p) OVR::Allocator::GetInstance()->Free((p)) +#define OVR_ALLOC_ALIGNED(s,a) OVR::Allocator::GetInstance()->AllocAligned((s),(a)) +#define OVR_FREE_ALIGNED(p) OVR::Allocator::GetInstance()->FreeAligned((p)) + +#ifdef OVR_BUILD_DEBUG +#define OVR_ALLOC(s) OVR::Allocator::GetInstance()->AllocDebug((s), __FILE__, __LINE__) +#define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->AllocDebug((s), f, l) +#else +#define OVR_ALLOC(s) OVR::Allocator::GetInstance()->Alloc((s)) +#define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->Alloc((s)) +#endif + +//------------------------------------------------------------------------ + +// Base class that overrides the new and delete operators. +// Deriving from this class, even as a multiple base, incurs no space overhead. +class NewOverrideBase +{ +public: + + // Redefine all new & delete operators. + OVR_MEMORY_REDEFINE_NEW(NewOverrideBase) +}; + + +} // OVR + + +// Redefine operator 'new' if necessary. +#if defined(OVR_DEFINE_NEW) +#define new OVR_DEFINE_NEW +#endif + + +#endif // OVR_Memory diff --git a/LibOVR/Src/Kernel/OVR_Array.h b/LibOVR/Src/Kernel/OVR_Array.h new file mode 100644 index 0000000..905d58f --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Array.h @@ -0,0 +1,793 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Array.h +Content : Template implementation for Array +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_Array_h +#define OVR_Array_h + +#include "OVR_ContainerAllocator.h" + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** ArrayDefaultPolicy +// +// Default resize behavior. No minimal capacity, Granularity=4, +// Shrinking as needed. ArrayConstPolicy actually is the same as +// ArrayDefaultPolicy, but parametrized with constants. +// This struct is used only in order to reduce the template "matroska". +struct ArrayDefaultPolicy +{ + ArrayDefaultPolicy() : Capacity(0) {} + ArrayDefaultPolicy(const ArrayDefaultPolicy&) : Capacity(0) {} + + UPInt GetMinCapacity() const { return 0; } + UPInt GetGranularity() const { return 4; } + bool NeverShrinking() const { return 0; } + + UPInt GetCapacity() const { return Capacity; } + void SetCapacity(UPInt capacity) { Capacity = capacity; } +private: + UPInt Capacity; +}; + + +//----------------------------------------------------------------------------------- +// ***** ArrayConstPolicy +// +// Statically parametrized resizing behavior: +// MinCapacity, Granularity, and Shrinking flag. +template +struct ArrayConstPolicy +{ + typedef ArrayConstPolicy SelfType; + + ArrayConstPolicy() : Capacity(0) {} + ArrayConstPolicy(const SelfType&) : Capacity(0) {} + + UPInt GetMinCapacity() const { return MinCapacity; } + UPInt GetGranularity() const { return Granularity; } + bool NeverShrinking() const { return NeverShrink; } + + UPInt GetCapacity() const { return Capacity; } + void SetCapacity(UPInt capacity) { Capacity = capacity; } +private: + UPInt Capacity; +}; + +//----------------------------------------------------------------------------------- +// ***** ArrayDataBase +// +// Basic operations with array data: Reserve, Resize, Free, ArrayPolicy. +// For internal use only: ArrayData,ArrayDataCC and others. +template +struct ArrayDataBase +{ + typedef T ValueType; + typedef Allocator AllocatorType; + typedef SizePolicy SizePolicyType; + typedef ArrayDataBase SelfType; + + ArrayDataBase() + : Data(0), Size(0), Policy() {} + + ArrayDataBase(const SizePolicy& p) + : Data(0), Size(0), Policy(p) {} + + ~ArrayDataBase() + { + Allocator::DestructArray(Data, Size); + Allocator::Free(Data); + } + + UPInt GetCapacity() const + { + return Policy.GetCapacity(); + } + + void ClearAndRelease() + { + Allocator::DestructArray(Data, Size); + Allocator::Free(Data); + Data = 0; + Size = 0; + Policy.SetCapacity(0); + } + + void Reserve(UPInt newCapacity) + { + if (Policy.NeverShrinking() && newCapacity < GetCapacity()) + return; + + if (newCapacity < Policy.GetMinCapacity()) + newCapacity = Policy.GetMinCapacity(); + + // Resize the buffer. + if (newCapacity == 0) + { + if (Data) + { + Allocator::Free(Data); + Data = 0; + } + Policy.SetCapacity(0); + } + else + { + UPInt gran = Policy.GetGranularity(); + newCapacity = (newCapacity + gran - 1) / gran * gran; + if (Data) + { + if (Allocator::IsMovable()) + { + Data = (T*)Allocator::Realloc(Data, sizeof(T) * newCapacity); + } + else + { + T* newData = (T*)Allocator::Alloc(sizeof(T) * newCapacity); + UPInt i, s; + s = (Size < newCapacity) ? Size : newCapacity; + for (i = 0; i < s; ++i) + { + Allocator::Construct(&newData[i], Data[i]); + Allocator::Destruct(&Data[i]); + } + for (i = s; i < Size; ++i) + { + Allocator::Destruct(&Data[i]); + } + Allocator::Free(Data); + Data = newData; + } + } + else + { + Data = (T*)Allocator::Alloc(sizeof(T) * newCapacity); + //memset(Buffer, 0, (sizeof(ValueType) * newSize)); // Do we need this? + } + Policy.SetCapacity(newCapacity); + // OVR_ASSERT(Data); // need to throw (or something) on alloc failure! + } + } + + // This version of Resize DOES NOT construct the elements. + // It's done to optimize PushBack, which uses a copy constructor + // instead of the default constructor and assignment + void ResizeNoConstruct(UPInt newSize) + { + UPInt oldSize = Size; + + if (newSize < oldSize) + { + Allocator::DestructArray(Data + newSize, oldSize - newSize); + if (newSize < (Policy.GetCapacity() >> 1)) + { + Reserve(newSize); + } + } + else if(newSize >= Policy.GetCapacity()) + { + Reserve(newSize + (newSize >> 2)); + } + //! IMPORTANT to modify Size only after Reserve completes, because garbage collectable + // array may use this array and may traverse it during Reserve (in the case, if + // collection occurs because of heap limit exceeded). + Size = newSize; + } + + ValueType* Data; + UPInt Size; + SizePolicy Policy; +}; + + + +//----------------------------------------------------------------------------------- +// ***** ArrayData +// +// General purpose array data. +// For internal use only in Array, ArrayLH, ArrayPOD and so on. +template +struct ArrayData : ArrayDataBase +{ + typedef T ValueType; + typedef Allocator AllocatorType; + typedef SizePolicy SizePolicyType; + typedef ArrayDataBase BaseType; + typedef ArrayData SelfType; + + ArrayData() + : BaseType() { } + + ArrayData(int size) + : BaseType() { Resize(size); } + + ArrayData(const SelfType& a) + : BaseType(a.Policy) { Append(a.Data, a.Size); } + + + void Resize(UPInt newSize) + { + UPInt oldSize = this->Size; + BaseType::ResizeNoConstruct(newSize); + if(newSize > oldSize) + Allocator::ConstructArray(this->Data + oldSize, newSize - oldSize); + } + + void PushBack(const ValueType& val) + { + BaseType::ResizeNoConstruct(this->Size + 1); + Allocator::Construct(this->Data + this->Size - 1, val); + } + + template + void PushBackAlt(const S& val) + { + BaseType::ResizeNoConstruct(this->Size + 1); + Allocator::ConstructAlt(this->Data + this->Size - 1, val); + } + + // Append the given data to the array. + void Append(const ValueType other[], UPInt count) + { + if (count) + { + UPInt oldSize = this->Size; + BaseType::ResizeNoConstruct(this->Size + count); + Allocator::ConstructArray(this->Data + oldSize, count, other); + } + } +}; + + + +//----------------------------------------------------------------------------------- +// ***** ArrayDataCC +// +// A modification of ArrayData that always copy-constructs new elements +// using a specified DefaultValue. For internal use only in ArrayCC. +template +struct ArrayDataCC : ArrayDataBase +{ + typedef T ValueType; + typedef Allocator AllocatorType; + typedef SizePolicy SizePolicyType; + typedef ArrayDataBase BaseType; + typedef ArrayDataCC SelfType; + + ArrayDataCC(const ValueType& defval) + : BaseType(), DefaultValue(defval) { } + + ArrayDataCC(const ValueType& defval, int size) + : BaseType(), DefaultValue(defval) { Resize(size); } + + ArrayDataCC(const SelfType& a) + : BaseType(a.Policy), DefaultValue(a.DefaultValue) { Append(a.Data, a.Size); } + + + void Resize(UPInt newSize) + { + UPInt oldSize = this->Size; + BaseType::ResizeNoConstruct(newSize); + if(newSize > oldSize) + Allocator::ConstructArray(this->Data + oldSize, newSize - oldSize, DefaultValue); + } + + void PushBack(const ValueType& val) + { + BaseType::ResizeNoConstruct(this->Size + 1); + Allocator::Construct(this->Data + this->Size - 1, val); + } + + template + void PushBackAlt(const S& val) + { + BaseType::ResizeNoConstruct(this->Size + 1); + Allocator::ConstructAlt(this->Data + this->Size - 1, val); + } + + // Append the given data to the array. + void Append(const ValueType other[], UPInt count) + { + if (count) + { + UPInt oldSize = this->Size; + BaseType::ResizeNoConstruct(this->Size + count); + Allocator::ConstructArray(this->Data + oldSize, count, other); + } + } + + ValueType DefaultValue; +}; + + + + + +//----------------------------------------------------------------------------------- +// ***** ArrayBase +// +// Resizable array. The behavior can be POD (suffix _POD) and +// Movable (no suffix) depending on the allocator policy. +// In case of _POD the constructors and destructors are not called. +// +// Arrays can't handle non-movable objects! Don't put anything in here +// that can't be moved around by bitwise copy. +// +// The addresses of elements are not persistent! Don't keep the address +// of an element; the array contents will move around as it gets resized. +template +class ArrayBase +{ +public: + typedef typename ArrayData::ValueType ValueType; + typedef typename ArrayData::AllocatorType AllocatorType; + typedef typename ArrayData::SizePolicyType SizePolicyType; + typedef ArrayBase SelfType; + + +#undef new + OVR_MEMORY_REDEFINE_NEW(ArrayBase) +// Redefine operator 'new' if necessary. +#if defined(OVR_DEFINE_NEW) +#define new OVR_DEFINE_NEW +#endif + + + ArrayBase() + : Data() {} + ArrayBase(int size) + : Data(size) {} + ArrayBase(const SelfType& a) + : Data(a.Data) {} + + ArrayBase(const ValueType& defval) + : Data(defval) {} + ArrayBase(const ValueType& defval, int size) + : Data(defval, size) {} + + SizePolicyType* GetSizePolicy() const { return Data.Policy; } + void SetSizePolicy(const SizePolicyType& p) { Data.Policy = p; } + + bool NeverShrinking()const { return Data.Policy.NeverShrinking(); } + UPInt GetSize() const { return Data.Size; } + bool IsEmpty() const { return Data.Size == 0; } + UPInt GetCapacity() const { return Data.GetCapacity(); } + UPInt GetNumBytes() const { return Data.GetCapacity() * sizeof(ValueType); } + + void ClearAndRelease() { Data.ClearAndRelease(); } + void Clear() { Data.Resize(0); } + void Resize(UPInt newSize) { Data.Resize(newSize); } + + // Reserve can only increase the capacity + void Reserve(UPInt newCapacity) + { + if (newCapacity > Data.GetCapacity()) + Data.Reserve(newCapacity); + } + + // Basic access. + ValueType& At(UPInt index) + { + OVR_ASSERT(index < Data.Size); + return Data.Data[index]; + } + const ValueType& At(UPInt index) const + { + OVR_ASSERT(index < Data.Size); + return Data.Data[index]; + } + + ValueType ValueAt(UPInt index) const + { + OVR_ASSERT(index < Data.Size); + return Data.Data[index]; + } + + // Basic access. + ValueType& operator [] (UPInt index) + { + OVR_ASSERT(index < Data.Size); + return Data.Data[index]; + } + const ValueType& operator [] (UPInt index) const + { + OVR_ASSERT(index < Data.Size); + return Data.Data[index]; + } + + // Raw pointer to the data. Use with caution! + const ValueType* GetDataPtr() const { return Data.Data; } + ValueType* GetDataPtr() { return Data.Data; } + + // Insert the given element at the end of the array. + void PushBack(const ValueType& val) + { + // DO NOT pass elements of your own vector into + // push_back()! Since we're using references, + // resize() may munge the element storage! + // OVR_ASSERT(&val < &Buffer[0] || &val > &Buffer[BufferSize]); + Data.PushBack(val); + } + + template + void PushBackAlt(const S& val) + { + Data.PushBackAlt(val); + } + + // Remove the last element. + void PopBack(UPInt count = 1) + { + OVR_ASSERT(Data.Size >= count); + Data.Resize(Data.Size - count); + } + + ValueType& PushDefault() + { + Data.PushBack(ValueType()); + return Back(); + } + + ValueType Pop() + { + ValueType t = Back(); + PopBack(); + return t; + } + + + // Access the first element. + ValueType& Front() { return At(0); } + const ValueType& Front() const { return At(0); } + + // Access the last element. + ValueType& Back() { return At(Data.Size - 1); } + const ValueType& Back() const { return At(Data.Size - 1); } + + // Array copy. Copies the contents of a into this array. + const SelfType& operator = (const SelfType& a) + { + Resize(a.GetSize()); + for (UPInt i = 0; i < Data.Size; i++) { + *(Data.Data + i) = a[i]; + } + return *this; + } + + // Removing multiple elements from the array. + void RemoveMultipleAt(UPInt index, UPInt num) + { + OVR_ASSERT(index + num <= Data.Size); + if (Data.Size == num) + { + Clear(); + } + else + { + AllocatorType::DestructArray(Data.Data + index, num); + AllocatorType::CopyArrayForward( + Data.Data + index, + Data.Data + index + num, + Data.Size - num - index); + Data.Size -= num; + } + } + + // Removing an element from the array is an expensive operation! + // It compacts only after removing the last element. + void RemoveAt(UPInt index) + { + OVR_ASSERT(index < Data.Size); + if (Data.Size == 1) + { + Clear(); + } + else + { + AllocatorType::Destruct(Data.Data + index); + AllocatorType::CopyArrayForward( + Data.Data + index, + Data.Data + index + 1, + Data.Size - 1 - index); + --Data.Size; + } + } + + // Insert the given object at the given index shifting all the elements up. + void InsertAt(UPInt index, const ValueType& val = ValueType()) + { + OVR_ASSERT(index <= Data.Size); + + Data.Resize(Data.Size + 1); + if (index < Data.Size - 1) + { + AllocatorType::CopyArrayBackward( + Data.Data + index + 1, + Data.Data + index, + Data.Size - 1 - index); + } + AllocatorType::Construct(Data.Data + index, val); + } + + // Insert the given object at the given index shifting all the elements up. + void InsertMultipleAt(UPInt index, UPInt num, const ValueType& val = ValueType()) + { + OVR_ASSERT(index <= Data.Size); + + Data.Resize(Data.Size + num); + if (index < Data.Size - num) + { + AllocatorType::CopyArrayBackward( + Data.Data + index + num, + Data.Data + index, + Data.Size - num - index); + } + for (UPInt i = 0; i < num; ++i) + AllocatorType::Construct(Data.Data + index + i, val); + } + + // Append the given data to the array. + void Append(const SelfType& other) + { + Append(other.Data.Data, other.GetSize()); + } + + // Append the given data to the array. + void Append(const ValueType other[], UPInt count) + { + Data.Append(other, count); + } + + class Iterator + { + SelfType* pArray; + SPInt CurIndex; + + public: + Iterator() : pArray(0), CurIndex(-1) {} + Iterator(SelfType* parr, SPInt idx = 0) : pArray(parr), CurIndex(idx) {} + + bool operator==(const Iterator& it) const { return pArray == it.pArray && CurIndex == it.CurIndex; } + bool operator!=(const Iterator& it) const { return pArray != it.pArray || CurIndex != it.CurIndex; } + + Iterator& operator++() + { + if (pArray) + { + if (CurIndex < (SPInt)pArray->GetSize()) + ++CurIndex; + } + return *this; + } + Iterator operator++(int) + { + Iterator it(*this); + operator++(); + return it; + } + Iterator& operator--() + { + if (pArray) + { + if (CurIndex >= 0) + --CurIndex; + } + return *this; + } + Iterator operator--(int) + { + Iterator it(*this); + operator--(); + return it; + } + Iterator operator+(int delta) const + { + return Iterator(pArray, CurIndex + delta); + } + Iterator operator-(int delta) const + { + return Iterator(pArray, CurIndex - delta); + } + SPInt operator-(const Iterator& right) const + { + OVR_ASSERT(pArray == right.pArray); + return CurIndex - right.CurIndex; + } + ValueType& operator*() const { OVR_ASSERT(pArray); return (*pArray)[CurIndex]; } + ValueType* operator->() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } + ValueType* GetPtr() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } + + bool IsFinished() const { return !pArray || CurIndex < 0 || CurIndex >= (int)pArray->GetSize(); } + + void Remove() + { + if (!IsFinished()) + pArray->RemoveAt(CurIndex); + } + + SPInt GetIndex() const { return CurIndex; } + }; + + Iterator Begin() { return Iterator(this); } + Iterator End() { return Iterator(this, (SPInt)GetSize()); } + Iterator Last() { return Iterator(this, (SPInt)GetSize() - 1); } + + class ConstIterator + { + const SelfType* pArray; + SPInt CurIndex; + + public: + ConstIterator() : pArray(0), CurIndex(-1) {} + ConstIterator(const SelfType* parr, SPInt idx = 0) : pArray(parr), CurIndex(idx) {} + + bool operator==(const ConstIterator& it) const { return pArray == it.pArray && CurIndex == it.CurIndex; } + bool operator!=(const ConstIterator& it) const { return pArray != it.pArray || CurIndex != it.CurIndex; } + + ConstIterator& operator++() + { + if (pArray) + { + if (CurIndex < (int)pArray->GetSize()) + ++CurIndex; + } + return *this; + } + ConstIterator operator++(int) + { + ConstIterator it(*this); + operator++(); + return it; + } + ConstIterator& operator--() + { + if (pArray) + { + if (CurIndex >= 0) + --CurIndex; + } + return *this; + } + ConstIterator operator--(int) + { + ConstIterator it(*this); + operator--(); + return it; + } + ConstIterator operator+(int delta) const + { + return ConstIterator(pArray, CurIndex + delta); + } + ConstIterator operator-(int delta) const + { + return ConstIterator(pArray, CurIndex - delta); + } + SPInt operator-(const ConstIterator& right) const + { + OVR_ASSERT(pArray == right.pArray); + return CurIndex - right.CurIndex; + } + const ValueType& operator*() const { OVR_ASSERT(pArray); return (*pArray)[CurIndex]; } + const ValueType* operator->() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } + const ValueType* GetPtr() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } + + bool IsFinished() const { return !pArray || CurIndex < 0 || CurIndex >= (int)pArray->GetSize(); } + + SPInt GetIndex() const { return CurIndex; } + }; + ConstIterator Begin() const { return ConstIterator(this); } + ConstIterator End() const { return ConstIterator(this, (SPInt)GetSize()); } + ConstIterator Last() const { return ConstIterator(this, (SPInt)GetSize() - 1); } + +protected: + ArrayData Data; +}; + + + +//----------------------------------------------------------------------------------- +// ***** Array +// +// General purpose array for movable objects that require explicit +// construction/destruction. +template +class Array : public ArrayBase, SizePolicy> > +{ +public: + typedef T ValueType; + typedef ContainerAllocator AllocatorType; + typedef SizePolicy SizePolicyType; + typedef Array SelfType; + typedef ArrayBase, SizePolicy> > BaseType; + + Array() : BaseType() {} + Array(int size) : BaseType(size) {} + Array(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); } + Array(const SelfType& a) : BaseType(a) {} + const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } +}; + +// ***** ArrayPOD +// +// General purpose array for movable objects that DOES NOT require +// construction/destruction. Constructors and destructors are not called! +// Global heap is in use. +template +class ArrayPOD : public ArrayBase, SizePolicy> > +{ +public: + typedef T ValueType; + typedef ContainerAllocator_POD AllocatorType; + typedef SizePolicy SizePolicyType; + typedef ArrayPOD SelfType; + typedef ArrayBase, SizePolicy> > BaseType; + + ArrayPOD() : BaseType() {} + ArrayPOD(int size) : BaseType(size) {} + ArrayPOD(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); } + ArrayPOD(const SelfType& a) : BaseType(a) {} + const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } +}; + + +// ***** ArrayCPP +// +// General purpose, fully C++ compliant array. Can be used with non-movable data. +// Global heap is in use. +template +class ArrayCPP : public ArrayBase, SizePolicy> > +{ +public: + typedef T ValueType; + typedef ContainerAllocator_CPP AllocatorType; + typedef SizePolicy SizePolicyType; + typedef ArrayCPP SelfType; + typedef ArrayBase, SizePolicy> > BaseType; + + ArrayCPP() : BaseType() {} + ArrayCPP(int size) : BaseType(size) {} + ArrayCPP(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); } + ArrayCPP(const SelfType& a) : BaseType(a) {} + const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } +}; + + +// ***** ArrayCC +// +// A modification of the array that uses the given default value to +// construct the elements. The constructors and destructors are +// properly called, the objects must be movable. + +template +class ArrayCC : public ArrayBase, SizePolicy> > +{ +public: + typedef T ValueType; + typedef ContainerAllocator AllocatorType; + typedef SizePolicy SizePolicyType; + typedef ArrayCC SelfType; + typedef ArrayBase, SizePolicy> > BaseType; + + ArrayCC(const ValueType& defval) : BaseType(defval) {} + ArrayCC(const ValueType& defval, int size) : BaseType(defval, size) {} + ArrayCC(const ValueType& defval, const SizePolicyType& p) : BaseType(defval) { SetSizePolicy(p); } + ArrayCC(const SelfType& a) : BaseType(a) {} + const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } +}; + +} // OVR + +#endif diff --git a/LibOVR/Src/Kernel/OVR_Atomic.cpp b/LibOVR/Src/Kernel/OVR_Atomic.cpp new file mode 100644 index 0000000..a2f9230 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Atomic.cpp @@ -0,0 +1,82 @@ +/************************************************************************************ + +Filename : OVR_Atomic.cpp +Content : Contains atomic operations and inline fastest locking + functionality. Will contain #ifdefs for OS efficiency. + Have non-thread-safe implementation if not available. +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_Atomic.h" + +#ifdef OVR_ENABLE_THREADS + +// Include Windows 8-Metro compatible Synchronization API +#if defined(OVR_OS_WIN32) && defined(NTDDI_WIN8) && (NTDDI_VERSION >= NTDDI_WIN8) +#include +#endif + + +namespace OVR { + +// ***** Windows Lock implementation + +#if defined(OVR_OS_WIN32) + +// ***** Standard Win32 Lock implementation + +// Constructors +Lock::Lock(unsigned spinCount) +{ +#if defined(NTDDI_WIN8) && (NTDDI_VERSION >= NTDDI_WIN8) + // On Windows 8 we use InitializeCriticalSectionEx due to Metro-Compatibility + InitializeCriticalSectionEx(&cs, spinCount, + OVR_DEBUG_SELECT(NULL, CRITICAL_SECTION_NO_DEBUG_INFO)); +#else + // Spin count init critical section function prototype for Window NT + typedef BOOL (WINAPI *Function_InitializeCriticalSectionAndSpinCount) + (LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount); + + + // Try to load function dynamically so that we don't require NT + // On Windows NT we will use InitializeCriticalSectionAndSpinCount + static bool initTried = 0; + static Function_InitializeCriticalSectionAndSpinCount pInitFn = 0; + + if (!initTried) + { + HMODULE hmodule = ::LoadLibrary(OVR_STR("kernel32.dll")); + pInitFn = (Function_InitializeCriticalSectionAndSpinCount) + ::GetProcAddress(hmodule, "InitializeCriticalSectionAndSpinCount"); + initTried = true; + } + + // Initialize the critical section + if (pInitFn) + pInitFn(&cs, spinCount); + else + ::InitializeCriticalSection(&cs); +#endif + +} + + +Lock::~Lock() +{ + DeleteCriticalSection(&cs); +} + + +#endif + +} // OVR + +#endif // OVR_ENABLE_THREADS diff --git a/LibOVR/Src/Kernel/OVR_Atomic.h b/LibOVR/Src/Kernel/OVR_Atomic.h new file mode 100644 index 0000000..089125b --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Atomic.h @@ -0,0 +1,859 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Atomic.h +Content : Contains atomic operations and inline fastest locking + functionality. Will contain #ifdefs for OS efficiency. + Have non-thread-safe implementaion if not available. +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ +#ifndef OVR_Atomic_h +#define OVR_Atomic_h + +#include "OVR_Types.h" + +// Include System thread functionality. +#if defined(OVR_OS_WIN32) +#include +#else +#include +#endif + + +namespace OVR { + + +// ****** Declared classes + +// If there is NO thread support we implement AtomicOps and +// Lock objects as no-ops. The other classes are not defined. +template class AtomicOps; +template class AtomicInt; +template class AtomicPtr; + +class Lock; + + +//----------------------------------------------------------------------------------- +// ***** AtomicOps + +// Atomic operations are provided by the AtomicOps templates class, +// implemented through system-specific AtomicOpsRaw specializations. +// It provides several fundamental operations such as Exchange, ExchangeAdd +// CompareAndSet, and Store_Release. Each function includes several memory +// synchronization versions, important for multiprocessing CPUs with weak +// memory consistency. The following memory fencing strategies are supported: +// +// - NoSync. No memory synchronization is done for atomic op. +// - Release. All other memory writes are completed before atomic op +// writes its results. +// - Acquire. Further memory reads are forced to wait until atomic op +// executes, guaranteeing that the right values will be seen. +// - Sync. A combination of Release and Acquire. + + +// *** AtomicOpsRaw + +// AtomicOpsRaw is a specialized template that provides atomic operations +// used by AtomicOps. This class has two fundamental qualities: (1) it +// defines a type T of correct size, and (2) provides operations that work +// atomically, such as Exchange_Sync and CompareAndSet_Release. + +// AtomicOpsRawBase class contains shared constants/classes for AtomicOpsRaw. +// The primary thing is does is define sync class objects, whose destructor and +// constructor provide places to insert appropriate synchronization calls, on +// systems where such calls are necessary. So far, the breakdown is as follows: +// +// - X86 systems don't need custom syncs, since their exchange/atomic +// instructions are implicitly synchronized. +// - PowerPC requires lwsync/isync instructions that can use this mechanism. +// - If some other systems require a mechanism where syncing type is associated +// with a particular instruction, the default implementation (which implements +// all Sync, Acquire, and Release modes in terms of NoSync and fence) may not +// work. Ii that case it will need to be #ifdef-ed conditionally. + +struct AtomicOpsRawBase +{ +#if !defined(OVR_ENABLE_THREADS) || defined(OVR_CPU_X86) || defined(OVR_OS_WIN32) || defined(OVR_OS_IPHONE) + // Need to have empty constructor to avoid class 'unused' variable warning. + struct FullSync { inline FullSync() { } }; + struct AcquireSync { inline AcquireSync() { } }; + struct ReleaseSync { inline ReleaseSync() { } }; + +#elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC) + struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("isync\n"); } }; + struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("isync\n"); } }; + struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } }; + +#elif defined(OVR_CPU_MIPS) + struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("sync\n"); } }; + struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("sync\n"); } }; + struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } }; + +#elif defined(OVR_CPU_ARM) + struct FullSync { inline FullSync() { asm volatile("dmb\n"); } ~FullSync() { asm volatile("dmb\n"); } }; + struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("dmb\n"); } }; + struct ReleaseSync { inline ReleaseSync() { asm volatile("dmb\n"); } }; + + +#elif defined(OVR_CC_GNU) && (__GNUC__ >= 4) + // __sync functions are already full sync + struct FullSync { inline FullSync() { } }; + struct AcquireSync { inline AcquireSync() { } }; + struct ReleaseSync { inline ReleaseSync() { } }; +#endif +}; + + +// 4-Byte raw data atomic op implementation class. +struct AtomicOpsRaw_4ByteImpl : public AtomicOpsRawBase +{ +#if !defined(OVR_ENABLE_THREADS) + + // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl. + typedef UInt32 T; + + // *** Thread - Safe Atomic Versions. + +#elif defined(OVR_OS_WIN32) + + // Use special defined for VC6, where volatile is not used and + // InterlockedCompareExchange is declared incorrectly. + typedef LONG T; +#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC < 1300) + typedef T* InterlockTPtr; + typedef LPVOID ET; + typedef ET* InterlockETPtr; +#else + typedef volatile T* InterlockTPtr; + typedef T ET; + typedef InterlockTPtr InterlockETPtr; +#endif + inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange((InterlockTPtr)p, val); } + inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd((InterlockTPtr)p, val); } + inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange((InterlockETPtr)p, (ET)val, (ET)c) == (ET)c; } + +#elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC) + typedef UInt32 T; + static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) + { + UInt32 ret; + + asm volatile("1:\n\t" + "lwarx %[r],0,%[i]\n\t" + "stwcx. %[j],0,%[i]\n\t" + "bne- 1b\n" + : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [j] "b" (j) : "cc", "memory"); + + return ret; + } + + static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) + { + UInt32 dummy, ret; + + asm volatile("1:\n\t" + "lwarx %[r],0,%[i]\n\t" + "add %[o],%[r],%[j]\n\t" + "stwcx. %[o],0,%[i]\n\t" + "bne- 1b\n" + : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc", "memory"); + + return ret; + } + + static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) + { + UInt32 ret; + + asm volatile("1:\n\t" + "lwarx %[r],0,%[i]\n\t" + "cmpw 0,%[r],%[cmp]\n\t" + "mfcr %[r]\n\t" + "bne- 2f\n\t" + "stwcx. %[val],0,%[i]\n\t" + "bne- 1b\n\t" + "2:\n" + : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc", "memory"); + + return (ret & 0x20000000) ? 1 : 0; + } + +#elif defined(OVR_CPU_MIPS) + typedef UInt32 T; + + static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) + { + UInt32 ret; + + asm volatile("1:\n\t" + "ll %[r],0(%[i])\n\t" + "sc %[j],0(%[i])\n\t" + "beq %[j],$0,1b\n\t" + "nop \n" + : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory"); + + return ret; + } + + static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) + { + UInt32 ret; + + asm volatile("1:\n\t" + "ll %[r],0(%[i])\n\t" + "addu %[j],%[r],%[j]\n\t" + "sc %[j],0(%[i])\n\t" + "beq %[j],$0,1b\n\t" + "nop \n" + : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory"); + + return ret; + } + + static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) + { + UInt32 ret, dummy; + + asm volatile("1:\n\t" + "move %[r],$0\n\t" + "ll %[o],0(%[i])\n\t" + "bne %[o],%[c],2f\n\t" + "move %[r],%[v]\n\t" + "sc %[r],0(%[i])\n\t" + "beq %[r],$0,1b\n\t" + "nop \n\t" + "2:\n" + : "+m" (*i),[r] "=&d" (ret), [o] "=&d" (dummy) : [i] "d" (i), [c] "d" (c), [v] "d" (value) + : "cc", "memory"); + + return ret; + } + +#elif defined(OVR_CPU_ARM) && defined(OVR_CC_ARM) + typedef UInt32 T; + + static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) + { + for(;;) + { + T r = __ldrex(i); + if (__strex(j, i) == 0) + return r; + } + } + static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) + { + for(;;) + { + T r = __ldrex(i); + if (__strex(r + j, i) == 0) + return r; + } + } + + static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) + { + for(;;) + { + T r = __ldrex(i); + if (r != c) + return 0; + if (__strex(value, i) == 0) + return 1; + } + } + +#elif defined(OVR_CPU_ARM) + typedef UInt32 T; + + static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) + { + UInt32 ret, dummy; + + asm volatile("1:\n\t" + "ldrex %[r],[%[i]]\n\t" + "strex %[t],%[j],[%[i]]\n\t" + "cmp %[t],#0\n\t" + "bne 1b\n\t" + : "+m" (*i), [r] "=&r" (ret), [t] "=&r" (dummy) : [i] "r" (i), [j] "r" (j) : "cc", "memory"); + + return ret; + } + + static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) + { + UInt32 ret, dummy, test; + + asm volatile("1:\n\t" + "ldrex %[r],[%[i]]\n\t" + "add %[o],%[r],%[j]\n\t" + "strex %[t],%[o],[%[i]]\n\t" + "cmp %[t],#0\n\t" + "bne 1b\n\t" + : "+m" (*i), [r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [j] "r" (j) : "cc", "memory"); + + return ret; + } + + static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) + { + UInt32 ret = 1, dummy, test; + + asm volatile("1:\n\t" + "ldrex %[o],[%[i]]\n\t" + "cmp %[o],%[c]\n\t" + "bne 2f\n\t" + "strex %[r],%[v],[%[i]]\n\t" + "cmp %[r],#0\n\t" + "bne 1b\n\t" + "2:\n" + : "+m" (*i),[r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [c] "r" (c), [v] "r" (value) + : "cc", "memory"); + + return !ret; + } + +#elif defined(OVR_CPU_X86) + typedef UInt32 T; + + static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) + { + asm volatile("xchgl %1,%[i]\n" + : "+m" (*i), "=q" (j) : [i] "m" (*i), "1" (j) : "cc", "memory"); + + return j; + } + + static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) + { + asm volatile("lock; xaddl %1,%[i]\n" + : "+m" (*i), "+q" (j) : [i] "m" (*i) : "cc", "memory"); + + return j; + } + + static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) + { + UInt32 ret; + + asm volatile("lock; cmpxchgl %[v],%[i]\n" + : "+m" (*i), "=a" (ret) : [i] "m" (*i), "1" (c), [v] "q" (value) : "cc", "memory"); + + return (ret == c); + } + +#elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1) + + typedef UInt32 T; + + static inline T Exchange_NoSync(volatile T *i, T j) + { + T v; + do { + v = *i; + } while (!__sync_bool_compare_and_swap(i, v, j)); + return v; + } + + static inline T ExchangeAdd_NoSync(volatile T *i, T j) + { + return __sync_fetch_and_add(i, j); + } + + static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value) + { + return __sync_bool_compare_and_swap(i, c, value); + } + +#endif // OS +}; + + +// 8-Byte raw data data atomic op implementation class. +// Currently implementation is provided only on systems with 64-bit pointers. +struct AtomicOpsRaw_8ByteImpl : public AtomicOpsRawBase +{ +#if !defined(OVR_64BIT_POINTERS) || !defined(OVR_ENABLE_THREADS) + + // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl. + typedef UInt64 T; + + // *** Thread - Safe OS specific versions. +#elif defined(OVR_OS_WIN32) + + // This is only for 64-bit systems. + typedef LONG64 T; + typedef volatile T* InterlockTPtr; + inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange64((InterlockTPtr)p, val); } + inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd64((InterlockTPtr)p, val); } + inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange64((InterlockTPtr)p, val, c) == c; } + +#elif defined(OVR_CPU_PPC64) + + typedef UInt64 T; + + static inline UInt64 Exchange_NoSync(volatile UInt64 *i, UInt64 j) + { + UInt64 dummy, ret; + + asm volatile("1:\n\t" + "ldarx %[r],0,%[i]\n\t" + "mr %[o],%[j]\n\t" + "stdcx. %[o],0,%[i]\n\t" + "bne- 1b\n" + : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc"); + + return ret; + } + + static inline UInt64 ExchangeAdd_NoSync(volatile UInt64 *i, UInt64 j) + { + UInt64 dummy, ret; + + asm volatile("1:\n\t" + "ldarx %[r],0,%[i]\n\t" + "add %[o],%[r],%[j]\n\t" + "stdcx. %[o],0,%[i]\n\t" + "bne- 1b\n" + : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc"); + + return ret; + } + + static inline bool CompareAndSet_NoSync(volatile UInt64 *i, UInt64 c, UInt64 value) + { + UInt64 ret, dummy; + + asm volatile("1:\n\t" + "ldarx %[r],0,%[i]\n\t" + "cmpw 0,%[r],%[cmp]\n\t" + "mfcr %[r]\n\t" + "bne- 2f\n\t" + "stdcx. %[val],0,%[i]\n\t" + "bne- 1b\n\t" + "2:\n" + : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc"); + + return (ret & 0x20000000) ? 1 : 0; + } + +#elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1) + + typedef UInt64 T; + + static inline T Exchange_NoSync(volatile T *i, T j) + { + T v; + do { + v = *i; + } while (!__sync_bool_compare_and_swap(i, v, j)); + return v; + } + + static inline T ExchangeAdd_NoSync(volatile T *i, T j) + { + return __sync_fetch_and_add(i, j); + } + + static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value) + { + return __sync_bool_compare_and_swap(i, c, value); + } + +#endif // OS +}; + + +// Default implementation for AtomicOpsRaw; provides implementation of mem-fenced +// atomic operations where fencing is done with a sync object wrapped around a NoSync +// operation implemented in the base class. If such implementation is not possible +// on a given platform, #ifdefs can be used to disable it and then op functions can be +// implemented individually in the appropriate AtomicOpsRaw class. + +template +struct AtomicOpsRaw_DefImpl : public O +{ + typedef typename O::T O_T; + typedef typename O::FullSync O_FullSync; + typedef typename O::AcquireSync O_AcquireSync; + typedef typename O::ReleaseSync O_ReleaseSync; + + // If there is no thread support, provide the default implementation. In this case, + // the base class (0) must still provide the T declaration. +#ifndef OVR_ENABLE_THREADS + + // Atomic exchange of val with argument. Returns old val. + inline static O_T Exchange_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p = val; return old; } + // Adds a new val to argument; returns its old val. + inline static O_T ExchangeAdd_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p += val; return old; } + // Compares the argument data with 'c' val. + // If succeeded, stores val int '*p' and returns true; otherwise returns false. + inline static bool CompareAndSet_NoSync(volatile O_T* p, O_T c, O_T val) { if (*p==c) { *p = val; return 1; } return 0; } + +#endif + + // If NoSync wrapped implementation may not be possible, it this block should be + // replaced with per-function implementation in O. + // "AtomicOpsRaw_DefImpl::" prefix in calls below. + inline static O_T Exchange_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::Exchange_NoSync(p, val); } + inline static O_T Exchange_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::Exchange_NoSync(p, val); } + inline static O_T Exchange_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::Exchange_NoSync(p, val); } + inline static O_T ExchangeAdd_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::ExchangeAdd_NoSync(p, val); } + inline static O_T ExchangeAdd_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::ExchangeAdd_NoSync(p, val); } + inline static O_T ExchangeAdd_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::ExchangeAdd_NoSync(p, val); } + inline static bool CompareAndSet_Sync(volatile O_T* p, O_T c, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::CompareAndSet_NoSync(p,c,val); } + inline static bool CompareAndSet_Release(volatile O_T* p, O_T c, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::CompareAndSet_NoSync(p,c,val); } + inline static bool CompareAndSet_Acquire(volatile O_T* p, O_T c, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::CompareAndSet_NoSync(p,c,val); } + + // Loads and stores with memory fence. These have only the relevant versions. +#ifdef OVR_CPU_X86 + // On X86, Store_Release is implemented as exchange. Note that we can also + // consider 'sfence' in the future, although it is not as compatible with older CPUs. + inline static void Store_Release(volatile O_T* p, O_T val) { Exchange_Release(p, val); } +#else + inline static void Store_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); *p = val; } +#endif + inline static O_T Load_Acquire(const volatile O_T* p) { O_AcquireSync sync; OVR_UNUSED(sync); return *p; } +}; + + +template +struct AtomicOpsRaw : public AtomicOpsRawBase { }; + +template<> +struct AtomicOpsRaw<4> : public AtomicOpsRaw_DefImpl +{ + // Ensure that assigned type size is correct. + AtomicOpsRaw() + { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl::T) == 4); } +}; +template<> +struct AtomicOpsRaw<8> : public AtomicOpsRaw_DefImpl +{ + AtomicOpsRaw() + { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl::T) == 8); } +}; + + +// *** AtomicOps - implementation of atomic Ops for specified class + +// Implements atomic ops on a class, provided that the object is either +// 4 or 8 bytes in size (depending on the AtomicOpsRaw specializations +// available). Relies on AtomicOpsRaw for much of implementation. + +template +class AtomicOps +{ + typedef AtomicOpsRaw Ops; + typedef typename Ops::T T; + typedef volatile typename Ops::T* PT; + // We cast through unions to (1) avoid pointer size compiler warnings + // and (2) ensure that there are no problems with strict pointer aliasing. + union C2T_union { C c; T t; }; + +public: + // General purpose implementation for standard syncs. + inline static C Exchange_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Sync((PT)p, u.t); return u.c; } + inline static C Exchange_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Release((PT)p, u.t); return u.c; } + inline static C Exchange_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Acquire((PT)p, u.t); return u.c; } + inline static C Exchange_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_NoSync((PT)p, u.t); return u.c; } + inline static C ExchangeAdd_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Sync((PT)p, u.t); return u.c; } + inline static C ExchangeAdd_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Release((PT)p, u.t); return u.c; } + inline static C ExchangeAdd_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Acquire((PT)p, u.t); return u.c; } + inline static C ExchangeAdd_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_NoSync((PT)p, u.t); return u.c; } + inline static bool CompareAndSet_Sync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Sync((PT)p, cu.t, u.t); } + inline static bool CompareAndSet_Release(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Release((PT)p, cu.t, u.t); } + inline static bool CompareAndSet_Relse(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Acquire((PT)p, cu.t, u.t); } + inline static bool CompareAndSet_NoSync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_NoSync((PT)p, cu.t, u.t); } + // Loads and stores with memory fence. These have only the relevant versions. + inline static void Store_Release(volatile C* p, C val) { C2T_union u; u.c = val; Ops::Store_Release((PT)p, u.t); } + inline static C Load_Acquire(const volatile C* p) { C2T_union u; u.t = Ops::Load_Acquire((PT)p); return u.c; } +}; + + + +// Atomic value base class - implements operations shared for integers and pointers. +template +class AtomicValueBase +{ +protected: + typedef AtomicOps Ops; +public: + + volatile T Value; + + inline AtomicValueBase() { } + explicit inline AtomicValueBase(T val) { Ops::Store_Release(&Value, val); } + + // Most libraries (TBB and Joshua Scholar's) library do not do Load_Acquire + // here, since most algorithms do not require atomic loads. Needs some research. + inline operator T() const { return Value; } + + // *** Standard Atomic inlines + inline T Exchange_Sync(T val) { return Ops::Exchange_Sync(&Value, val); } + inline T Exchange_Release(T val) { return Ops::Exchange_Release(&Value, val); } + inline T Exchange_Acquire(T val) { return Ops::Exchange_Acquire(&Value, val); } + inline T Exchange_NoSync(T val) { return Ops::Exchange_NoSync(&Value, val); } + inline bool CompareAndSet_Sync(T c, T val) { return Ops::CompareAndSet_Sync(&Value, c, val); } + inline bool CompareAndSet_Release(T c, T val) { return Ops::CompareAndSet_Release(&Value, c, val); } + inline bool CompareAndSet_Acquire(T c, T val) { return Ops::CompareAndSet_Relse(&Value, c, val); } + inline bool CompareAndSet_NoSync(T c, T val) { return Ops::CompareAndSet_NoSync(&Value, c, val); } + // Load & Store. + inline void Store_Release(T val) { Ops::Store_Release(&Value, val); } + inline T Load_Acquire() const { return Ops::Load_Acquire(&Value); } +}; + + +// ***** AtomicPtr - Atomic pointer template + +// This pointer class supports atomic assignments with release, +// increment / decrement operations, and conditional compare + set. + +template +class AtomicPtr : public AtomicValueBase +{ + typedef typename AtomicValueBase::Ops Ops; + +public: + // Initialize pointer value to 0 by default; use Store_Release only with explicit constructor. + inline AtomicPtr() : AtomicValueBase() { this->Value = 0; } + explicit inline AtomicPtr(T* val) : AtomicValueBase(val) { } + + // Pointer access. + inline T* operator -> () const { return this->Load_Acquire(); } + + // It looks like it is convenient to have Load_Acquire characteristics + // for this, since that is convenient for algorithms such as linked + // list traversals that can be added to bu another thread. + inline operator T* () const { return this->Load_Acquire(); } + + + // *** Standard Atomic inlines (applicable to pointers) + + // ExhangeAdd considers pointer size for pointers. + template + inline T* ExchangeAdd_Sync(I incr) { return Ops::ExchangeAdd_Sync(&this->Value, ((T*)0) + incr); } + template + inline T* ExchangeAdd_Release(I incr) { return Ops::ExchangeAdd_Release(&this->Value, ((T*)0) + incr); } + template + inline T* ExchangeAdd_Acquire(I incr) { return Ops::ExchangeAdd_Acquire(&this->Value, ((T*)0) + incr); } + template + inline T* ExchangeAdd_NoSync(I incr) { return Ops::ExchangeAdd_NoSync(&this->Value, ((T*)0) + incr); } + + // *** Atomic Operators + + inline T* operator = (T* val) { this->Store_Release(val); return val; } + + template + inline T* operator += (I val) { return ExchangeAdd_Sync(val) + val; } + template + inline T* operator -= (I val) { return operator += (-val); } + + inline T* operator ++ () { return ExchangeAdd_Sync(1) + 1; } + inline T* operator -- () { return ExchangeAdd_Sync(-1) - 1; } + inline T* operator ++ (int) { return ExchangeAdd_Sync(1); } + inline T* operator -- (int) { return ExchangeAdd_Sync(-1); } +}; + + +// ***** AtomicInt - Atomic integer template + +// Implements an atomic integer type; the exact type to use is provided +// as an argument. Supports atomic Acquire / Release semantics, atomic +// arithmetic operations, and atomic conditional compare + set. + +template +class AtomicInt : public AtomicValueBase +{ + typedef typename AtomicValueBase::Ops Ops; + +public: + inline AtomicInt() : AtomicValueBase() { } + explicit inline AtomicInt(T val) : AtomicValueBase(val) { } + + + // *** Standard Atomic inlines (applicable to int) + inline T ExchangeAdd_Sync(T val) { return Ops::ExchangeAdd_Sync(&this->Value, val); } + inline T ExchangeAdd_Release(T val) { return Ops::ExchangeAdd_Release(&this->Value, val); } + inline T ExchangeAdd_Acquire(T val) { return Ops::ExchangeAdd_Acquire(&this->Value, val); } + inline T ExchangeAdd_NoSync(T val) { return Ops::ExchangeAdd_NoSync(&this->Value, val); } + // These increments could be more efficient because they don't return a value. + inline void Increment_Sync() { ExchangeAdd_Sync((T)1); } + inline void Increment_Release() { ExchangeAdd_Release((T)1); } + inline void Increment_Acquire() { ExchangeAdd_Acquire((T)1); } + inline void Increment_NoSync() { ExchangeAdd_NoSync((T)1); } + + // *** Atomic Operators + + inline T operator = (T val) { this->Store_Release(val); return val; } + inline T operator += (T val) { return ExchangeAdd_Sync(val) + val; } + inline T operator -= (T val) { return ExchangeAdd_Sync(0 - val) - val; } + + inline T operator ++ () { return ExchangeAdd_Sync((T)1) + 1; } + inline T operator -- () { return ExchangeAdd_Sync(((T)0)-1) - 1; } + inline T operator ++ (int) { return ExchangeAdd_Sync((T)1); } + inline T operator -- (int) { return ExchangeAdd_Sync(((T)0)-1); } + + // More complex atomic operations. Leave it to compiler whether to optimize them or not. + T operator &= (T arg) + { + T comp, newVal; + do { + comp = this->Value; + newVal = comp & arg; + } while(!this->CompareAndSet_Sync(comp, newVal)); + return newVal; + } + + T operator |= (T arg) + { + T comp, newVal; + do { + comp = this->Value; + newVal = comp | arg; + } while(!this->CompareAndSet_Sync(comp, newVal)); + return newVal; + } + + T operator ^= (T arg) + { + T comp, newVal; + do { + comp = this->Value; + newVal = comp ^ arg; + } while(!this->CompareAndSet_Sync(comp, newVal)); + return newVal; + } + + T operator *= (T arg) + { + T comp, newVal; + do { + comp = this->Value; + newVal = comp * arg; + } while(!this->CompareAndSet_Sync(comp, newVal)); + return newVal; + } + + T operator /= (T arg) + { + T comp, newVal; + do { + comp = this->Value; + newVal = comp / arg; + } while(!CompareAndSet_Sync(comp, newVal)); + return newVal; + } + + T operator >>= (unsigned bits) + { + T comp, newVal; + do { + comp = this->Value; + newVal = comp >> bits; + } while(!CompareAndSet_Sync(comp, newVal)); + return newVal; + } + + T operator <<= (unsigned bits) + { + T comp, newVal; + do { + comp = this->Value; + newVal = comp << bits; + } while(!this->CompareAndSet_Sync(comp, newVal)); + return newVal; + } +}; + + + +//----------------------------------------------------------------------------------- +// ***** Lock + +// Lock is a simplest and most efficient mutual-exclusion lock class. +// Unlike Mutex, it cannot be waited on. + +class Lock +{ + // NOTE: Locks are not allocatable and they themselves should not allocate + // memory by standard means. This is the case because StandardAllocator + // relies on this class. + // Make 'delete' private. Don't do this for 'new' since it can be redefined. + void operator delete(void*) {} + + + // *** Lock implementation for various platforms. + +#if !defined(OVR_ENABLE_THREADS) + +public: + // With no thread support, lock does nothing. + inline Lock() { } + inline Lock(unsigned) { } + inline ~Lock() { } + inline void DoLock() { } + inline void Unlock() { } + + // Windows. +#elif defined(OVR_OS_WIN32) + + CRITICAL_SECTION cs; +public: + Lock(unsigned spinCount = 0); + ~Lock(); + // Locking functions. + inline void DoLock() { ::EnterCriticalSection(&cs); } + inline void Unlock() { ::LeaveCriticalSection(&cs); } + +#else + pthread_mutex_t mutex; + +public: + static pthread_mutexattr_t RecursiveAttr; + static bool RecursiveAttrInit; + + Lock (unsigned dummy = 0) + { + if (!RecursiveAttrInit) + { + pthread_mutexattr_init(&RecursiveAttr); + pthread_mutexattr_settype(&RecursiveAttr, PTHREAD_MUTEX_RECURSIVE); + RecursiveAttrInit = 1; + } + pthread_mutex_init(&mutex,&RecursiveAttr); + } + ~Lock () { pthread_mutex_destroy(&mutex); } + inline void DoLock() { pthread_mutex_lock(&mutex); } + inline void Unlock() { pthread_mutex_unlock(&mutex); } + +#endif // OVR_ENABLE_THREDS + + +public: + // Locker class, used for automatic locking + class Locker + { + public: + Lock *pLock; + inline Locker(Lock *plock) + { pLock = plock; pLock->DoLock(); } + inline ~Locker() + { pLock->Unlock(); } + }; +}; + + + +} // OVR + +#endif diff --git a/LibOVR/Src/Kernel/OVR_Color.h b/LibOVR/Src/Kernel/OVR_Color.h new file mode 100644 index 0000000..7980d57 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Color.h @@ -0,0 +1,55 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Color.h +Content : Contains color struct. +Created : February 7, 2013 +Notes : + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ +#ifndef OVR_Color_h +#define OVR_Color_h + +#include "OVR_Types.h" + +namespace OVR { + +struct Color +{ + UByte R,G,B,A; + + Color() {} + + // Constructs color by channel. Alpha is set to 0xFF (fully visible) + // if not specified. + Color(unsigned char r,unsigned char g,unsigned char b, unsigned char a = 0xFF) + : R(r), G(g), B(b), A(a) { } + + // 0xAARRGGBB - Common HTML color Hex layout + Color(unsigned c) + : R((unsigned char)(c>>16)), G((unsigned char)(c>>8)), + B((unsigned char)c), A((unsigned char)(c>>24)) { } + + bool operator==(const Color& b) const + { + return R == b.R && G == b.G && B == b.B && A == b.A; + } + + void GetRGBA(float *r, float *g, float *b, float* a) const + { + *r = R / 255.0f; + *g = G / 255.0f; + *b = B / 255.0f; + *a = A / 255.0f; + } +}; + +} + +#endif diff --git a/LibOVR/Src/Kernel/OVR_ContainerAllocator.h b/LibOVR/Src/Kernel/OVR_ContainerAllocator.h new file mode 100644 index 0000000..d2adf49 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_ContainerAllocator.h @@ -0,0 +1,256 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_ContainerAllocator.h +Content : Template allocators and constructors for containers. +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_ContainerAllocator_h +#define OVR_ContainerAllocator_h + +#include "OVR_Allocator.h" +#include + + +namespace OVR { + + +//----------------------------------------------------------------------------------- +// ***** Container Allocator + +// ContainerAllocator serves as a template argument for allocations done by +// containers, such as Array and Hash; replacing it could allow allocator +// substitution in containers. + +class ContainerAllocatorBase +{ +public: + static void* Alloc(UPInt size) { return OVR_ALLOC(size); } + static void* Realloc(void* p, UPInt newSize) { return OVR_REALLOC(p, newSize); } + static void Free(void *p) { OVR_FREE(p); } +}; + + + +//----------------------------------------------------------------------------------- +// ***** Constructors, Destructors, Copiers + +// Plain Old Data - movable, no special constructors/destructor. +template +class ConstructorPOD +{ +public: + static void Construct(void *) {} + static void Construct(void *p, const T& source) + { + *(T*)p = source; + } + + // Same as above, but allows for a different type of constructor. + template + static void ConstructAlt(void *p, const S& source) + { + *(T*)p = source; + } + + static void ConstructArray(void*, UPInt) {} + + static void ConstructArray(void* p, UPInt count, const T& source) + { + UByte *pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + *(T*)pdata = source; + } + + static void ConstructArray(void* p, UPInt count, const T* psource) + { + memcpy(p, psource, sizeof(T) * count); + } + + static void Destruct(T*) {} + static void DestructArray(T*, UPInt) {} + + static void CopyArrayForward(T* dst, const T* src, UPInt count) + { + memmove(dst, src, count * sizeof(T)); + } + + static void CopyArrayBackward(T* dst, const T* src, UPInt count) + { + memmove(dst, src, count * sizeof(T)); + } + + static bool IsMovable() { return true; } +}; + + +//----------------------------------------------------------------------------------- +// ***** ConstructorMov +// +// Correct C++ construction and destruction for movable objects +template +class ConstructorMov +{ +public: + static void Construct(void* p) + { + OVR::Construct(p); + } + + static void Construct(void* p, const T& source) + { + OVR::Construct(p, source); + } + + // Same as above, but allows for a different type of constructor. + template + static void ConstructAlt(void* p, const S& source) + { + OVR::ConstructAlt(p, source); + } + + static void ConstructArray(void* p, UPInt count) + { + UByte* pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + Construct(pdata); + } + + static void ConstructArray(void* p, UPInt count, const T& source) + { + UByte* pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + Construct(pdata, source); + } + + static void ConstructArray(void* p, UPInt count, const T* psource) + { + UByte* pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + Construct(pdata, *psource++); + } + + static void Destruct(T* p) + { + p->~T(); + OVR_UNUSED(p); // Suppress silly MSVC warning + } + + static void DestructArray(T* p, UPInt count) + { + p += count - 1; + for (UPInt i=0; i~T(); + } + + static void CopyArrayForward(T* dst, const T* src, UPInt count) + { + memmove(dst, src, count * sizeof(T)); + } + + static void CopyArrayBackward(T* dst, const T* src, UPInt count) + { + memmove(dst, src, count * sizeof(T)); + } + + static bool IsMovable() { return true; } +}; + + +//----------------------------------------------------------------------------------- +// ***** ConstructorCPP +// +// Correct C++ construction and destruction for movable objects +template +class ConstructorCPP +{ +public: + static void Construct(void* p) + { + OVR::Construct(p); + } + + static void Construct(void* p, const T& source) + { + OVR::Construct(p, source); + } + + // Same as above, but allows for a different type of constructor. + template + static void ConstructAlt(void* p, const S& source) + { + OVR::ConstructAlt(p, source); + } + + static void ConstructArray(void* p, UPInt count) + { + UByte* pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + Construct(pdata); + } + + static void ConstructArray(void* p, UPInt count, const T& source) + { + UByte* pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + Construct(pdata, source); + } + + static void ConstructArray(void* p, UPInt count, const T* psource) + { + UByte* pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + Construct(pdata, *psource++); + } + + static void Destruct(T* p) + { + p->~T(); + OVR_UNUSED(p); // Suppress silly MSVC warning + } + + static void DestructArray(T* p, UPInt count) + { + p += count - 1; + for (UPInt i=0; i~T(); + } + + static void CopyArrayForward(T* dst, const T* src, UPInt count) + { + for(UPInt i = 0; i < count; ++i) + dst[i] = src[i]; + } + + static void CopyArrayBackward(T* dst, const T* src, UPInt count) + { + for(UPInt i = count; i; --i) + dst[i-1] = src[i-1]; + } + + static bool IsMovable() { return false; } +}; + + +//----------------------------------------------------------------------------------- +// ***** Container Allocator with movement policy +// +// Simple wraps as specialized allocators +template struct ContainerAllocator_POD : ContainerAllocatorBase, ConstructorPOD {}; +template struct ContainerAllocator : ContainerAllocatorBase, ConstructorMov {}; +template struct ContainerAllocator_CPP : ContainerAllocatorBase, ConstructorCPP {}; + + +} // OVR + + +#endif diff --git a/LibOVR/Src/Kernel/OVR_File.cpp b/LibOVR/Src/Kernel/OVR_File.cpp new file mode 100644 index 0000000..7620e37 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_File.cpp @@ -0,0 +1,571 @@ +/************************************************************************** + +Filename : OVR_File.cpp +Content : File wrapper class implementation (Win32) + +Created : April 5, 1999 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +**************************************************************************/ + +#define GFILE_CXX + +// Standard C library (Captain Obvious guarantees!) +#include + +#include "OVR_File.h" + +namespace OVR { + +// Buffered file adds buffering to an existing file +// FILEBUFFER_SIZE defines the size of internal buffer, while +// FILEBUFFER_TOLERANCE controls the amount of data we'll effectively try to buffer +#define FILEBUFFER_SIZE (8192-8) +#define FILEBUFFER_TOLERANCE 4096 + +// ** Constructor/Destructor + +// Hidden constructor +// Not supposed to be used +BufferedFile::BufferedFile() : DelegatedFile(0) +{ + pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE); + BufferMode = NoBuffer; + FilePos = 0; + Pos = 0; + DataSize = 0; +} + +// Takes another file as source +BufferedFile::BufferedFile(File *pfile) : DelegatedFile(pfile) +{ + pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE); + BufferMode = NoBuffer; + FilePos = pfile->LTell(); + Pos = 0; + DataSize = 0; +} + + +// Destructor +BufferedFile::~BufferedFile() +{ + // Flush in case there's data + if (pFile) + FlushBuffer(); + // Get rid of buffer + if (pBuffer) + OVR_FREE(pBuffer); +} + +/* +bool BufferedFile::VCopy(const Object &source) +{ + if (!DelegatedFile::VCopy(source)) + return 0; + + // Data members + BufferedFile *psource = (BufferedFile*)&source; + + // Buffer & the mode it's in + pBuffer = psource->pBuffer; + BufferMode = psource->BufferMode; + Pos = psource->Pos; + DataSize = psource->DataSize; + return 1; +} +*/ + +// Initializes buffering to a certain mode +bool BufferedFile::SetBufferMode(BufferModeType mode) +{ + if (!pBuffer) + return false; + if (mode == BufferMode) + return true; + + FlushBuffer(); + + // Can't set write mode if we can't write + if ((mode==WriteBuffer) && (!pFile || !pFile->IsWritable()) ) + return 0; + + // And SetMode + BufferMode = mode; + Pos = 0; + DataSize = 0; + return 1; +} + +// Flushes buffer +void BufferedFile::FlushBuffer() +{ + switch(BufferMode) + { + case WriteBuffer: + // Write data in buffer + FilePos += pFile->Write(pBuffer,Pos); + Pos = 0; + break; + + case ReadBuffer: + // Seek back & reset buffer data + if ((DataSize-Pos)>0) + FilePos = pFile->LSeek(-(int)(DataSize-Pos), Seek_Cur); + DataSize = 0; + Pos = 0; + break; + default: + // not handled! + break; + } +} + +// Reloads data for ReadBuffer +void BufferedFile::LoadBuffer() +{ + if (BufferMode == ReadBuffer) + { + // We should only reload once all of pre-loaded buffer is consumed. + OVR_ASSERT(Pos == DataSize); + + // WARNING: Right now LoadBuffer() assumes the buffer's empty + int sz = pFile->Read(pBuffer,FILEBUFFER_SIZE); + DataSize = sz<0 ? 0 : (unsigned)sz; + Pos = 0; + FilePos += DataSize; + } +} + + +// ** Overridden functions + +// We override all the functions that can possibly +// require buffer mode switch, flush, or extra calculations + +// Tell() requires buffer adjustment +int BufferedFile::Tell() +{ + if (BufferMode == ReadBuffer) + return int (FilePos - DataSize + Pos); + + int pos = pFile->Tell(); + // Adjust position based on buffer mode & data + if (pos!=-1) + { + OVR_ASSERT(BufferMode != ReadBuffer); + if (BufferMode == WriteBuffer) + pos += Pos; + } + return pos; +} + +SInt64 BufferedFile::LTell() +{ + if (BufferMode == ReadBuffer) + return FilePos - DataSize + Pos; + + SInt64 pos = pFile->LTell(); + if (pos!=-1) + { + OVR_ASSERT(BufferMode != ReadBuffer); + if (BufferMode == WriteBuffer) + pos += Pos; + } + return pos; +} + +int BufferedFile::GetLength() +{ + int len = pFile->GetLength(); + // If writing through buffer, file length may actually be bigger + if ((len!=-1) && (BufferMode==WriteBuffer)) + { + int currPos = pFile->Tell() + Pos; + if (currPos>len) + len = currPos; + } + return len; +} +SInt64 BufferedFile::LGetLength() +{ + SInt64 len = pFile->LGetLength(); + // If writing through buffer, file length may actually be bigger + if ((len!=-1) && (BufferMode==WriteBuffer)) + { + SInt64 currPos = pFile->LTell() + Pos; + if (currPos>len) + len = currPos; + } + return len; +} + +/* +bool BufferedFile::Stat(FileStats *pfs) +{ + // Have to fix up length is stat + if (pFile->Stat(pfs)) + { + if (BufferMode==WriteBuffer) + { + SInt64 currPos = pFile->LTell() + Pos; + if (currPos > pfs->Size) + { + pfs->Size = currPos; + // ?? + pfs->Blocks = (pfs->Size+511) >> 9; + } + } + return 1; + } + return 0; +} +*/ + +int BufferedFile::Write(const UByte *psourceBuffer, int numBytes) +{ + if ( (BufferMode==WriteBuffer) || SetBufferMode(WriteBuffer)) + { + // If not data space in buffer, flush + if ((FILEBUFFER_SIZE-(int)Pos)FILEBUFFER_TOLERANCE) + { + int sz = pFile->Write(psourceBuffer,numBytes); + if (sz > 0) + FilePos += sz; + return sz; + } + } + + // Enough space in buffer.. so copy to it + memcpy(pBuffer+Pos, psourceBuffer, numBytes); + Pos += numBytes; + return numBytes; + } + int sz = pFile->Write(psourceBuffer,numBytes); + if (sz > 0) + FilePos += sz; + return sz; +} + +int BufferedFile::Read(UByte *pdestBuffer, int numBytes) +{ + if ( (BufferMode==ReadBuffer) || SetBufferMode(ReadBuffer)) + { + // Data in buffer... copy it + if ((int)(DataSize-Pos) >= numBytes) + { + memcpy(pdestBuffer, pBuffer+Pos, numBytes); + Pos += numBytes; + return numBytes; + } + + // Not enough data in buffer, copy buffer + int readBytes = DataSize-Pos; + memcpy(pdestBuffer, pBuffer+Pos, readBytes); + numBytes -= readBytes; + pdestBuffer += readBytes; + Pos = DataSize; + + // Don't reload buffer if more then tolerance + // (No major advantage, and we don't want to write a loop) + if (numBytes>FILEBUFFER_TOLERANCE) + { + numBytes = pFile->Read(pdestBuffer,numBytes); + if (numBytes > 0) + { + FilePos += numBytes; + Pos = DataSize = 0; + } + return readBytes + ((numBytes==-1) ? 0 : numBytes); + } + + // Reload the buffer + // WARNING: Right now LoadBuffer() assumes the buffer's empty + LoadBuffer(); + if ((int)(DataSize-Pos) < numBytes) + numBytes = (int)DataSize-Pos; + + memcpy(pdestBuffer, pBuffer+Pos, numBytes); + Pos += numBytes; + return numBytes + readBytes; + + /* + // Alternative Read implementation. The one above is probably better + // due to FILEBUFFER_TOLERANCE. + int total = 0; + + do { + int bufferBytes = (int)(DataSize-Pos); + int copyBytes = (bufferBytes > numBytes) ? numBytes : bufferBytes; + + memcpy(pdestBuffer, pBuffer+Pos, copyBytes); + numBytes -= copyBytes; + pdestBuffer += copyBytes; + Pos += copyBytes; + total += copyBytes; + + if (numBytes == 0) + break; + LoadBuffer(); + + } while (DataSize > 0); + + return total; + */ + } + int sz = pFile->Read(pdestBuffer,numBytes); + if (sz > 0) + FilePos += sz; + return sz; +} + + +int BufferedFile::SkipBytes(int numBytes) +{ + int skippedBytes = 0; + + // Special case for skipping a little data in read buffer + if (BufferMode==ReadBuffer) + { + skippedBytes = (((int)DataSize-(int)Pos) >= numBytes) ? numBytes : (DataSize-Pos); + Pos += skippedBytes; + numBytes -= skippedBytes; + } + + if (numBytes) + { + numBytes = pFile->SkipBytes(numBytes); + // Make sure we return the actual number skipped, or error + if (numBytes!=-1) + { + skippedBytes += numBytes; + FilePos += numBytes; + Pos = DataSize = 0; + } + else if (skippedBytes <= 0) + skippedBytes = -1; + } + return skippedBytes; +} + +int BufferedFile::BytesAvailable() +{ + int available = pFile->BytesAvailable(); + // Adjust available size based on buffers + switch(BufferMode) + { + case ReadBuffer: + available += DataSize-Pos; + break; + case WriteBuffer: + available -= Pos; + if (available<0) + available= 0; + break; + default: + break; + } + return available; +} + +bool BufferedFile::Flush() +{ + FlushBuffer(); + return pFile->Flush(); +} + +// Seeking could be optimized better.. +int BufferedFile::Seek(int offset, int origin) +{ + if (BufferMode == ReadBuffer) + { + if (origin == Seek_Cur) + { + // Seek can fall either before or after Pos in the buffer, + // but it must be within bounds. + if (((unsigned(offset) + Pos)) <= DataSize) + { + Pos += offset; + return int (FilePos - DataSize + Pos); + } + + // Lightweight buffer "Flush". We do this to avoid an extra seek + // back operation which would take place if we called FlushBuffer directly. + origin = Seek_Set; + OVR_ASSERT(((FilePos - DataSize + Pos) + (UInt64)offset) < ~(UInt64)0); + offset = (int)(FilePos - DataSize + Pos) + offset; + Pos = DataSize = 0; + } + else if (origin == Seek_Set) + { + if (((unsigned)offset - (FilePos-DataSize)) <= DataSize) + { + OVR_ASSERT((FilePos-DataSize) < ~(UInt64)0); + Pos = (unsigned)offset - (unsigned)(FilePos-DataSize); + return offset; + } + Pos = DataSize = 0; + } + else + { + FlushBuffer(); + } + } + else + { + FlushBuffer(); + } + + /* + // Old Seek Logic + if (origin == Seek_Cur && offset + Pos < DataSize) + { + //OVR_ASSERT((FilePos - DataSize) >= (FilePos - DataSize + Pos + offset)); + Pos += offset; + OVR_ASSERT(int (Pos) >= 0); + return int (FilePos - DataSize + Pos); + } + else if (origin == Seek_Set && unsigned(offset) >= FilePos - DataSize && unsigned(offset) < FilePos) + { + Pos = unsigned(offset - FilePos + DataSize); + OVR_ASSERT(int (Pos) >= 0); + return int (FilePos - DataSize + Pos); + } + + FlushBuffer(); + */ + + + FilePos = pFile->Seek(offset,origin); + return int (FilePos); +} + +SInt64 BufferedFile::LSeek(SInt64 offset, int origin) +{ + if (BufferMode == ReadBuffer) + { + if (origin == Seek_Cur) + { + // Seek can fall either before or after Pos in the buffer, + // but it must be within bounds. + if (((unsigned(offset) + Pos)) <= DataSize) + { + Pos += (unsigned)offset; + return SInt64(FilePos - DataSize + Pos); + } + + // Lightweight buffer "Flush". We do this to avoid an extra seek + // back operation which would take place if we called FlushBuffer directly. + origin = Seek_Set; + offset = (SInt64)(FilePos - DataSize + Pos) + offset; + Pos = DataSize = 0; + } + else if (origin == Seek_Set) + { + if (((UInt64)offset - (FilePos-DataSize)) <= DataSize) + { + Pos = (unsigned)((UInt64)offset - (FilePos-DataSize)); + return offset; + } + Pos = DataSize = 0; + } + else + { + FlushBuffer(); + } + } + else + { + FlushBuffer(); + } + +/* + OVR_ASSERT(BufferMode != NoBuffer); + + if (origin == Seek_Cur && offset + Pos < DataSize) + { + Pos += int (offset); + return FilePos - DataSize + Pos; + } + else if (origin == Seek_Set && offset >= SInt64(FilePos - DataSize) && offset < SInt64(FilePos)) + { + Pos = unsigned(offset - FilePos + DataSize); + return FilePos - DataSize + Pos; + } + + FlushBuffer(); + */ + + FilePos = pFile->LSeek(offset,origin); + return FilePos; +} + +int BufferedFile::CopyFromStream(File *pstream, int byteSize) +{ + // We can't rely on overridden Write() + // because delegation doesn't override virtual pointers + // So, just re-implement + UByte buff[0x4000]; + int count = 0; + int szRequest, szRead, szWritten; + + while(byteSize) + { + szRequest = (byteSize > int(sizeof(buff))) ? int(sizeof(buff)) : byteSize; + + szRead = pstream->Read(buff,szRequest); + szWritten = 0; + if (szRead > 0) + szWritten = Write(buff,szRead); + + count +=szWritten; + byteSize-=szWritten; + if (szWritten < szRequest) + break; + } + return count; +} + +// Closing files +bool BufferedFile::Close() +{ + switch(BufferMode) + { + case WriteBuffer: + FlushBuffer(); + break; + case ReadBuffer: + // No need to seek back on close + BufferMode = NoBuffer; + break; + default: + break; + } + return pFile->Close(); +} + + +// ***** Global path helpers + +// Find trailing short filename in a path. +const char* OVR_CDECL GetShortFilename(const char* purl) +{ + UPInt len = OVR_strlen(purl); + for (UPInt i=len; i>0; i--) + if (purl[i]=='\\' || purl[i]=='/') + return purl+i+1; + return purl; +} + +} // OVR + diff --git a/LibOVR/Src/Kernel/OVR_File.h b/LibOVR/Src/Kernel/OVR_File.h new file mode 100644 index 0000000..262f5cf --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_File.h @@ -0,0 +1,518 @@ +/************************************************************************************ + +PublicHeader: Kernel +Filename : OVR_File.h +Content : Header for all internal file management - functions and structures + to be inherited by OS specific subclasses. +Created : September 19, 2012 +Notes : + +Notes : errno may not be preserved across use of BaseFile member functions + : Directories cannot be deleted while files opened from them are in use + (For the GetFullName function) + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_File_h +#define OVR_File_h + +#include "OVR_RefCount.h" +#include "OVR_Std.h" +#include "OVR_Alg.h" + +#include +#include "OVR_String.h" + +namespace OVR { + +// ***** Declared classes +class FileConstants; +class File; +class DelegatedFile; +class BufferedFile; + + +// ***** Flags for File & Directory accesses + +class FileConstants +{ +public: + + // *** File open flags + enum OpenFlags + { + Open_Read = 1, + Open_Write = 2, + Open_ReadWrite = 3, + + // Opens file and truncates it to zero length + // - file must have write permission + // - when used with Create, it opens an existing + // file and empties it or creates a new file + Open_Truncate = 4, + + // Creates and opens new file + // - does not erase contents if file already + // exists unless combined with Truncate + Open_Create = 8, + + // Returns an error value if the file already exists + Open_CreateOnly = 24, + + // Open file with buffering + Open_Buffered = 32 + }; + + // *** File Mode flags + enum Modes + { + Mode_Read = 0444, + Mode_Write = 0222, + Mode_Execute = 0111, + + Mode_ReadWrite = 0666 + }; + + // *** Seek operations + enum SeekOps + { + Seek_Set = 0, + Seek_Cur = 1, + Seek_End = 2 + }; + + // *** Errors + enum Errors + { + Error_FileNotFound = 0x1001, + Error_Access = 0x1002, + Error_IOError = 0x1003, + Error_DiskFull = 0x1004 + }; +}; + + +//----------------------------------------------------------------------------------- +// ***** File Class + +// The pure virtual base random-access file +// This is a base class to all files + +class File : public RefCountBase, public FileConstants +{ +public: + File() { } + // ** Location Information + + // Returns a file name path relative to the 'reference' directory + // This is often a path that was used to create a file + // (this is not a global path, global path can be obtained with help of directory) + virtual const char* GetFilePath() = 0; + + + // ** File Information + + // Return 1 if file's usable (open) + virtual bool IsValid() = 0; + // Return 1 if file's writable, otherwise 0 + virtual bool IsWritable() = 0; + + // Return position + virtual int Tell() = 0; + virtual SInt64 LTell() = 0; + + // File size + virtual int GetLength() = 0; + virtual SInt64 LGetLength() = 0; + + // Returns file stats + // 0 for failure + //virtual bool Stat(FileStats *pfs) = 0; + + // Return errno-based error code + // Useful if any other function failed + virtual int GetErrorCode() = 0; + + + // ** Stream implementation & I/O + + // Blocking write, will write in the given number of bytes to the stream + // Returns : -1 for error + // Otherwise number of bytes read + virtual int Write(const UByte *pbufer, int numBytes) = 0; + // Blocking read, will read in the given number of bytes or less from the stream + // Returns : -1 for error + // Otherwise number of bytes read, + // if 0 or < numBytes, no more bytes available; end of file or the other side of stream is closed + virtual int Read(UByte *pbufer, int numBytes) = 0; + + // Skips (ignores) a given # of bytes + // Same return values as Read + virtual int SkipBytes(int numBytes) = 0; + + // Returns the number of bytes available to read from a stream without blocking + // For a file, this should generally be number of bytes to the end + virtual int BytesAvailable() = 0; + + // Causes any implementation's buffered data to be delivered to destination + // Return 0 for error + virtual bool Flush() = 0; + + + // Need to provide a more optimized implementation that doe snot necessarily involve a lot of seeking + inline bool IsEOF() { return !BytesAvailable(); } + + + // Seeking + // Returns new position, -1 for error + virtual int Seek(int offset, int origin=Seek_Set) = 0; + virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set) = 0; + // Seek simplification + int SeekToBegin() {return Seek(0); } + int SeekToEnd() {return Seek(0,Seek_End); } + int Skip(int numBytes) {return Seek(numBytes,Seek_Cur); } + + + // Appends other file data from a stream + // Return -1 for error, else # of bytes written + virtual int CopyFromStream(File *pstream, int byteSize) = 0; + + // Closes the file + // After close, file cannot be accessed + virtual bool Close() = 0; + + + // ***** Inlines for convenient primitive type serialization + + // Read/Write helpers +private: + UInt64 PRead64() { UInt64 v = 0; Read((UByte*)&v, 8); return v; } + UInt32 PRead32() { UInt32 v = 0; Read((UByte*)&v, 4); return v; } + UInt16 PRead16() { UInt16 v = 0; Read((UByte*)&v, 2); return v; } + UByte PRead8() { UByte v = 0; Read((UByte*)&v, 1); return v; } + void PWrite64(UInt64 v) { Write((UByte*)&v, 8); } + void PWrite32(UInt32 v) { Write((UByte*)&v, 4); } + void PWrite16(UInt16 v) { Write((UByte*)&v, 2); } + void PWrite8(UByte v) { Write((UByte*)&v, 1); } + +public: + + // Writing primitive types - Little Endian + inline void WriteUByte(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteSByte(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteUInt8(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteSInt8(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteUInt16(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteSInt16(SInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteUInt32(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteSInt32(SInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteUInt64(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteSInt64(SInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteFloat(float v) { v = Alg::ByteUtil::SystemToLE(v); Write((UByte*)&v, 4); } + inline void WriteDouble(double v) { v = Alg::ByteUtil::SystemToLE(v); Write((UByte*)&v, 8); } + // Writing primitive types - Big Endian + inline void WriteUByteBE(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteSByteBE(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteUInt8BE(UInt16 v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteSInt8BE(SInt16 v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteUInt16BE(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteSInt16BE(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteUInt32BE(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteSInt32BE(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteUInt64BE(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteSInt64BE(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteFloatBE(float v) { v = Alg::ByteUtil::SystemToBE(v); Write((UByte*)&v, 4); } + inline void WriteDoubleBE(double v) { v = Alg::ByteUtil::SystemToBE(v); Write((UByte*)&v, 8); } + + // Reading primitive types - Little Endian + inline UByte ReadUByte() { return (UByte)Alg::ByteUtil::LEToSystem(PRead8()); } + inline SByte ReadSByte() { return (SByte)Alg::ByteUtil::LEToSystem(PRead8()); } + inline UByte ReadUInt8() { return (UByte)Alg::ByteUtil::LEToSystem(PRead8()); } + inline SByte ReadSInt8() { return (SByte)Alg::ByteUtil::LEToSystem(PRead8()); } + inline UInt16 ReadUInt16() { return (UInt16)Alg::ByteUtil::LEToSystem(PRead16()); } + inline SInt16 ReadSInt16() { return (SInt16)Alg::ByteUtil::LEToSystem(PRead16()); } + inline UInt32 ReadUInt32() { return (UInt32)Alg::ByteUtil::LEToSystem(PRead32()); } + inline SInt32 ReadSInt32() { return (SInt32)Alg::ByteUtil::LEToSystem(PRead32()); } + inline UInt64 ReadUInt64() { return (UInt64)Alg::ByteUtil::LEToSystem(PRead64()); } + inline SInt64 ReadSInt64() { return (SInt64)Alg::ByteUtil::LEToSystem(PRead64()); } + inline float ReadFloat() { float v = 0.0f; Read((UByte*)&v, 4); return Alg::ByteUtil::LEToSystem(v); } + inline double ReadDouble() { double v = 0.0; Read((UByte*)&v, 8); return Alg::ByteUtil::LEToSystem(v); } + // Reading primitive types - Big Endian + inline UByte ReadUByteBE() { return (UByte)Alg::ByteUtil::BEToSystem(PRead8()); } + inline SByte ReadSByteBE() { return (SByte)Alg::ByteUtil::BEToSystem(PRead8()); } + inline UByte ReadUInt8BE() { return (UByte)Alg::ByteUtil::BEToSystem(PRead8()); } + inline SByte ReadSInt8BE() { return (SByte)Alg::ByteUtil::BEToSystem(PRead8()); } + inline UInt16 ReadUInt16BE() { return (UInt16)Alg::ByteUtil::BEToSystem(PRead16()); } + inline SInt16 ReadSInt16BE() { return (SInt16)Alg::ByteUtil::BEToSystem(PRead16()); } + inline UInt32 ReadUInt32BE() { return (UInt32)Alg::ByteUtil::BEToSystem(PRead32()); } + inline SInt32 ReadSInt32BE() { return (SInt32)Alg::ByteUtil::BEToSystem(PRead32()); } + inline UInt64 ReadUInt64BE() { return (UInt64)Alg::ByteUtil::BEToSystem(PRead64()); } + inline SInt64 ReadSInt64BE() { return (SInt64)Alg::ByteUtil::BEToSystem(PRead64()); } + inline float ReadFloatBE() { float v = 0.0f; Read((UByte*)&v, 4); return Alg::ByteUtil::BEToSystem(v); } + inline double ReadDoubleBE() { double v = 0.0; Read((UByte*)&v, 8); return Alg::ByteUtil::BEToSystem(v); } +}; + + +// *** Delegated File + +class DelegatedFile : public File +{ +protected: + // Delegating file pointer + Ptr pFile; + + // Hidden default constructor + DelegatedFile() : pFile(0) { } + DelegatedFile(const DelegatedFile &source) : File() { OVR_UNUSED(source); } +public: + // Constructors + DelegatedFile(File *pfile) : pFile(pfile) { } + + // ** Location Information + virtual const char* GetFilePath() { return pFile->GetFilePath(); } + + // ** File Information + virtual bool IsValid() { return pFile && pFile->IsValid(); } + virtual bool IsWritable() { return pFile->IsWritable(); } +// virtual bool IsRecoverable() { return pFile->IsRecoverable(); } + + virtual int Tell() { return pFile->Tell(); } + virtual SInt64 LTell() { return pFile->LTell(); } + + virtual int GetLength() { return pFile->GetLength(); } + virtual SInt64 LGetLength() { return pFile->LGetLength(); } + + //virtual bool Stat(FileStats *pfs) { return pFile->Stat(pfs); } + + virtual int GetErrorCode() { return pFile->GetErrorCode(); } + + // ** Stream implementation & I/O + virtual int Write(const UByte *pbuffer, int numBytes) { return pFile->Write(pbuffer,numBytes); } + virtual int Read(UByte *pbuffer, int numBytes) { return pFile->Read(pbuffer,numBytes); } + + virtual int SkipBytes(int numBytes) { return pFile->SkipBytes(numBytes); } + + virtual int BytesAvailable() { return pFile->BytesAvailable(); } + + virtual bool Flush() { return pFile->Flush(); } + + // Seeking + virtual int Seek(int offset, int origin=Seek_Set) { return pFile->Seek(offset,origin); } + virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set) { return pFile->LSeek(offset,origin); } + + virtual int CopyFromStream(File *pstream, int byteSize) { return pFile->CopyFromStream(pstream,byteSize); } + + // Closing the file + virtual bool Close() { return pFile->Close(); } +}; + + +//----------------------------------------------------------------------------------- +// ***** Buffered File + +// This file class adds buffering to an existing file +// Buffered file never fails by itself; if there's not +// enough memory for buffer, no buffer's used + +class BufferedFile : public DelegatedFile +{ +protected: + enum BufferModeType + { + NoBuffer, + ReadBuffer, + WriteBuffer + }; + + // Buffer & the mode it's in + UByte* pBuffer; + BufferModeType BufferMode; + // Position in buffer + unsigned Pos; + // Data in buffer if reading + unsigned DataSize; + // Underlying file position + UInt64 FilePos; + + // Initializes buffering to a certain mode + bool SetBufferMode(BufferModeType mode); + // Flushes buffer + // WriteBuffer - write data to disk, ReadBuffer - reset buffer & fix file position + void FlushBuffer(); + // Loads data into ReadBuffer + // WARNING: Right now LoadBuffer() assumes the buffer's empty + void LoadBuffer(); + + // Hidden constructor + BufferedFile(); + inline BufferedFile(const BufferedFile &source) : DelegatedFile() { OVR_UNUSED(source); } +public: + + // Constructor + // - takes another file as source + BufferedFile(File *pfile); + ~BufferedFile(); + + + // ** Overridden functions + + // We override all the functions that can possibly + // require buffer mode switch, flush, or extra calculations + virtual int Tell(); + virtual SInt64 LTell(); + + virtual int GetLength(); + virtual SInt64 LGetLength(); + +// virtual bool Stat(GFileStats *pfs); + + virtual int Write(const UByte *pbufer, int numBytes); + virtual int Read(UByte *pbufer, int numBytes); + + virtual int SkipBytes(int numBytes); + + virtual int BytesAvailable(); + + virtual bool Flush(); + + virtual int Seek(int offset, int origin=Seek_Set); + virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set); + + virtual int CopyFromStream(File *pstream, int byteSize); + + virtual bool Close(); +}; + + +//----------------------------------------------------------------------------------- +// ***** Memory File + +class MemoryFile : public File +{ +public: + + const char* GetFilePath() { return FilePath.ToCStr(); } + + bool IsValid() { return Valid; } + bool IsWritable() { return false; } + + bool Flush() { return true; } + int GetErrorCode() { return 0; } + + int Tell() { return FileIndex; } + SInt64 LTell() { return (SInt64) FileIndex; } + + int GetLength() { return FileSize; } + SInt64 LGetLength() { return (SInt64) FileSize; } + + bool Close() + { + Valid = false; + return false; + } + + int CopyFromStream(File *pstream, int byteSize) + { OVR_UNUSED2(pstream, byteSize); + return 0; + } + + int Write(const UByte *pbuffer, int numBytes) + { OVR_UNUSED2(pbuffer, numBytes); + return 0; + } + + int Read(UByte *pbufer, int numBytes) + { + if (FileIndex + numBytes > FileSize) + { + numBytes = FileSize - FileIndex; + } + + if (numBytes > 0) + { + ::memcpy (pbufer, &FileData [FileIndex], numBytes); + + FileIndex += numBytes; + } + + return numBytes; + } + + int SkipBytes(int numBytes) + { + if (FileIndex + numBytes > FileSize) + { + numBytes = FileSize - FileIndex; + } + + FileIndex += numBytes; + + return numBytes; + } + + int BytesAvailable() + { + return (FileSize - FileIndex); + } + + int Seek(int offset, int origin = Seek_Set) + { + switch (origin) + { + case Seek_Set : FileIndex = offset; break; + case Seek_Cur : FileIndex += offset; break; + case Seek_End : FileIndex = FileSize - offset; break; + } + + return FileIndex; + } + + SInt64 LSeek(SInt64 offset, int origin = Seek_Set) + { + return (SInt64) Seek((int) offset, origin); + } + +public: + + MemoryFile (const String& fileName, const UByte *pBuffer, int buffSize) + : FilePath(fileName) + { + FileData = pBuffer; + FileSize = buffSize; + FileIndex = 0; + Valid = (!fileName.IsEmpty() && pBuffer && buffSize > 0) ? true : false; + } + + // pfileName should be encoded as UTF-8 to support international file names. + MemoryFile (const char* pfileName, const UByte *pBuffer, int buffSize) + : FilePath(pfileName) + { + FileData = pBuffer; + FileSize = buffSize; + FileIndex = 0; + Valid = (pfileName && pBuffer && buffSize > 0) ? true : false; + } +private: + + String FilePath; + const UByte *FileData; + int FileSize; + int FileIndex; + bool Valid; +}; + + +// ***** Global path helpers + +// Find trailing short filename in a path. +const char* OVR_CDECL GetShortFilename(const char* purl); + +} // OVR + +#endif diff --git a/LibOVR/Src/Kernel/OVR_FileFILE.cpp b/LibOVR/Src/Kernel/OVR_FileFILE.cpp new file mode 100644 index 0000000..4ee32b6 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_FileFILE.cpp @@ -0,0 +1,583 @@ +/************************************************************************** + +Filename : OVR_FileFILE.cpp +Content : File wrapper class implementation (Win32) + +Created : April 5, 1999 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +**************************************************************************/ + +#define GFILE_CXX + +#include "OVR_Types.h" +#include "OVR_Log.h" + +// Standard C library (Captain Obvious guarantees!) +#include +#ifndef OVR_OS_WINCE +#include +#endif + +#include "OVR_SysFile.h" + +#ifndef OVR_OS_WINCE +#include +#endif + +namespace OVR { + +// ***** File interface + +// ***** FILEFile - C streams file + +static int SFerror () +{ + if (errno == ENOENT) + return FileConstants::Error_FileNotFound; + else if (errno == EACCES || errno == EPERM) + return FileConstants::Error_Access; + else if (errno == ENOSPC) + return FileConstants::Error_DiskFull; + else + return FileConstants::Error_IOError; +}; + +#ifdef OVR_OS_WIN32 +#include "windows.h" +// A simple helper class to disable/enable system error mode, if necessary +// Disabling happens conditionally only if a drive name is involved +class SysErrorModeDisabler +{ + BOOL Disabled; + UINT OldMode; +public: + SysErrorModeDisabler(const char* pfileName) + { + if (pfileName && (pfileName[0]!=0) && pfileName[1]==':') + { + Disabled = 1; + OldMode = ::SetErrorMode(SEM_FAILCRITICALERRORS); + } + else + Disabled = 0; + } + + ~SysErrorModeDisabler() + { + if (Disabled) ::SetErrorMode(OldMode); + } +}; +#else +class SysErrorModeDisabler +{ +public: + SysErrorModeDisabler(const char* pfileName) { } +}; +#endif // OVR_OS_WIN32 + + +// This macro enables verification of I/O results after seeks against a pre-loaded +// full file buffer copy. This is generally not necessary, but can been used to debug +// memory corruptions; we've seen this fail due to EAX2/DirectSound corrupting memory +// under FMOD with XP64 (32-bit) and Realtek HA Audio driver. +//#define GFILE_VERIFY_SEEK_ERRORS + + +// This is the simplest possible file implementation, it wraps around the descriptor +// This file is delegated to by SysFile. + +class FILEFile : public File +{ +protected: + + // Allocated filename + String FileName; + + // File handle & open mode + bool Opened; + FILE* fs; + int OpenFlags; + // Error code for last request + int ErrorCode; + + int LastOp; + +#ifdef OVR_FILE_VERIFY_SEEK_ERRORS + UByte* pFileTestBuffer; + unsigned FileTestLength; + unsigned TestPos; // File pointer position during tests. +#endif + +public: + + FILEFile() + { + Opened = 0; FileName = ""; + +#ifdef OVR_FILE_VERIFY_SEEK_ERRORS + pFileTestBuffer =0; + FileTestLength =0; + TestPos =0; +#endif + } + // Initialize file by opening it + FILEFile(const String& fileName, int flags, int Mode); + // The 'pfileName' should be encoded as UTF-8 to support international file names. + FILEFile(const char* pfileName, int flags, int Mode); + + ~FILEFile() + { + if (Opened) + Close(); + } + + virtual const char* GetFilePath(); + + // ** File Information + virtual bool IsValid(); + virtual bool IsWritable(); + + // Return position / file size + virtual int Tell(); + virtual SInt64 LTell(); + virtual int GetLength(); + virtual SInt64 LGetLength(); + +// virtual bool Stat(FileStats *pfs); + virtual int GetErrorCode(); + + // ** Stream implementation & I/O + virtual int Write(const UByte *pbuffer, int numBytes); + virtual int Read(UByte *pbuffer, int numBytes); + virtual int SkipBytes(int numBytes); + virtual int BytesAvailable(); + virtual bool Flush(); + virtual int Seek(int offset, int origin); + virtual SInt64 LSeek(SInt64 offset, int origin); + + virtual int CopyFromStream(File *pStream, int byteSize); + virtual bool Close(); +private: + void init(); +}; + + +// Initialize file by opening it +FILEFile::FILEFile(const String& fileName, int flags, int mode) + : FileName(fileName), OpenFlags(flags) +{ + OVR_UNUSED(mode); + init(); +} + +// The 'pfileName' should be encoded as UTF-8 to support international file names. +FILEFile::FILEFile(const char* pfileName, int flags, int mode) + : FileName(pfileName), OpenFlags(flags) +{ + OVR_UNUSED(mode); + init(); +} + +void FILEFile::init() +{ + // Open mode for file's open + const char *omode = "rb"; + + if (OpenFlags & Open_Truncate) + { + if (OpenFlags & Open_Read) + omode = "w+b"; + else + omode = "wb"; + } + else if (OpenFlags & Open_Create) + { + if (OpenFlags & Open_Read) + omode = "a+b"; + else + omode = "ab"; + } + else if (OpenFlags & Open_Write) + omode = "r+b"; + +#ifdef OVR_OS_WIN32 + SysErrorModeDisabler disabler(FileName.ToCStr()); +#endif + +#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) + wchar_t womode[16]; + wchar_t *pwFileName = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(FileName.ToCStr())+1) * sizeof(wchar_t)); + UTF8Util::DecodeString(pwFileName, FileName.ToCStr()); + OVR_ASSERT(strlen(omode) < sizeof(womode)/sizeof(womode[0])); + UTF8Util::DecodeString(womode, omode); + _wfopen_s(&fs, pwFileName, womode); + OVR_FREE(pwFileName); +#else + fs = fopen(FileName.ToCStr(), omode); +#endif + if (fs) + rewind (fs); + Opened = (fs != NULL); + // Set error code + if (!Opened) + ErrorCode = SFerror(); + else + { + // If we are testing file seek correctness, pre-load the entire file so + // that we can do comparison tests later. +#ifdef OVR_FILE_VERIFY_SEEK_ERRORS + TestPos = 0; + fseek(fs, 0, SEEK_END); + FileTestLength = ftell(fs); + fseek(fs, 0, SEEK_SET); + pFileTestBuffer = (UByte*)OVR_ALLOC(FileTestLength); + if (pFileTestBuffer) + { + OVR_ASSERT(FileTestLength == (unsigned)Read(pFileTestBuffer, FileTestLength)); + Seek(0, Seek_Set); + } +#endif + + ErrorCode = 0; + } + LastOp = 0; +} + + +const char* FILEFile::GetFilePath() +{ + return FileName.ToCStr(); +} + + +// ** File Information +bool FILEFile::IsValid() +{ + return Opened; +} +bool FILEFile::IsWritable() +{ + return IsValid() && (OpenFlags&Open_Write); +} +/* +bool FILEFile::IsRecoverable() +{ + return IsValid() && ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC); +} +*/ + +// Return position / file size +int FILEFile::Tell() +{ + int pos = (int)ftell (fs); + if (pos < 0) + ErrorCode = SFerror(); + return pos; +} + +SInt64 FILEFile::LTell() +{ + SInt64 pos = ftell(fs); + if (pos < 0) + ErrorCode = SFerror(); + return pos; +} + +int FILEFile::GetLength() +{ + int pos = Tell(); + if (pos >= 0) + { + Seek (0, Seek_End); + int size = Tell(); + Seek (pos, Seek_Set); + return size; + } + return -1; +} +SInt64 FILEFile::LGetLength() +{ + SInt64 pos = LTell(); + if (pos >= 0) + { + LSeek (0, Seek_End); + SInt64 size = LTell(); + LSeek (pos, Seek_Set); + return size; + } + return -1; +} + +int FILEFile::GetErrorCode() +{ + return ErrorCode; +} + +// ** Stream implementation & I/O +int FILEFile::Write(const UByte *pbuffer, int numBytes) +{ + if (LastOp && LastOp != Open_Write) + fflush(fs); + LastOp = Open_Write; + int written = (int) fwrite(pbuffer, 1, numBytes, fs); + if (written < numBytes) + ErrorCode = SFerror(); + +#ifdef OVR_FILE_VERIFY_SEEK_ERRORS + if (written > 0) + TestPos += written; +#endif + + return written; +} + +int FILEFile::Read(UByte *pbuffer, int numBytes) +{ + if (LastOp && LastOp != Open_Read) + fflush(fs); + LastOp = Open_Read; + int read = (int) fread(pbuffer, 1, numBytes, fs); + if (read < numBytes) + ErrorCode = SFerror(); + +#ifdef OVR_FILE_VERIFY_SEEK_ERRORS + if (read > 0) + { + // Read-in data must match our pre-loaded buffer data! + UByte* pcompareBuffer = pFileTestBuffer + TestPos; + for (int i=0; i< read; i++) + { + OVR_ASSERT(pcompareBuffer[i] == pbuffer[i]); + } + + //OVR_ASSERT(!memcmp(pFileTestBuffer + TestPos, pbuffer, read)); + TestPos += read; + OVR_ASSERT(ftell(fs) == (int)TestPos); + } +#endif + + return read; +} + +// Seeks ahead to skip bytes +int FILEFile::SkipBytes(int numBytes) +{ + SInt64 pos = LTell(); + SInt64 newPos = LSeek(numBytes, Seek_Cur); + + // Return -1 for major error + if ((pos==-1) || (newPos==-1)) + { + return -1; + } + //ErrorCode = ((NewPos-Pos) int(sizeof(buff))) ? int(sizeof(buff)) : byteSize; + + szRead = pstream->Read(buff, szRequest); + szWritten = 0; + if (szRead > 0) + szWritten = Write(buff, szRead); + + count += szWritten; + byteSize -= szWritten; + if (szWritten < szRequest) + break; + } + return count; +} + + +bool FILEFile::Close() +{ +#ifdef OVR_FILE_VERIFY_SEEK_ERRORS + if (pFileTestBuffer) + { + OVR_FREE(pFileTestBuffer); + pFileTestBuffer = 0; + FileTestLength = 0; + } +#endif + + bool closeRet = !fclose(fs); + + if (!closeRet) + { + ErrorCode = SFerror(); + return 0; + } + else + { + Opened = 0; + fs = 0; + ErrorCode = 0; + } + + // Handle safe truncate + /* + if ((OpenFlags & OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC) + { + // Delete original file (if it existed) + DWORD oldAttributes = FileUtilWin32::GetFileAttributes(FileName); + if (oldAttributes!=0xFFFFFFFF) + if (!FileUtilWin32::DeleteFile(FileName)) + { + // Try to remove the readonly attribute + FileUtilWin32::SetFileAttributes(FileName, oldAttributes & (~FILE_ATTRIBUTE_READONLY) ); + // And delete the file again + if (!FileUtilWin32::DeleteFile(FileName)) + return 0; + } + + // Rename temp file to real filename + if (!FileUtilWin32::MoveFile(TempName, FileName)) + { + //ErrorCode = errno; + return 0; + } + } + */ + return 1; +} + +/* +bool FILEFile::CloseCancel() +{ + bool closeRet = (bool)::CloseHandle(fd); + + if (!closeRet) + { + //ErrorCode = errno; + return 0; + } + else + { + Opened = 0; + fd = INVALID_HANDLE_VALUE; + ErrorCode = 0; + } + + // Handle safe truncate (delete tmp file, leave original unchanged) + if ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC) + if (!FileUtilWin32::DeleteFile(TempName)) + { + //ErrorCode = errno; + return 0; + } + return 1; +} +*/ + +File *FileFILEOpen(const String& path, int flags, int mode) +{ + return new FILEFile(path, flags, mode); +} + +// Helper function: obtain file information time. +bool SysFile::GetFileStat(FileStat* pfileStat, const String& path) +{ +#if defined(OVR_OS_WIN32) + // 64-bit implementation on Windows. + struct __stat64 fileStat; + // Stat returns 0 for success. + wchar_t *pwpath = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(path.ToCStr())+1)*sizeof(wchar_t)); + UTF8Util::DecodeString(pwpath, path.ToCStr()); + + int ret = _wstat64(pwpath, &fileStat); + OVR_FREE(pwpath); + if (ret) return false; +#else + struct stat fileStat; + // Stat returns 0 for success. + if (stat(path, &fileStat) != 0) + return false; +#endif + pfileStat->AccessTime = fileStat.st_atime; + pfileStat->ModifyTime = fileStat.st_mtime; + pfileStat->FileSize = fileStat.st_size; + return true; +} + +} // Scaleform diff --git a/LibOVR/Src/Kernel/OVR_Hash.h b/LibOVR/Src/Kernel/OVR_Hash.h new file mode 100644 index 0000000..6667ac2 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Hash.h @@ -0,0 +1,1291 @@ +/************************************************************************************ + +PublicHeader: None +Filename : OVR_Hash.h +Content : Template hash-table/set implementation +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_Hash_h +#define OVR_Hash_h + +#include "OVR_ContainerAllocator.h" +#include "OVR_Alg.h" + +// 'new' operator is redefined/used in this file. +#undef new + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** Hash Table Implementation + +// HastSet and Hash. +// +// Hash table, linear probing, internal chaining. One interesting/nice thing +// about this implementation is that the table itself is a flat chunk of memory +// containing no pointers, only relative indices. If the key and value types +// of the Hash contain no pointers, then the Hash can be serialized using raw IO. +// +// Never shrinks, unless you explicitly Clear() it. Expands on +// demand, though. For best results, if you know roughly how big your +// table will be, default it to that size when you create it. +// +// Key usability feature: +// +// 1. Allows node hash values to either be cached or not. +// +// 2. Allows for alternative keys with methods such as GetAlt(). Handy +// if you need to search nodes by their components; no need to create +// temporary nodes. +// + + +// *** Hash functors: +// +// IdentityHash - use when the key is already a good hash +// HFixedSizeHash - general hash based on object's in-memory representation. + + +// Hash is just the input value; can use this for integer-indexed hash tables. +template +class IdentityHash +{ +public: + UPInt operator()(const C& data) const + { return (UPInt) data; } +}; + +// Computes a hash of an object's representation. +template +class FixedSizeHash +{ +public: + // Alternative: "sdbm" hash function, suggested at same web page + // above, http::/www.cs.yorku.ca/~oz/hash.html + // This is somewhat slower then Bernstein, but it works way better than the above + // hash function for hashing large numbers of 32-bit ints. + static OVR_FORCE_INLINE UPInt SDBM_Hash(const void* data_in, UPInt size, UPInt seed = 5381) + { + const UByte* data = (const UByte*) data_in; + UPInt h = seed; + while (size > 0) + { + size--; + h = (h << 16) + (h << 6) - h + (UPInt)data[size]; + } + return h; + } + + UPInt operator()(const C& data) const + { + unsigned char* p = (unsigned char*) &data; + int size = sizeof(C); + + return SDBM_Hash(p, size); + } +}; + + + +// *** HashsetEntry Entry types. + +// Compact hash table Entry type that re-computes hash keys during hash traversal. +// Good to use if the hash function is cheap or the hash value is already cached in C. +template +class HashsetEntry +{ +public: + // Internal chaining for collisions. + SPInt NextInChain; + C Value; + + HashsetEntry() + : NextInChain(-2) { } + HashsetEntry(const HashsetEntry& e) + : NextInChain(e.NextInChain), Value(e.Value) { } + HashsetEntry(const C& key, SPInt next) + : NextInChain(next), Value(key) { } + + bool IsEmpty() const { return NextInChain == -2; } + bool IsEndOfChain() const { return NextInChain == -1; } + + // Cached hash value access - can be optimized bu storing hash locally. + // Mask value only needs to be used if SetCachedHash is not implemented. + UPInt GetCachedHash(UPInt maskValue) const { return HashF()(Value) & maskValue; } + void SetCachedHash(UPInt) {} + + void Clear() + { + Value.~C(); // placement delete + NextInChain = -2; + } + // Free is only used from dtor of hash; Clear is used during regular operations: + // assignment, hash reallocations, value reassignments, so on. + void Free() { Clear(); } +}; + +// Hash table Entry type that caches the Entry hash value for nodes, so that it +// does not need to be re-computed during access. +template +class HashsetCachedEntry +{ +public: + // Internal chaining for collisions. + SPInt NextInChain; + UPInt HashValue; + C Value; + + HashsetCachedEntry() + : NextInChain(-2) { } + HashsetCachedEntry(const HashsetCachedEntry& e) + : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { } + HashsetCachedEntry(const C& key, SPInt next) + : NextInChain(next), Value(key) { } + + bool IsEmpty() const { return NextInChain == -2; } + bool IsEndOfChain() const { return NextInChain == -1; } + + // Cached hash value access - can be optimized bu storing hash locally. + // Mask value only needs to be used if SetCachedHash is not implemented. + UPInt GetCachedHash(UPInt maskValue) const { OVR_UNUSED(maskValue); return HashValue; } + void SetCachedHash(UPInt hashValue) { HashValue = hashValue; } + + void Clear() + { + Value.~C(); + NextInChain = -2; + } + // Free is only used from dtor of hash; Clear is used during regular operations: + // assignment, hash reallocations, value reassignments, so on. + void Free() { Clear(); } +}; + + +//----------------------------------------------------------------------------------- +// *** HashSet implementation - relies on either cached or regular entries. +// +// Use: Entry = HashsetCachedEntry if hashes are expensive to +// compute and thus need caching in entries. +// Entry = HashsetEntry if hashes are already externally cached. +// +template, + class AltHashF = HashF, + class Allocator = ContainerAllocator, + class Entry = HashsetCachedEntry > +class HashSetBase +{ + enum { HashMinSize = 8 }; + +public: + OVR_MEMORY_REDEFINE_NEW(HashSetBase) + + typedef HashSetBase SelfType; + + HashSetBase() : pTable(NULL) { } + HashSetBase(int sizeHint) : pTable(NULL) { SetCapacity(this, sizeHint); } + HashSetBase(const SelfType& src) : pTable(NULL) { Assign(this, src); } + + ~HashSetBase() + { + if (pTable) + { + // Delete the entries. + for (UPInt i = 0, n = pTable->SizeMask; i <= n; i++) + { + Entry* e = &E(i); + if (!e->IsEmpty()) + e->Free(); + } + + Allocator::Free(pTable); + pTable = NULL; + } + } + + + void Assign(const SelfType& src) + { + Clear(); + if (src.IsEmpty() == false) + { + SetCapacity(src.GetSize()); + + for (ConstIterator it = src.Begin(); it != src.End(); ++it) + { + Add(*it); + } + } + } + + + // Remove all entries from the HashSet table. + void Clear() + { + if (pTable) + { + // Delete the entries. + for (UPInt i = 0, n = pTable->SizeMask; i <= n; i++) + { + Entry* e = &E(i); + if (!e->IsEmpty()) + e->Clear(); + } + + Allocator::Free(pTable); + pTable = NULL; + } + } + + // Returns true if the HashSet is empty. + bool IsEmpty() const + { + return pTable == NULL || pTable->EntryCount == 0; + } + + + // Set a new or existing value under the key, to the value. + // Pass a different class of 'key' so that assignment reference object + // can be passed instead of the actual object. + template + void Set(const CRef& key) + { + UPInt hashValue = HashF()(key); + SPInt index = (SPInt)-1; + + if (pTable != NULL) + index = findIndexCore(key, hashValue & pTable->SizeMask); + + if (index >= 0) + { + E(index).Value = key; + } + else + { + // Entry under key doesn't exist. + add(key, hashValue); + } + } + + template + inline void Add(const CRef& key) + { + UPInt hashValue = HashF()(key); + add(key, hashValue); + } + + // Remove by alternative key. + template + void RemoveAlt(const K& key) + { + if (pTable == NULL) + return; + + UPInt hashValue = AltHashF()(key); + SPInt index = hashValue & pTable->SizeMask; + + Entry* e = &E(index); + + // If empty node or occupied by collider, we have nothing to remove. + if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != (UPInt)index)) + return; + + // Save index + SPInt naturalIndex = index; + SPInt prevIndex = -1; + + while ((e->GetCachedHash(pTable->SizeMask) != (UPInt)naturalIndex) || !(e->Value == key)) + { + // Keep looking through the chain. + prevIndex = index; + index = e->NextInChain; + if (index == -1) + return; // End of chain, item not found + e = &E(index); + } + + // Found it - our item is at index + if (naturalIndex == index) + { + // If we have a follower, move it to us + if (!e->IsEndOfChain()) + { + Entry* enext = &E(e->NextInChain); + e->Clear(); + new (e) Entry(*enext); + // Point us to the follower's cell that will be cleared + e = enext; + } + } + else + { + // We are not at natural index, so deal with the prev items next index + E(prevIndex).NextInChain = e->NextInChain; + } + + // Clear us, of the follower cell that was moved. + e->Clear(); + pTable->EntryCount --; + // Should we check the size to condense hash? ... + } + + // Remove by main key. + template + void Remove(const CRef& key) + { + RemoveAlt(key); + } + + // Retrieve the pointer to a value under the given key. + // - If there's no value under the key, then return NULL. + // - If there is a value, return the pointer. + template + C* Get(const K& key) + { + SPInt index = findIndex(key); + if (index >= 0) + return &E(index).Value; + return 0; + } + + template + const C* Get(const K& key) const + { + SPInt index = findIndex(key); + if (index >= 0) + return &E(index).Value; + return 0; + } + + // Alternative key versions of Get. Used by Hash. + template + const C* GetAlt(const K& key) const + { + SPInt index = findIndexAlt(key); + if (index >= 0) + return &E(index).Value; + return 0; + } + + template + C* GetAlt(const K& key) + { + SPInt index = findIndexAlt(key); + if (index >= 0) + return &E(index).Value; + return 0; + } + + template + bool GetAlt(const K& key, C* pval) const + { + SPInt index = findIndexAlt(key); + if (index >= 0) + { + if (pval) + *pval = E(index).Value; + return true; + } + return false; + } + + + UPInt GetSize() const + { + return pTable == NULL ? 0 : (UPInt)pTable->EntryCount; + } + + + // Resize the HashSet table to fit one more Entry. Often this + // doesn't involve any action. + void CheckExpand() + { + if (pTable == NULL) + { + // Initial creation of table. Make a minimum-sized table. + setRawCapacity(HashMinSize); + } + else if (pTable->EntryCount * 5 > (pTable->SizeMask + 1) * 4) + { + // pTable is more than 5/4 ths full. Expand. + setRawCapacity((pTable->SizeMask + 1) * 2); + } + } + + // Hint the bucket count to >= n. + void Resize(UPInt n) + { + // Not really sure what this means in relation to + // STLport's hash_map... they say they "increase the + // bucket count to at least n" -- but does that mean + // their real capacity after Resize(n) is more like + // n*2 (since they do linked-list chaining within + // buckets?). + SetCapacity(n); + } + + // Size the HashSet so that it can comfortably contain the given + // number of elements. If the HashSet already contains more + // elements than newSize, then this may be a no-op. + void SetCapacity(UPInt newSize) + { + UPInt newRawSize = (newSize * 5) / 4; + if (newRawSize <= GetSize()) + return; + setRawCapacity(newRawSize); + } + + // Disable inappropriate 'operator ->' warning on MSVC6. +#ifdef OVR_CC_MSVC +#if (OVR_CC_MSVC < 1300) +# pragma warning(disable : 4284) +#endif +#endif + + // Iterator API, like STL. + struct ConstIterator + { + const C& operator * () const + { + OVR_ASSERT(Index >= 0 && Index <= (SPInt)pHash->pTable->SizeMask); + return pHash->E(Index).Value; + } + + const C* operator -> () const + { + OVR_ASSERT(Index >= 0 && Index <= (SPInt)pHash->pTable->SizeMask); + return &pHash->E(Index).Value; + } + + void operator ++ () + { + // Find next non-empty Entry. + if (Index <= (SPInt)pHash->pTable->SizeMask) + { + Index++; + while ((UPInt)Index <= pHash->pTable->SizeMask && + pHash->E(Index).IsEmpty()) + { + Index++; + } + } + } + + bool operator == (const ConstIterator& it) const + { + if (IsEnd() && it.IsEnd()) + { + return true; + } + else + { + return (pHash == it.pHash) && (Index == it.Index); + } + } + + bool operator != (const ConstIterator& it) const + { + return ! (*this == it); + } + + + bool IsEnd() const + { + return (pHash == NULL) || + (pHash->pTable == NULL) || + (Index > (SPInt)pHash->pTable->SizeMask); + } + + ConstIterator() + : pHash(NULL), Index(0) + { } + + public: + // Constructor was intentionally made public to allow create + // iterator with arbitrary index. + ConstIterator(const SelfType* h, SPInt index) + : pHash(h), Index(index) + { } + + const SelfType* GetContainer() const + { + return pHash; + } + SPInt GetIndex() const + { + return Index; + } + + protected: + friend class HashSetBase; + + const SelfType* pHash; + SPInt Index; + }; + + friend struct ConstIterator; + + + // Non-const Iterator; Get most of it from ConstIterator. + struct Iterator : public ConstIterator + { + // Allow non-const access to entries. + C& operator*() const + { + OVR_ASSERT(ConstIterator::Index >= 0 && ConstIterator::Index <= (SPInt)ConstIterator::pHash->pTable->SizeMask); + return const_cast(ConstIterator::pHash)->E(ConstIterator::Index).Value; + } + + C* operator->() const + { + return &(operator*()); + } + + Iterator() + : ConstIterator(NULL, 0) + { } + + // Removes current element from Hash + void Remove() + { + RemoveAlt(operator*()); + } + + template + void RemoveAlt(const K& key) + { + SelfType* phash = const_cast(ConstIterator::pHash); + //Entry* ee = &phash->E(ConstIterator::Index); + //const C& key = ee->Value; + + UPInt hashValue = AltHashF()(key); + SPInt index = hashValue & phash->pTable->SizeMask; + + Entry* e = &phash->E(index); + + // If empty node or occupied by collider, we have nothing to remove. + if (e->IsEmpty() || (e->GetCachedHash(phash->pTable->SizeMask) != (UPInt)index)) + return; + + // Save index + SPInt naturalIndex = index; + SPInt prevIndex = -1; + + while ((e->GetCachedHash(phash->pTable->SizeMask) != (UPInt)naturalIndex) || !(e->Value == key)) + { + // Keep looking through the chain. + prevIndex = index; + index = e->NextInChain; + if (index == -1) + return; // End of chain, item not found + e = &phash->E(index); + } + + if (index == (SPInt)ConstIterator::Index) + { + // Found it - our item is at index + if (naturalIndex == index) + { + // If we have a follower, move it to us + if (!e->IsEndOfChain()) + { + Entry* enext = &phash->E(e->NextInChain); + e->Clear(); + new (e) Entry(*enext); + // Point us to the follower's cell that will be cleared + e = enext; + --ConstIterator::Index; + } + } + else + { + // We are not at natural index, so deal with the prev items next index + phash->E(prevIndex).NextInChain = e->NextInChain; + } + + // Clear us, of the follower cell that was moved. + e->Clear(); + phash->pTable->EntryCount --; + } + else + OVR_ASSERT(0); //? + } + + private: + friend class HashSetBase; + + Iterator(SelfType* h, SPInt i0) + : ConstIterator(h, i0) + { } + }; + + friend struct Iterator; + + Iterator Begin() + { + if (pTable == 0) + return Iterator(NULL, 0); + + // Scan till we hit the First valid Entry. + UPInt i0 = 0; + while (i0 <= pTable->SizeMask && E(i0).IsEmpty()) + { + i0++; + } + return Iterator(this, i0); + } + Iterator End() { return Iterator(NULL, 0); } + + ConstIterator Begin() const { return const_cast(this)->Begin(); } + ConstIterator End() const { return const_cast(this)->End(); } + + template + Iterator Find(const K& key) + { + SPInt index = findIndex(key); + if (index >= 0) + return Iterator(this, index); + return Iterator(NULL, 0); + } + + template + Iterator FindAlt(const K& key) + { + SPInt index = findIndexAlt(key); + if (index >= 0) + return Iterator(this, index); + return Iterator(NULL, 0); + } + + template + ConstIterator Find(const K& key) const { return const_cast(this)->Find(key); } + + template + ConstIterator FindAlt(const K& key) const { return const_cast(this)->FindAlt(key); } + +private: + // Find the index of the matching Entry. If no match, then return -1. + template + SPInt findIndex(const K& key) const + { + if (pTable == NULL) + return -1; + UPInt hashValue = HashF()(key) & pTable->SizeMask; + return findIndexCore(key, hashValue); + } + + template + SPInt findIndexAlt(const K& key) const + { + if (pTable == NULL) + return -1; + UPInt hashValue = AltHashF()(key) & pTable->SizeMask; + return findIndexCore(key, hashValue); + } + + // Find the index of the matching Entry. If no match, then return -1. + template + SPInt findIndexCore(const K& key, UPInt hashValue) const + { + // Table must exist. + OVR_ASSERT(pTable != 0); + // Hash key must be 'and-ed' by the caller. + OVR_ASSERT((hashValue & ~pTable->SizeMask) == 0); + + UPInt index = hashValue; + const Entry* e = &E(index); + + // If empty or occupied by a collider, not found. + if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != index)) + return -1; + + while(1) + { + OVR_ASSERT(e->GetCachedHash(pTable->SizeMask) == hashValue); + + if (e->GetCachedHash(pTable->SizeMask) == hashValue && e->Value == key) + { + // Found it. + return index; + } + // Values can not be equal at this point. + // That would mean that the hash key for the same value differs. + OVR_ASSERT(!(e->Value == key)); + + // Keep looking through the chain. + index = e->NextInChain; + if (index == (UPInt)-1) + break; // end of chain + + e = &E(index); + OVR_ASSERT(!e->IsEmpty()); + } + return -1; + } + + + // Add a new value to the HashSet table, under the specified key. + template + void add(const CRef& key, UPInt hashValue) + { + CheckExpand(); + hashValue &= pTable->SizeMask; + + pTable->EntryCount++; + + SPInt index = hashValue; + Entry* naturalEntry = &(E(index)); + + if (naturalEntry->IsEmpty()) + { + // Put the new Entry in. + new (naturalEntry) Entry(key, -1); + } + else + { + // Find a blank spot. + SPInt blankIndex = index; + do { + blankIndex = (blankIndex + 1) & pTable->SizeMask; + } while(!E(blankIndex).IsEmpty()); + + Entry* blankEntry = &E(blankIndex); + + if (naturalEntry->GetCachedHash(pTable->SizeMask) == (UPInt)index) + { + // Collision. Link into this chain. + + // Move existing list head. + new (blankEntry) Entry(*naturalEntry); // placement new, copy ctor + + // Put the new info in the natural Entry. + naturalEntry->Value = key; + naturalEntry->NextInChain = blankIndex; + } + else + { + // Existing Entry does not naturally + // belong in this slot. Existing + // Entry must be moved. + + // Find natural location of collided element (i.e. root of chain) + SPInt collidedIndex = naturalEntry->GetCachedHash(pTable->SizeMask); + OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (SPInt)pTable->SizeMask); + for (;;) + { + Entry* e = &E(collidedIndex); + if (e->NextInChain == index) + { + // Here's where we need to splice. + new (blankEntry) Entry(*naturalEntry); + e->NextInChain = blankIndex; + break; + } + collidedIndex = e->NextInChain; + OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (SPInt)pTable->SizeMask); + } + + // Put the new data in the natural Entry. + naturalEntry->Value = key; + naturalEntry->NextInChain = -1; + } + } + + // Record hash value: has effect only if cached node is used. + naturalEntry->SetCachedHash(hashValue); + } + + // Index access helpers. + Entry& E(UPInt index) + { + // Must have pTable and access needs to be within bounds. + OVR_ASSERT(index <= pTable->SizeMask); + return *(((Entry*) (pTable + 1)) + index); + } + const Entry& E(UPInt index) const + { + OVR_ASSERT(index <= pTable->SizeMask); + return *(((Entry*) (pTable + 1)) + index); + } + + + // Resize the HashSet table to the given size (Rehash the + // contents of the current table). The arg is the number of + // HashSet table entries, not the number of elements we should + // actually contain (which will be less than this). + void setRawCapacity(UPInt newSize) + { + if (newSize == 0) + { + // Special case. + Clear(); + return; + } + + // Minimum size; don't incur rehashing cost when expanding + // very small tables. Not that we perform this check before + // 'log2f' call to avoid fp exception with newSize == 1. + if (newSize < HashMinSize) + newSize = HashMinSize; + else + { + // Force newSize to be a power of two. + int bits = Alg::UpperBit(newSize-1) + 1; // Chop( Log2f((float)(newSize-1)) + 1); + OVR_ASSERT((UPInt(1) << bits) >= newSize); + newSize = UPInt(1) << bits; + } + + SelfType newHash; + newHash.pTable = (TableType*) + Allocator::Alloc( + sizeof(TableType) + sizeof(Entry) * newSize); + // Need to do something on alloc failure! + OVR_ASSERT(newHash.pTable); + + newHash.pTable->EntryCount = 0; + newHash.pTable->SizeMask = newSize - 1; + UPInt i, n; + + // Mark all entries as empty. + for (i = 0; i < newSize; i++) + newHash.E(i).NextInChain = -2; + + // Copy stuff to newHash + if (pTable) + { + for (i = 0, n = pTable->SizeMask; i <= n; i++) + { + Entry* e = &E(i); + if (e->IsEmpty() == false) + { + // Insert old Entry into new HashSet. + newHash.Add(e->Value); + // placement delete of old element + e->Clear(); + } + } + + // Delete our old data buffer. + Allocator::Free(pTable); + } + + // Steal newHash's data. + pTable = newHash.pTable; + newHash.pTable = NULL; + } + + struct TableType + { + UPInt EntryCount; + UPInt SizeMask; + // Entry array follows this structure + // in memory. + }; + TableType* pTable; +}; + + + +//----------------------------------------------------------------------------------- +template, + class AltHashF = HashF, + class Allocator = ContainerAllocator, + class Entry = HashsetCachedEntry > +class HashSet : public HashSetBase +{ +public: + typedef HashSetBase BaseType; + typedef HashSet SelfType; + + HashSet() { } + HashSet(int sizeHint) : BaseType(sizeHint) { } + HashSet(const SelfType& src) : BaseType(src) { } + ~HashSet() { } + + void operator = (const SelfType& src) { BaseType::Assign(src); } + + // Set a new or existing value under the key, to the value. + // Pass a different class of 'key' so that assignment reference object + // can be passed instead of the actual object. + template + void Set(const CRef& key) + { + BaseType::Set(key); + } + + template + inline void Add(const CRef& key) + { + BaseType::Add(key); + } + + // Hint the bucket count to >= n. + void Resize(UPInt n) + { + BaseType::SetCapacity(n); + } + + // Size the HashSet so that it can comfortably contain the given + // number of elements. If the HashSet already contains more + // elements than newSize, then this may be a no-op. + void SetCapacity(UPInt newSize) + { + BaseType::SetCapacity(newSize); + } + +}; + +// HashSet with uncached hash code; declared for convenience. +template, + class AltHashF = HashF, + class Allocator = ContainerAllocator > +class HashSetUncached : public HashSet > +{ +public: + + typedef HashSetUncached SelfType; + typedef HashSet > BaseType; + + // Delegated constructors. + HashSetUncached() { } + HashSetUncached(int sizeHint) : BaseType(sizeHint) { } + HashSetUncached(const SelfType& src) : BaseType(src) { } + ~HashSetUncached() { } + + void operator = (const SelfType& src) + { + BaseType::operator = (src); + } +}; + + +//----------------------------------------------------------------------------------- +// ***** Hash hash table implementation + +// Node for Hash - necessary so that Hash can delegate its implementation +// to HashSet. +template +struct HashNode +{ + typedef HashNode SelfType; + typedef C FirstType; + typedef U SecondType; + + C First; + U Second; + + // NodeRef is used to allow passing of elements into HashSet + // without using a temporary object. + struct NodeRef + { + const C* pFirst; + const U* pSecond; + + NodeRef(const C& f, const U& s) : pFirst(&f), pSecond(&s) { } + NodeRef(const NodeRef& src) : pFirst(src.pFirst), pSecond(src.pSecond) { } + + // Enable computation of ghash_node_hashf. + inline UPInt GetHash() const { return HashF()(*pFirst); } + // Necessary conversion to allow HashNode::operator == to work. + operator const C& () const { return *pFirst; } + }; + + // Note: No default constructor is necessary. + HashNode(const HashNode& src) : First(src.First), Second(src.Second) { } + HashNode(const NodeRef& src) : First(*src.pFirst), Second(*src.pSecond) { } + void operator = (const NodeRef& src) { First = *src.pFirst; Second = *src.pSecond; } + + template + bool operator == (const K& src) const { return (First == src); } + + template + static UPInt CalcHash(const K& data) { return HashF()(data); } + inline UPInt GetHash() const { return HashF()(First); } + + // Hash functors used with this node. A separate functor is used for alternative + // key lookup so that it does not need to access the '.First' element. + struct NodeHashF + { + template + UPInt operator()(const K& data) const { return data.GetHash(); } + }; + struct NodeAltHashF + { + template + UPInt operator()(const K& data) const { return HashNode::CalcHash(data); } + }; +}; + + + +// **** Extra hashset_entry types to allow NodeRef construction. + +// The big difference between the below types and the ones used in hash_set is that +// these allow initializing the node with 'typename C::NodeRef& keyRef', which +// is critical to avoid temporary node allocation on stack when using placement new. + +// Compact hash table Entry type that re-computes hash keys during hash traversal. +// Good to use if the hash function is cheap or the hash value is already cached in C. +template +class HashsetNodeEntry +{ +public: + // Internal chaining for collisions. + SPInt NextInChain; + C Value; + + HashsetNodeEntry() + : NextInChain(-2) { } + HashsetNodeEntry(const HashsetNodeEntry& e) + : NextInChain(e.NextInChain), Value(e.Value) { } + HashsetNodeEntry(const C& key, SPInt next) + : NextInChain(next), Value(key) { } + HashsetNodeEntry(const typename C::NodeRef& keyRef, SPInt next) + : NextInChain(next), Value(keyRef) { } + + bool IsEmpty() const { return NextInChain == -2; } + bool IsEndOfChain() const { return NextInChain == -1; } + UPInt GetCachedHash(UPInt maskValue) const { return HashF()(Value) & maskValue; } + void SetCachedHash(UPInt hashValue) { OVR_UNUSED(hashValue); } + + void Clear() + { + Value.~C(); // placement delete + NextInChain = -2; + } + // Free is only used from dtor of hash; Clear is used during regular operations: + // assignment, hash reallocations, value reassignments, so on. + void Free() { Clear(); } +}; + +// Hash table Entry type that caches the Entry hash value for nodes, so that it +// does not need to be re-computed during access. +template +class HashsetCachedNodeEntry +{ +public: + // Internal chaining for collisions. + SPInt NextInChain; + UPInt HashValue; + C Value; + + HashsetCachedNodeEntry() + : NextInChain(-2) { } + HashsetCachedNodeEntry(const HashsetCachedNodeEntry& e) + : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { } + HashsetCachedNodeEntry(const C& key, SPInt next) + : NextInChain(next), Value(key) { } + HashsetCachedNodeEntry(const typename C::NodeRef& keyRef, SPInt next) + : NextInChain(next), Value(keyRef) { } + + bool IsEmpty() const { return NextInChain == -2; } + bool IsEndOfChain() const { return NextInChain == -1; } + UPInt GetCachedHash(UPInt maskValue) const { OVR_UNUSED(maskValue); return HashValue; } + void SetCachedHash(UPInt hashValue) { HashValue = hashValue; } + + void Clear() + { + Value.~C(); + NextInChain = -2; + } + // Free is only used from dtor of hash; Clear is used during regular operations: + // assignment, hash reallocations, value reassignments, so on. + void Free() { Clear(); } +}; + + +//----------------------------------------------------------------------------------- +template, + class Allocator = ContainerAllocator, + class HashNode = OVR::HashNode, + class Entry = HashsetCachedNodeEntry, + class Container = HashSet > +class Hash +{ +public: + OVR_MEMORY_REDEFINE_NEW(Hash) + + // Types used for hash_set. + typedef U ValueType; + typedef Hash SelfType; + + // Actual hash table itself, implemented as hash_set. + Container mHash; + +public: + Hash() { } + Hash(int sizeHint) : mHash(sizeHint) { } + Hash(const SelfType& src) : mHash(src.mHash) { } + ~Hash() { } + + void operator = (const SelfType& src) { mHash = src.mHash; } + + // Remove all entries from the Hash table. + inline void Clear() { mHash.Clear(); } + // Returns true if the Hash is empty. + inline bool IsEmpty() const { return mHash.IsEmpty(); } + + // Access (set). + inline void Set(const C& key, const U& value) + { + typename HashNode::NodeRef e(key, value); + mHash.Set(e); + } + inline void Add(const C& key, const U& value) + { + typename HashNode::NodeRef e(key, value); + mHash.Add(e); + } + + // Removes an element by clearing its Entry. + inline void Remove(const C& key) + { + mHash.RemoveAlt(key); + } + template + inline void RemoveAlt(const K& key) + { + mHash.RemoveAlt(key); + } + + // Retrieve the value under the given key. + // - If there's no value under the key, then return false and leave *pvalue alone. + // - If there is a value, return true, and Set *Pvalue to the Entry's value. + // - If value == NULL, return true or false according to the presence of the key. + bool Get(const C& key, U* pvalue) const + { + const HashNode* p = mHash.GetAlt(key); + if (p) + { + if (pvalue) + *pvalue = p->Second; + return true; + } + return false; + } + + template + bool GetAlt(const K& key, U* pvalue) const + { + const HashNode* p = mHash.GetAlt(key); + if (p) + { + if (pvalue) + *pvalue = p->Second; + return true; + } + return false; + } + + // Retrieve the pointer to a value under the given key. + // - If there's no value under the key, then return NULL. + // - If there is a value, return the pointer. + inline U* Get(const C& key) + { + HashNode* p = mHash.GetAlt(key); + return p ? &p->Second : 0; + } + inline const U* Get(const C& key) const + { + const HashNode* p = mHash.GetAlt(key); + return p ? &p->Second : 0; + } + + template + inline U* GetAlt(const K& key) + { + HashNode* p = mHash.GetAlt(key); + return p ? &p->Second : 0; + } + template + inline const U* GetAlt(const K& key) const + { + const HashNode* p = mHash.GetAlt(key); + return p ? &p->Second : 0; + } + + // Sizing methods - delegate to Hash. + inline UPInt GetSize() const { return mHash.GetSize(); } + inline void Resize(UPInt n) { mHash.Resize(n); } + inline void SetCapacity(UPInt newSize) { mHash.SetCapacity(newSize); } + + // Iterator API, like STL. + typedef typename Container::ConstIterator ConstIterator; + typedef typename Container::Iterator Iterator; + + inline Iterator Begin() { return mHash.Begin(); } + inline Iterator End() { return mHash.End(); } + inline ConstIterator Begin() const { return mHash.Begin(); } + inline ConstIterator End() const { return mHash.End(); } + + Iterator Find(const C& key) { return mHash.FindAlt(key); } + ConstIterator Find(const C& key) const { return mHash.FindAlt(key); } + + template + Iterator FindAlt(const K& key) { return mHash.FindAlt(key); } + template + ConstIterator FindAlt(const K& key) const { return mHash.FindAlt(key); } +}; + + + +// Hash with uncached hash code; declared for convenience. +template, class Allocator = ContainerAllocator > +class HashUncached + : public Hash, + HashsetNodeEntry, typename HashNode::NodeHashF> > +{ +public: + typedef HashUncached SelfType; + typedef Hash, + HashsetNodeEntry, + typename HashNode::NodeHashF> > BaseType; + + // Delegated constructors. + HashUncached() { } + HashUncached(int sizeHint) : BaseType(sizeHint) { } + HashUncached(const SelfType& src) : BaseType(src) { } + ~HashUncached() { } + void operator = (const SelfType& src) { BaseType::operator = (src); } +}; + + + +// And identity hash in which keys serve as hash value. Can be uncached, +// since hash computation is assumed cheap. +template, class HashF = IdentityHash > +class HashIdentity + : public HashUncached +{ +public: + typedef HashIdentity SelfType; + typedef HashUncached BaseType; + + // Delegated constructors. + HashIdentity() { } + HashIdentity(int sizeHint) : BaseType(sizeHint) { } + HashIdentity(const SelfType& src) : BaseType(src) { } + ~HashIdentity() { } + void operator = (const SelfType& src) { BaseType::operator = (src); } +}; + + +} // OVR + + +#ifdef OVR_DEFINE_NEW +#define new OVR_DEFINE_NEW +#endif + +#endif diff --git a/LibOVR/Src/Kernel/OVR_KeyCodes.h b/LibOVR/Src/Kernel/OVR_KeyCodes.h new file mode 100644 index 0000000..647688a --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_KeyCodes.h @@ -0,0 +1,240 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_KeyCodes.h +Content : Common keyboard constants +Created : September 19, 2012 + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_KeyCodes_h +#define OVR_KeyCodes_h + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** KeyCode + +// KeyCode enumeration defines platform-independent keyboard key constants. +// Note that Key_A through Key_Z are mapped to capital ascii constants. + +enum KeyCode +{ + // Key_None indicates that no key was specified. + Key_None = 0, + + // A through Z and numbers 0 through 9. + Key_A = 65, + Key_B, + Key_C, + Key_D, + Key_E, + Key_F, + Key_G, + Key_H, + Key_I, + Key_J, + Key_K, + Key_L, + Key_M, + Key_N, + Key_O, + Key_P, + Key_Q, + Key_R, + Key_S, + Key_T, + Key_U, + Key_V, + Key_W, + Key_X, + Key_Y, + Key_Z, + Key_Num0 = 48, + Key_Num1, + Key_Num2, + Key_Num3, + Key_Num4, + Key_Num5, + Key_Num6, + Key_Num7, + Key_Num8, + Key_Num9, + + // Numeric keypad. + Key_KP_0 = 0xa0, + Key_KP_1, + Key_KP_2, + Key_KP_3, + Key_KP_4, + Key_KP_5, + Key_KP_6, + Key_KP_7, + Key_KP_8, + Key_KP_9, + Key_KP_Multiply, + Key_KP_Add, + Key_KP_Enter, + Key_KP_Subtract, + Key_KP_Decimal, + Key_KP_Divide, + + // Function keys. + Key_F1 = 0xb0, + Key_F2, + Key_F3, + Key_F4, + Key_F5, + Key_F6, + Key_F7, + Key_F8, + Key_F9, + Key_F10, + Key_F11, + Key_F12, + Key_F13, + Key_F14, + Key_F15, + + // Other keys. + Key_Backspace = 8, + Key_Tab, + Key_Clear = 12, + Key_Return, + Key_Shift = 16, + Key_Control, + Key_Alt, + Key_Pause, + Key_CapsLock = 20, // Toggle + Key_Escape = 27, + Key_Space = 32, + Key_Quote = 39, + Key_PageUp = 0xc0, + Key_PageDown, + Key_End, + Key_Home, + Key_Left, + Key_Up, + Key_Right, + Key_Down, + Key_Insert, + Key_Delete, + Key_Help, + + Key_Comma = 44, + Key_Minus, + Key_Slash = 47, + Key_Period, + Key_NumLock = 144, // Toggle + Key_ScrollLock = 145, // Toggle + + Key_Semicolon = 59, + Key_Equal = 61, + Key_Bar = 192, + Key_BracketLeft = 91, + Key_Backslash, + Key_BracketRight, + + Key_OEM_AX = 0xE1, // 'AX' key on Japanese AX keyboard + Key_OEM_102 = 0xE2, // "<>" or "\|" on RT 102-key keyboard. + Key_ICO_HELP = 0xE3, // Help key on ICO + Key_ICO_00 = 0xE4, // 00 key on ICO + + Key_Meta, + + // Total number of keys. + Key_CodeCount +}; + + +//----------------------------------------------------------------------------------- + +class KeyModifiers +{ +public: + enum + { + Key_ShiftPressed = 0x01, + Key_CtrlPressed = 0x02, + Key_AltPressed = 0x04, + Key_MetaPressed = 0x08, + Key_CapsToggled = 0x10, + Key_NumToggled = 0x20, + Key_ScrollToggled = 0x40, + + Initialized_Bit = 0x80, + Initialized_Mask = 0xFF + }; + unsigned char States; + + KeyModifiers() : States(0) { } + KeyModifiers(unsigned char st) : States((unsigned char)(st | Initialized_Bit)) { } + + void Reset() { States = 0; } + + bool IsShiftPressed() const { return (States & Key_ShiftPressed) != 0; } + bool IsCtrlPressed() const { return (States & Key_CtrlPressed) != 0; } + bool IsAltPressed() const { return (States & Key_AltPressed) != 0; } + bool IsMetaPressed() const { return (States & Key_MetaPressed) != 0; } + bool IsCapsToggled() const { return (States & Key_CapsToggled) != 0; } + bool IsNumToggled() const { return (States & Key_NumToggled) != 0; } + bool IsScrollToggled() const{ return (States & Key_ScrollToggled) != 0; } + + void SetShiftPressed(bool v = true) { (v) ? States |= Key_ShiftPressed : States &= ~Key_ShiftPressed; } + void SetCtrlPressed(bool v = true) { (v) ? States |= Key_CtrlPressed : States &= ~Key_CtrlPressed; } + void SetAltPressed(bool v = true) { (v) ? States |= Key_AltPressed : States &= ~Key_AltPressed; } + void SetMetaPressed(bool v = true) { (v) ? States |= Key_MetaPressed : States &= ~Key_MetaPressed; } + void SetCapsToggled(bool v = true) { (v) ? States |= Key_CapsToggled : States &= ~Key_CapsToggled; } + void SetNumToggled(bool v = true) { (v) ? States |= Key_NumToggled : States &= ~Key_NumToggled; } + void SetScrollToggled(bool v = true) { (v) ? States |= Key_ScrollToggled: States &= ~Key_ScrollToggled; } + + bool IsInitialized() const { return (States & Initialized_Mask) != 0; } +}; + + +//----------------------------------------------------------------------------------- + +/* +enum PadKeyCode +{ + Pad_None, // Indicates absence of key code. + Pad_Back, + Pad_Start, + Pad_A, + Pad_B, + Pad_X, + Pad_Y, + Pad_R1, // RightShoulder; + Pad_L1, // LeftShoulder; + Pad_R2, // RightTrigger; + Pad_L2, // LeftTrigger; + Pad_Up, + Pad_Down, + Pad_Right, + Pad_Left, + Pad_Plus, + Pad_Minus, + Pad_1, + Pad_2, + Pad_H, + Pad_C, + Pad_Z, + Pad_O, + Pad_T, + Pad_S, + Pad_Select, + Pad_Home, + Pad_RT, // RightThumb; + Pad_LT // LeftThumb; +}; +*/ + +} // OVR + +#endif diff --git a/LibOVR/Src/Kernel/OVR_List.h b/LibOVR/Src/Kernel/OVR_List.h new file mode 100644 index 0000000..0292f51 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_List.h @@ -0,0 +1,325 @@ +/************************************************************************************ + +PublicHeader: OVR +Filename : OVR_List.h +Content : Template implementation for doubly-connected linked List +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_List_h +#define OVR_List_h + +#include "OVR_Types.h" + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** ListNode +// +// Base class for the elements of the intrusive linked list. +// To store elements in the List do: +// +// struct MyData : ListNode +// { +// . . . +// }; + +template +struct ListNode +{ + union { + T* pPrev; + void* pVoidPrev; + }; + union { + T* pNext; + void* pVoidNext; + }; + + void RemoveNode() + { + pPrev->pNext = pNext; + pNext->pPrev = pPrev; + } + + // Removes us from the list and inserts pnew there instead. + void ReplaceNodeWith(T* pnew) + { + pPrev->pNext = pnew; + pNext->pPrev = pnew; + pnew->pPrev = pPrev; + pnew->pNext = pNext; + } + + // Inserts the argument linked list node after us in the list. + void InsertNodeAfter(T* p) + { + p->pPrev = pNext->pPrev; // this + p->pNext = pNext; + pNext->pPrev = p; + pNext = p; + } + // Inserts the argument linked list node before us in the list. + void InsertNodeBefore(T* p) + { + p->pNext = pNext->pPrev; // this + p->pPrev = pPrev; + pPrev->pNext = p; + pPrev = p; + } + + void Alloc_MoveTo(ListNode* pdest) + { + pdest->pNext = pNext; + pdest->pPrev = pPrev; + pPrev->pNext = (T*)pdest; + pNext->pPrev = (T*)pdest; + } +}; + + +//------------------------------------------------------------------------ +// ***** List +// +// Doubly linked intrusive list. +// The data type must be derived from ListNode. +// +// Adding: PushFront(), PushBack(). +// Removing: Remove() - the element must be in the list! +// Moving: BringToFront(), SendToBack() - the element must be in the list! +// +// Iterating: +// MyData* data = MyList.GetFirst(); +// while (!MyList.IsNull(data)) +// { +// . . . +// data = MyList.GetNext(data); +// } +// +// Removing: +// MyData* data = MyList.GetFirst(); +// while (!MyList.IsNull(data)) +// { +// MyData* next = MyList.GetNext(data); +// if (ToBeRemoved(data)) +// MyList.Remove(data); +// data = next; +// } +// + +// List<> represents a doubly-linked list if T, where each T must derive +// from ListNode. B specifies the base class that was directly +// derived from ListNode, and is only necessary if there is an intermediate +// inheritance chain. + +template class List +{ +public: + typedef T ValueType; + + List() + { + Root.pNext = Root.pPrev = (ValueType*)&Root; + } + + void Clear() + { + Root.pNext = Root.pPrev = (ValueType*)&Root; + } + + const ValueType* GetFirst() const { return (const ValueType*)Root.pNext; } + const ValueType* GetLast () const { return (const ValueType*)Root.pPrev; } + ValueType* GetFirst() { return (ValueType*)Root.pNext; } + ValueType* GetLast () { return (ValueType*)Root.pPrev; } + + // Determine if list is empty (i.e.) points to itself. + // Go through void* access to avoid issues with strict-aliasing optimizing out the + // access after RemoveNode(), etc. + bool IsEmpty() const { return Root.pVoidNext == (const T*)(const B*)&Root; } + bool IsFirst(const ValueType* p) const { return p == Root.pNext; } + bool IsLast (const ValueType* p) const { return p == Root.pPrev; } + bool IsNull (const ValueType* p) const { return p == (const T*)(const B*)&Root; } + + inline static const ValueType* GetPrev(const ValueType* p) { return (const ValueType*)p->pPrev; } + inline static const ValueType* GetNext(const ValueType* p) { return (const ValueType*)p->pNext; } + inline static ValueType* GetPrev( ValueType* p) { return (ValueType*)p->pPrev; } + inline static ValueType* GetNext( ValueType* p) { return (ValueType*)p->pNext; } + + void PushFront(ValueType* p) + { + p->pNext = Root.pNext; + p->pPrev = (ValueType*)&Root; + Root.pNext->pPrev = p; + Root.pNext = p; + } + + void PushBack(ValueType* p) + { + p->pPrev = Root.pPrev; + p->pNext = (ValueType*)&Root; + Root.pPrev->pNext = p; + Root.pPrev = p; + } + + static void Remove(ValueType* p) + { + p->pPrev->pNext = p->pNext; + p->pNext->pPrev = p->pPrev; + } + + void BringToFront(ValueType* p) + { + Remove(p); + PushFront(p); + } + + void SendToBack(ValueType* p) + { + Remove(p); + PushBack(p); + } + + // Appends the contents of the argument list to the front of this list; + // items are removed from the argument list. + void PushListToFront(List& src) + { + if (!src.IsEmpty()) + { + ValueType* pfirst = src.GetFirst(); + ValueType* plast = src.GetLast(); + src.Clear(); + plast->pNext = Root.pNext; + pfirst->pPrev = (ValueType*)&Root; + Root.pNext->pPrev = plast; + Root.pNext = pfirst; + } + } + + void PushListToBack(List& src) + { + if (!src.IsEmpty()) + { + ValueType* pfirst = src.GetFirst(); + ValueType* plast = src.GetLast(); + src.Clear(); + plast->pNext = (ValueType*)&Root; + pfirst->pPrev = Root.pPrev; + Root.pPrev->pNext = pfirst; + Root.pPrev = plast; + } + } + + // Removes all source list items after (and including) the 'pfirst' node from the + // source list and adds them to out list. + void PushFollowingListItemsToFront(List& src, ValueType *pfirst) + { + if (pfirst != &src.Root) + { + ValueType *plast = src.Root.pPrev; + + // Remove list remainder from source. + pfirst->pPrev->pNext = (ValueType*)&src.Root; + src.Root.pPrev = pfirst->pPrev; + // Add the rest of the items to list. + plast->pNext = Root.pNext; + pfirst->pPrev = (ValueType*)&Root; + Root.pNext->pPrev = plast; + Root.pNext = pfirst; + } + } + + // Removes all source list items up to but NOT including the 'pend' node from the + // source list and adds them to out list. + void PushPrecedingListItemsToFront(List& src, ValueType *ptail) + { + if (src.GetFirst() != ptail) + { + ValueType *pfirst = src.Root.pNext; + ValueType *plast = ptail->pPrev; + + // Remove list remainder from source. + ptail->pPrev = (ValueType*)&src.Root; + src.Root.pNext = ptail; + + // Add the rest of the items to list. + plast->pNext = Root.pNext; + pfirst->pPrev = (ValueType*)&Root; + Root.pNext->pPrev = plast; + Root.pNext = pfirst; + } + } + + + // Removes a range of source list items starting at 'pfirst' and up to, but not including 'pend', + // and adds them to out list. Note that source items MUST already be in the list. + void PushListItemsToFront(ValueType *pfirst, ValueType *pend) + { + if (pfirst != pend) + { + ValueType *plast = pend->pPrev; + + // Remove list remainder from source. + pfirst->pPrev->pNext = pend; + pend->pPrev = pfirst->pPrev; + // Add the rest of the items to list. + plast->pNext = Root.pNext; + pfirst->pPrev = (ValueType*)&Root; + Root.pNext->pPrev = plast; + Root.pNext = pfirst; + } + } + + + void Alloc_MoveTo(List* pdest) + { + if (IsEmpty()) + pdest->Clear(); + else + { + pdest->Root.pNext = Root.pNext; + pdest->Root.pPrev = Root.pPrev; + + Root.pNext->pPrev = (ValueType*)&pdest->Root; + Root.pPrev->pNext = (ValueType*)&pdest->Root; + } + } + + +private: + // Copying is prohibited + List(const List&); + const List& operator = (const List&); + + ListNode Root; +}; + + +//------------------------------------------------------------------------ +// ***** FreeListElements +// +// Remove all elements in the list and free them in the allocator + +template +void FreeListElements(List& list, Allocator& allocator) +{ + typename List::ValueType* self = list.GetFirst(); + while(!list.IsNull(self)) + { + typename List::ValueType* next = list.GetNext(self); + allocator.Free(self); + self = next; + } + list.Clear(); +} + +} // OVR + +#endif diff --git a/LibOVR/Src/Kernel/OVR_Log.cpp b/LibOVR/Src/Kernel/OVR_Log.cpp new file mode 100644 index 0000000..baede98 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Log.cpp @@ -0,0 +1,173 @@ +/************************************************************************************ + +Filename : OVR_Log.cpp +Content : Logging support +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_Log.h" +#include "OVR_Std.h" +#include +#include + +#if defined(OVR_OS_WIN32) +#include +#elif defined(OVR_OS_ANDROID) +#include +#endif + +namespace OVR { + +// Global Log pointer. +Log* volatile OVR_GlobalLog = 0; + +//----------------------------------------------------------------------------------- +// ***** Log Implementation + +Log::~Log() +{ + // Clear out global log + if (this == OVR_GlobalLog) + { + // TBD: perhaps we should ASSERT if this happens before system shutdown? + OVR_GlobalLog = 0; + } +} + +void Log::LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList) +{ + if ((messageType & LoggingMask) == 0) + return; +#ifndef OVR_BUILD_DEBUG + if (IsDebugMessage(messageType)) + return; +#endif + + char buffer[MaxLogBufferMessageSize]; + FormatLog(buffer, MaxLogBufferMessageSize, messageType, fmt, argList); + DefaultLogOutput(buffer, IsDebugMessage(messageType)); +} + +void OVR::Log::LogMessage(LogMessageType messageType, const char* pfmt, ...) +{ + va_list argList; + va_start(argList, pfmt); + LogMessageVarg(messageType, pfmt, argList); + va_end(argList); +} + + +void Log::FormatLog(char* buffer, unsigned bufferSize, LogMessageType messageType, + const char* fmt, va_list argList) +{ + bool addNewline = true; + + switch(messageType) + { + case Log_Error: OVR_strcpy(buffer, bufferSize, "Error: "); break; + case Log_Debug: OVR_strcpy(buffer, bufferSize, "Debug: "); break; + case Log_Assert: OVR_strcpy(buffer, bufferSize, "Assert: "); break; + case Log_Text: buffer[0] = 0; addNewline = false; break; + case Log_DebugText: buffer[0] = 0; addNewline = false; break; + default: + buffer[0] = 0; + addNewline = false; + break; + } + + UPInt prefixLength = OVR_strlen(buffer); + char *buffer2 = buffer + prefixLength; + OVR_vsprintf(buffer2, bufferSize - prefixLength, fmt, argList); + + if (addNewline) + OVR_strcat(buffer, bufferSize, "\n"); +} + + +void Log::DefaultLogOutput(const char* formattedText, bool debug) +{ + +#if defined(OVR_OS_WIN32) + // Under Win32, output regular messages to console if it exists; debug window otherwise. + static DWORD dummyMode; + static bool hasConsole = (GetStdHandle(STD_OUTPUT_HANDLE) != INVALID_HANDLE_VALUE) && + (GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dummyMode)); + + if (!hasConsole || debug) + { + ::OutputDebugStringA(formattedText); + } + else + { + fputs(formattedText, stdout); + } + +#elif defined(OVR_OS_ANDROID) + __android_log_write(ANDROID_LOG_INFO, "OVR", formattedText); + +#else + fputs(formattedText, stdout); + +#endif + + // Just in case. + OVR_UNUSED2(formattedText, debug); +} + + +//static +void Log::SetGlobalLog(Log *log) +{ + OVR_GlobalLog = log; +} +//static +Log* Log::GetGlobalLog() +{ +// No global log by default? +// if (!OVR_GlobalLog) +// OVR_GlobalLog = GetDefaultLog(); + return OVR_GlobalLog; +} + +//static +Log* Log::GetDefaultLog() +{ + // Create default log pointer statically so that it can be used + // even during startup. + static Log defaultLog; + return &defaultLog; +} + + +//----------------------------------------------------------------------------------- +// ***** Global Logging functions + +#define OVR_LOG_FUNCTION_IMPL(Name) \ + void Log##Name(const char* fmt, ...) \ + { \ + if (OVR_GlobalLog) \ + { \ + va_list argList; va_start(argList, fmt); \ + OVR_GlobalLog->LogMessageVarg(Log_##Name, fmt, argList); \ + va_end(argList); \ + } \ + } + +OVR_LOG_FUNCTION_IMPL(Text) +OVR_LOG_FUNCTION_IMPL(Error) + +#ifdef OVR_BUILD_DEBUG +OVR_LOG_FUNCTION_IMPL(DebugText) +OVR_LOG_FUNCTION_IMPL(Debug) +OVR_LOG_FUNCTION_IMPL(Assert) +#endif + +} // OVR diff --git a/LibOVR/Src/Kernel/OVR_Log.h b/LibOVR/Src/Kernel/OVR_Log.h new file mode 100644 index 0000000..acc298d --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Log.h @@ -0,0 +1,193 @@ +/************************************************************************************ + +PublicHeader: OVR +Filename : OVR_Log.h +Content : Logging support +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_Log_h +#define OVR_Log_h + +#include "OVR_Types.h" +#include + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** Logging Constants + +// LogMaskConstants defined bit mask constants that describe what log messages +// should be displayed. +enum LogMaskConstants +{ + LogMask_Regular = 0x100, + LogMask_Debug = 0x200, + LogMask_None = 0, + LogMask_All = LogMask_Regular|LogMask_Debug +}; + + +// LogMessageType describes the type of the log message, controls when it is +// displayed and what prefix/suffix is given to it. Messages are subdivided into +// regular and debug logging types. Debug logging is only generated in debug builds. +// +// Log_Text - General output text displayed without prefix or new-line. +// Used in OVR libraries for general log flow messages +// such as "Device Initialized". +// +// Log_Error - Error message output with "Error: %s\n", intended for +// application/sample-level use only, in cases where an expected +// operation failed. OVR libraries should not use this internally, +// reporting status codes instead. +// +// Log_DebugText - Message without prefix or new lines; output in Debug build only. +// +// Log_Debug - Debug-build only message, formatted with "Debug: %s\n". +// Intended to comment on incorrect API usage that doesn't lead +// to crashes but can be avoided with proper use. +// There is no Debug Error on purpose, since real errors should +// be handled by API user. +// +// Log_Assert - Debug-build only message, formatted with "Assert: %s\n". +// Intended for severe unrecoverable conditions in library +// source code. Generated though OVR_ASSERT_MSG(c, "Text"). + +enum LogMessageType +{ + // General Logging + Log_Text = LogMask_Regular | 0, + Log_Error = LogMask_Regular | 1, // "Error: %s\n". + + // Debug-only messages (not generated in release build) + Log_DebugText = LogMask_Debug | 0, + Log_Debug = LogMask_Debug | 1, // "Debug: %s\n". + Log_Assert = LogMask_Debug | 2, // "Assert: %s\n". +}; + + +// LOG_VAARG_ATTRIBUTE macro, enforces printf-style fromatting for message types +#ifdef __GNUC__ +# define OVR_LOG_VAARG_ATTRIBUTE(a,b) __attribute__((format (printf, a, b))) +#else +# define OVR_LOG_VAARG_ATTRIBUTE(a,b) +#endif + + +//----------------------------------------------------------------------------------- +// ***** Log + +// Log defines a base class interface that can be implemented to catch both +// debug and runtime messages. +// Debug logging can be overridden by calling Log::SetGlobalLog. + +class Log +{ + friend class System; +public: + Log(unsigned logMask = LogMask_Debug) : LoggingMask(logMask) { } + virtual ~Log(); + + // Log formating buffer size used by default LogMessageVarg. Longer strings are truncated. + enum { MaxLogBufferMessageSize = 2048 }; + + unsigned GetLoggingMask() const { return LoggingMask; } + void SetLoggingMask(unsigned logMask) { LoggingMask = logMask; } + + // This virtual function receives all the messages, + // developers should override this function in order to do custom logging + virtual void LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList); + + // Call the logging function with specific message type, with no type filtering. + void LogMessage(LogMessageType messageType, + const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(3,4); + + + // Helper used by LogMessageVarg to format the log message, writing the resulting + // string into buffer. It formats text based on fmt and appends prefix/new line + // based on LogMessageType. + static void FormatLog(char* buffer, unsigned bufferSize, LogMessageType messageType, + const char* fmt, va_list argList); + + // Default log output implementation used by by LogMessageVarg. + // Debug flag may be used to re-direct output on some platforms, but doesn't + // necessarily disable it in release builds; that is the job of the called. + static void DefaultLogOutput(const char* textBuffer, bool debug); + + // Determines if the specified message type is for debugging only. + static bool IsDebugMessage(LogMessageType messageType) + { + return (messageType & LogMask_Debug) != 0; + } + + // *** Global APIs + + // Global Log registration APIs. + // - Global log is used for OVR_DEBUG messages. Set global log to null (0) + // to disable all logging. + static void SetGlobalLog(Log *log); + static Log* GetGlobalLog(); + + // Returns default log singleton instance. + static Log* GetDefaultLog(); + + // Applies logMask to the default log and returns a pointer to it. + // By default, only Debug logging is enabled, so to avoid SDK generating console + // messages in user app (those are always disabled in release build, + // even if the flag is set). This function is useful in System constructor. + static Log* ConfigureDefaultLog(unsigned logMask = LogMask_Debug) + { + Log* log = GetDefaultLog(); + log->SetLoggingMask(logMask); + return log; + } + +private: + // Logging mask described by LogMaskConstants. + unsigned LoggingMask; +}; + + +//----------------------------------------------------------------------------------- +// ***** Global Logging Functions and Debug Macros + +// These functions will output text to global log with semantics described by +// their LogMessageType. +void LogText(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); +void LogError(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); + +#ifdef OVR_BUILD_DEBUG + + // Debug build only logging. + void LogDebugText(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); + void LogDebug(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); + void LogAssert(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); + + // Macro to do debug logging, printf-style. + // An extra set of set of parenthesis must be used around arguments, + // as in: OVR_LOG_DEBUG(("Value %d", 2)). + #define OVR_DEBUG_LOG(args) do { OVR::LogDebug args; } while(0) + #define OVR_DEBUG_LOG_TEXT(args) do { OVR::LogDebugText args; } while(0) + + #define OVR_ASSERT_LOG(c, args) do { if (!(c)) { OVR::LogAssert args; OVR_DEBUG_BREAK; } } while(0) + +#else + + // If not in debug build, macros do nothing. + #define OVR_DEBUG_LOG(args) ((void)0) + #define OVR_DEBUG_LOG_TEXT(args) ((void)0) + #define OVR_ASSERT_LOG(c, args) ((void)0) + +#endif + +} // OVR + +#endif diff --git a/LibOVR/Src/Kernel/OVR_Math.cpp b/LibOVR/Src/Kernel/OVR_Math.cpp new file mode 100644 index 0000000..eaf23fe --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Math.cpp @@ -0,0 +1,153 @@ +/************************************************************************************ + +Filename : OVR_Math.h +Content : Implementation of 3D primitives such as vectors, matrices. +Created : September 4, 2012 +Authors : Andrew Reisse, Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_Math.h" + +#include + +namespace OVR { + + +//------------------------------------------------------------------------------------- +// ***** Math + + +// Single-precision Math constants class. +const float Math::Pi = 3.1415926f; +const float Math::TwoPi = 3.1415926f * 2; +const float Math::PiOver2 = 3.1415926f / 2.0f; +const float Math::PiOver4 = 3.1415926f / 4.0f; +const float Math::E = 2.7182818f; + +const float Math::MaxValue = FLT_MAX; +const float Math::MinPositiveValue = FLT_MIN; + +const float Math::RadToDegreeFactor = 360.0f / Math::TwoPi; +const float Math::DegreeToRadFactor = Math::TwoPi / 360.0f; + +const float Math::Tolerance = 0.00001f; +const float Math::SingularityRadius = 0.0000001f; // Use for Gimbal lock numerical problems + + +// Double-precision Math constants class. +const double Math::Pi = 3.14159265358979; +const double Math::TwoPi = 3.14159265358979 * 2; +const double Math::PiOver2 = 3.14159265358979 / 2.0; +const double Math::PiOver4 = 3.14159265358979 / 4.0; +const double Math::E = 2.71828182845905; + +const double Math::MaxValue = DBL_MAX; +const double Math::MinPositiveValue = DBL_MIN; + +const double Math::RadToDegreeFactor = 360.0 / Math::TwoPi; +const double Math::DegreeToRadFactor = Math::TwoPi / 360.0; + +const double Math::Tolerance = 0.00001; +const double Math::SingularityRadius = 0.000000000001; // Use for Gimbal lock numerical problems + + + +//------------------------------------------------------------------------------------- +// ***** Matrix4f + + +Matrix4f Matrix4f::LookAtRH(const Vector3f& eye, const Vector3f& at, const Vector3f& up) +{ + Vector3f z = (eye - at).Normalized(); // Forward + Vector3f x = up.Cross(z).Normalized(); // Right + Vector3f y = z.Cross(x); + + Matrix4f m(x.x, x.y, x.z, -(x * eye), + y.x, y.y, y.z, -(y * eye), + z.x, z.y, z.z, -(z * eye), + 0, 0, 0, 1 ); + return m; +} + +Matrix4f Matrix4f::LookAtLH(const Vector3f& eye, const Vector3f& at, const Vector3f& up) +{ + Vector3f z = (at - eye).Normalized(); // Forward + Vector3f x = up.Cross(z).Normalized(); // Right + Vector3f y = z.Cross(x); + + Matrix4f m(x.x, x.y, x.z, -(x * eye), + y.x, y.y, y.z, -(y * eye), + z.x, z.y, z.z, -(z * eye), + 0, 0, 0, 1 ); + return m; +} + + +Matrix4f Matrix4f::PerspectiveLH(float yfov, float aspect, float znear, float zfar) +{ + Matrix4f m; + float tanHalfFov = tan(yfov * 0.5f); + + m.M[0][0] = 1.0f / (aspect * tanHalfFov); + m.M[1][1] = 1.0f / tanHalfFov; + m.M[2][2] = zfar / (zfar - znear); + m.M[3][2] = 1.0f; + m.M[2][3] = (zfar * znear) / (znear - zfar); + m.M[3][3] = 0.0f; + + // Note: Post-projection matrix result assumes Left-Handed coordinate system, + // with Y up, X right and Z forward. This supports positive z-buffer values. + return m; +} + + +Matrix4f Matrix4f::PerspectiveRH(float yfov, float aspect, float znear, float zfar) +{ + Matrix4f m; + float tanHalfFov = tan(yfov * 0.5f); + + m.M[0][0] = 1.0f / (aspect * tanHalfFov); + m.M[1][1] = 1.0f / tanHalfFov; + m.M[2][2] = zfar / (znear - zfar); + // m.M[2][2] = zfar / (zfar - znear); + m.M[3][2] = -1.0f; + m.M[2][3] = (zfar * znear) / (znear - zfar); + m.M[3][3] = 0.0f; + + // Note: Post-projection matrix result assumes Left-Handed coordinate system, + // with Y up, X right and Z forward. This supports positive z-buffer values. + // This is the case even for RHS cooridnate input. + return m; +} + + +/* +OffCenterLH + +2*zn/(r-l) 0 0 0 +0 2*zn/(t-b) 0 0 +(l+r)/(l-r) (t+b)/(b-t) zf/(zf-zn) 1 +0 0 zn*zf/(zn-zf) 0 + +*/ + + +Matrix4f Matrix4f::Ortho2D(float w, float h) +{ + Matrix4f m; + m.M[0][0] = 2.0f/w; + m.M[1][1] = -2.0f/h; + m.M[0][3] = -1.0; + m.M[1][3] = 1.0; + m.M[2][2] = 0; + return m; +} + +} diff --git a/LibOVR/Src/Kernel/OVR_Math.h b/LibOVR/Src/Kernel/OVR_Math.h new file mode 100644 index 0000000..8c5a7ba --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Math.h @@ -0,0 +1,1070 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Math.h +Content : Implementation of 3D primitives such as vectors, matrices. +Created : September 4, 2012 +Authors : Andrew Reisse, Michael Antonov, Steve LaValle, Anna Yershova + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Math_h +#define OVR_Math_h + +#include +#include +#include + +#include "OVR_Types.h" +#include "OVR_RefCount.h" + +namespace OVR { + +//------------------------------------------------------------------------------------- +// Constants for 3D world/axis definitions. + +// Definitions of axes for coordinate and rotation conversions. +enum Axis +{ + Axis_X = 0, Axis_Y = 1, Axis_Z = 2 +}; + +// RotateDirection describes the rotation direction around an axis, interpreted as follows: +// CW - Clockwise while looking "down" from positive axis towards the origin. +// CCW - Counter-clockwise while looking from the positive axis towards the origin, +// which is in the negative axis direction. +// CCW is the default for the RHS coordinate system. Oculus standard RHS coordinate +// system defines Y up, X right, and Z back (pointing out from the screen). In this +// system Rotate_CCW around Z will specifies counter-clockwise rotation in XY plane. +enum RotateDirection +{ + Rotate_CCW = 1, + Rotate_CW = -1 +}; + +enum HandedSystem +{ + Handed_R = 1, Handed_L = -1 +}; + +// AxisDirection describes which way the axis points. Used by WorldAxes. +enum AxisDirection +{ + Axis_Up = 2, + Axis_Down = -2, + Axis_Right = 1, + Axis_Left = -1, + Axis_In = 3, + Axis_Out = -3 +}; + +struct WorldAxes +{ + AxisDirection XAxis, YAxis, ZAxis; + + WorldAxes(AxisDirection x, AxisDirection y, AxisDirection z) + : XAxis(x), YAxis(y), ZAxis(z) + { OVR_ASSERT(abs(x) != abs(y) && abs(y) != abs(z) && abs(z) != abs(x));} +}; + + +//------------------------------------------------------------------------------------- +// ***** Math + +// Math class contains constants and functions. This class is a template specialized +// per type, with Math and Math being distinct. +template +class Math +{ +}; + +// Single-precision Math constants class. +template<> +class Math +{ +public: + static const float Pi; + static const float TwoPi; + static const float PiOver2; + static const float PiOver4; + static const float E; + + static const float MaxValue; // Largest positive float Value + static const float MinPositiveValue; // Smallest possible positive value + + static const float RadToDegreeFactor; + static const float DegreeToRadFactor; + + static const float Tolerance; // 0.00001f; + static const float SingularityRadius; //0.00000000001f for Gimbal lock numerical problems +}; + +// Double-precision Math constants class. +template<> +class Math +{ +public: + static const double Pi; + static const double TwoPi; + static const double PiOver2; + static const double PiOver4; + static const double E; + + static const double MaxValue; // Largest positive double Value + static const double MinPositiveValue; // Smallest possible positive value + + static const double RadToDegreeFactor; + static const double DegreeToRadFactor; + + static const double Tolerance; // 0.00001f; + static const double SingularityRadius; //0.00000000001 for Gimbal lock numerical problems +}; + +typedef Math Mathf; +typedef Math Mathd; + +// Conversion functions between degrees and radians +template +FT RadToDegree(FT rads) { return rads * Math::RadToDegreeFactor; } +template +FT DegreeToRad(FT rads) { return rads * Math::DegreeToRadFactor; } + +template +class Quat; + +//------------------------------------------------------------------------------------- +// ***** Vector2f - 2D Vector2f + +// Vector2f represents a 2-dimensional vector or point in space, +// consisting of coordinates x and y, + +template +class Vector2 +{ +public: + T x, y; + + Vector2() : x(0), y(0) { } + Vector2(T x_, T y_) : x(x_), y(y_) { } + explicit Vector2(T s) : x(s), y(s) { } + + bool operator== (const Vector2& b) const { return x == b.x && y == b.y; } + bool operator!= (const Vector2& b) const { return x != b.x || y != b.y; } + + Vector2 operator+ (const Vector2& b) const { return Vector2(x + b.x, y + b.y); } + Vector2& operator+= (const Vector2& b) { x += b.x; y += b.y; return *this; } + Vector2 operator- (const Vector2& b) const { return Vector2(x - b.x, y - b.y); } + Vector2& operator-= (const Vector2& b) { x -= b.x; y -= b.y; return *this; } + Vector2 operator- () const { return Vector2(-x, -y); } + + // Scalar multiplication/division scales vector. + Vector2 operator* (T s) const { return Vector2(x*s, y*s); } + Vector2& operator*= (T s) { x *= s; y *= s; return *this; } + + Vector2 operator/ (T s) const { T rcp = T(1)/s; + return Vector2(x*rcp, y*rcp); } + Vector2& operator/= (T s) { T rcp = T(1)/s; + x *= rcp; y *= rcp; + return *this; } + + // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance. + bool Compare(const Vector2&b, T tolerance = Mathf::Tolerance) + { + return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance); + } + + // Dot product overload. + // Used to calculate angle q between two vectors among other things, + // as (A dot B) = |a||b|cos(q). + T operator* (const Vector2& b) const { return x*b.x + y*b.y; } + + // Returns the angle from this vector to b, in radians. + T Angle(const Vector2& b) const { return acos((*this * b)/(Length()*b.Length())); } + + // Return Length of the vector squared. + T LengthSq() const { return (x * x + y * y); } + // Return vector length. + T Length() const { return sqrt(LengthSq()); } + + // Returns distance between two points represented by vectors. + T Distance(Vector2& b) const { return (*this - b).Length(); } + + // Determine if this a unit vector. + bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math::Tolerance; } + // Normalize, convention vector length to 1. + void Normalize() { *this /= Length(); } + // Returns normalized (unit) version of the vector without modifying itself. + Vector2 Normalized() const { return *this / Length(); } + + // Linearly interpolates from this vector to another. + // Factor should be between 0.0 and 1.0, with 0 giving full value to this. + Vector2 Lerp(const Vector2& b, T f) const { return *this*(T(1) - f) + b*f; } + + // Projects this vector onto the argument; in other words, + // A.Project(B) returns projection of vector A onto B. + Vector2 ProjectTo(const Vector2& b) const { return b * ((*this * b) / b.LengthSq()); } +}; + + +typedef Vector2 Vector2f; +typedef Vector2 Vector2d; + +//------------------------------------------------------------------------------------- +// ***** Vector3f - 3D Vector3f + +// Vector3f represents a 3-dimensional vector or point in space, +// consisting of coordinates x, y and z. + +template +class Vector3 +{ +public: + T x, y, z; + + Vector3() : x(0), y(0), z(0) { } + Vector3(T x_, T y_, T z_ = 0) : x(x_), y(y_), z(z_) { } + explicit Vector3(T s) : x(s), y(s), z(s) { } + + bool operator== (const Vector3& b) const { return x == b.x && y == b.y && z == b.z; } + bool operator!= (const Vector3& b) const { return x != b.x || y != b.y || z != b.z; } + + Vector3 operator+ (const Vector3& b) const { return Vector3(x + b.x, y + b.y, z + b.z); } + Vector3& operator+= (const Vector3& b) { x += b.x; y += b.y; z += b.z; return *this; } + Vector3 operator- (const Vector3& b) const { return Vector3(x - b.x, y - b.y, z - b.z); } + Vector3& operator-= (const Vector3& b) { x -= b.x; y -= b.y; z -= b.z; return *this; } + Vector3 operator- () const { return Vector3(-x, -y, -z); } + + // Scalar multiplication/division scales vector. + Vector3 operator* (T s) const { return Vector3(x*s, y*s, z*s); } + Vector3& operator*= (T s) { x *= s; y *= s; z *= s; return *this; } + + Vector3 operator/ (T s) const { T rcp = T(1)/s; + return Vector3(x*rcp, y*rcp, z*rcp); } + Vector3& operator/= (T s) { T rcp = T(1)/s; + x *= rcp; y *= rcp; z *= rcp; + return *this; } + + // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance. + bool Compare(const Vector3&b, T tolerance = Mathf::Tolerance) + { + return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance) && (fabs(b.z-z) < tolerance); + } + + // Dot product overload. + // Used to calculate angle q between two vectors among other things, + // as (A dot B) = |a||b|cos(q). + T operator* (const Vector3& b) const { return x*b.x + y*b.y + z*b.z; } + + // Compute cross product, which generates a normal vector. + // Direction vector can be determined by right-hand rule: Pointing index finder in + // direction a and middle finger in direction b, thumb will point in a.Cross(b). + Vector3 Cross(const Vector3& b) const { return Vector3(y*b.z - z*b.y, + z*b.x - x*b.z, + x*b.y - y*b.x); } + + // Returns the angle from this vector to b, in radians. + T Angle(const Vector3& b) const { return acos((*this * b)/(Length()*b.Length())); } + + // Return Length of the vector squared. + T LengthSq() const { return (x * x + y * y + z * z); } + // Return vector length. + T Length() const { return sqrt(LengthSq()); } + + // Returns distance between two points represented by vectors. + T Distance(Vector3& b) const { return (*this - b).Length(); } + + // Determine if this a unit vector. + bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math::Tolerance; } + // Normalize, convention vector length to 1. + void Normalize() { *this /= Length(); } + // Returns normalized (unit) version of the vector without modifying itself. + Vector3 Normalized() const { return *this / Length(); } + + // Linearly interpolates from this vector to another. + // Factor should be between 0.0 and 1.0, with 0 giving full value to this. + Vector3 Lerp(const Vector3& b, T f) const { return *this*(T(1) - f) + b*f; } + + // Projects this vector onto the argument; in other words, + // A.Project(B) returns projection of vector A onto B. + Vector3 ProjectTo(const Vector3& b) const { return b * ((*this * b) / b.LengthSq()); } +}; + + +typedef Vector3 Vector3f; +typedef Vector3 Vector3d; + + +//------------------------------------------------------------------------------------- +// ***** Matrix4f + +// Matrix4f is a 4x4 matrix used for 3d transformations and projections. +// Translation stored in the last column. +// The matrix is stored in row-major order in memory, meaning that values +// of the first row are stored before the next one. +// +// The arrangement of the matrix is chosen to be in Right-Handed +// coordinate system and counterclockwise rotations when looking down +// the axis +// +// Transformation Order: +// - Transformations are applied from right to left, so the expression +// M1 * M2 * M3 * V means that the vector V is transformed by M3 first, +// followed by M2 and M1. +// +// Coordinate system: Right Handed +// +// Rotations: Counterclockwise when looking down the axis. All angles are in radians. +// +// | sx 01 02 tx | // First column (sx, 10, 20): Axis X basis vector. +// | 10 sy 12 ty | // Second column (01, sy, 21): Axis Y basis vector. +// | 20 21 sz tz | // Third columnt (02, 12, sz): Axis Z basis vector. +// | 30 31 32 33 | +// +// The basis vectors are first three columns. + +class Matrix4f +{ + static Matrix4f IdentityValue; + +public: + float M[4][4]; + + enum NoInitType { NoInit }; + + // Construct with no memory initialization. + Matrix4f(NoInitType) { } + + // By default, we construct identity matrix. + Matrix4f() + { + SetIdentity(); + } + + Matrix4f(float m11, float m12, float m13, float m14, + float m21, float m22, float m23, float m24, + float m31, float m32, float m33, float m34, + float m41, float m42, float m43, float m44) + { + M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = m14; + M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = m24; + M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = m34; + M[3][0] = m41; M[3][1] = m42; M[3][2] = m43; M[3][3] = m44; + } + + Matrix4f(float m11, float m12, float m13, + float m21, float m22, float m23, + float m31, float m32, float m33) + { + M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = 0; + M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = 0; + M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = 0; + M[3][0] = 0; M[3][1] = 0; M[3][2] = 0; M[3][3] = 1; + } + + static const Matrix4f& Identity() { return IdentityValue; } + + void SetIdentity() + { + M[0][0] = M[1][1] = M[2][2] = M[3][3] = 1; + M[0][1] = M[1][0] = M[2][3] = M[3][1] = 0; + M[0][2] = M[1][2] = M[2][0] = M[3][2] = 0; + M[0][3] = M[1][3] = M[2][1] = M[3][0] = 0; + } + + // Multiplies two matrices into destination with minimum copying. + static Matrix4f& Multiply(Matrix4f* d, const Matrix4f& a, const Matrix4f& b) + { + OVR_ASSERT((d != &a) && (d != &b)); + int i = 0; + do { + d->M[i][0] = a.M[i][0] * b.M[0][0] + a.M[i][1] * b.M[1][0] + a.M[i][2] * b.M[2][0] + a.M[i][3] * b.M[3][0]; + d->M[i][1] = a.M[i][0] * b.M[0][1] + a.M[i][1] * b.M[1][1] + a.M[i][2] * b.M[2][1] + a.M[i][3] * b.M[3][1]; + d->M[i][2] = a.M[i][0] * b.M[0][2] + a.M[i][1] * b.M[1][2] + a.M[i][2] * b.M[2][2] + a.M[i][3] * b.M[3][2]; + d->M[i][3] = a.M[i][0] * b.M[0][3] + a.M[i][1] * b.M[1][3] + a.M[i][2] * b.M[2][3] + a.M[i][3] * b.M[3][3]; + } while((++i) < 4); + + return *d; + } + + Matrix4f operator* (const Matrix4f& b) const + { + Matrix4f result(Matrix4f::NoInit); + Multiply(&result, *this, b); + return result; + } + + Matrix4f& operator*= (const Matrix4f& b) + { + return Multiply(this, Matrix4f(*this), b); + } + + Matrix4f operator* (float s) const + { + return Matrix4f(M[0][0] * s, M[0][1] * s, M[0][2] * s, M[0][3] * s, + M[1][0] * s, M[1][1] * s, M[1][2] * s, M[1][3] * s, + M[2][0] * s, M[2][1] * s, M[2][2] * s, M[2][3] * s, + M[3][0] * s, M[3][1] * s, M[3][2] * s, M[3][3] * s); + } + + Matrix4f& operator*= (float s) + { + M[0][0] *= s; M[0][1] *= s; M[0][2] *= s; M[0][3] *= s; + M[1][0] *= s; M[1][1] *= s; M[1][2] *= s; M[1][3] *= s; + M[2][0] *= s; M[2][1] *= s; M[2][2] *= s; M[2][3] *= s; + M[3][0] *= s; M[3][1] *= s; M[3][2] *= s; M[3][3] *= s; + return *this; + } + + Vector3f Transform(const Vector3f& v) const + { + return Vector3f(M[0][0] * v.x + M[0][1] * v.y + M[0][2] * v.z + M[0][3], + M[1][0] * v.x + M[1][1] * v.y + M[1][2] * v.z + M[1][3], + M[2][0] * v.x + M[2][1] * v.y + M[2][2] * v.z + M[2][3]); + } + + Matrix4f Transposed() const + { + return Matrix4f(M[0][0], M[1][0], M[2][0], M[3][0], + M[0][1], M[1][1], M[2][1], M[3][1], + M[0][2], M[1][2], M[2][2], M[3][2], + M[0][3], M[1][3], M[2][3], M[3][3]); + } + + void Transpose() + { + *this = Transposed(); + } + + + float SubDet (const int* rows, const int* cols) const + { + return M[rows[0]][cols[0]] * (M[rows[1]][cols[1]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[1]]) + - M[rows[0]][cols[1]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[0]]) + + M[rows[0]][cols[2]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[1]] - M[rows[1]][cols[1]] * M[rows[2]][cols[0]]); + } + + float Cofactor(int I, int J) const + { + const int indices[4][3] = {{1,2,3},{0,2,3},{0,1,3},{0,1,2}}; + return ((I+J)&1) ? -SubDet(indices[I],indices[J]) : SubDet(indices[I],indices[J]); + } + + float Determinant() const + { + return M[0][0] * Cofactor(0,0) + M[0][1] * Cofactor(0,1) + M[0][2] * Cofactor(0,2) + M[0][3] * Cofactor(0,3); + } + + Matrix4f Adjugated() const + { + return Matrix4f(Cofactor(0,0), Cofactor(1,0), Cofactor(2,0), Cofactor(3,0), + Cofactor(0,1), Cofactor(1,1), Cofactor(2,1), Cofactor(3,1), + Cofactor(0,2), Cofactor(1,2), Cofactor(2,2), Cofactor(3,2), + Cofactor(0,3), Cofactor(1,3), Cofactor(2,3), Cofactor(3,3)); + } + + Matrix4f Inverted() const + { + float det = Determinant(); + assert(det != 0); + return Adjugated() * (1.0f/det); + } + + void Invert() + { + *this = Inverted(); + } + + //AnnaSteve: + // a,b,c, are the YawPitchRoll angles to be returned + // rotation a around axis A1 + // is followed by rotation b around axis A2 + // is followed by rotation c around axis A3 + // rotations are CCW or CW (D) in LH or RH coordinate system (S) + template + void ToEulerAngles(float *a, float *b, float *c) + { + OVR_COMPILER_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3)); + + float psign = -1.0f; + if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) // Determine whether even permutation + psign = 1.0f; + + float pm = psign*M[A1][A3]; + if (pm < -1.0f + Math::SingularityRadius) + { // South pole singularity + *a = 0.0f; + *b = -S*D*Math::PiOver2; + *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] ); + } + else if (pm > 1.0 - Math::SingularityRadius) + { // North pole singularity + *a = 0.0f; + *b = S*D*Math::PiOver2; + *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] ); + } + else + { // Normal case (nonsingular) + *a = S*D*atan2( -psign*M[A2][A3], M[A3][A3] ); + *b = S*D*asin(pm); + *c = S*D*atan2( -psign*M[A1][A2], M[A1][A1] ); + } + + return; + } + + //AnnaSteve: + // a,b,c, are the YawPitchRoll angles to be returned + // rotation a around axis A1 + // is followed by rotation b around axis A2 + // is followed by rotation c around axis A1 + // rotations are CCW or CW (D) in LH or RH coordinate system (S) + template + void ToEulerAnglesABA(float *a, float *b, float *c) + { + OVR_COMPILER_ASSERT(A1 != A2); + + // Determine the axis that was not supplied + int m = 3 - A1 - A2; + + float psign = -1.0f; + if ((A1 + 1) % 3 == A2) // Determine whether even permutation + psign = 1.0f; + + float c2 = M[A1][A1]; + if (c2 < -1.0 + Math::SingularityRadius) + { // South pole singularity + *a = 0.0f; + *b = S*D*Math::Pi; + *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]); + } + else if (c2 > 1.0 - Math::SingularityRadius) + { // North pole singularity + *a = 0.0f; + *b = 0.0f; + *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]); + } + else + { // Normal case (nonsingular) + *a = S*D*atan2( M[A2][A1],-psign*M[m][A1]); + *b = S*D*acos(c2); + *c = S*D*atan2( M[A1][A2],psign*M[A1][m]); + } + return; + } + + // Creates a matrix that converts the vertices from one coordinate system + // to another. + // + static Matrix4f AxisConversion(const WorldAxes& to, const WorldAxes& from) + { + // Holds axis values from the 'to' structure + int toArray[3] = { to.XAxis, to.YAxis, to.ZAxis }; + + // The inverse of the toArray + int inv[4]; + inv[0] = inv[abs(to.XAxis)] = 0; + inv[abs(to.YAxis)] = 1; + inv[abs(to.ZAxis)] = 2; + + Matrix4f m(0, 0, 0, + 0, 0, 0, + 0, 0, 0); + + // Only three values in the matrix need to be changed to 1 or -1. + m.M[inv[abs(from.XAxis)]][0] = float(from.XAxis/toArray[inv[abs(from.XAxis)]]); + m.M[inv[abs(from.YAxis)]][1] = float(from.YAxis/toArray[inv[abs(from.YAxis)]]); + m.M[inv[abs(from.ZAxis)]][2] = float(from.ZAxis/toArray[inv[abs(from.ZAxis)]]); + return m; + } + + + + static Matrix4f Translation(const Vector3f& v) + { + Matrix4f t; + t.M[0][3] = v.x; + t.M[1][3] = v.y; + t.M[2][3] = v.z; + return t; + } + + static Matrix4f Translation(float x, float y, float z = 0.0f) + { + Matrix4f t; + t.M[0][3] = x; + t.M[1][3] = y; + t.M[2][3] = z; + return t; + } + + static Matrix4f Scaling(const Vector3f& v) + { + Matrix4f t; + t.M[0][0] = v.x; + t.M[1][1] = v.y; + t.M[2][2] = v.z; + return t; + } + + static Matrix4f Scaling(float x, float y, float z) + { + Matrix4f t; + t.M[0][0] = x; + t.M[1][1] = y; + t.M[2][2] = z; + return t; + } + + static Matrix4f Scaling(float s) + { + Matrix4f t; + t.M[0][0] = s; + t.M[1][1] = s; + t.M[2][2] = s; + return t; + } + + + + //AnnaSteve : Just for quick testing. Not for final API. Need to remove case. + static Matrix4f RotationAxis(Axis A, float angle, RotateDirection d, HandedSystem s) + { + float sina = s * d *sin(angle); + float cosa = cos(angle); + + switch(A) + { + case Axis_X: + return Matrix4f(1, 0, 0, + 0, cosa, -sina, + 0, sina, cosa); + case Axis_Y: + return Matrix4f(cosa, 0, sina, + 0, 1, 0, + -sina, 0, cosa); + case Axis_Z: + return Matrix4f(cosa, -sina, 0, + sina, cosa, 0, + 0, 0, 1); + } + } + + + // Creates a rotation matrix rotating around the X axis by 'angle' radians. + // Rotation direction is depends on the coordinate system: + // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), + // while looking in the negative axis direction. This is the + // same as looking down from positive axis values towards origin. + // LHS: Positive angle values rotate clock-wise (CW), while looking in the + // negative axis direction. + static Matrix4f RotationX(float angle) + { + float sina = sin(angle); + float cosa = cos(angle); + return Matrix4f(1, 0, 0, + 0, cosa, -sina, + 0, sina, cosa); + } + + // Creates a rotation matrix rotating around the Y axis by 'angle' radians. + // Rotation direction is depends on the coordinate system: + // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), + // while looking in the negative axis direction. This is the + // same as looking down from positive axis values towards origin. + // LHS: Positive angle values rotate clock-wise (CW), while looking in the + // negative axis direction. + static Matrix4f RotationY(float angle) + { + float sina = sin(angle); + float cosa = cos(angle); + return Matrix4f(cosa, 0, sina, + 0, 1, 0, + -sina, 0, cosa); + } + + // Creates a rotation matrix rotating around the Z axis by 'angle' radians. + // Rotation direction is depends on the coordinate system: + // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), + // while looking in the negative axis direction. This is the + // same as looking down from positive axis values towards origin. + // LHS: Positive angle values rotate clock-wise (CW), while looking in the + // negative axis direction. + static Matrix4f RotationZ(float angle) + { + float sina = sin(angle); + float cosa = cos(angle); + return Matrix4f(cosa, -sina, 0, + sina, cosa, 0, + 0, 0, 1); + } + + + // LookAtRH creates a View transformation matrix for right-handed coordinate system. + // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up' + // specifying the up vector. The resulting matrix should be used with PerspectiveRH + // projection. + static Matrix4f LookAtRH(const Vector3f& eye, const Vector3f& at, const Vector3f& up); + + // LookAtLH creates a View transformation matrix for left-handed coordinate system. + // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up' + // specifying the up vector. + static Matrix4f LookAtLH(const Vector3f& eye, const Vector3f& at, const Vector3f& up); + + + // PerspectiveRH creates a right-handed perspective projection matrix that can be + // used with the Oculus sample renderer. + // yfov - Specifies vertical field of view in radians. + // aspect - Screen aspect ration, which is usually width/height for square pixels. + // Note that xfov = yfov * aspect. + // znear - Absolute value of near Z clipping clipping range. + // zfar - Absolute value of far Z clipping clipping range (larger then near). + // Even though RHS usually looks in the direction of negative Z, positive values + // are expected for znear and zfar. + static Matrix4f PerspectiveRH(float yfov, float aspect, float znear, float zfar); + + + // PerspectiveRH creates a left-handed perspective projection matrix that can be + // used with the Oculus sample renderer. + // yfov - Specifies vertical field of view in radians. + // aspect - Screen aspect ration, which is usually width/height for square pixels. + // Note that xfov = yfov * aspect. + // znear - Absolute value of near Z clipping clipping range. + // zfar - Absolute value of far Z clipping clipping range (larger then near). + static Matrix4f PerspectiveLH(float yfov, float aspect, float znear, float zfar); + + + static Matrix4f Ortho2D(float w, float h); +}; + + +//------------------------------------------------------------------------------------- +// ***** Quat + +// Quatf represents a quaternion class used for rotations. +// +// Quaternion multiplications are done in right-to-left order, to match the +// behavior of matrices. + + +template +class Quat +{ +public: + // w + Xi + Yj + Zk + T x, y, z, w; + + Quat() : x(0), y(0), z(0), w(1) {} + Quat(T x_, T y_, T z_, T w_) : x(x_), y(y_), z(z_), w(w_) {} + + + // Constructs rotation quaternion around the axis. + Quat(const Vector3& axis, T angle) + { + Vector3 unitAxis = axis.Normalized(); + T sinHalfAngle = sin(angle * T(0.5)); + + w = cos(angle * T(0.5)); + x = unitAxis.x * sinHalfAngle; + y = unitAxis.y * sinHalfAngle; + z = unitAxis.z * sinHalfAngle; + } + + //AnnaSteve: + void AxisAngle(Axis A, T angle, RotateDirection d, HandedSystem s) + { + T sinHalfAngle = s * d *sin(angle * (T)0.5); + T v[3]; + v[0] = v[1] = v[2] = (T)0; + v[A] = sinHalfAngle; + //return Quat(v[0], v[1], v[2], cos(angle * (T)0.5)); + w = cos(angle * (T)0.5); + x = v[0]; + y = v[1]; + z = v[2]; + } + + + void GetAxisAngle(Vector3* axis, T* angle) const + { + if (LengthSq() > Math::Tolerance * Math::Tolerance) + { + *axis = Vector3(x, y, z).Normalized(); + *angle = 2 * acos(w); + } + else + { + *axis = Vector3(1, 0, 0); + *angle= 0; + } + } + + bool operator== (const Quat& b) const { return x == b.x && y == b.y && z == b.z && w == b.w; } + bool operator!= (const Quat& b) const { return x != b.x || y != b.y || z != b.z || w != b.w; } + + Quat operator+ (const Quat& b) const { return Quat(x + b.x, y + b.y, z + b.z, w + b.w); } + Quat& operator+= (const Quat& b) { w += b.w; x += b.x; y += b.y; z += b.z; return *this; } + Quat operator- (const Quat& b) const { return Quat(x - b.x, y - b.y, z - b.z, w - b.w); } + Quat& operator-= (const Quat& b) { w -= b.w; x -= b.x; y -= b.y; z -= b.z; return *this; } + + Quat operator* (T s) const { return Quat(x * s, y * s, z * s, w * s); } + Quat& operator*= (T s) { w *= s; x *= s; y *= s; z *= s; return *this; } + Quat operator/ (T s) const { T rcp = T(1)/s; return Quat(x * rcp, y * rcp, z * rcp, w *rcp); } + Quat& operator/= (T s) { T rcp = T(1)/s; w *= rcp; x *= rcp; y *= rcp; z *= rcp; return *this; } + + // Get Imaginary part vector + Vector3 Imag() const { return Vector3(x,y,z); } + + // Get quaternion length. + T Length() const { return sqrt(x * x + y * y + z * z + w * w); } + // Get quaternion length squared. + T LengthSq() const { return (x * x + y * y + z * z + w * w); } + // Simple Eulidean distance in R^4 (not SLERP distance, but at least respects Haar measure) + T Distance(const Quat& q) const + { + T d1 = (*this - q).Length(); + T d2 = (*this + q).Length(); // Antipoldal point check + return (d1 < d2) ? d1 : d2; + } + T DistanceSq(const Quat& q) const + { + T d1 = (*this - q).LengthSq(); + T d2 = (*this + q).LengthSq(); // Antipoldal point check + return (d1 < d2) ? d1 : d2; + } + + // Normalize + bool IsNormalized() const { return fabs(LengthSq() - 1) < Math::Tolerance; } + void Normalize() { *this /= Length(); } + Quat Normalized() const { return *this / Length(); } + + // Returns conjugate of the quaternion. Produces inverse rotation if quaternion is normalized. + Quat Conj() const { return Quat(-x, -y, -z, w); } + + // AnnaSteve fixed: order of quaternion multiplication + // Quaternion multiplication. Combines quaternion rotations, performing the one on the + // right hand side first. + Quat operator* (const Quat& b) const { return Quat(w * b.x + x * b.w + y * b.z - z * b.y, + w * b.y - x * b.z + y * b.w + z * b.x, + w * b.z + x * b.y - y * b.x + z * b.w, + w * b.w - x * b.x - y * b.y - z * b.z); } + + // + // this^p normalized; same as rotating by this p times. + Quat PowNormalized(T p) const + { + Vector3 v; + T a; + GetAxisAngle(&v, &a); + return Quat(v, a * p); + } + + // Rotate transforms vector in a manner that matches Matrix rotations (counter-clockwise, + // assuming negative direction of the axis). Standard formula: q(t) * V * q(t)^-1. + Vector3 Rotate(const Vector3& v) const + { + return ((*this * Quat(v.x, v.y, v.z, 0)) * Inverted()).Imag(); + } + + + // Inversed quaternion rotates in the opposite direction. + Quat Inverted() const + { + return Quat(-x, -y, -z, w); + } + + // Sets this quaternion to the one rotates in the opposite direction. + void Invert() const + { + *this = Quat(-x, -y, -z, w); + } + + // Converting quaternion to matrix. + operator Matrix4f() const + { + T ww = w*w; + T xx = x*x; + T yy = y*y; + T zz = z*z; + + return Matrix4f(float(ww + xx - yy - zz), float(T(2) * (x*y - w*z)), float(T(2) * (x*z + w*y)), + float(T(2) * (x*y + w*z)), float(ww - xx + yy - zz), float(T(2) * (y*z - w*x)), + float(T(2) * (x*z - w*y)), float(T(2) * (y*z + w*x)), float(ww - xx - yy + zz) ); + } + + + // GetEulerAngles extracts Euler angles from the quaternion, in the specified order of + // axis rotations and the specified coordinate system. Right-handed coordinate system + // is the default, with CCW rotations while looking in the negative axis direction. + // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned. + // rotation a around axis A1 + // is followed by rotation b around axis A2 + // is followed by rotation c around axis A3 + // rotations are CCW or CW (D) in LH or RH coordinate system (S) + template + void GetEulerAngles(T *a, T *b, T *c) + { + OVR_COMPILER_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3)); + + T Q[3] = { x, y, z }; //Quaternion components x,y,z + + T ww = w*w; + T Q11 = Q[A1]*Q[A1]; + T Q22 = Q[A2]*Q[A2]; + T Q33 = Q[A3]*Q[A3]; + + T psign = T(-1.0); + // Determine whether even permutation + if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) + psign = T(1.0); + + T s2 = psign * T(2.0) * (psign*w*Q[A2] + Q[A1]*Q[A3]); + + if (s2 < (T)-1.0 + Math::SingularityRadius) + { // South pole singularity + *a = T(0.0); + *b = -S*D*Math::PiOver2; + *c = S*D*atan2((T)2.0*(psign*Q[A1]*Q[A2] + w*Q[A3]), + ww + Q22 - Q11 - Q33 ); + } + else if (s2 > (T)1.0 - Math::SingularityRadius) + { // North pole singularity + *a = (T)0.0; + *b = S*D*Math::PiOver2; + *c = S*D*atan2((T)2.0*(psign*Q[A1]*Q[A2] + w*Q[A3]), + ww + Q22 - Q11 - Q33); + } + else + { + *a = -S*D*atan2((T)-2.0*(w*Q[A1] - psign*Q[A2]*Q[A3]), + ww + Q33 - Q11 - Q22); + *b = S*D*asin(s2); + *c = S*D*atan2((T)2.0*(w*Q[A3] - psign*Q[A1]*Q[A2]), + ww + Q11 - Q22 - Q33); + } + return; + } + + template + void GetEulerAngles(T *a, T *b, T *c) + { GetEulerAngles(a, b, c); } + + template + void GetEulerAngles(T *a, T *b, T *c) + { GetEulerAngles(a, b, c); } + + + // GetEulerAnglesABA extracts Euler angles from the quaternion, in the specified order of + // axis rotations and the specified coordinate system. Right-handed coordinate system + // is the default, with CCW rotations while looking in the negative axis direction. + // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned. + // rotation a around axis A1 + // is followed by rotation b around axis A2 + // is followed by rotation c around axis A1 + // Rotations are CCW or CW (D) in LH or RH coordinate system (S) + template + void GetEulerAnglesABA(T *a, T *b, T *c) + { + OVR_COMPILER_ASSERT(A1 != A2); + + T Q[3] = {x, y, z}; // Quaternion components + + // Determine the missing axis that was not supplied + int m = 3 - A1 - A2; + + T ww = w*w; + T Q11 = Q[A1]*Q[A1]; + T Q22 = Q[A2]*Q[A2]; + T Qmm = Q[m]*Q[m]; + + T psign = T(-1.0); + if ((A1 + 1) % 3 == A2) // Determine whether even permutation + { + psign = (T)1.0; + } + + T c2 = ww + Q11 - Q22 - Qmm; + if (c2 < (T)-1.0 + Math::SingularityRadius) + { // South pole singularity + *a = (T)0.0; + *b = S*D*Math::Pi; + *c = S*D*atan2( (T)2.0*(w*Q[A1] - psign*Q[A2]*Q[m]), + ww + Q22 - Q11 - Qmm); + } + else if (c2 > (T)1.0 - Math::SingularityRadius) + { // North pole singularity + *a = (T)0.0; + *b = (T)0.0; + *c = S*D*atan2( (T)2.0*(w*Q[A1] - psign*Q[A2]*Q[m]), + ww + Q22 - Q11 - Qmm); + } + else + { + *a = S*D*atan2( psign*w*Q[m] + Q[A1]*Q[A2], + w*Q[A2] -psign*Q[A1]*Q[m]); + *b = S*D*acos(c2); + *c = S*D*atan2( -psign*w*Q[m] + Q[A1]*Q[A2], + w*Q[A2] + psign*Q[A1]*Q[m]); + } + return; + } +}; + + +typedef Quat Quatf; +typedef Quat Quatd; + +//------------------------------------------------------------------------------------- +// ***** Plane + +// Consists of a normal vector and distance from the origin where the plane is located. + +template +class Plane : public RefCountBase > +{ +public: + Vector3 N; + T D; + + Plane() : D(0) {} + + // Normals must already be normalized + Plane(const Vector3& n, T d) : N(n), D(d) {} + Plane(T x, T y, T z, T d) : N(x,y,z), D(d) {} + + // construct from a point on the plane and the normal + Plane(const Vector3& p, const Vector3& n) : N(n), D(-(p * n)) {} + + // Find the point to plane distance. The sign indicates what side of the plane the point is on (0 = point on plane). + T TestSide(const Vector3& p) const + { + return (N * p) + D; + } + + Plane Flipped() const + { + return Plane(-N, -D); + } + + void Flip() + { + N = -N; + D = -D; + } + + bool operator==(const Plane& rhs) const + { + return (this->D == rhs.D && this->N == rhs.N); + } +}; + +typedef Plane Planef; + +} + +#endif diff --git a/LibOVR/Src/Kernel/OVR_RefCount.cpp b/LibOVR/Src/Kernel/OVR_RefCount.cpp new file mode 100644 index 0000000..a1171fa --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_RefCount.cpp @@ -0,0 +1,100 @@ +/************************************************************************************ + +Filename : OVR_RefCount.cpp +Content : Reference counting implementation +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_RefCount.h" +#include "OVR_Atomic.h" +#include "OVR_Log.h" + +namespace OVR { + +#ifdef OVR_CC_ARM +void* ReturnArg0(void* p) +{ + return p; +} +#endif + +// ***** Reference Count Base implementation + +RefCountImplCore::~RefCountImplCore() +{ + // RefCount can be either 1 or 0 here. + // 0 if Release() was properly called. + // 1 if the object was declared on stack or as an aggregate. + OVR_ASSERT(RefCount <= 1); +} + +#ifdef OVR_BUILD_DEBUG +void RefCountImplCore::reportInvalidDelete(void *pmem) +{ + OVR_DEBUG_LOG( + ("Invalid delete call on ref-counted object at %p. Please use Release()", pmem)); + OVR_ASSERT(0); +} +#endif + +RefCountNTSImplCore::~RefCountNTSImplCore() +{ + // RefCount can be either 1 or 0 here. + // 0 if Release() was properly called. + // 1 if the object was declared on stack or as an aggregate. + OVR_ASSERT(RefCount <= 1); +} + +#ifdef OVR_BUILD_DEBUG +void RefCountNTSImplCore::reportInvalidDelete(void *pmem) +{ + OVR_DEBUG_LOG( + ("Invalid delete call on ref-counted object at %p. Please use Release()", pmem)); + OVR_ASSERT(0); +} +#endif + + +// *** Thread-Safe RefCountImpl + +void RefCountImpl::AddRef() +{ + AtomicOps::ExchangeAdd_NoSync(&RefCount, 1); +} +void RefCountImpl::Release() +{ + if ((AtomicOps::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0) + delete this; +} + +// *** Thread-Safe RefCountVImpl w/virtual AddRef/Release + +void RefCountVImpl::AddRef() +{ + AtomicOps::ExchangeAdd_NoSync(&RefCount, 1); +} +void RefCountVImpl::Release() +{ + if ((AtomicOps::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0) + delete this; +} + +// *** NON-Thread-Safe RefCountImpl + +void RefCountNTSImpl::Release() const +{ + RefCount--; + if (RefCount == 0) + delete this; +} + + +} // OVR diff --git a/LibOVR/Src/Kernel/OVR_RefCount.h b/LibOVR/Src/Kernel/OVR_RefCount.h new file mode 100644 index 0000000..8622050 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_RefCount.h @@ -0,0 +1,522 @@ +/************************************************************************************ + +PublicHeader: Kernel +Filename : OVR_RefCount.h +Content : Reference counting implementation headers +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_RefCount_h +#define OVR_RefCount_h + +#include "OVR_Types.h" +#include "OVR_Allocator.h" + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** Reference Counting + +// There are three types of reference counting base classes: +// +// RefCountBase - Provides thread-safe reference counting (Default). +// RefCountBaseNTS - Non Thread Safe version of reference counting. + + +// ***** Declared classes + +template +class RefCountBase; +template +class RefCountBaseNTS; + +class RefCountImpl; +class RefCountNTSImpl; + + +//----------------------------------------------------------------------------------- +// ***** Implementation For Reference Counting + +// RefCountImplCore holds RefCount value and defines a few utility +// functions shared by all implementations. + +class RefCountImplCore +{ +protected: + volatile int RefCount; + +public: + // RefCountImpl constructor always initializes RefCount to 1 by default. + OVR_FORCE_INLINE RefCountImplCore() : RefCount(1) { } + + // Need virtual destructor + // This: 1. Makes sure the right destructor's called. + // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem() + virtual ~RefCountImplCore(); + + // Debug method only. + int GetRefCount() const { return RefCount; } + + // This logic is used to detect invalid 'delete' calls of reference counted + // objects. Direct delete calls are not allowed on them unless they come in + // internally from Release. +#ifdef OVR_BUILD_DEBUG + static void OVR_CDECL reportInvalidDelete(void *pmem); + inline static void checkInvalidDelete(RefCountImplCore *pmem) + { + if (pmem->RefCount != 0) + reportInvalidDelete(pmem); + } +#else + inline static void checkInvalidDelete(RefCountImplCore *) { } +#endif + + // Base class ref-count content should not be copied. + void operator = (const RefCountImplCore &) { } +}; + +class RefCountNTSImplCore +{ +protected: + mutable int RefCount; + +public: + // RefCountImpl constructor always initializes RefCount to 1 by default. + OVR_FORCE_INLINE RefCountNTSImplCore() : RefCount(1) { } + + // Need virtual destructor + // This: 1. Makes sure the right destructor's called. + // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem() + virtual ~RefCountNTSImplCore(); + + // Debug method only. + int GetRefCount() const { return RefCount; } + + // This logic is used to detect invalid 'delete' calls of reference counted + // objects. Direct delete calls are not allowed on them unless they come in + // internally from Release. +#ifdef OVR_BUILD_DEBUG + static void OVR_CDECL reportInvalidDelete(void *pmem); + OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *pmem) + { + if (pmem->RefCount != 0) + reportInvalidDelete(pmem); + } +#else + OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *) { } +#endif + + // Base class ref-count content should not be copied. + void operator = (const RefCountNTSImplCore &) { } +}; + + + +// RefCountImpl provides Thread-Safe implementation of reference counting, so +// it should be used by default in most places. + +class RefCountImpl : public RefCountImplCore +{ +public: + // Thread-Safe Ref-Count Implementation. + void AddRef(); + void Release(); +}; + +// RefCountVImpl provides Thread-Safe implementation of reference counting, plus, +// virtual AddRef and Release. + +class RefCountVImpl : public RefCountImplCore +{ +public: + // Thread-Safe Ref-Count Implementation. + virtual void AddRef(); + virtual void Release(); +}; + + +// RefCountImplNTS provides Non-Thread-Safe implementation of reference counting, +// which is slightly more efficient since it doesn't use atomics. + +class RefCountNTSImpl : public RefCountNTSImplCore +{ +public: + OVR_FORCE_INLINE void AddRef() const { RefCount++; } + void Release() const; +}; + + + +// RefCountBaseStatImpl<> is a common class that adds new/delete override with Stat tracking +// to the reference counting implementation. Base must be one of the RefCountImpl classes. + +template +class RefCountBaseStatImpl : public Base +{ +public: + RefCountBaseStatImpl() { } + + // *** Override New and Delete + + // DOM-IGNORE-BEGIN + // Undef new temporarily if it is being redefined +#ifdef OVR_DEFINE_NEW +#undef new +#endif + +#ifdef OVR_BUILD_DEBUG + // Custom check used to detect incorrect calls of 'delete' on ref-counted objects. + #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p) \ + do {if (p) Base::checkInvalidDelete((class_name*)p); } while(0) +#else + #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p) +#endif + + // Redefine all new & delete operators. + OVR_MEMORY_REDEFINE_NEW_IMPL(Base, OVR_REFCOUNTALLOC_CHECK_DELETE) + +#ifdef OVR_DEFINE_NEW +#define new OVR_DEFINE_NEW +#endif + // OVR_BUILD_DEFINE_NEW + // DOM-IGNORE-END +}; + + + +//----------------------------------------------------------------------------------- +// *** End user RefCountBase<> classes + + +// RefCountBase is a base class for classes that require thread-safe reference +// counting; it also overrides the new and delete operators to use MemoryHeap. +// +// Reference counted objects start out with RefCount value of 1. Further lifetime +// management is done through the AddRef() and Release() methods, typically +// hidden by Ptr<>. + +template +class RefCountBase : public RefCountBaseStatImpl +{ +public: + // Constructor. + OVR_FORCE_INLINE RefCountBase() : RefCountBaseStatImpl() { } +}; + +// RefCountBaseV is the same as RefCountBase but with virtual AddRef/Release + +template +class RefCountBaseV : public RefCountBaseStatImpl +{ +public: + // Constructor. + OVR_FORCE_INLINE RefCountBaseV() : RefCountBaseStatImpl() { } +}; + + +// RefCountBaseNTS is a base class for classes that require Non-Thread-Safe reference +// counting; it also overrides the new and delete operators to use MemoryHeap. +// This class should only be used if all pointers to it are known to be assigned, +// destroyed and manipulated within one thread. +// +// Reference counted objects start out with RefCount value of 1. Further lifetime +// management is done through the AddRef() and Release() methods, typically +// hidden by Ptr<>. + +template +class RefCountBaseNTS : public RefCountBaseStatImpl +{ +public: + // Constructor. + OVR_FORCE_INLINE RefCountBaseNTS() : RefCountBaseStatImpl() { } +}; + +//----------------------------------------------------------------------------------- +// ***** Pickable template pointer +enum PickType { PickValue }; + +template +class Pickable +{ +public: + Pickable() : pV(NULL) {} + explicit Pickable(T* p) : pV(p) {} + Pickable(T* p, PickType) : pV(p) + { + OVR_ASSERT(pV); + if (pV) + pV->AddRef(); + } + template + Pickable(const Pickable& other) : pV(other.GetPtr()) {} + +public: + Pickable& operator =(const Pickable& other) + { + OVR_ASSERT(pV == NULL); + pV = other.pV; + // Extra check. + //other.pV = NULL; + return *this; + } + +public: + T* GetPtr() const { return pV; } + T* operator->() const + { + return pV; + } + T& operator*() const + { + OVR_ASSERT(pV); + return *pV; + } + +private: + T* pV; +}; + +template +OVR_FORCE_INLINE +Pickable MakePickable(T* p) +{ + return Pickable(p); +} + +//----------------------------------------------------------------------------------- +// ***** Ref-Counted template pointer + +// Automatically AddRefs and Releases interfaces + +void* ReturnArg0(void* p); + +template +class Ptr +{ +#ifdef OVR_CC_ARM + static C* ReturnArg(void* p) { return (C*)ReturnArg0(p); } +#endif + +protected: + C *pObject; + +public: + + // Constructors + OVR_FORCE_INLINE Ptr() : pObject(0) + { } +#ifdef OVR_CC_ARM + OVR_FORCE_INLINE Ptr(C &robj) : pObject(ReturnArg(&robj)) +#else + OVR_FORCE_INLINE Ptr(C &robj) : pObject(&robj) +#endif + { } + OVR_FORCE_INLINE Ptr(Pickable v) : pObject(v.GetPtr()) + { + // No AddRef() on purpose. + } + OVR_FORCE_INLINE Ptr(Ptr& other, PickType) : pObject(other.pObject) + { + other.pObject = NULL; + // No AddRef() on purpose. + } + OVR_FORCE_INLINE Ptr(C *pobj) + { + if (pobj) pobj->AddRef(); + pObject = pobj; + } + OVR_FORCE_INLINE Ptr(const Ptr &src) + { + if (src.pObject) src.pObject->AddRef(); + pObject = src.pObject; + } + + template + OVR_FORCE_INLINE Ptr(Ptr &src) + { + if (src) src->AddRef(); + pObject = src; + } + template + OVR_FORCE_INLINE Ptr(Pickable v) : pObject(v.GetPtr()) + { + // No AddRef() on purpose. + } + + // Destructor + OVR_FORCE_INLINE ~Ptr() + { + if (pObject) pObject->Release(); + } + + // Compares + OVR_FORCE_INLINE bool operator == (const Ptr &other) const { return pObject == other.pObject; } + OVR_FORCE_INLINE bool operator != (const Ptr &other) const { return pObject != other.pObject; } + + OVR_FORCE_INLINE bool operator == (C *pother) const { return pObject == pother; } + OVR_FORCE_INLINE bool operator != (C *pother) const { return pObject != pother; } + + + OVR_FORCE_INLINE bool operator < (const Ptr &other) const { return pObject < other.pObject; } + + // Assignment + template + OVR_FORCE_INLINE const Ptr& operator = (const Ptr &src) + { + if (src) src->AddRef(); + if (pObject) pObject->Release(); + pObject = src; + return *this; + } + // Specialization + OVR_FORCE_INLINE const Ptr& operator = (const Ptr &src) + { + if (src) src->AddRef(); + if (pObject) pObject->Release(); + pObject = src; + return *this; + } + + OVR_FORCE_INLINE const Ptr& operator = (C *psrc) + { + if (psrc) psrc->AddRef(); + if (pObject) pObject->Release(); + pObject = psrc; + return *this; + } + OVR_FORCE_INLINE const Ptr& operator = (C &src) + { + if (pObject) pObject->Release(); + pObject = &src; + return *this; + } + OVR_FORCE_INLINE Ptr& operator = (Pickable src) + { + return Pick(src); + } + template + OVR_FORCE_INLINE Ptr& operator = (Pickable src) + { + return Pick(src); + } + + // Set Assignment + template + OVR_FORCE_INLINE Ptr& SetPtr(const Ptr &src) + { + if (src) src->AddRef(); + if (pObject) pObject->Release(); + pObject = src; + return *this; + } + // Specialization + OVR_FORCE_INLINE Ptr& SetPtr(const Ptr &src) + { + if (src) src->AddRef(); + if (pObject) pObject->Release(); + pObject = src; + return *this; + } + + OVR_FORCE_INLINE Ptr& SetPtr(C *psrc) + { + if (psrc) psrc->AddRef(); + if (pObject) pObject->Release(); + pObject = psrc; + return *this; + } + OVR_FORCE_INLINE Ptr& SetPtr(C &src) + { + if (pObject) pObject->Release(); + pObject = &src; + return *this; + } + OVR_FORCE_INLINE Ptr& SetPtr(Pickable src) + { + return Pick(src); + } + + // Nulls ref-counted pointer without decrement + OVR_FORCE_INLINE void NullWithoutRelease() + { + pObject = 0; + } + + // Clears the pointer to the object + OVR_FORCE_INLINE void Clear() + { + if (pObject) pObject->Release(); + pObject = 0; + } + + // Obtain pointer reference directly, for D3D interfaces + OVR_FORCE_INLINE C*& GetRawRef() { return pObject; } + + // Access Operators + OVR_FORCE_INLINE C* GetPtr() const { return pObject; } + OVR_FORCE_INLINE C& operator * () const { return *pObject; } + OVR_FORCE_INLINE C* operator -> () const { return pObject; } + // Conversion + OVR_FORCE_INLINE operator C* () const { return pObject; } + + // Pickers. + + // Pick a value. + OVR_FORCE_INLINE Ptr& Pick(Ptr& other) + { + if (&other != this) + { + if (pObject) pObject->Release(); + pObject = other.pObject; + other.pObject = 0; + } + + return *this; + } + + OVR_FORCE_INLINE Ptr& Pick(Pickable v) + { + if (v.GetPtr() != pObject) + { + if (pObject) pObject->Release(); + pObject = v.GetPtr(); + } + + return *this; + } + + template + OVR_FORCE_INLINE Ptr& Pick(Pickable v) + { + if (v.GetPtr() != pObject) + { + if (pObject) pObject->Release(); + pObject = v.GetPtr(); + } + + return *this; + } + + OVR_FORCE_INLINE Ptr& Pick(C* p) + { + if (p != pObject) + { + if (pObject) pObject->Release(); + pObject = p; + } + + return *this; + } +}; + +} // OVR + +#endif diff --git a/LibOVR/Src/Kernel/OVR_Std.cpp b/LibOVR/Src/Kernel/OVR_Std.cpp new file mode 100644 index 0000000..088f9e9 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Std.cpp @@ -0,0 +1,1025 @@ +/************************************************************************************ + +Filename : OVR_Std.cpp +Content : Standard C function implementation +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_Std.h" +#include "OVR_Alg.h" + +// localeconv() call in OVR_strtod() +#include + +namespace OVR { + +// Source for functions not available on all platforms is included here. + +// Case insensitive compare implemented in platform-specific way. +int OVR_CDECL OVR_stricmp(const char* a, const char* b) +{ +#if defined(OVR_OS_WIN32) + #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) + return ::_stricmp(a, b); + #else + return ::stricmp(a, b); + #endif + +#else + return strcasecmp(a, b); +#endif +} + +int OVR_CDECL OVR_strnicmp(const char* a, const char* b, UPInt count) +{ +#if defined(OVR_OS_WIN32) +#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) + return ::_strnicmp(a, b, count); +#else + return ::strnicmp(a, b, count); +#endif + +#else + return strncasecmp(a, b, count); +#endif +} + +wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, UPInt destsize, const wchar_t* src) +{ +#if defined(OVR_MSVC_SAFESTRING) + wcscpy_s(dest, destsize, src); + return dest; +#elif defined(OVR_OS_WIN32) + OVR_UNUSED(destsize); + wcscpy(dest, src); + return dest; +#else + UPInt l = OVR_wcslen(src) + 1; // incl term null + l = (l < destsize) ? l : destsize; + memcpy(dest, src, l * sizeof(wchar_t)); + return dest; +#endif +} + +wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, UPInt destsize, const wchar_t* src, UPInt count) +{ +#if defined(OVR_MSVC_SAFESTRING) + wcsncpy_s(dest, destsize, src, count); + return dest; +#else + UPInt srclen = OVR_wcslen(src); + UPInt l = Alg::Min(srclen, count); + l = (l < destsize) ? l : destsize; + memcpy(dest, src, l * sizeof(wchar_t)); + if (count > srclen) + { + UPInt remLen = Alg::Min(destsize - l, (count - srclen)); + memset(&dest[l], 0, sizeof(wchar_t)*remLen); + } + else if (l < destsize) + dest[l] = 0; + return dest; +#endif +} + + +wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, UPInt destsize, const wchar_t* src) +{ +#if defined(OVR_MSVC_SAFESTRING) + wcscat_s(dest, destsize, src); + return dest; +#elif defined(OVR_OS_WIN32) + OVR_UNUSED(destsize); + wcscat(dest, src); + return dest; +#else + UPInt dstlen = OVR_wcslen(dest); // do not incl term null + UPInt srclen = OVR_wcslen(src) + 1; // incl term null + UPInt copylen = (dstlen + srclen < destsize) ? srclen : destsize - dstlen; + memcpy(dest + dstlen, src, copylen * sizeof(wchar_t)); + return dest; +#endif +} + +UPInt OVR_CDECL OVR_wcslen(const wchar_t* str) +{ +#if defined(OVR_OS_WIN32) + return wcslen(str); +#else + UPInt i = 0; + while(str[i] != '\0') + ++i; + return i; +#endif +} + +int OVR_CDECL OVR_wcscmp(const wchar_t* a, const wchar_t* b) +{ +#if defined(OVR_OS_WIN32) || defined(OVR_OS_LINUX) + return wcscmp(a, b); +#else + // not supported, use custom implementation + const wchar_t *pa = a, *pb = b; + while (*pa && *pb) + { + wchar_t ca = *pa; + wchar_t cb = *pb; + if (ca < cb) + return -1; + else if (ca > cb) + return 1; + pa++; + pb++; + } + if (*pa) + return 1; + else if (*pb) + return -1; + else + return 0; +#endif +} + +int OVR_CDECL OVR_wcsicmp(const wchar_t* a, const wchar_t* b) +{ +#if defined(OVR_OS_WIN32) +#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) + return ::_wcsicmp(a, b); +#else + return ::wcsicmp(a, b); +#endif +#elif defined(OVR_OS_MAC) || defined(__CYGWIN__) || defined(OVR_OS_ANDROID) || defined(OVR_OS_IPHONE) + // not supported, use custom implementation + const wchar_t *pa = a, *pb = b; + while (*pa && *pb) + { + wchar_t ca = OVR_towlower(*pa); + wchar_t cb = OVR_towlower(*pb); + if (ca < cb) + return -1; + else if (ca > cb) + return 1; + pa++; + pb++; + } + if (*pa) + return 1; + else if (*pb) + return -1; + else + return 0; +#else + return wcscasecmp(a, b); +#endif +} + +// This function is not inline because of dependency on +double OVR_CDECL OVR_strtod(const char* string, char** tailptr) +{ +#if !defined(OVR_OS_ANDROID) + const char s = *localeconv()->decimal_point; + + if (s != '.') + { + char buffer[347 + 1]; + + OVR_strcpy(buffer, sizeof(buffer), string); + + for (char* c = buffer; *c != '\0'; ++c) + { + if (*c == '.') + { + *c = s; + break; + } + } + + return strtod(buffer, tailptr); + } +#endif + + return strtod(string, tailptr); +} + + +#ifndef OVR_NO_WCTYPE + +//// Use this class to generate Unicode bitsets. For example: +//// +//// UnicodeBitSet bitSet; +//// for(unsigned i = 0; i < 65536; ++i) +//// { +//// if (iswalpha(i)) +//// bitSet.Set(i); +//// } +//// bitSet.Dump(); +//// +////--------------------------------------------------------------- +//class UnicodeBitSet +//{ +//public: +// UnicodeBitSet() +// { +// memset(Offsets, 0, sizeof(Offsets)); +// memset(Bits, 0, sizeof(Bits)); +// } +// +// void Set(unsigned bit) { Bits[bit >> 8][(bit >> 4) & 15] |= 1 << (bit & 15); } +// +// void Dump() +// { +// unsigned i, j; +// unsigned offsetCount = 0; +// for(i = 0; i < 256; ++i) +// { +// if (isNull(i)) Offsets[i] = 0; +// else +// if (isFull(i)) Offsets[i] = 1; +// else Offsets[i] = UInt16(offsetCount++ * 16 + 256); +// } +// for(i = 0; i < 16; ++i) +// { +// for(j = 0; j < 16; ++j) +// { +// printf("%5u,", Offsets[i*16+j]); +// } +// printf("\n"); +// } +// for(i = 0; i < 256; ++i) +// { +// if (Offsets[i] > 255) +// { +// for(j = 0; j < 16; j++) +// { +// printf("%5u,", Bits[i][j]); +// } +// printf("\n"); +// } +// } +// } +// +//private: +// bool isNull(unsigned n) const +// { +// const UInt16* p = Bits[n]; +// for(unsigned i = 0; i < 16; ++i) +// if (p[i] != 0) return false; +// return true; +// } +// +// bool isFull(unsigned n) const +// { +// const UInt16* p = Bits[n]; +// for(unsigned i = 0; i < 16; ++i) +// if (p[i] != 0xFFFF) return false; +// return true; +// } +// +// UInt16 Offsets[256]; +// UInt16 Bits[256][16]; +//}; + + +const UInt16 UnicodeAlnumBits[] = { + 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, + 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, + 640, 656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 672, 688, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 704, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, + 1, 1, 1, 1, 736, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 768, 784, 1, 800, 816, 832, + 0, 0, 0, 1023,65534, 2047,65534, 2047, 0, 0, 0, 524,65535,65407,65535,65407, +65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, + 0, 0, 0, 0, 32, 0, 0, 1024,55104,65535,65531,65535,32767,64767,65535, 15, +65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, + 0, 0, 0,65534,65535, 639,65534,65535, 255, 0, 0, 0, 0,65535, 2047, 7, + 0, 0,65534, 2047,65534, 63, 1023,65535,65535,65535,65535,65535,65535, 8175, 8702, 8191, + 0,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, +65518,65535,65535,58367, 8191,65281,65487, 0,40942,65529,65023,50117, 6559,45184,65487, 3, +34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0, +40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0, +57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0, +57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 12, +65534,65535,65535, 2047,32767, 1023, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0, + 1, 0, 1023, 0,65279,65535, 2047,65534, 3843,65279,65535, 8191, 0, 0, 0, 0, +65535,65535,63227, 327, 1023, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 127, +65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, +65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, +32767,32573,65535,65535,65407, 2047,65024, 3, 0, 0,65535,65535,65535,65535,65535, 31, +65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, +65535,65535,65535,65535,65535,65535,40959, 127,65534, 2047,65535,65535,65535,65535, 2047, 0, + 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 0, 1023, 0, + 0, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, +65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, + 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0, +64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, + 192, 0, 1022, 1792,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 2047, +65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, +65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, +65535,65535,65535,16383, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, + 0, 0, 0, 0, 0, 0, 0,65495,65535,65535,65535,65535,65535,65535,65535, 8191, + 0, 1023,65534, 2047,65534, 2047,65472,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; + +const UInt16 UnicodeAlphaBits[] = { + 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, + 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, + 640, 656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 672, 688, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 704, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, + 1, 1, 1, 1, 736, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 768, 784, 1, 800, 816, 832, + 0, 0, 0, 0,65534, 2047,65534, 2047, 0, 0, 0, 0,65535,65407,65535,65407, +65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, + 0, 0, 0, 0, 32, 0, 0, 1024,55104,65535,65531,65535,32767,64767,65535, 15, +65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, + 0, 0, 0,65534,65535, 639,65534,65535, 255, 0, 0, 0, 0,65535, 2047, 7, + 0, 0,65534, 2047,65534, 63, 0,65535,65535,65535,65535,65535,65535, 8175, 8702, 7168, + 0,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, +65518,65535,65535,58367, 8191,65281, 15, 0,40942,65529,65023,50117, 6559,45184, 15, 3, +34788,65529,65023,50029, 6535,24064, 0, 31,45038,65531,65023,58349, 7103, 1, 1, 0, +40942,65529,65023,58317, 6543,45248, 3, 0,51180,54845,50968,50111, 7623, 128, 0, 0, +57326,65533,65023,50159, 7647, 96, 3, 0,57324,65533,65023,50159, 7647,16480, 3, 0, +57324,65533,65023,50175, 7631, 128, 3, 0,65516,64639,65535,12283,32895,65375, 0, 12, +65534,65535,65535, 2047,32767, 0, 0, 0, 9622,65264,60590,15359, 8223,12288, 0, 0, + 1, 0, 0, 0,65279,65535, 2047,65534, 3843,65279,65535, 8191, 0, 0, 0, 0, +65535,65535,63227, 327, 0, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 127, +65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, +65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, +32767,32573,65535,65535,65407, 2047, 0, 0, 0, 0,65535,65535,65535,65535,65535, 31, +65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, +65535,65535,65535,65535,65535,65535,40959, 127,65534, 2047,65535,65535,65535,65535, 2047, 0, + 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 0, 0, 0, + 0, 0,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, +65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, + 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0, +64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, + 192, 0, 1022, 1792,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 2047, +65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, +65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, +65535,65535,65535,16383, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, + 0, 0, 0, 0, 0, 0, 0,65495,65535,65535,65535,65535,65535,65535,65535, 8191, + 0, 0,65534, 2047,65534, 2047,65472,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; + +const UInt16 UnicodeDigitBits[] = { + 256, 0, 0, 0, 0, 0, 272, 0, 0, 288, 304, 320, 336, 352, 368, 384, + 400, 0, 0, 416, 0, 0, 0, 432, 448, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 464, + 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 524, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 1023, + 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, + 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, + 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65408, 0, + 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, + 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, + 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,65024, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, + 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +const UInt16 UnicodeSpaceBits[] = { + 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 288, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 304, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +15872, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 4095, 0,33536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +const UInt16 UnicodeXDigitBits[] = { + 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272, + 0, 0, 0, 1023, 126, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1023, 126, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +// Uncomment if necessary +//const UInt16 UnicodeCntrlBits[] = { +// 256, 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 288, 0, 0, 0, 0, 0, 0, 0, +// 304, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 320, 336, +//65535,65535, 0, 0, 0, 0, 0,32768,65535,65535, 0, 0, 0, 0, 0, 0, +//32768, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +//30720, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +//61440, 0,31744, 0, 0, 0,64512, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,32768, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3584}; +// +//const UInt16 UnicodeGraphBits[] = { +// 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, +// 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, +// 640, 656, 0, 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 688, 704, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 736, +// 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 768, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 784, 800, 1, 816, 832, 848, +// 0, 0,65534,65535,65535,65535,65535,32767, 0, 0,65534,65535,65535,65535,65535,65535, +//65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, +// 0, 0, 0, 0, 32, 0, 0,17408,55232,65535,65531,65535,32767,64767,65535, 15, +//65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, +// 0, 0, 0,65534,65535,65151,65534,65535, 1791, 0, 0,16384, 9,65535, 2047, 31, +// 4096,34816,65534, 2047,65534, 63,16383,65535,65535,65535,65535,65535,65535, 8191, 8702, 8191, +//16383,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, +//65518,65535,65535,58367, 8191,65281,65535, 1,40942,65529,65023,50117, 6559,45184,65487, 3, +//34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0, +//40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0, +//57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0, +//57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 28, +//65534,65535,65535, 2047,65535, 4095, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0, +//65521, 7, 1023,15360,65279,65535, 2047,65534, 3875,65279,65535, 8191, 0, 0, 0, 0, +//65535,65535,63227, 327,65535, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 2175, +//65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, +//65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, +//32767,32573,65535,65535,65407, 2047,65534, 3, 0, 0,65535,65535,65535,65535,65535, 31, +//65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, +//65535,65535,65535,65535,65535,65535,65535, 127,65534, 8191,65535,65535,65535,65535,16383, 0, +// 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 6128, 1023, 0, +// 2047, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, +//65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, +// 0,65535, 255,65535,16239, 0, 0,57344,24576, 0, 0, 0, 0, 0, 0, 0, +//64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +//65486,65523, 1022, 1793,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 4095, +//65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, +//65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, +//65535,65535,65535,65535, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, +// 0, 0, 0,65535,65055,65527, 3339,65495,65535,65535,65535,65535,65535,65535,65535, 8191, +//63470,36863,65535,49151,65534,12287,65534,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; +// +//const UInt16 UnicodePrintBits[] = { +// 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, +// 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, +// 640, 656, 0, 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 688, 704, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 736, +// 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 768, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 784, 800, 1, 816, 832, 848, +// 512, 0,65535,65535,65535,65535,65535,32767, 0, 0,65535,65535,65535,65535,65535,65535, +//65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, +// 0, 0, 0, 0, 32, 0, 0,17408,55232,65535,65531,65535,32767,64767,65535, 15, +//65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, +// 0, 0, 0,65534,65535,65151,65534,65535, 1791, 0, 0,16384, 9,65535, 2047, 31, +// 4096,34816,65534, 2047,65534, 63,16383,65535,65535,65535,65535,65535,65535, 8191, 8702, 8191, +//16383,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, +//65518,65535,65535,58367, 8191,65281,65535, 1,40942,65529,65023,50117, 6559,45184,65487, 3, +//34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0, +//40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0, +//57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0, +//57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 28, +//65534,65535,65535, 2047,65535, 4095, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0, +//65521, 7, 1023,15360,65279,65535, 2047,65534, 3875,65279,65535, 8191, 0, 0, 0, 0, +//65535,65535,63227, 327,65535, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 2175, +//65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, +//65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, +//32767,32573,65535,65535,65407, 2047,65534, 3, 0, 0,65535,65535,65535,65535,65535, 31, +//65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, +//65535,65535,65535,65535,65535,65535,65535, 127,65534, 8191,65535,65535,65535,65535,16383, 0, +// 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 6128, 1023, 0, +// 2047, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, +//65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, +// 0,65535, 255,65535,16239, 0, 0,57344,24576, 0, 0, 0, 0, 0, 0, 0, +//64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +//65487,65523, 1022, 1793,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 4095, +//65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, +//65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, +//65535,65535,65535,65535, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, +// 0, 0, 0,65535,65055,65527, 3339,65495,65535,65535,65535,65535,65535,65535,65535,40959, +//63470,36863,65535,49151,65534,12287,65534,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; +// +//const UInt16 UnicodePunctBits[] = { +// 256, 0, 0, 272, 0, 288, 304, 320, 0, 336, 0, 0, 0, 352, 368, 384, +// 400, 0, 0, 416, 0, 0, 432, 448, 464, 0, 0, 0, 0, 0, 0, 0, +// 480, 0, 0, 496, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 528, 544, 560, +// 0, 0,65534,64512, 1,63488, 1,30720, 0, 0,65534,65535, 0, 128, 0, 128, +// 0, 0, 0, 0, 0, 0, 0,16384, 128, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0,64512, 0, 0, 1536, 0, 0,16384, 9, 0, 0, 24, +// 4096,34816, 0, 0, 0, 0,15360, 0, 0, 0, 0, 0, 0, 16, 0, 0, +//16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, +// 0, 0, 0, 0,32768, 3072, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +//65520, 7, 0,15360, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0,64512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2048, +// 0, 0, 0, 0, 0, 0, 510, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0,24576, 0, 0, 6144, 0, 0, 0, 0,14336, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6128, 0, 0, +// 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0,65535, 255,65535,16239, 0, 0,24576,24576, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +//65294,65523, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2048, +// 0, 0, 0,49152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0,65535,65055,65527, 3339, 0, 0, 0, 0, 0, 0, 0, 0, 0, +//63470,35840, 1,47104, 0,10240, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +// +//const UInt16 UnicodeLowerBits[] = { +// 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 368, +// 384, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, 0, 432, +// 0, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0,32768,65535,65407, +//43690,43690,43690,21930,43861,43690,43690,54442,12585,20004,11562,58961,23392,46421,43690,43565, +//43690,43690,43688, 10, 0,65535,65535,65535,65535,65535,16383, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,61440,65535,32767,43235,43690, 15, +// 0, 0, 0,65535,65535,65535,43690,43690,40962,43690,43690,43690, 4372,43690,43690, 554, +// 0, 0, 0, 0, 0, 0,65534,65535, 255, 0, 0, 0, 0, 0, 0, 0, +//43690,43690,43690,43690,43690,43690,43690,43690,43690, 4074,43690,43690,43690,43690,43690, 682, +// 255, 63, 255, 255, 63, 255, 255,16383,65535,65535,65535,20703, 4316, 207, 255, 4316, +// 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0, +//50176, 8,32768, 528, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 127, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +// +//const UInt16 UnicodeUpperBits[] = { +// 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, 384, +// 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, +// 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,32639, 0, 0, +//21845,21845,21845,43605,21674,21845,21845,11093,52950,45531,53973, 4526,44464,19114,21845,21974, +//21845,21845,21844, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0,55104,65534, 4091, 0, 0,21532,21845, 0, +//65535,65535,65535, 0, 0, 0,21845,21845,20481,21845,21845,21845, 2187,21845,21845, 277, +// 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535, 63, 0, 0, 0, +//21845,21845,21845,21845,21845,21845,21845,21845,21845, 21,21845,21845,21845,21845,21845, 341, +//65280,16128,65280,65280,16128,43520,65280, 0,65280,65280,65280, 7936, 7936, 3840, 7936, 7936, +//14468,15911,15696, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + +// MA: March 19, 2010 +// Modified ToUpper and ToLower tables to match values expected by AS3 tests. +// ToLower modifications: +// 304 -> 105 +// 1024 -> 1104 * +// 1037 -> 1117 * +// UoUpper modifications: +// 255 -> 376 +// 305 -> 73 +// 383 -> 83 +// 1104 -> 1024 * +// 1117 -> 1037 * +// Entries marked with a '*' don't make complete sense based on Unicode manual, although +// they match AS3. + + +static const UInt16 UnicodeToUpperBits[] = { + 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 368, + 0, 384, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, + 0, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,65407, +43690,43690,43690,21674,43349,43690,43690,54442, 4392, 516, 8490, 8785,21056,46421,43690,43048, // MA: Modified for AS3. +43690, 170, 0, 0, 0, 2776,33545, 36, 3336, 4, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,61440,65534,32767, 0,43688, 0, + 0, 0, 0,65535,65535,65535,43690,43690, 2,43690,43690,43690, 4372,43690,35498, 554, // MA: Modified for AS3. + 0, 0, 0, 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, +43690,43690,43690,43690,43690,43690,43690,43690,43690, 42,43690,43690,43690,43690,43690, 682, + 255, 63, 255, 255, 63, 170, 255,16383, 0, 0, 0, 3, 0, 3, 35, 0, + 0, 0, 0, 0, 0, 0, 0,65535, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535, 1023, 0, + 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static const UInt16 UnicodeToLowerBits[] = { + 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, 384, + 0, 400, 0, 0, 416, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 432, + 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,32639, 0, 0, +21845,21845,21845,43605,21674,21845,21845,11093,52950,45531,53909, 4526,42128,19114,21845,21522,// MA: Modidied for AS3. +21845, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,55104,65534, 4091, 0, 0, 0,21844, 0, +65535,65535,65535, 0, 0, 0,21845,21845, 1,21845,21845,21845, 2186,21845,17749, 277, + 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535, 63, 0, 0, 0, +21845,21845,21845,21845,21845,21845,21845,21845,21845, 21,21845,21845,21845,21845,21845, 341, +65280,16128,65280,65280,16128,43520,65280, 0, 0, 0, 0, 3840, 3840, 3840, 7936, 3840, + 0, 0, 0, 0, 0, 0,65535, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65472,65535, 0, 0, 0, + 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +struct GUnicodePairType +{ + UInt16 Key, Value; +}; + +static inline bool CmpUnicodeKey(const GUnicodePairType& a, UInt16 key) +{ + return a.Key < key; +} + +static const GUnicodePairType UnicodeToUpperTable[] = { +{ 97, 65}, { 98, 66}, { 99, 67}, { 100, 68}, { 101, 69}, { 102, 70}, { 103, 71}, +{ 104, 72}, { 105, 73}, { 106, 74}, { 107, 75}, { 108, 76}, { 109, 77}, { 110, 78}, +{ 111, 79}, { 112, 80}, { 113, 81}, { 114, 82}, { 115, 83}, { 116, 84}, { 117, 85}, +{ 118, 86}, { 119, 87}, { 120, 88}, { 121, 89}, { 122, 90}, { 224, 192}, { 225, 193}, +{ 226, 194}, { 227, 195}, { 228, 196}, { 229, 197}, { 230, 198}, { 231, 199}, { 232, 200}, +{ 233, 201}, { 234, 202}, { 235, 203}, { 236, 204}, { 237, 205}, { 238, 206}, { 239, 207}, +{ 240, 208}, { 241, 209}, { 242, 210}, { 243, 211}, { 244, 212}, { 245, 213}, { 246, 214}, +{ 248, 216}, { 249, 217}, { 250, 218}, { 251, 219}, { 252, 220}, { 253, 221}, { 254, 222}, +{ 255, 376}, { 257, 256}, { 259, 258}, { 261, 260}, { 263, 262}, { 265, 264}, { 267, 266}, +{ 269, 268}, { 271, 270}, { 273, 272}, { 275, 274}, { 277, 276}, { 279, 278}, { 281, 280}, +{ 283, 282}, { 285, 284}, { 287, 286}, { 289, 288}, { 291, 290}, { 293, 292}, { 295, 294}, +{ 297, 296}, { 299, 298}, { 301, 300}, { 303, 302}, { 305, 73}, { 307, 306}, { 309, 308}, { 311, 310}, +{ 314, 313}, { 316, 315}, { 318, 317}, { 320, 319}, { 322, 321}, { 324, 323}, { 326, 325}, +{ 328, 327}, { 331, 330}, { 333, 332}, { 335, 334}, { 337, 336}, { 339, 338}, { 341, 340}, +{ 343, 342}, { 345, 344}, { 347, 346}, { 349, 348}, { 351, 350}, { 353, 352}, { 355, 354}, +{ 357, 356}, { 359, 358}, { 361, 360}, { 363, 362}, { 365, 364}, { 367, 366}, { 369, 368}, +{ 371, 370}, { 373, 372}, { 375, 374}, { 378, 377}, { 380, 379}, { 382, 381}, { 383, 83}, { 387, 386}, +{ 389, 388}, { 392, 391}, { 396, 395}, { 402, 401}, { 409, 408}, { 417, 416}, { 419, 418}, +{ 421, 420}, { 424, 423}, { 429, 428}, { 432, 431}, { 436, 435}, { 438, 437}, { 441, 440}, +{ 445, 444}, { 454, 452}, { 457, 455}, { 460, 458}, { 462, 461}, { 464, 463}, { 466, 465}, +{ 468, 467}, { 470, 469}, { 472, 471}, { 474, 473}, { 476, 475}, { 477, 398}, { 479, 478}, +{ 481, 480}, { 483, 482}, { 485, 484}, { 487, 486}, { 489, 488}, { 491, 490}, { 493, 492}, +{ 495, 494}, { 499, 497}, { 501, 500}, { 507, 506}, { 509, 508}, { 511, 510}, { 513, 512}, +{ 515, 514}, { 517, 516}, { 519, 518}, { 521, 520}, { 523, 522}, { 525, 524}, { 527, 526}, +{ 529, 528}, { 531, 530}, { 533, 532}, { 535, 534}, { 595, 385}, { 596, 390}, { 598, 393}, +{ 599, 394}, { 601, 399}, { 603, 400}, { 608, 403}, { 611, 404}, { 616, 407}, { 617, 406}, +{ 623, 412}, { 626, 413}, { 629, 415}, { 643, 425}, { 648, 430}, { 650, 433}, { 651, 434}, +{ 658, 439}, { 940, 902}, { 941, 904}, { 942, 905}, { 943, 906}, { 945, 913}, { 946, 914}, +{ 947, 915}, { 948, 916}, { 949, 917}, { 950, 918}, { 951, 919}, { 952, 920}, { 953, 921}, +{ 954, 922}, { 955, 923}, { 956, 924}, { 957, 925}, { 958, 926}, { 959, 927}, { 960, 928}, +{ 961, 929}, { 962, 931}, { 963, 931}, { 964, 932}, { 965, 933}, { 966, 934}, { 967, 935}, +{ 968, 936}, { 969, 937}, { 970, 938}, { 971, 939}, { 972, 908}, { 973, 910}, { 974, 911}, +{ 995, 994}, { 997, 996}, { 999, 998}, { 1001, 1000}, { 1003, 1002}, { 1005, 1004}, { 1007, 1006}, +{ 1072, 1040}, { 1073, 1041}, { 1074, 1042}, { 1075, 1043}, { 1076, 1044}, { 1077, 1045}, { 1078, 1046}, +{ 1079, 1047}, { 1080, 1048}, { 1081, 1049}, { 1082, 1050}, { 1083, 1051}, { 1084, 1052}, { 1085, 1053}, +{ 1086, 1054}, { 1087, 1055}, { 1088, 1056}, { 1089, 1057}, { 1090, 1058}, { 1091, 1059}, { 1092, 1060}, +{ 1093, 1061}, { 1094, 1062}, { 1095, 1063}, { 1096, 1064}, { 1097, 1065}, { 1098, 1066}, { 1099, 1067}, +{ 1100, 1068}, { 1101, 1069}, { 1102, 1070}, { 1103, 1071}, { 1104, 1024}, { 1105, 1025}, { 1106, 1026}, { 1107, 1027}, +{ 1108, 1028}, { 1109, 1029}, { 1110, 1030}, { 1111, 1031}, { 1112, 1032}, { 1113, 1033}, { 1114, 1034}, +{ 1115, 1035}, { 1116, 1036}, { 1117, 1037}, { 1118, 1038}, { 1119, 1039}, { 1121, 1120}, { 1123, 1122}, { 1125, 1124}, +{ 1127, 1126}, { 1129, 1128}, { 1131, 1130}, { 1133, 1132}, { 1135, 1134}, { 1137, 1136}, { 1139, 1138}, +{ 1141, 1140}, { 1143, 1142}, { 1145, 1144}, { 1147, 1146}, { 1149, 1148}, { 1151, 1150}, { 1153, 1152}, +{ 1169, 1168}, { 1171, 1170}, { 1173, 1172}, { 1175, 1174}, { 1177, 1176}, { 1179, 1178}, { 1181, 1180}, +{ 1183, 1182}, { 1185, 1184}, { 1187, 1186}, { 1189, 1188}, { 1191, 1190}, { 1193, 1192}, { 1195, 1194}, +{ 1197, 1196}, { 1199, 1198}, { 1201, 1200}, { 1203, 1202}, { 1205, 1204}, { 1207, 1206}, { 1209, 1208}, +{ 1211, 1210}, { 1213, 1212}, { 1215, 1214}, { 1218, 1217}, { 1220, 1219}, { 1224, 1223}, { 1228, 1227}, +{ 1233, 1232}, { 1235, 1234}, { 1237, 1236}, { 1239, 1238}, { 1241, 1240}, { 1243, 1242}, { 1245, 1244}, +{ 1247, 1246}, { 1249, 1248}, { 1251, 1250}, { 1253, 1252}, { 1255, 1254}, { 1257, 1256}, { 1259, 1258}, +{ 1263, 1262}, { 1265, 1264}, { 1267, 1266}, { 1269, 1268}, { 1273, 1272}, { 1377, 1329}, { 1378, 1330}, +{ 1379, 1331}, { 1380, 1332}, { 1381, 1333}, { 1382, 1334}, { 1383, 1335}, { 1384, 1336}, { 1385, 1337}, +{ 1386, 1338}, { 1387, 1339}, { 1388, 1340}, { 1389, 1341}, { 1390, 1342}, { 1391, 1343}, { 1392, 1344}, +{ 1393, 1345}, { 1394, 1346}, { 1395, 1347}, { 1396, 1348}, { 1397, 1349}, { 1398, 1350}, { 1399, 1351}, +{ 1400, 1352}, { 1401, 1353}, { 1402, 1354}, { 1403, 1355}, { 1404, 1356}, { 1405, 1357}, { 1406, 1358}, +{ 1407, 1359}, { 1408, 1360}, { 1409, 1361}, { 1410, 1362}, { 1411, 1363}, { 1412, 1364}, { 1413, 1365}, +{ 1414, 1366}, { 7681, 7680}, { 7683, 7682}, { 7685, 7684}, { 7687, 7686}, { 7689, 7688}, { 7691, 7690}, +{ 7693, 7692}, { 7695, 7694}, { 7697, 7696}, { 7699, 7698}, { 7701, 7700}, { 7703, 7702}, { 7705, 7704}, +{ 7707, 7706}, { 7709, 7708}, { 7711, 7710}, { 7713, 7712}, { 7715, 7714}, { 7717, 7716}, { 7719, 7718}, +{ 7721, 7720}, { 7723, 7722}, { 7725, 7724}, { 7727, 7726}, { 7729, 7728}, { 7731, 7730}, { 7733, 7732}, +{ 7735, 7734}, { 7737, 7736}, { 7739, 7738}, { 7741, 7740}, { 7743, 7742}, { 7745, 7744}, { 7747, 7746}, +{ 7749, 7748}, { 7751, 7750}, { 7753, 7752}, { 7755, 7754}, { 7757, 7756}, { 7759, 7758}, { 7761, 7760}, +{ 7763, 7762}, { 7765, 7764}, { 7767, 7766}, { 7769, 7768}, { 7771, 7770}, { 7773, 7772}, { 7775, 7774}, +{ 7777, 7776}, { 7779, 7778}, { 7781, 7780}, { 7783, 7782}, { 7785, 7784}, { 7787, 7786}, { 7789, 7788}, +{ 7791, 7790}, { 7793, 7792}, { 7795, 7794}, { 7797, 7796}, { 7799, 7798}, { 7801, 7800}, { 7803, 7802}, +{ 7805, 7804}, { 7807, 7806}, { 7809, 7808}, { 7811, 7810}, { 7813, 7812}, { 7815, 7814}, { 7817, 7816}, +{ 7819, 7818}, { 7821, 7820}, { 7823, 7822}, { 7825, 7824}, { 7827, 7826}, { 7829, 7828}, { 7841, 7840}, +{ 7843, 7842}, { 7845, 7844}, { 7847, 7846}, { 7849, 7848}, { 7851, 7850}, { 7853, 7852}, { 7855, 7854}, +{ 7857, 7856}, { 7859, 7858}, { 7861, 7860}, { 7863, 7862}, { 7865, 7864}, { 7867, 7866}, { 7869, 7868}, +{ 7871, 7870}, { 7873, 7872}, { 7875, 7874}, { 7877, 7876}, { 7879, 7878}, { 7881, 7880}, { 7883, 7882}, +{ 7885, 7884}, { 7887, 7886}, { 7889, 7888}, { 7891, 7890}, { 7893, 7892}, { 7895, 7894}, { 7897, 7896}, +{ 7899, 7898}, { 7901, 7900}, { 7903, 7902}, { 7905, 7904}, { 7907, 7906}, { 7909, 7908}, { 7911, 7910}, +{ 7913, 7912}, { 7915, 7914}, { 7917, 7916}, { 7919, 7918}, { 7921, 7920}, { 7923, 7922}, { 7925, 7924}, +{ 7927, 7926}, { 7929, 7928}, { 7936, 7944}, { 7937, 7945}, { 7938, 7946}, { 7939, 7947}, { 7940, 7948}, +{ 7941, 7949}, { 7942, 7950}, { 7943, 7951}, { 7952, 7960}, { 7953, 7961}, { 7954, 7962}, { 7955, 7963}, +{ 7956, 7964}, { 7957, 7965}, { 7968, 7976}, { 7969, 7977}, { 7970, 7978}, { 7971, 7979}, { 7972, 7980}, +{ 7973, 7981}, { 7974, 7982}, { 7975, 7983}, { 7984, 7992}, { 7985, 7993}, { 7986, 7994}, { 7987, 7995}, +{ 7988, 7996}, { 7989, 7997}, { 7990, 7998}, { 7991, 7999}, { 8000, 8008}, { 8001, 8009}, { 8002, 8010}, +{ 8003, 8011}, { 8004, 8012}, { 8005, 8013}, { 8017, 8025}, { 8019, 8027}, { 8021, 8029}, { 8023, 8031}, +{ 8032, 8040}, { 8033, 8041}, { 8034, 8042}, { 8035, 8043}, { 8036, 8044}, { 8037, 8045}, { 8038, 8046}, +{ 8039, 8047}, { 8048, 8122}, { 8049, 8123}, { 8050, 8136}, { 8051, 8137}, { 8052, 8138}, { 8053, 8139}, +{ 8054, 8154}, { 8055, 8155}, { 8056, 8184}, { 8057, 8185}, { 8058, 8170}, { 8059, 8171}, { 8060, 8186}, +{ 8061, 8187}, { 8112, 8120}, { 8113, 8121}, { 8144, 8152}, { 8145, 8153}, { 8160, 8168}, { 8161, 8169}, +{ 8165, 8172}, { 8560, 8544}, { 8561, 8545}, { 8562, 8546}, { 8563, 8547}, { 8564, 8548}, { 8565, 8549}, +{ 8566, 8550}, { 8567, 8551}, { 8568, 8552}, { 8569, 8553}, { 8570, 8554}, { 8571, 8555}, { 8572, 8556}, +{ 8573, 8557}, { 8574, 8558}, { 8575, 8559}, { 9424, 9398}, { 9425, 9399}, { 9426, 9400}, { 9427, 9401}, +{ 9428, 9402}, { 9429, 9403}, { 9430, 9404}, { 9431, 9405}, { 9432, 9406}, { 9433, 9407}, { 9434, 9408}, +{ 9435, 9409}, { 9436, 9410}, { 9437, 9411}, { 9438, 9412}, { 9439, 9413}, { 9440, 9414}, { 9441, 9415}, +{ 9442, 9416}, { 9443, 9417}, { 9444, 9418}, { 9445, 9419}, { 9446, 9420}, { 9447, 9421}, { 9448, 9422}, +{ 9449, 9423}, {65345,65313}, {65346,65314}, {65347,65315}, {65348,65316}, {65349,65317}, {65350,65318}, +{65351,65319}, {65352,65320}, {65353,65321}, {65354,65322}, {65355,65323}, {65356,65324}, {65357,65325}, +{65358,65326}, {65359,65327}, {65360,65328}, {65361,65329}, {65362,65330}, {65363,65331}, {65364,65332}, +{65365,65333}, {65366,65334}, {65367,65335}, {65368,65336}, {65369,65337}, {65370,65338}, {65535, 0}}; + +static const GUnicodePairType UnicodeToLowerTable[] = { +{ 65, 97}, { 66, 98}, { 67, 99}, { 68, 100}, { 69, 101}, { 70, 102}, { 71, 103}, +{ 72, 104}, { 73, 105}, { 74, 106}, { 75, 107}, { 76, 108}, { 77, 109}, { 78, 110}, +{ 79, 111}, { 80, 112}, { 81, 113}, { 82, 114}, { 83, 115}, { 84, 116}, { 85, 117}, +{ 86, 118}, { 87, 119}, { 88, 120}, { 89, 121}, { 90, 122}, { 192, 224}, { 193, 225}, +{ 194, 226}, { 195, 227}, { 196, 228}, { 197, 229}, { 198, 230}, { 199, 231}, { 200, 232}, +{ 201, 233}, { 202, 234}, { 203, 235}, { 204, 236}, { 205, 237}, { 206, 238}, { 207, 239}, +{ 208, 240}, { 209, 241}, { 210, 242}, { 211, 243}, { 212, 244}, { 213, 245}, { 214, 246}, +{ 216, 248}, { 217, 249}, { 218, 250}, { 219, 251}, { 220, 252}, { 221, 253}, { 222, 254}, +{ 256, 257}, { 258, 259}, { 260, 261}, { 262, 263}, { 264, 265}, { 266, 267}, { 268, 269}, +{ 270, 271}, { 272, 273}, { 274, 275}, { 276, 277}, { 278, 279}, { 280, 281}, { 282, 283}, +{ 284, 285}, { 286, 287}, { 288, 289}, { 290, 291}, { 292, 293}, { 294, 295}, { 296, 297}, +{ 298, 299}, { 300, 301}, { 302, 303}, { 304, 105}, { 306, 307}, { 308, 309}, { 310, 311}, { 313, 314}, +{ 315, 316}, { 317, 318}, { 319, 320}, { 321, 322}, { 323, 324}, { 325, 326}, { 327, 328}, +{ 330, 331}, { 332, 333}, { 334, 335}, { 336, 337}, { 338, 339}, { 340, 341}, { 342, 343}, +{ 344, 345}, { 346, 347}, { 348, 349}, { 350, 351}, { 352, 353}, { 354, 355}, { 356, 357}, +{ 358, 359}, { 360, 361}, { 362, 363}, { 364, 365}, { 366, 367}, { 368, 369}, { 370, 371}, +{ 372, 373}, { 374, 375}, { 376, 255}, { 377, 378}, { 379, 380}, { 381, 382}, { 385, 595}, +{ 386, 387}, { 388, 389}, { 390, 596}, { 391, 392}, { 393, 598}, { 394, 599}, { 395, 396}, +{ 398, 477}, { 399, 601}, { 400, 603}, { 401, 402}, { 403, 608}, { 404, 611}, { 406, 617}, +{ 407, 616}, { 408, 409}, { 412, 623}, { 413, 626}, { 415, 629}, { 416, 417}, { 418, 419}, +{ 420, 421}, { 423, 424}, { 425, 643}, { 428, 429}, { 430, 648}, { 431, 432}, { 433, 650}, +{ 434, 651}, { 435, 436}, { 437, 438}, { 439, 658}, { 440, 441}, { 444, 445}, { 452, 454}, +{ 455, 457}, { 458, 460}, { 461, 462}, { 463, 464}, { 465, 466}, { 467, 468}, { 469, 470}, +{ 471, 472}, { 473, 474}, { 475, 476}, { 478, 479}, { 480, 481}, { 482, 483}, { 484, 485}, +{ 486, 487}, { 488, 489}, { 490, 491}, { 492, 493}, { 494, 495}, { 497, 499}, { 500, 501}, +{ 506, 507}, { 508, 509}, { 510, 511}, { 512, 513}, { 514, 515}, { 516, 517}, { 518, 519}, +{ 520, 521}, { 522, 523}, { 524, 525}, { 526, 527}, { 528, 529}, { 530, 531}, { 532, 533}, +{ 534, 535}, { 902, 940}, { 904, 941}, { 905, 942}, { 906, 943}, { 908, 972}, { 910, 973}, +{ 911, 974}, { 913, 945}, { 914, 946}, { 915, 947}, { 916, 948}, { 917, 949}, { 918, 950}, +{ 919, 951}, { 920, 952}, { 921, 953}, { 922, 954}, { 923, 955}, { 924, 956}, { 925, 957}, +{ 926, 958}, { 927, 959}, { 928, 960}, { 929, 961}, { 931, 963}, { 932, 964}, { 933, 965}, +{ 934, 966}, { 935, 967}, { 936, 968}, { 937, 969}, { 938, 970}, { 939, 971}, { 994, 995}, +{ 996, 997}, { 998, 999}, { 1000, 1001}, { 1002, 1003}, { 1004, 1005}, { 1006, 1007}, { 1024, 1104}, { 1025, 1105}, +{ 1026, 1106}, { 1027, 1107}, { 1028, 1108}, { 1029, 1109}, { 1030, 1110}, { 1031, 1111}, { 1032, 1112}, +{ 1033, 1113}, { 1034, 1114}, { 1035, 1115}, { 1036, 1116}, { 1037, 1117}, { 1038, 1118}, { 1039, 1119}, { 1040, 1072}, +{ 1041, 1073}, { 1042, 1074}, { 1043, 1075}, { 1044, 1076}, { 1045, 1077}, { 1046, 1078}, { 1047, 1079}, +{ 1048, 1080}, { 1049, 1081}, { 1050, 1082}, { 1051, 1083}, { 1052, 1084}, { 1053, 1085}, { 1054, 1086}, +{ 1055, 1087}, { 1056, 1088}, { 1057, 1089}, { 1058, 1090}, { 1059, 1091}, { 1060, 1092}, { 1061, 1093}, +{ 1062, 1094}, { 1063, 1095}, { 1064, 1096}, { 1065, 1097}, { 1066, 1098}, { 1067, 1099}, { 1068, 1100}, +{ 1069, 1101}, { 1070, 1102}, { 1071, 1103}, { 1120, 1121}, { 1122, 1123}, { 1124, 1125}, { 1126, 1127}, +{ 1128, 1129}, { 1130, 1131}, { 1132, 1133}, { 1134, 1135}, { 1136, 1137}, { 1138, 1139}, { 1140, 1141}, +{ 1142, 1143}, { 1144, 1145}, { 1146, 1147}, { 1148, 1149}, { 1150, 1151}, { 1152, 1153}, { 1168, 1169}, +{ 1170, 1171}, { 1172, 1173}, { 1174, 1175}, { 1176, 1177}, { 1178, 1179}, { 1180, 1181}, { 1182, 1183}, +{ 1184, 1185}, { 1186, 1187}, { 1188, 1189}, { 1190, 1191}, { 1192, 1193}, { 1194, 1195}, { 1196, 1197}, +{ 1198, 1199}, { 1200, 1201}, { 1202, 1203}, { 1204, 1205}, { 1206, 1207}, { 1208, 1209}, { 1210, 1211}, +{ 1212, 1213}, { 1214, 1215}, { 1217, 1218}, { 1219, 1220}, { 1223, 1224}, { 1227, 1228}, { 1232, 1233}, +{ 1234, 1235}, { 1236, 1237}, { 1238, 1239}, { 1240, 1241}, { 1242, 1243}, { 1244, 1245}, { 1246, 1247}, +{ 1248, 1249}, { 1250, 1251}, { 1252, 1253}, { 1254, 1255}, { 1256, 1257}, { 1258, 1259}, { 1262, 1263}, +{ 1264, 1265}, { 1266, 1267}, { 1268, 1269}, { 1272, 1273}, { 1329, 1377}, { 1330, 1378}, { 1331, 1379}, +{ 1332, 1380}, { 1333, 1381}, { 1334, 1382}, { 1335, 1383}, { 1336, 1384}, { 1337, 1385}, { 1338, 1386}, +{ 1339, 1387}, { 1340, 1388}, { 1341, 1389}, { 1342, 1390}, { 1343, 1391}, { 1344, 1392}, { 1345, 1393}, +{ 1346, 1394}, { 1347, 1395}, { 1348, 1396}, { 1349, 1397}, { 1350, 1398}, { 1351, 1399}, { 1352, 1400}, +{ 1353, 1401}, { 1354, 1402}, { 1355, 1403}, { 1356, 1404}, { 1357, 1405}, { 1358, 1406}, { 1359, 1407}, +{ 1360, 1408}, { 1361, 1409}, { 1362, 1410}, { 1363, 1411}, { 1364, 1412}, { 1365, 1413}, { 1366, 1414}, +{ 4256, 4304}, { 4257, 4305}, { 4258, 4306}, { 4259, 4307}, { 4260, 4308}, { 4261, 4309}, { 4262, 4310}, +{ 4263, 4311}, { 4264, 4312}, { 4265, 4313}, { 4266, 4314}, { 4267, 4315}, { 4268, 4316}, { 4269, 4317}, +{ 4270, 4318}, { 4271, 4319}, { 4272, 4320}, { 4273, 4321}, { 4274, 4322}, { 4275, 4323}, { 4276, 4324}, +{ 4277, 4325}, { 4278, 4326}, { 4279, 4327}, { 4280, 4328}, { 4281, 4329}, { 4282, 4330}, { 4283, 4331}, +{ 4284, 4332}, { 4285, 4333}, { 4286, 4334}, { 4287, 4335}, { 4288, 4336}, { 4289, 4337}, { 4290, 4338}, +{ 4291, 4339}, { 4292, 4340}, { 4293, 4341}, { 7680, 7681}, { 7682, 7683}, { 7684, 7685}, { 7686, 7687}, +{ 7688, 7689}, { 7690, 7691}, { 7692, 7693}, { 7694, 7695}, { 7696, 7697}, { 7698, 7699}, { 7700, 7701}, +{ 7702, 7703}, { 7704, 7705}, { 7706, 7707}, { 7708, 7709}, { 7710, 7711}, { 7712, 7713}, { 7714, 7715}, +{ 7716, 7717}, { 7718, 7719}, { 7720, 7721}, { 7722, 7723}, { 7724, 7725}, { 7726, 7727}, { 7728, 7729}, +{ 7730, 7731}, { 7732, 7733}, { 7734, 7735}, { 7736, 7737}, { 7738, 7739}, { 7740, 7741}, { 7742, 7743}, +{ 7744, 7745}, { 7746, 7747}, { 7748, 7749}, { 7750, 7751}, { 7752, 7753}, { 7754, 7755}, { 7756, 7757}, +{ 7758, 7759}, { 7760, 7761}, { 7762, 7763}, { 7764, 7765}, { 7766, 7767}, { 7768, 7769}, { 7770, 7771}, +{ 7772, 7773}, { 7774, 7775}, { 7776, 7777}, { 7778, 7779}, { 7780, 7781}, { 7782, 7783}, { 7784, 7785}, +{ 7786, 7787}, { 7788, 7789}, { 7790, 7791}, { 7792, 7793}, { 7794, 7795}, { 7796, 7797}, { 7798, 7799}, +{ 7800, 7801}, { 7802, 7803}, { 7804, 7805}, { 7806, 7807}, { 7808, 7809}, { 7810, 7811}, { 7812, 7813}, +{ 7814, 7815}, { 7816, 7817}, { 7818, 7819}, { 7820, 7821}, { 7822, 7823}, { 7824, 7825}, { 7826, 7827}, +{ 7828, 7829}, { 7840, 7841}, { 7842, 7843}, { 7844, 7845}, { 7846, 7847}, { 7848, 7849}, { 7850, 7851}, +{ 7852, 7853}, { 7854, 7855}, { 7856, 7857}, { 7858, 7859}, { 7860, 7861}, { 7862, 7863}, { 7864, 7865}, +{ 7866, 7867}, { 7868, 7869}, { 7870, 7871}, { 7872, 7873}, { 7874, 7875}, { 7876, 7877}, { 7878, 7879}, +{ 7880, 7881}, { 7882, 7883}, { 7884, 7885}, { 7886, 7887}, { 7888, 7889}, { 7890, 7891}, { 7892, 7893}, +{ 7894, 7895}, { 7896, 7897}, { 7898, 7899}, { 7900, 7901}, { 7902, 7903}, { 7904, 7905}, { 7906, 7907}, +{ 7908, 7909}, { 7910, 7911}, { 7912, 7913}, { 7914, 7915}, { 7916, 7917}, { 7918, 7919}, { 7920, 7921}, +{ 7922, 7923}, { 7924, 7925}, { 7926, 7927}, { 7928, 7929}, { 7944, 7936}, { 7945, 7937}, { 7946, 7938}, +{ 7947, 7939}, { 7948, 7940}, { 7949, 7941}, { 7950, 7942}, { 7951, 7943}, { 7960, 7952}, { 7961, 7953}, +{ 7962, 7954}, { 7963, 7955}, { 7964, 7956}, { 7965, 7957}, { 7976, 7968}, { 7977, 7969}, { 7978, 7970}, +{ 7979, 7971}, { 7980, 7972}, { 7981, 7973}, { 7982, 7974}, { 7983, 7975}, { 7992, 7984}, { 7993, 7985}, +{ 7994, 7986}, { 7995, 7987}, { 7996, 7988}, { 7997, 7989}, { 7998, 7990}, { 7999, 7991}, { 8008, 8000}, +{ 8009, 8001}, { 8010, 8002}, { 8011, 8003}, { 8012, 8004}, { 8013, 8005}, { 8025, 8017}, { 8027, 8019}, +{ 8029, 8021}, { 8031, 8023}, { 8040, 8032}, { 8041, 8033}, { 8042, 8034}, { 8043, 8035}, { 8044, 8036}, +{ 8045, 8037}, { 8046, 8038}, { 8047, 8039}, { 8120, 8112}, { 8121, 8113}, { 8122, 8048}, { 8123, 8049}, +{ 8136, 8050}, { 8137, 8051}, { 8138, 8052}, { 8139, 8053}, { 8152, 8144}, { 8153, 8145}, { 8154, 8054}, +{ 8155, 8055}, { 8168, 8160}, { 8169, 8161}, { 8170, 8058}, { 8171, 8059}, { 8172, 8165}, { 8184, 8056}, +{ 8185, 8057}, { 8186, 8060}, { 8187, 8061}, { 8544, 8560}, { 8545, 8561}, { 8546, 8562}, { 8547, 8563}, +{ 8548, 8564}, { 8549, 8565}, { 8550, 8566}, { 8551, 8567}, { 8552, 8568}, { 8553, 8569}, { 8554, 8570}, +{ 8555, 8571}, { 8556, 8572}, { 8557, 8573}, { 8558, 8574}, { 8559, 8575}, { 9398, 9424}, { 9399, 9425}, +{ 9400, 9426}, { 9401, 9427}, { 9402, 9428}, { 9403, 9429}, { 9404, 9430}, { 9405, 9431}, { 9406, 9432}, +{ 9407, 9433}, { 9408, 9434}, { 9409, 9435}, { 9410, 9436}, { 9411, 9437}, { 9412, 9438}, { 9413, 9439}, +{ 9414, 9440}, { 9415, 9441}, { 9416, 9442}, { 9417, 9443}, { 9418, 9444}, { 9419, 9445}, { 9420, 9446}, +{ 9421, 9447}, { 9422, 9448}, { 9423, 9449}, {65313,65345}, {65314,65346}, {65315,65347}, {65316,65348}, +{65317,65349}, {65318,65350}, {65319,65351}, {65320,65352}, {65321,65353}, {65322,65354}, {65323,65355}, +{65324,65356}, {65325,65357}, {65326,65358}, {65327,65359}, {65328,65360}, {65329,65361}, {65330,65362}, +{65331,65363}, {65332,65364}, {65333,65365}, {65334,65366}, {65335,65367}, {65336,65368}, {65337,65369}, +{65338,65370}, {65535, 0}}; + +int OVR_CDECL OVR_towupper(wchar_t charCode) +{ + // Don't use UnicodeUpperBits! It differs from UnicodeToUpperBits. + if (UnicodeCharIs(UnicodeToUpperBits, charCode)) + { + // To protect from memory overrun in case the character is not found + // we use one extra fake element in the table {65536, 0}. + UPInt idx = Alg::LowerBoundSliced( + UnicodeToUpperTable, + 0, + sizeof(UnicodeToUpperTable) / sizeof(UnicodeToUpperTable[0]) - 1, + (UInt16)charCode, + CmpUnicodeKey); + return UnicodeToUpperTable[idx].Value; + } + return charCode; +} + +int OVR_CDECL OVR_towlower(wchar_t charCode) +{ + // Don't use UnicodeLowerBits! It differs from UnicodeToLowerBits. + if (UnicodeCharIs(UnicodeToLowerBits, charCode)) + { + // To protect from memory overrun in case the character is not found + // we use one extra fake element in the table {65536, 0}. + UPInt idx = Alg::LowerBoundSliced( + UnicodeToLowerTable, + 0, + sizeof(UnicodeToLowerTable) / sizeof(UnicodeToLowerTable[0]) - 1, + (UInt16)charCode, + CmpUnicodeKey); + return UnicodeToLowerTable[idx].Value; + } + return charCode; +} + +#endif //OVR_NO_WCTYPE + +} // OVR diff --git a/LibOVR/Src/Kernel/OVR_Std.h b/LibOVR/Src/Kernel/OVR_Std.h new file mode 100644 index 0000000..b20a376 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Std.h @@ -0,0 +1,503 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Std.h +Content : Standard C function interface +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_Std_h +#define OVR_Std_h + +#include "OVR_Types.h" +#include // for va_list args +#include +#include +#include +#include + +#if !defined(OVR_OS_WINCE) && defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) +#define OVR_MSVC_SAFESTRING +#include +#endif + +// Wide-char funcs +#include +#include + +namespace OVR { + +#if defined(OVR_OS_WIN32) +inline char* OVR_CDECL OVR_itoa(int val, char *dest, UPInt destsize, int radix) +{ +#if defined(OVR_MSVC_SAFESTRING) + _itoa_s(val, dest, destsize, radix); + return dest; +#else + OVR_UNUSED(destsize); + return itoa(val, dest, radix); +#endif +} +#else // OVR_OS_WIN32 +inline char* OVR_itoa(int val, char* dest, unsigned int len, int radix) +{ + if (val == 0) + { + if (len > 1) + { + dest[0] = '0'; + dest[1] = '\0'; + } + return dest; + } + + int cur = val; + unsigned int i = 0; + unsigned int sign = 0; + + if (val < 0) + { + val = -val; + sign = 1; + } + + while ((val != 0) && (i < (len - 1 - sign))) + { + cur = val % radix; + val /= radix; + + if (radix == 16) + { + switch(cur) + { + case 10: + dest[i] = 'a'; + break; + case 11: + dest[i] = 'b'; + break; + case 12: + dest[i] = 'c'; + break; + case 13: + dest[i] = 'd'; + break; + case 14: + dest[i] = 'e'; + break; + case 15: + dest[i] = 'f'; + break; + default: + dest[i] = (char)('0' + cur); + break; + } + } + else + { + dest[i] = (char)('0' + cur); + } + ++i; + } + + if (sign) + { + dest[i++] = '-'; + } + + for (unsigned int j = 0; j < i / 2; ++j) + { + char tmp = dest[j]; + dest[j] = dest[i - 1 - j]; + dest[i - 1 - j] = tmp; + } + dest[i] = '\0'; + + return dest; +} + +#endif + + +// String functions + +inline UPInt OVR_CDECL OVR_strlen(const char* str) +{ + return strlen(str); +} + +inline char* OVR_CDECL OVR_strcpy(char* dest, UPInt destsize, const char* src) +{ +#if defined(OVR_MSVC_SAFESTRING) + strcpy_s(dest, destsize, src); + return dest; +#else + OVR_UNUSED(destsize); + return strcpy(dest, src); +#endif +} + +inline char* OVR_CDECL OVR_strncpy(char* dest, UPInt destsize, const char* src, UPInt count) +{ +#if defined(OVR_MSVC_SAFESTRING) + strncpy_s(dest, destsize, src, count); + return dest; +#else + OVR_UNUSED(destsize); + return strncpy(dest, src, count); +#endif +} + +inline char * OVR_CDECL OVR_strcat(char* dest, UPInt destsize, const char* src) +{ +#if defined(OVR_MSVC_SAFESTRING) + strcat_s(dest, destsize, src); + return dest; +#else + OVR_UNUSED(destsize); + return strcat(dest, src); +#endif +} + +inline int OVR_CDECL OVR_strcmp(const char* dest, const char* src) +{ + return strcmp(dest, src); +} + +inline const char* OVR_CDECL OVR_strchr(const char* str, char c) +{ + return strchr(str, c); +} + +inline char* OVR_CDECL OVR_strchr(char* str, char c) +{ + return strchr(str, c); +} + +inline const char* OVR_strrchr(const char* str, char c) +{ + UPInt len = OVR_strlen(str); + for (UPInt i=len; i>0; i--) + if (str[i]==c) + return str+i; + return 0; +} + +inline const UByte* OVR_CDECL OVR_memrchr(const UByte* str, UPInt size, UByte c) +{ + for (SPInt i = (SPInt)size - 1; i >= 0; i--) + { + if (str[i] == c) + return str + i; + } + return 0; +} + +inline char* OVR_CDECL OVR_strrchr(char* str, char c) +{ + UPInt len = OVR_strlen(str); + for (UPInt i=len; i>0; i--) + if (str[i]==c) + return str+i; + return 0; +} + + +double OVR_CDECL OVR_strtod(const char* string, char** tailptr); + +inline long OVR_CDECL OVR_strtol(const char* string, char** tailptr, int radix) +{ + return strtol(string, tailptr, radix); +} + +inline long OVR_CDECL OVR_strtoul(const char* string, char** tailptr, int radix) +{ + return strtoul(string, tailptr, radix); +} + +inline int OVR_CDECL OVR_strncmp(const char* ws1, const char* ws2, UPInt size) +{ + return strncmp(ws1, ws2, size); +} + +inline UInt64 OVR_CDECL OVR_strtouq(const char *nptr, char **endptr, int base) +{ +#if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE) + return _strtoui64(nptr, endptr, base); +#else + return strtoull(nptr, endptr, base); +#endif +} + +inline SInt64 OVR_CDECL OVR_strtoq(const char *nptr, char **endptr, int base) +{ +#if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE) + return _strtoi64(nptr, endptr, base); +#else + return strtoll(nptr, endptr, base); +#endif +} + + +inline SInt64 OVR_CDECL OVR_atoq(const char* string) +{ +#if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE) + return _atoi64(string); +#else + return atoll(string); +#endif +} + +inline UInt64 OVR_CDECL OVR_atouq(const char* string) +{ + return OVR_strtouq(string, NULL, 10); +} + + +// Implemented in GStd.cpp in platform-specific manner. +int OVR_CDECL OVR_stricmp(const char* dest, const char* src); +int OVR_CDECL OVR_strnicmp(const char* dest, const char* src, UPInt count); + +inline UPInt OVR_CDECL OVR_sprintf(char *dest, UPInt destsize, const char* format, ...) +{ + va_list argList; + va_start(argList,format); + UPInt ret; +#if defined(OVR_CC_MSVC) + #if defined(OVR_MSVC_SAFESTRING) + ret = _vsnprintf_s(dest, destsize, _TRUNCATE, format, argList); + OVR_ASSERT(ret != -1); + #else + OVR_UNUSED(destsize); + ret = _vsnprintf(dest, destsize - 1, format, argList); // -1 for space for the null character + OVR_ASSERT(ret != -1); + dest[destsize-1] = 0; + #endif +#else + OVR_UNUSED(destsize); + ret = vsprintf(dest, format, argList); + OVR_ASSERT(ret < destsize); +#endif + va_end(argList); + return ret; +} + +inline UPInt OVR_CDECL OVR_vsprintf(char *dest, UPInt destsize, const char * format, va_list argList) +{ + UPInt ret; +#if defined(OVR_CC_MSVC) + #if defined(OVR_MSVC_SAFESTRING) + dest[0] = '\0'; + int rv = vsnprintf_s(dest, destsize, _TRUNCATE, format, argList); + if (rv == -1) + { + dest[destsize - 1] = '\0'; + ret = destsize - 1; + } + else + ret = (UPInt)rv; + #else + OVR_UNUSED(destsize); + int rv = _vsnprintf(dest, destsize - 1, format, argList); + OVR_ASSERT(rv != -1); + ret = (UPInt)rv; + dest[destsize-1] = 0; + #endif +#else + OVR_UNUSED(destsize); + ret = (UPInt)vsprintf(dest, format, argList); + OVR_ASSERT(ret < destsize); +#endif + return ret; +} + +// Returns the number of characters in the formatted string. +inline UPInt OVR_CDECL OVR_vscprintf(const char * format, va_list argList) +{ + UPInt ret; +#if defined(OVR_CC_MSVC) + ret = (UPInt) _vscprintf(format, argList); +#else + ret = (UPInt) vsnprintf(NULL, 0, format, argList); +#endif + return ret; +} + + +wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, UPInt destsize, const wchar_t* src); +wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, UPInt destsize, const wchar_t* src, UPInt count); +wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, UPInt destsize, const wchar_t* src); +UPInt OVR_CDECL OVR_wcslen(const wchar_t* str); +int OVR_CDECL OVR_wcscmp(const wchar_t* a, const wchar_t* b); +int OVR_CDECL OVR_wcsicmp(const wchar_t* a, const wchar_t* b); + +inline int OVR_CDECL OVR_wcsicoll(const wchar_t* a, const wchar_t* b) +{ +#if defined(OVR_OS_WIN32) +#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) + return ::_wcsicoll(a, b); +#else + return ::wcsicoll(a, b); +#endif +#else + // not supported, use regular wcsicmp + return OVR_wcsicmp(a, b); +#endif +} + +inline int OVR_CDECL OVR_wcscoll(const wchar_t* a, const wchar_t* b) +{ +#if defined(OVR_OS_WIN32) || defined(OVR_OS_LINUX) + return wcscoll(a, b); +#else + // not supported, use regular wcscmp + return OVR_wcscmp(a, b); +#endif +} + +#ifndef OVR_NO_WCTYPE + +inline int OVR_CDECL UnicodeCharIs(const UInt16* table, wchar_t charCode) +{ + unsigned offset = table[charCode >> 8]; + if (offset == 0) return 0; + if (offset == 1) return 1; + return (table[offset + ((charCode >> 4) & 15)] & (1 << (charCode & 15))) != 0; +} + +extern const UInt16 UnicodeAlnumBits[]; +extern const UInt16 UnicodeAlphaBits[]; +extern const UInt16 UnicodeDigitBits[]; +extern const UInt16 UnicodeSpaceBits[]; +extern const UInt16 UnicodeXDigitBits[]; + +// Uncomment if necessary +//extern const UInt16 UnicodeCntrlBits[]; +//extern const UInt16 UnicodeGraphBits[]; +//extern const UInt16 UnicodeLowerBits[]; +//extern const UInt16 UnicodePrintBits[]; +//extern const UInt16 UnicodePunctBits[]; +//extern const UInt16 UnicodeUpperBits[]; + +inline int OVR_CDECL OVR_iswalnum (wchar_t charCode) { return UnicodeCharIs(UnicodeAlnumBits, charCode); } +inline int OVR_CDECL OVR_iswalpha (wchar_t charCode) { return UnicodeCharIs(UnicodeAlphaBits, charCode); } +inline int OVR_CDECL OVR_iswdigit (wchar_t charCode) { return UnicodeCharIs(UnicodeDigitBits, charCode); } +inline int OVR_CDECL OVR_iswspace (wchar_t charCode) { return UnicodeCharIs(UnicodeSpaceBits, charCode); } +inline int OVR_CDECL OVR_iswxdigit(wchar_t charCode) { return UnicodeCharIs(UnicodeXDigitBits, charCode); } + +// Uncomment if necessary +//inline int OVR_CDECL OVR_iswcntrl (wchar_t charCode) { return UnicodeCharIs(UnicodeCntrlBits, charCode); } +//inline int OVR_CDECL OVR_iswgraph (wchar_t charCode) { return UnicodeCharIs(UnicodeGraphBits, charCode); } +//inline int OVR_CDECL OVR_iswlower (wchar_t charCode) { return UnicodeCharIs(UnicodeLowerBits, charCode); } +//inline int OVR_CDECL OVR_iswprint (wchar_t charCode) { return UnicodeCharIs(UnicodePrintBits, charCode); } +//inline int OVR_CDECL OVR_iswpunct (wchar_t charCode) { return UnicodeCharIs(UnicodePunctBits, charCode); } +//inline int OVR_CDECL OVR_iswupper (wchar_t charCode) { return UnicodeCharIs(UnicodeUpperBits, charCode); } + +int OVR_CDECL OVR_towupper(wchar_t charCode); +int OVR_CDECL OVR_towlower(wchar_t charCode); + +#else // OVR_NO_WCTYPE + +inline int OVR_CDECL OVR_iswspace(wchar_t c) +{ + return iswspace(c); +} + +inline int OVR_CDECL OVR_iswdigit(wchar_t c) +{ + return iswdigit(c); +} + +inline int OVR_CDECL OVR_iswxdigit(wchar_t c) +{ + return iswxdigit(c); +} + +inline int OVR_CDECL OVR_iswalpha(wchar_t c) +{ + return iswalpha(c); +} + +inline int OVR_CDECL OVR_iswalnum(wchar_t c) +{ + return iswalnum(c); +} + +inline wchar_t OVR_CDECL OVR_towlower(wchar_t c) +{ + return (wchar_t)towlower(c); +} + +inline wchar_t OVR_towupper(wchar_t c) +{ + return (wchar_t)towupper(c); +} + +#endif // OVR_NO_WCTYPE + +// ASCII versions of tolower and toupper. Don't use "char" +inline int OVR_CDECL OVR_tolower(int c) +{ + return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; +} + +inline int OVR_CDECL OVR_toupper(int c) +{ + return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; +} + + + +inline double OVR_CDECL OVR_wcstod(const wchar_t* string, wchar_t** tailptr) +{ +#if defined(OVR_OS_OTHER) + OVR_UNUSED(tailptr); + char buffer[64]; + char* tp = NULL; + UPInt max = OVR_wcslen(string); + if (max > 63) max = 63; + unsigned char c = 0; + for (UPInt i=0; i < max; i++) + { + c = (unsigned char)string[i]; + buffer[i] = ((c) < 128 ? (char)c : '!'); + } + buffer[max] = 0; + return OVR_strtod(buffer, &tp); +#else + return wcstod(string, tailptr); +#endif +} + +inline long OVR_CDECL OVR_wcstol(const wchar_t* string, wchar_t** tailptr, int radix) +{ +#if defined(OVR_OS_OTHER) + OVR_UNUSED(tailptr); + char buffer[64]; + char* tp = NULL; + UPInt max = OVR_wcslen(string); + if (max > 63) max = 63; + unsigned char c = 0; + for (UPInt i=0; i < max; i++) + { + c = (unsigned char)string[i]; + buffer[i] = ((c) < 128 ? (char)c : '!'); + } + buffer[max] = 0; + return strtol(buffer, &tp, radix); +#else + return wcstol(string, tailptr, radix); +#endif +} + +} // OVR + +#endif // OVR_Std_h diff --git a/LibOVR/Src/Kernel/OVR_String.cpp b/LibOVR/Src/Kernel/OVR_String.cpp new file mode 100644 index 0000000..b591ede --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_String.cpp @@ -0,0 +1,752 @@ +/************************************************************************************ + +Filename : OVR_String.cpp +Content : String UTF8 string implementation with copy-on-write semantics + (thread-safe for assignment but not modification). +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_String.h" + +#include +#include + +#ifdef OVR_OS_QNX +# include +#endif + +namespace OVR { + +#define String_LengthIsSize (UPInt(1) << String::Flag_LengthIsSizeShift) + +String::DataDesc String::NullData = {String_LengthIsSize, 1, {0} }; + + +String::String() +{ + pData = &NullData; + pData->AddRef(); +}; + +String::String(const char* pdata) +{ + // Obtain length in bytes; it doesn't matter if _data is UTF8. + UPInt size = pdata ? OVR_strlen(pdata) : 0; + pData = AllocDataCopy1(size, 0, pdata, size); +}; + +String::String(const char* pdata1, const char* pdata2, const char* pdata3) +{ + // Obtain length in bytes; it doesn't matter if _data is UTF8. + UPInt size1 = pdata1 ? OVR_strlen(pdata1) : 0; + UPInt size2 = pdata2 ? OVR_strlen(pdata2) : 0; + UPInt size3 = pdata3 ? OVR_strlen(pdata3) : 0; + + DataDesc *pdataDesc = AllocDataCopy2(size1 + size2 + size3, 0, + pdata1, size1, pdata2, size2); + memcpy(pdataDesc->Data + size1 + size2, pdata3, size3); + pData = pdataDesc; +} + +String::String(const char* pdata, UPInt size) +{ + OVR_ASSERT((size == 0) || (pdata != 0)); + pData = AllocDataCopy1(size, 0, pdata, size); +}; + + +String::String(const InitStruct& src, UPInt size) +{ + pData = AllocData(size, 0); + src.InitString(GetData()->Data, size); +} + +String::String(const String& src) +{ + pData = src.GetData(); + pData->AddRef(); +} + +String::String(const StringBuffer& src) +{ + pData = AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize()); +} + +String::String(const wchar_t* data) +{ + pData = &NullData; + pData->AddRef(); + // Simplified logic for wchar_t constructor. + if (data) + *this = data; +} + + +String::DataDesc* String::AllocData(UPInt size, UPInt lengthIsSize) +{ + String::DataDesc* pdesc; + + if (size == 0) + { + pdesc = &NullData; + pdesc->AddRef(); + return pdesc; + } + + pdesc = (DataDesc*)OVR_ALLOC(sizeof(DataDesc)+ size); + pdesc->Data[size] = 0; + pdesc->RefCount = 1; + pdesc->Size = size | lengthIsSize; + return pdesc; +} + + +String::DataDesc* String::AllocDataCopy1(UPInt size, UPInt lengthIsSize, + const char* pdata, UPInt copySize) +{ + String::DataDesc* pdesc = AllocData(size, lengthIsSize); + memcpy(pdesc->Data, pdata, copySize); + return pdesc; +} + +String::DataDesc* String::AllocDataCopy2(UPInt size, UPInt lengthIsSize, + const char* pdata1, UPInt copySize1, + const char* pdata2, UPInt copySize2) +{ + String::DataDesc* pdesc = AllocData(size, lengthIsSize); + memcpy(pdesc->Data, pdata1, copySize1); + memcpy(pdesc->Data + copySize1, pdata2, copySize2); + return pdesc; +} + + +UPInt String::GetLength() const +{ + // Optimize length accesses for non-UTF8 character strings. + DataDesc* pdata = GetData(); + UPInt length, size = pdata->GetSize(); + + if (pdata->LengthIsSize()) + return size; + + length = (UPInt)UTF8Util::GetLength(pdata->Data, (UPInt)size); + + if (length == size) + pdata->Size |= String_LengthIsSize; + + return length; +} + + +//static UInt32 String_CharSearch(const char* buf, ) + + +UInt32 String::GetCharAt(UPInt index) const +{ + SPInt i = (SPInt) index; + DataDesc* pdata = GetData(); + const char* buf = pdata->Data; + UInt32 c; + + if (pdata->LengthIsSize()) + { + OVR_ASSERT(index < pdata->GetSize()); + buf += i; + return UTF8Util::DecodeNextChar_Advance0(&buf); + } + + c = UTF8Util::GetCharAt(index, buf, pdata->GetSize()); + return c; +} + +UInt32 String::GetFirstCharAt(UPInt index, const char** offset) const +{ + DataDesc* pdata = GetData(); + SPInt i = (SPInt) index; + const char* buf = pdata->Data; + const char* end = buf + pdata->GetSize(); + UInt32 c; + + do + { + c = UTF8Util::DecodeNextChar_Advance0(&buf); + i--; + + if (buf >= end) + { + // We've hit the end of the string; don't go further. + OVR_ASSERT(i == 0); + return c; + } + } while (i >= 0); + + *offset = buf; + + return c; +} + +UInt32 String::GetNextChar(const char** offset) const +{ + return UTF8Util::DecodeNextChar(offset); +} + + + +void String::AppendChar(UInt32 ch) +{ + DataDesc* pdata = GetData(); + UPInt size = pdata->GetSize(); + char buff[8]; + SPInt encodeSize = 0; + + // Converts ch into UTF8 string and fills it into buff. + UTF8Util::EncodeChar(buff, &encodeSize, ch); + OVR_ASSERT(encodeSize >= 0); + + SetData(AllocDataCopy2(size + (UPInt)encodeSize, 0, + pdata->Data, size, buff, (UPInt)encodeSize)); + pdata->Release(); +} + + +void String::AppendString(const wchar_t* pstr, SPInt len) +{ + if (!pstr) + return; + + DataDesc* pdata = GetData(); + UPInt oldSize = pdata->GetSize(); + UPInt encodeSize = (UPInt)UTF8Util::GetEncodeStringSize(pstr, len); + + DataDesc* pnewData = AllocDataCopy1(oldSize + (UPInt)encodeSize, 0, + pdata->Data, oldSize); + UTF8Util::EncodeString(pnewData->Data + oldSize, pstr, len); + + SetData(pnewData); + pdata->Release(); +} + + +void String::AppendString(const char* putf8str, SPInt utf8StrSz) +{ + if (!putf8str || !utf8StrSz) + return; + if (utf8StrSz == -1) + utf8StrSz = (SPInt)OVR_strlen(putf8str); + + DataDesc* pdata = GetData(); + UPInt oldSize = pdata->GetSize(); + + SetData(AllocDataCopy2(oldSize + (UPInt)utf8StrSz, 0, + pdata->Data, oldSize, putf8str, (UPInt)utf8StrSz)); + pdata->Release(); +} + +void String::AssignString(const InitStruct& src, UPInt size) +{ + DataDesc* poldData = GetData(); + DataDesc* pnewData = AllocData(size, 0); + src.InitString(pnewData->Data, size); + SetData(pnewData); + poldData->Release(); +} + +void String::AssignString(const char* putf8str, UPInt size) +{ + DataDesc* poldData = GetData(); + SetData(AllocDataCopy1(size, 0, putf8str, size)); + poldData->Release(); +} + +void String::operator = (const char* pstr) +{ + AssignString(pstr, pstr ? OVR_strlen(pstr) : 0); +} + +void String::operator = (const wchar_t* pwstr) +{ + DataDesc* poldData = GetData(); + UPInt size = pwstr ? (UPInt)UTF8Util::GetEncodeStringSize(pwstr) : 0; + + DataDesc* pnewData = AllocData(size, 0); + UTF8Util::EncodeString(pnewData->Data, pwstr); + SetData(pnewData); + poldData->Release(); +} + + +void String::operator = (const String& src) +{ + DataDesc* psdata = src.GetData(); + DataDesc* pdata = GetData(); + + SetData(psdata); + psdata->AddRef(); + pdata->Release(); +} + + +void String::operator = (const StringBuffer& src) +{ + DataDesc* polddata = GetData(); + SetData(AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize())); + polddata->Release(); +} + +void String::operator += (const String& src) +{ + DataDesc *pourData = GetData(), + *psrcData = src.GetData(); + UPInt ourSize = pourData->GetSize(), + srcSize = psrcData->GetSize(); + UPInt lflag = pourData->GetLengthFlag() & psrcData->GetLengthFlag(); + + SetData(AllocDataCopy2(ourSize + srcSize, lflag, + pourData->Data, ourSize, psrcData->Data, srcSize)); + pourData->Release(); +} + + +String String::operator + (const char* str) const +{ + String tmp1(*this); + tmp1 += (str ? str : ""); + return tmp1; +} + +String String::operator + (const String& src) const +{ + String tmp1(*this); + tmp1 += src; + return tmp1; +} + +void String::Remove(UPInt posAt, SPInt removeLength) +{ + DataDesc* pdata = GetData(); + UPInt oldSize = pdata->GetSize(); + // Length indicates the number of characters to remove. + UPInt length = GetLength(); + + // If index is past the string, nothing to remove. + if (posAt >= length) + return; + // Otherwise, cap removeLength to the length of the string. + if ((posAt + removeLength) > length) + removeLength = length - posAt; + + // Get the byte position of the UTF8 char at position posAt. + SPInt bytePos = UTF8Util::GetByteIndex(posAt, pdata->Data, oldSize); + SPInt removeSize = UTF8Util::GetByteIndex(removeLength, pdata->Data + bytePos, oldSize-bytePos); + + SetData(AllocDataCopy2(oldSize - removeSize, pdata->GetLengthFlag(), + pdata->Data, bytePos, + pData->Data + bytePos + removeSize, (oldSize - bytePos - removeSize))); + pdata->Release(); +} + + +String String::Substring(UPInt start, UPInt end) const +{ + UPInt length = GetLength(); + if ((start >= length) || (start >= end)) + return String(); + + DataDesc* pdata = GetData(); + + // If size matches, we know the exact index range. + if (pdata->LengthIsSize()) + return String(pdata->Data + start, end - start); + + // Get position of starting character. + SPInt byteStart = UTF8Util::GetByteIndex(start, pdata->Data, pdata->GetSize()); + SPInt byteSize = UTF8Util::GetByteIndex(end - start, pdata->Data + byteStart, pdata->GetSize()-byteStart); + return String(pdata->Data + byteStart, (UPInt)byteSize); +} + +void String::Clear() +{ + NullData.AddRef(); + GetData()->Release(); + SetData(&NullData); +} + + +String String::ToUpper() const +{ + UInt32 c; + const char* psource = GetData()->Data; + const char* pend = psource + GetData()->GetSize(); + String str; + SPInt bufferOffset = 0; + char buffer[512]; + + while(psource < pend) + { + do { + c = UTF8Util::DecodeNextChar_Advance0(&psource); + UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towupper(wchar_t(c))); + } while ((psource < pend) && (bufferOffset < SPInt(sizeof(buffer)-8))); + + // Append string a piece at a time. + str.AppendString(buffer, bufferOffset); + bufferOffset = 0; + } + + return str; +} + +String String::ToLower() const +{ + UInt32 c; + const char* psource = GetData()->Data; + const char* pend = psource + GetData()->GetSize(); + String str; + SPInt bufferOffset = 0; + char buffer[512]; + + while(psource < pend) + { + do { + c = UTF8Util::DecodeNextChar_Advance0(&psource); + UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towlower(wchar_t(c))); + } while ((psource < pend) && (bufferOffset < SPInt(sizeof(buffer)-8))); + + // Append string a piece at a time. + str.AppendString(buffer, bufferOffset); + bufferOffset = 0; + } + + return str; +} + + + +String& String::Insert(const char* substr, UPInt posAt, SPInt strSize) +{ + DataDesc* poldData = GetData(); + UPInt oldSize = poldData->GetSize(); + UPInt insertSize = (strSize < 0) ? OVR_strlen(substr) : (UPInt)strSize; + UPInt byteIndex = (poldData->LengthIsSize()) ? + posAt : (UPInt)UTF8Util::GetByteIndex(posAt, poldData->Data, oldSize); + + OVR_ASSERT(byteIndex <= oldSize); + + DataDesc* pnewData = AllocDataCopy2(oldSize + insertSize, 0, + poldData->Data, byteIndex, substr, insertSize); + memcpy(pnewData->Data + byteIndex + insertSize, + poldData->Data + byteIndex, oldSize - byteIndex); + SetData(pnewData); + poldData->Release(); + return *this; +} + +/* +String& String::Insert(const UInt32* substr, UPInt posAt, SPInt len) +{ + for (SPInt i = 0; i < len; ++i) + { + UPInt charw = InsertCharAt(substr[i], posAt); + posAt += charw; + } + return *this; +} +*/ + +UPInt String::InsertCharAt(UInt32 c, UPInt posAt) +{ + char buf[8]; + SPInt index = 0; + UTF8Util::EncodeChar(buf, &index, c); + OVR_ASSERT(index >= 0); + buf[(UPInt)index] = 0; + + Insert(buf, posAt, index); + return (UPInt)index; +} + + +int String::CompareNoCase(const char* a, const char* b) +{ + return OVR_stricmp(a, b); +} + +int String::CompareNoCase(const char* a, const char* b, SPInt len) +{ + if (len) + { + SPInt f,l; + SPInt slen = len; + const char *s = b; + do { + f = (SPInt)OVR_tolower((int)(*(a++))); + l = (SPInt)OVR_tolower((int)(*(b++))); + } while (--len && f && (f == l) && *b != 0); + + if (f == l && (len != 0 || *b != 0)) + { + f = (SPInt)slen; + l = (SPInt)OVR_strlen(s); + return int(f - l); + } + + return int(f - l); + } + else + return (0-(int)OVR_strlen(b)); +} + +// ***** Implement hash static functions + +// Hash function +UPInt String::BernsteinHashFunction(const void* pdataIn, UPInt size, UPInt seed) +{ + const UByte* pdata = (const UByte*) pdataIn; + UPInt h = seed; + while (size > 0) + { + size--; + h = ((h << 5) + h) ^ (unsigned) pdata[size]; + } + + return h; +} + +// Hash function, case-insensitive +UPInt String::BernsteinHashFunctionCIS(const void* pdataIn, UPInt size, UPInt seed) +{ + const UByte* pdata = (const UByte*) pdataIn; + UPInt h = seed; + while (size > 0) + { + size--; + h = ((h << 5) + h) ^ OVR_tolower(pdata[size]); + } + + // Alternative: "sdbm" hash function, suggested at same web page above. + // h = 0; + // for bytes { h = (h << 16) + (h << 6) - hash + *p; } + return h; +} + + + +// ***** String Buffer used for Building Strings + + +#define OVR_SBUFF_DEFAULT_GROW_SIZE 512 +// Constructors / Destructor. +StringBuffer::StringBuffer() + : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) +{ +} + +StringBuffer::StringBuffer(UPInt growSize) + : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) +{ + SetGrowSize(growSize); +} + +StringBuffer::StringBuffer(const char* data) + : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) +{ + *this = data; +} + +StringBuffer::StringBuffer(const char* data, UPInt dataSize) + : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) +{ + AppendString(data, dataSize); +} + +StringBuffer::StringBuffer(const String& src) + : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) +{ + AppendString(src.ToCStr(), src.GetSize()); +} + +StringBuffer::StringBuffer(const StringBuffer& src) + : pData(NULL), Size(0), BufferSize(src.GetGrowSize()), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) +{ + AppendString(src.ToCStr(), src.GetSize()); + LengthIsSize = src.LengthIsSize; +} + +StringBuffer::StringBuffer(const wchar_t* data) + : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) +{ + *this = data; +} + +StringBuffer::~StringBuffer() +{ + if (pData) + OVR_FREE(pData); +} +void StringBuffer::SetGrowSize(UPInt growSize) +{ + if (growSize <= 16) + GrowSize = 16; + else + { + UByte bits = Alg::UpperBit(UInt32(growSize-1)); + UPInt size = 1<= BufferSize) // >= because of trailing zero! (!AB) + { + BufferSize = (_size + 1 + GrowSize - 1)& ~(GrowSize-1); + if (!pData) + pData = (char*)OVR_ALLOC(BufferSize); + else + pData = (char*)OVR_REALLOC(pData, BufferSize); + } +} +void StringBuffer::Resize(UPInt _size) +{ + Reserve(_size); + LengthIsSize = false; + Size = _size; + if (pData) + pData[Size] = 0; +} + +void StringBuffer::Clear() +{ + Resize(0); + /* + if (pData != pEmptyNullData) + { + OVR_FREE(pHeap, pData); + pData = pEmptyNullData; + Size = BufferSize = 0; + LengthIsSize = false; + } + */ +} +// Appends a character +void StringBuffer::AppendChar(UInt32 ch) +{ + char buff[8]; + UPInt origSize = GetSize(); + + // Converts ch into UTF8 string and fills it into buff. Also increments index according to the number of bytes + // in the UTF8 string. + SPInt srcSize = 0; + UTF8Util::EncodeChar(buff, &srcSize, ch); + OVR_ASSERT(srcSize >= 0); + + UPInt size = origSize + srcSize; + Resize(size); + memcpy(pData + origSize, buff, srcSize); +} + +// Append a string +void StringBuffer::AppendString(const wchar_t* pstr, SPInt len) +{ + if (!pstr) + return; + + SPInt srcSize = UTF8Util::GetEncodeStringSize(pstr, len); + UPInt origSize = GetSize(); + UPInt size = srcSize + origSize; + + Resize(size); + UTF8Util::EncodeString(pData + origSize, pstr, len); +} + +void StringBuffer::AppendString(const char* putf8str, SPInt utf8StrSz) +{ + if (!putf8str || !utf8StrSz) + return; + if (utf8StrSz == -1) + utf8StrSz = (SPInt)OVR_strlen(putf8str); + + UPInt origSize = GetSize(); + UPInt size = utf8StrSz + origSize; + + Resize(size); + memcpy(pData + origSize, putf8str, utf8StrSz); +} + + +void StringBuffer::operator = (const char* pstr) +{ + pstr = pstr ? pstr : ""; + UPInt size = OVR_strlen(pstr); + Resize(size); + memcpy(pData, pstr, size); +} + +void StringBuffer::operator = (const wchar_t* pstr) +{ + pstr = pstr ? pstr : L""; + UPInt size = (UPInt)UTF8Util::GetEncodeStringSize(pstr); + Resize(size); + UTF8Util::EncodeString(pData, pstr); +} + +void StringBuffer::operator = (const String& src) +{ + Resize(src.GetSize()); + memcpy(pData, src.ToCStr(), src.GetSize()); +} + + +// Inserts substr at posAt +void StringBuffer::Insert(const char* substr, UPInt posAt, SPInt len) +{ + UPInt oldSize = Size; + UPInt insertSize = (len < 0) ? OVR_strlen(substr) : (UPInt)len; + UPInt byteIndex = LengthIsSize ? posAt : + (UPInt)UTF8Util::GetByteIndex(posAt, pData, (SPInt)Size); + + OVR_ASSERT(byteIndex <= oldSize); + Reserve(oldSize + insertSize); + + memmove(pData + byteIndex + insertSize, pData + byteIndex, oldSize - byteIndex + 1); + memcpy (pData + byteIndex, substr, insertSize); + LengthIsSize = false; + Size = oldSize + insertSize; + pData[Size] = 0; +} + +// Inserts character at posAt +UPInt StringBuffer::InsertCharAt(UInt32 c, UPInt posAt) +{ + char buf[8]; + SPInt len = 0; + UTF8Util::EncodeChar(buf, &len, c); + OVR_ASSERT(len >= 0); + buf[(UPInt)len] = 0; + + Insert(buf, posAt, len); + return (UPInt)len; +} + +} // OVR diff --git a/LibOVR/Src/Kernel/OVR_String.h b/LibOVR/Src/Kernel/OVR_String.h new file mode 100644 index 0000000..112878c --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_String.h @@ -0,0 +1,645 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_String.h +Content : String UTF8 string implementation with copy-on-write semantics + (thread-safe for assignment but not modification). +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_String_h +#define OVR_String_h + +#include "OVR_Types.h" +#include "OVR_Allocator.h" +#include "OVR_UTF8Util.h" +#include "OVR_Atomic.h" +#include "OVR_Std.h" +#include "OVR_Alg.h" + +namespace OVR { + +// ***** Classes + +class String; +class StringBuffer; + + +//----------------------------------------------------------------------------------- +// ***** String Class + +// String is UTF8 based string class with copy-on-write implementation +// for assignment. + +class String +{ +protected: + + enum FlagConstants + { + //Flag_GetLength = 0x7FFFFFFF, + // This flag is set if GetLength() == GetSize() for a string. + // Avoid extra scanning is Substring and indexing logic. + Flag_LengthIsSizeShift = (sizeof(UPInt)*8 - 1) + }; + + + // Internal structure to hold string data + struct DataDesc + { + // Number of bytes. Will be the same as the number of chars if the characters + // are ascii, may not be equal to number of chars in case string data is UTF8. + UPInt Size; + volatile SInt32 RefCount; + char Data[1]; + + void AddRef() + { + AtomicOps::ExchangeAdd_NoSync(&RefCount, 1); + } + // Decrement ref count. This needs to be thread-safe, since + // a different thread could have also decremented the ref count. + // For example, if u start off with a ref count = 2. Now if u + // decrement the ref count and check against 0 in different + // statements, a different thread can also decrement the ref count + // in between our decrement and checking against 0 and will find + // the ref count = 0 and delete the object. This will lead to a crash + // when context switches to our thread and we'll be trying to delete + // an already deleted object. Hence decrementing the ref count and + // checking against 0 needs to made an atomic operation. + void Release() + { + if ((AtomicOps::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0) + OVR_FREE(this); + } + + static UPInt GetLengthFlagBit() { return UPInt(1) << Flag_LengthIsSizeShift; } + UPInt GetSize() const { return Size & ~GetLengthFlagBit() ; } + UPInt GetLengthFlag() const { return Size & GetLengthFlagBit(); } + bool LengthIsSize() const { return GetLengthFlag() != 0; } + }; + + // Heap type of the string is encoded in the lower bits. + enum HeapType + { + HT_Global = 0, // Heap is global. + HT_Local = 1, // SF::String_loc: Heap is determined based on string's address. + HT_Dynamic = 2, // SF::String_temp: Heap is stored as a part of the class. + HT_Mask = 3 + }; + + union { + DataDesc* pData; + UPInt HeapTypeBits; + }; + typedef union { + DataDesc* pData; + UPInt HeapTypeBits; + } DataDescUnion; + + inline HeapType GetHeapType() const { return (HeapType) (HeapTypeBits & HT_Mask); } + + inline DataDesc* GetData() const + { + DataDescUnion u; + u.pData = pData; + u.HeapTypeBits = (u.HeapTypeBits & ~(UPInt)HT_Mask); + return u.pData; + } + + inline void SetData(DataDesc* pdesc) + { + HeapType ht = GetHeapType(); + pData = pdesc; + OVR_ASSERT((HeapTypeBits & HT_Mask) == 0); + HeapTypeBits |= ht; + } + + + DataDesc* AllocData(UPInt size, UPInt lengthIsSize); + DataDesc* AllocDataCopy1(UPInt size, UPInt lengthIsSize, + const char* pdata, UPInt copySize); + DataDesc* AllocDataCopy2(UPInt size, UPInt lengthIsSize, + const char* pdata1, UPInt copySize1, + const char* pdata2, UPInt copySize2); + + // Special constructor to avoid data initalization when used in derived class. + struct NoConstructor { }; + String(const NoConstructor&) { } + +public: + + // For initializing string with dynamic buffer + struct InitStruct + { + virtual ~InitStruct() { } + virtual void InitString(char* pbuffer, UPInt size) const = 0; + }; + + + // Constructors / Destructors. + String(); + String(const char* data); + String(const char* data1, const char* pdata2, const char* pdata3 = 0); + String(const char* data, UPInt buflen); + String(const String& src); + String(const StringBuffer& src); + String(const InitStruct& src, UPInt size); + explicit String(const wchar_t* data); + + // Destructor (Captain Obvious guarantees!) + ~String() + { + GetData()->Release(); + } + + // Declaration of NullString + static DataDesc NullData; + + + // *** General Functions + + void Clear(); + + // For casting to a pointer to char. + operator const char*() const { return GetData()->Data; } + // Pointer to raw buffer. + const char* ToCStr() const { return GetData()->Data; } + + // Returns number of bytes + UPInt GetSize() const { return GetData()->GetSize() ; } + // Tells whether or not the string is empty + bool IsEmpty() const { return GetSize() == 0; } + + // Returns number of characters + UPInt GetLength() const; + + // Returns character at the specified index + UInt32 GetCharAt(UPInt index) const; + UInt32 GetFirstCharAt(UPInt index, const char** offset) const; + UInt32 GetNextChar(const char** offset) const; + + // Appends a character + void AppendChar(UInt32 ch); + + // Append a string + void AppendString(const wchar_t* pstr, SPInt len = -1); + void AppendString(const char* putf8str, SPInt utf8StrSz = -1); + + // Assigned a string with dynamic data (copied through initializer). + void AssignString(const InitStruct& src, UPInt size); + // Assigns string with known size. + void AssignString(const char* putf8str, UPInt size); + + // Resize the string to the new size +// void Resize(UPInt _size); + + // Removes the character at posAt + void Remove(UPInt posAt, SPInt len = 1); + + // Returns a String that's a substring of this. + // -start is the index of the first UTF8 character you want to include. + // -end is the index one past the last UTF8 character you want to include. + String Substring(UPInt start, UPInt end) const; + + // Case-conversion + String ToUpper() const; + String ToLower() const; + + // Inserts substr at posAt + String& Insert (const char* substr, UPInt posAt, SPInt len = -1); + + // Inserts character at posAt + UPInt InsertCharAt(UInt32 c, UPInt posAt); + + // Inserts substr at posAt, which is an index of a character (not byte). + // Of size is specified, it is in bytes. +// String& Insert(const UInt32* substr, UPInt posAt, SPInt size = -1); + + // Get Byte index of the character at position = index + UPInt GetByteIndex(UPInt index) const { return (UPInt)UTF8Util::GetByteIndex(index, GetData()->Data); } + + // Utility: case-insensitive string compare. stricmp() & strnicmp() are not + // ANSI or POSIX, do not seem to appear in Linux. + static int OVR_STDCALL CompareNoCase(const char* a, const char* b); + static int OVR_STDCALL CompareNoCase(const char* a, const char* b, SPInt len); + + // Hash function, case-insensitive + static UPInt OVR_STDCALL BernsteinHashFunctionCIS(const void* pdataIn, UPInt size, UPInt seed = 5381); + + // Hash function, case-sensitive + static UPInt OVR_STDCALL BernsteinHashFunction(const void* pdataIn, UPInt size, UPInt seed = 5381); + + + // ***** File path parsing helper functions. + // Implemented in OVR_String_FilePath.cpp. + + // Absolute paths can star with: + // - protocols: 'file://', 'http://' + // - windows drive: 'c:\' + // - UNC share name: '\\share' + // - unix root '/' + static bool HasAbsolutePath(const char* path); + static bool HasExtension(const char* path); + static bool HasProtocol(const char* path); + + bool HasAbsolutePath() const { return HasAbsolutePath(ToCStr()); } + bool HasExtension() const { return HasExtension(ToCStr()); } + bool HasProtocol() const { return HasProtocol(ToCStr()); } + + String GetProtocol() const; // Returns protocol, if any, with trailing '://'. + String GetPath() const; // Returns path with trailing '/'. + String GetFilename() const; // Returns filename, including extension. + String GetExtension() const; // Returns extension with a dot. + + void StripProtocol(); // Strips front protocol, if any, from the string. + void StripExtension(); // Strips off trailing extension. + + + // Operators + // Assignment + void operator = (const char* str); + void operator = (const wchar_t* str); + void operator = (const String& src); + void operator = (const StringBuffer& src); + + // Addition + void operator += (const String& src); + void operator += (const char* psrc) { AppendString(psrc); } + void operator += (const wchar_t* psrc) { AppendString(psrc); } + void operator += (char ch) { AppendChar(ch); } + String operator + (const char* str) const; + String operator + (const String& src) const; + + // Comparison + bool operator == (const String& str) const + { + return (OVR_strcmp(GetData()->Data, str.GetData()->Data)== 0); + } + + bool operator != (const String& str) const + { + return !operator == (str); + } + + bool operator == (const char* str) const + { + return OVR_strcmp(GetData()->Data, str) == 0; + } + + bool operator != (const char* str) const + { + return !operator == (str); + } + + bool operator < (const char* pstr) const + { + return OVR_strcmp(GetData()->Data, pstr) < 0; + } + + bool operator < (const String& str) const + { + return *this < str.GetData()->Data; + } + + bool operator > (const char* pstr) const + { + return OVR_strcmp(GetData()->Data, pstr) > 0; + } + + bool operator > (const String& str) const + { + return *this > str.GetData()->Data; + } + + int CompareNoCase(const char* pstr) const + { + return CompareNoCase(GetData()->Data, pstr); + } + int CompareNoCase(const String& str) const + { + return CompareNoCase(GetData()->Data, str.ToCStr()); + } + + // Accesses raw bytes + const char& operator [] (int index) const + { + OVR_ASSERT(index >= 0 && (UPInt)index < GetSize()); + return GetData()->Data[index]; + } + const char& operator [] (UPInt index) const + { + OVR_ASSERT(index < GetSize()); + return GetData()->Data[index]; + } + + + // Case insensitive keys are used to look up insensitive string in hash tables + // for SWF files with version before SWF 7. + struct NoCaseKey + { + const String* pStr; + NoCaseKey(const String &str) : pStr(&str){}; + }; + + bool operator == (const NoCaseKey& strKey) const + { + return (CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0); + } + bool operator != (const NoCaseKey& strKey) const + { + return !(CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0); + } + + // Hash functor used for strings. + struct HashFunctor + { + UPInt operator()(const String& data) const + { + UPInt size = data.GetSize(); + return String::BernsteinHashFunction((const char*)data, size); + } + }; + // Case-insensitive hash functor used for strings. Supports additional + // lookup based on NoCaseKey. + struct NoCaseHashFunctor + { + UPInt operator()(const String& data) const + { + UPInt size = data.GetSize(); + return String::BernsteinHashFunctionCIS((const char*)data, size); + } + UPInt operator()(const NoCaseKey& data) const + { + UPInt size = data.pStr->GetSize(); + return String::BernsteinHashFunctionCIS((const char*)data.pStr->ToCStr(), size); + } + }; + +}; + + +//----------------------------------------------------------------------------------- +// ***** String Buffer used for Building Strings + +class StringBuffer +{ + char* pData; + UPInt Size; + UPInt BufferSize; + UPInt GrowSize; + mutable bool LengthIsSize; + +public: + + // Constructors / Destructor. + StringBuffer(); + explicit StringBuffer(UPInt growSize); + StringBuffer(const char* data); + StringBuffer(const char* data, UPInt buflen); + StringBuffer(const String& src); + StringBuffer(const StringBuffer& src); + explicit StringBuffer(const wchar_t* data); + ~StringBuffer(); + + + // Modify grow size used for growing/shrinking the buffer. + UPInt GetGrowSize() const { return GrowSize; } + void SetGrowSize(UPInt growSize); + + + // *** General Functions + // Does not release memory, just sets Size to 0 + void Clear(); + + // For casting to a pointer to char. + operator const char*() const { return (pData) ? pData : ""; } + // Pointer to raw buffer. + const char* ToCStr() const { return (pData) ? pData : ""; } + + // Returns number of bytes. + UPInt GetSize() const { return Size ; } + // Tells whether or not the string is empty. + bool IsEmpty() const { return GetSize() == 0; } + + // Returns number of characters + UPInt GetLength() const; + + // Returns character at the specified index + UInt32 GetCharAt(UPInt index) const; + UInt32 GetFirstCharAt(UPInt index, const char** offset) const; + UInt32 GetNextChar(const char** offset) const; + + + // Resize the string to the new size + void Resize(UPInt _size); + void Reserve(UPInt _size); + + // Appends a character + void AppendChar(UInt32 ch); + + // Append a string + void AppendString(const wchar_t* pstr, SPInt len = -1); + void AppendString(const char* putf8str, SPInt utf8StrSz = -1); + void AppendFormat(const char* format, ...); + + // Assigned a string with dynamic data (copied through initializer). + //void AssignString(const InitStruct& src, UPInt size); + + // Inserts substr at posAt + void Insert (const char* substr, UPInt posAt, SPInt len = -1); + // Inserts character at posAt + UPInt InsertCharAt(UInt32 c, UPInt posAt); + + // Assignment + void operator = (const char* str); + void operator = (const wchar_t* str); + void operator = (const String& src); + + // Addition + void operator += (const String& src) { AppendString(src.ToCStr(),src.GetSize()); } + void operator += (const char* psrc) { AppendString(psrc); } + void operator += (const wchar_t* psrc) { AppendString(psrc); } + void operator += (char ch) { AppendChar(ch); } + //String operator + (const char* str) const ; + //String operator + (const String& src) const ; + + // Accesses raw bytes + char& operator [] (int index) + { + OVR_ASSERT(((UPInt)index) < GetSize()); + return pData[index]; + } + char& operator [] (UPInt index) + { + OVR_ASSERT(index < GetSize()); + return pData[index]; + } + + const char& operator [] (int index) const + { + OVR_ASSERT(((UPInt)index) < GetSize()); + return pData[index]; + } + const char& operator [] (UPInt index) const + { + OVR_ASSERT(index < GetSize()); + return pData[index]; + } +}; + + +// +// Wrapper for string data. The data must have a guaranteed +// lifespan throughout the usage of the wrapper. Not intended for +// cached usage. Not thread safe. +// +class StringDataPtr +{ +public: + StringDataPtr() : pStr(NULL), Size(0) {} + StringDataPtr(const StringDataPtr& p) + : pStr(p.pStr), Size(p.Size) {} + StringDataPtr(const char* pstr, UPInt sz) + : pStr(pstr), Size(sz) {} + StringDataPtr(const char* pstr) + : pStr(pstr), Size((pstr != NULL) ? OVR_strlen(pstr) : 0) {} + explicit StringDataPtr(const String& str) + : pStr(str.ToCStr()), Size(str.GetSize()) {} + template + StringDataPtr(const T (&v)[N]) + : pStr(v), Size(N) {} + +public: + const char* ToCStr() const { return pStr; } + UPInt GetSize() const { return Size; } + bool IsEmpty() const { return GetSize() == 0; } + + // value is a prefix of this string + // Character's values are not compared. + bool IsPrefix(const StringDataPtr& value) const + { + return ToCStr() == value.ToCStr() && GetSize() >= value.GetSize(); + } + // value is a suffix of this string + // Character's values are not compared. + bool IsSuffix(const StringDataPtr& value) const + { + return ToCStr() <= value.ToCStr() && (End()) == (value.End()); + } + + // Find first character. + // init_ind - initial index. + SPInt FindChar(char c, UPInt init_ind = 0) const + { + for (UPInt i = init_ind; i < GetSize(); ++i) + if (pStr[i] == c) + return static_cast(i); + + return -1; + } + + // Find last character. + // init_ind - initial index. + SPInt FindLastChar(char c, UPInt init_ind = ~0) const + { + if (init_ind == (UPInt)~0 || init_ind > GetSize()) + init_ind = GetSize(); + else + ++init_ind; + + for (UPInt i = init_ind; i > 0; --i) + if (pStr[i - 1] == c) + return static_cast(i - 1); + + return -1; + } + + // Create new object and trim size bytes from the left. + StringDataPtr GetTrimLeft(UPInt size) const + { + // Limit trim size to the size of the string. + size = Alg::PMin(GetSize(), size); + + return StringDataPtr(ToCStr() + size, GetSize() - size); + } + // Create new object and trim size bytes from the right. + StringDataPtr GetTrimRight(UPInt size) const + { + // Limit trim to the size of the string. + size = Alg::PMin(GetSize(), size); + + return StringDataPtr(ToCStr(), GetSize() - size); + } + + // Create new object, which contains next token. + // Useful for parsing. + StringDataPtr GetNextToken(char separator = ':') const + { + UPInt cur_pos = 0; + const char* cur_str = ToCStr(); + + for (; cur_pos < GetSize() && cur_str[cur_pos]; ++cur_pos) + { + if (cur_str[cur_pos] == separator) + { + break; + } + } + + return StringDataPtr(ToCStr(), cur_pos); + } + + // Trim size bytes from the left. + StringDataPtr& TrimLeft(UPInt size) + { + // Limit trim size to the size of the string. + size = Alg::PMin(GetSize(), size); + pStr += size; + Size -= size; + + return *this; + } + // Trim size bytes from the right. + StringDataPtr& TrimRight(UPInt size) + { + // Limit trim to the size of the string. + size = Alg::PMin(GetSize(), size); + Size -= size; + + return *this; + } + + const char* Begin() const { return ToCStr(); } + const char* End() const { return ToCStr() + GetSize(); } + + // Hash functor used string data pointers + struct HashFunctor + { + UPInt operator()(const StringDataPtr& data) const + { + return String::BernsteinHashFunction(data.ToCStr(), data.GetSize()); + } + }; + + bool operator== (const StringDataPtr& data) const + { + return (OVR_strncmp(pStr, data.pStr, data.Size) == 0); + } + +protected: + const char* pStr; + UPInt Size; +}; + +} // OVR + +#endif diff --git a/LibOVR/Src/Kernel/OVR_StringHash.h b/LibOVR/Src/Kernel/OVR_StringHash.h new file mode 100644 index 0000000..237dc7e --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_StringHash.h @@ -0,0 +1,89 @@ +/************************************************************************************ + +PublicHeader: None +Filename : OVR_StringHash.h +Content : String hash table used when optional case-insensitive + lookup is required. +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_StringHash_h +#define OVR_StringHash_h + +#include "OVR_String.h" +#include "OVR_Hash.h" + +namespace OVR { + +//----------------------------------------------------------------------------------- +// *** StringHash + +// This is a custom string hash table that supports case-insensitive +// searches through special functions such as GetCaseInsensitive, etc. +// This class is used for Flash labels, exports and other case-insensitive tables. + +template > +class StringHash : public Hash +{ +public: + typedef U ValueType; + typedef StringHash SelfType; + typedef Hash BaseType; + +public: + + void operator = (const SelfType& src) { BaseType::operator = (src); } + + bool GetCaseInsensitive(const String& key, U* pvalue) const + { + String::NoCaseKey ikey(key); + return BaseType::GetAlt(ikey, pvalue); + } + // Pointer-returning get variety. + const U* GetCaseInsensitive(const String& key) const + { + String::NoCaseKey ikey(key); + return BaseType::GetAlt(ikey); + } + U* GetCaseInsensitive(const String& key) + { + String::NoCaseKey ikey(key); + return BaseType::GetAlt(ikey); + } + + + typedef typename BaseType::Iterator base_iterator; + + base_iterator FindCaseInsensitive(const String& key) + { + String::NoCaseKey ikey(key); + return BaseType::FindAlt(ikey); + } + + // Set just uses a find and assigns value if found. The key is not modified; + // this behavior is identical to Flash string variable assignment. + void SetCaseInsensitive(const String& key, const U& value) + { + base_iterator it = FindCaseInsensitive(key); + if (it != BaseType::End()) + { + it->Second = value; + } + else + { + BaseType::Add(key, value); + } + } +}; + +} // OVR + +#endif diff --git a/LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp b/LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp new file mode 100644 index 0000000..a6ceb3a --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp @@ -0,0 +1,42 @@ +/************************************************************************************ + +Filename : OVR_String_FormatUtil.cpp +Content : String format functions. +Created : February 27, 2013 +Notes : + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_String.h" +#include "OVR_Log.h" + +namespace OVR { + +void StringBuffer::AppendFormat(const char* format, ...) +{ + va_list argList; + + va_start(argList, format); + UPInt size = OVR_vscprintf(format, argList); + va_end(argList); + + char* buffer = (char*) OVR_ALLOC(sizeof(char) * (size+1)); + + va_start(argList, format); + UPInt result = OVR_vsprintf(buffer, size+1, format, argList); + OVR_UNUSED1(result); + va_end(argList); + OVR_ASSERT_LOG(result == size, ("Error in OVR_vsprintf")); + + AppendString(buffer); + + OVR_FREE(buffer); +} + +} // OVR diff --git a/LibOVR/Src/Kernel/OVR_String_PathUtil.cpp b/LibOVR/Src/Kernel/OVR_String_PathUtil.cpp new file mode 100644 index 0000000..27e79c6 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_String_PathUtil.cpp @@ -0,0 +1,200 @@ +/************************************************************************************ + +Filename : OVR_String_PathUtil.cpp +Content : String filename/url helper function +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_String.h" +#include "OVR_UTF8Util.h" + +namespace OVR { + +//-------------------------------------------------------------------- +// ***** Path-Scanner helper function + +// Scans file path finding filename start and extension start, fills in their addess. +void ScanFilePath(const char* url, const char** pfilename, const char** pext) +{ + const char* urlStart = url; + const char *filename = 0; + const char *lastDot = 0; + + UInt32 charVal = UTF8Util::DecodeNextChar(&url); + + while (charVal != 0) + { + if ((charVal == '/') || (charVal == '\\')) + { + filename = url; + lastDot = 0; + } + else if (charVal == '.') + { + lastDot = url - 1; + } + + charVal = UTF8Util::DecodeNextChar(&url); + } + + if (pfilename) + { + // It was a naked filename + if (urlStart && (*urlStart != '.') && *urlStart) + *pfilename = urlStart; + else + *pfilename = filename; + } + + if (pext) + { + *pext = lastDot; + } +} + +// Scans till the end of protocol. Returns first character past protocol, +// 0 if not found. +// - protocol: 'file://', 'http://' +const char* ScanPathProtocol(const char* url) +{ + UInt32 charVal = UTF8Util::DecodeNextChar(&url); + UInt32 charVal2; + + while (charVal != 0) + { + // Treat a colon followed by a slash as absolute. + if (charVal == ':') + { + charVal2 = UTF8Util::DecodeNextChar(&url); + charVal = UTF8Util::DecodeNextChar(&url); + if ((charVal == '/') && (charVal2 == '\\')) + return url; + } + charVal = UTF8Util::DecodeNextChar(&url); + } + return 0; +} + + +//-------------------------------------------------------------------- +// ***** String Path API implementation + +bool String::HasAbsolutePath(const char* url) +{ + // Absolute paths can star with: + // - protocols: 'file://', 'http://' + // - windows drive: 'c:\' + // - UNC share name: '\\share' + // - unix root '/' + + // On the other hand, relative paths are: + // - directory: 'directory/file' + // - this directory: './file' + // - parent directory: '../file' + // + // For now, we don't parse '.' or '..' out, but instead let it be concatenated + // to string and let the OS figure it out. This, however, is not good for file + // name matching in library/etc, so it should be improved. + + if (!url || !*url) + return true; // Treat empty strings as absolute. + + UInt32 charVal = UTF8Util::DecodeNextChar(&url); + + // Fist character of '/' or '\\' means absolute url. + if ((charVal == '/') || (charVal == '\\')) + return true; + + while (charVal != 0) + { + // Treat a colon followed by a slash as absolute. + if (charVal == ':') + { + charVal = UTF8Util::DecodeNextChar(&url); + // Protocol or windows drive. Absolute. + if ((charVal == '/') || (charVal == '\\')) + return true; + } + else if ((charVal == '/') || (charVal == '\\')) + { + // Not a first character (else 'if' above the loop would have caught it). + // Must be a relative url. + break; + } + + charVal = UTF8Util::DecodeNextChar(&url); + } + + // We get here for relative paths. + return false; +} + + +bool String::HasExtension(const char* path) +{ + const char* ext = 0; + ScanFilePath(path, 0, &ext); + return ext != 0; +} +bool String::HasProtocol(const char* path) +{ + return ScanPathProtocol(path) != 0; +} + + +String String::GetPath() const +{ + const char* filename = 0; + ScanFilePath(ToCStr(), &filename, 0); + + // Technically we can have extra logic somewhere for paths, + // such as enforcing protocol and '/' only based on flags, + // but we keep it simple for now. + return String(ToCStr(), filename ? (filename-ToCStr()) : GetSize()); +} + +String String::GetProtocol() const +{ + const char* protocolEnd = ScanPathProtocol(ToCStr()); + return String(ToCStr(), protocolEnd ? (protocolEnd-ToCStr()) : 0); +} + +String String::GetFilename() const +{ + const char* filename = 0; + ScanFilePath(ToCStr(), &filename, 0); + return String(filename); +} +String String::GetExtension() const +{ + const char* ext = 0; + ScanFilePath(ToCStr(), 0, &ext); + return String(ext); +} + +void String::StripExtension() +{ + const char* ext = 0; + ScanFilePath(ToCStr(), 0, &ext); + if (ext) + { + *this = String(ToCStr(), ext-ToCStr()); + } +} + +void String::StripProtocol() +{ + const char* protocol = ScanPathProtocol(ToCStr()); + if (protocol) + AssignString(protocol, OVR_strlen(protocol)); +} + +} // OVR diff --git a/LibOVR/Src/Kernel/OVR_SysFile.cpp b/LibOVR/Src/Kernel/OVR_SysFile.cpp new file mode 100644 index 0000000..f72a8d2 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_SysFile.cpp @@ -0,0 +1,125 @@ +/************************************************************************** + +Filename : OVR_SysFile.cpp +Content : File wrapper class implementation (Win32) + +Created : April 5, 1999 +Authors : Michael Antonov + +Copyright : Copyright 2011 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +**************************************************************************/ + +#define GFILE_CXX + +// Standard C library (Captain Obvious guarantees!) +#include + +#include "OVR_SysFile.h" + +namespace OVR { + +// This is - a dummy file that fails on all calls. + +class UnopenedFile : public File +{ +public: + UnopenedFile() { } + ~UnopenedFile() { } + + virtual const char* GetFilePath() { return 0; } + + // ** File Information + virtual bool IsValid() { return 0; } + virtual bool IsWritable() { return 0; } + + // Return position / file size + virtual int Tell() { return 0; } + virtual SInt64 LTell() { return 0; } + virtual int GetLength() { return 0; } + virtual SInt64 LGetLength() { return 0; } + +// virtual bool Stat(FileStats *pfs) { return 0; } + virtual int GetErrorCode() { return Error_FileNotFound; } + + // ** Stream implementation & I/O + virtual int Write(const UByte *pbuffer, int numBytes) { return -1; OVR_UNUSED2(pbuffer, numBytes); } + virtual int Read(UByte *pbuffer, int numBytes) { return -1; OVR_UNUSED2(pbuffer, numBytes); } + virtual int SkipBytes(int numBytes) { return 0; OVR_UNUSED(numBytes); } + virtual int BytesAvailable() { return 0; } + virtual bool Flush() { return 0; } + virtual int Seek(int offset, int origin) { return -1; OVR_UNUSED2(offset, origin); } + virtual SInt64 LSeek(SInt64 offset, int origin) { return -1; OVR_UNUSED2(offset, origin); } + + virtual int CopyFromStream(File *pstream, int byteSize) { return -1; OVR_UNUSED2(pstream, byteSize); } + virtual bool Close() { return 0; } +}; + + + +// ***** System File + +// System file is created to access objects on file system directly +// This file can refer directly to path + +// ** Constructor +SysFile::SysFile() : DelegatedFile(0) +{ + pFile = *new UnopenedFile; +} + +File* FileFILEOpen(const String& path, int flags, int mode); + +// Opens a file +SysFile::SysFile(const String& path, int flags, int mode) : DelegatedFile(0) +{ + Open(path, flags, mode); +} + + +// ** Open & management +// Will fail if file's already open +bool SysFile::Open(const String& path, int flags, int mode) +{ + pFile = *FileFILEOpen(path, flags, mode); + if ((!pFile) || (!pFile->IsValid())) + { + pFile = *new UnopenedFile; + return 0; + } + //pFile = *OVR_NEW DelegatedFile(pFile); // MA Testing + if (flags & Open_Buffered) + pFile = *new BufferedFile(pFile); + return 1; +} + + +// ** Overrides + +int SysFile::GetErrorCode() +{ + return pFile ? pFile->GetErrorCode() : Error_FileNotFound; +} + + +// Overrides to provide re-open support +bool SysFile::IsValid() +{ + return pFile && pFile->IsValid(); +} +bool SysFile::Close() +{ + if (IsValid()) + { + DelegatedFile::Close(); + pFile = *new UnopenedFile; + return 1; + } + return 0; +} + +} // OVR diff --git a/LibOVR/Src/Kernel/OVR_SysFile.h b/LibOVR/Src/Kernel/OVR_SysFile.h new file mode 100644 index 0000000..a9bb0a0 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_SysFile.h @@ -0,0 +1,93 @@ +/************************************************************************************ + +PublicHeader: Kernel +Filename : OVR_SysFile.h +Content : Header for all internal file management - functions and structures + to be inherited by OS specific subclasses. +Created : September 19, 2012 +Notes : + +Notes : errno may not be preserved across use of GBaseFile member functions + : Directories cannot be deleted while files opened from them are in use + (For the GetFullName function) + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_SysFile_h +#define OVR_SysFile_h + +#include "OVR_File.h" + +namespace OVR { + +// ***** Declared classes +class SysFile; + +//----------------------------------------------------------------------------------- +// *** File Statistics + +// This class contents are similar to _stat, providing +// creation, modify and other information about the file. +struct FileStat +{ + // No change or create time because they are not available on most systems + SInt64 ModifyTime; + SInt64 AccessTime; + SInt64 FileSize; + + bool operator== (const FileStat& stat) const + { + return ( (ModifyTime == stat.ModifyTime) && + (AccessTime == stat.AccessTime) && + (FileSize == stat.FileSize) ); + } +}; + +//----------------------------------------------------------------------------------- +// *** System File + +// System file is created to access objects on file system directly +// This file can refer directly to path. +// System file can be open & closed several times; however, such use is not recommended +// This class is realy a wrapper around an implementation of File interface for a +// particular platform. + +class SysFile : public DelegatedFile +{ +protected: + SysFile(const SysFile &source) : DelegatedFile () { OVR_UNUSED(source); } +public: + + // ** Constructor + SysFile(); + // Opens a file + SysFile(const String& path, int flags = Open_Read|Open_Buffered, int mode = Mode_ReadWrite); + + // ** Open & management + bool Open(const String& path, int flags = Open_Read|Open_Buffered, int mode = Mode_ReadWrite); + + OVR_FORCE_INLINE bool Create(const String& path, int mode = Mode_ReadWrite) + { return Open(path, Open_ReadWrite|Open_Create, mode); } + + // Helper function: obtain file statistics information. In GFx, this is used to detect file changes. + // Return 0 if function failed, most likely because the file doesn't exist. + static bool OVR_CDECL GetFileStat(FileStat* pfileStats, const String& path); + + // ** Overrides + // Overridden to provide re-open support + virtual int GetErrorCode(); + + virtual bool IsValid(); + + virtual bool Close(); +}; + +} // Scaleform + +#endif diff --git a/LibOVR/Src/Kernel/OVR_System.cpp b/LibOVR/Src/Kernel/OVR_System.cpp new file mode 100644 index 0000000..8eba8e2 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_System.cpp @@ -0,0 +1,70 @@ +/************************************************************************************ + +Filename : OVR_System.cpp +Content : General kernel initialization/cleanup, including that + of the memory allocator. +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_System.h" +#include "OVR_Threads.h" +#include "OVR_Timer.h" + +namespace OVR { + +// ***** OVR::System Implementation + +// Initializes System core, installing allocator. +void System::Init(Log* log, Allocator *palloc) +{ + if (!Allocator::GetInstance()) + { + Log::SetGlobalLog(log); + Timer::initializeTimerSystem(); + Allocator::setInstance(palloc); + } + else + { + OVR_DEBUG_LOG(("System::Init failed - duplicate call.")); + } +} + +void System::Destroy() +{ + if (Allocator::GetInstance()) + { + // Wait for all threads to finish; this must be done so that memory + // allocator and all destructors finalize correctly. +#ifdef OVR_ENABLE_THREADS + Thread::FinishAllThreads(); +#endif + + // Shutdown heap and destroy SysAlloc singleton, if any. + Allocator::GetInstance()->onSystemShutdown(); + Allocator::setInstance(0); + + Timer::shutdownTimerSystem(); + Log::SetGlobalLog(Log::GetDefaultLog()); + } + else + { + OVR_DEBUG_LOG(("System::Destroy failed - System not initialized.")); + } +} + +// Returns 'true' if system was properly initialized. +bool System::IsInitialized() +{ + return Allocator::GetInstance() != 0; +} + +} // OVR + diff --git a/LibOVR/Src/Kernel/OVR_System.h b/LibOVR/Src/Kernel/OVR_System.h new file mode 100644 index 0000000..e53b4c8 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_System.h @@ -0,0 +1,67 @@ +/************************************************************************************ + +PublicHeader: OVR +Filename : OVR_System.h +Content : General kernel initialization/cleanup, including that + of the memory allocator. +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_System_h +#define OVR_System_h + +#include "OVR_Allocator.h" +#include "OVR_Log.h" + +namespace OVR { + +// ***** System Core Initialization class + +// System initialization must take place before any other OVR_Kernel objects are used; +// this is done my calling System::Init(). Among other things, this is necessary to +// initialize the memory allocator. Similarly, System::Destroy must be +// called before program exist for proper cleanup. Both of these tasks can be achieved by +// simply creating System object first, allowing its constructor/destructor do the work. + +// TBD: Require additional System class for Oculus Rift API? + +class System +{ +public: + + // System constructor expects allocator to be specified, if it is being substituted. + System(Log* log = Log::ConfigureDefaultLog(LogMask_Debug), + Allocator* palloc = DefaultAllocator::InitSystemSingleton()) + { + Init(log, palloc); + } + + ~System() + { + Destroy(); + } + + // Returns 'true' if system was properly initialized. + static bool OVR_CDECL IsInitialized(); + + // Initializes System core. Users can override memory implementation by passing + // a different Allocator here. + static void OVR_CDECL Init(Log* log = Log::ConfigureDefaultLog(LogMask_Debug), + Allocator *palloc = DefaultAllocator::InitSystemSingleton()); + + // De-initializes System more, finalizing the threading system and destroying + // the global memory allocator. + static void OVR_CDECL Destroy(); +}; + +} // OVR + +#endif diff --git a/LibOVR/Src/Kernel/OVR_Threads.h b/LibOVR/Src/Kernel/OVR_Threads.h new file mode 100644 index 0000000..9c29fe0 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Threads.h @@ -0,0 +1,396 @@ +/************************************************************************************ + +PublicHeader: None +Filename : OVR_Threads.h +Content : Contains thread-related (safe) functionality +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ +#ifndef OVR_Threads_h +#define OVR_Threads_h + +#include "OVR_Types.h" +#include "OVR_Atomic.h" +#include "OVR_RefCount.h" +#include "OVR_Array.h" + +// Defines the infinite wait delay timeout +#define OVR_WAIT_INFINITE 0xFFFFFFFF + +// To be defined in the project configuration options +#ifdef OVR_ENABLE_THREADS + + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ****** Declared classes + +// Declared with thread support only +class Mutex; +class WaitCondition; +class Event; +// Implementation forward declarations +class MutexImpl; +class WaitConditionImpl; + + + +//----------------------------------------------------------------------------------- +// ***** Mutex + +// Mutex class represents a system Mutex synchronization object that provides access +// serialization between different threads, allowing one thread mutually exclusive access +// to a resource. Mutex is more heavy-weight then Lock, but supports WaitCondition. + +class Mutex +{ + friend class WaitConditionImpl; + friend class MutexImpl; + + MutexImpl *pImpl; + +public: + // Constructor/destructor + Mutex(bool recursive = 1); + ~Mutex(); + + // Locking functions + void DoLock(); + bool TryLock(); + void Unlock(); + + // Returns 1 if the mutes is currently locked by another thread + // Returns 0 if the mutex is not locked by another thread, and can therefore be acquired. + bool IsLockedByAnotherThread(); + + // Locker class; Used for automatic locking of a mutex withing scope + class Locker + { + public: + Mutex *pMutex; + Locker(Mutex *pmutex) + { pMutex = pmutex; pMutex->DoLock(); } + ~Locker() + { pMutex->Unlock(); } + }; +}; + + +//----------------------------------------------------------------------------------- +// ***** WaitCondition + +/* + WaitCondition is a synchronization primitive that can be used to implement what is known as a monitor. + Dependent threads wait on a wait condition by calling Wait(), and get woken up by other threads that + call Notify() or NotifyAll(). + + The unique feature of this class is that it provides an atomic way of first releasing a Mutex, and then + starting a wait on a wait condition. If both the mutex and the wait condition are associated with the same + resource, this ensures that any condition checked for while the mutex was locked does not change before + the wait on the condition is actually initiated. +*/ + +class WaitCondition +{ + friend class WaitConditionImpl; + // Internal implementation structure + WaitConditionImpl *pImpl; + +public: + // Constructor/destructor + WaitCondition(); + ~WaitCondition(); + + // Release mutex and wait for condition. The mutex is re-aquired after the wait. + // Delay is specified in milliseconds (1/1000 of a second). + bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE); + + // Notify a condition, releasing at one object waiting + void Notify(); + // Notify a condition, releasing all objects waiting + void NotifyAll(); +}; + + +//----------------------------------------------------------------------------------- +// ***** Event + +// Event is a wait-able synchronization object similar to Windows event. +// Event can be waited on until it's signaled by another thread calling +// either SetEvent or PulseEvent. + +class Event +{ + // Event state, its mutex and the wait condition + volatile bool State; + volatile bool Temporary; + mutable Mutex StateMutex; + WaitCondition StateWaitCondition; + + void updateState(bool newState, bool newTemp, bool mustNotify); + +public: + Event(bool setInitially = 0) : State(setInitially), Temporary(false) { } + ~Event() { } + + // Wait on an event condition until it is set + // Delay is specified in milliseconds (1/1000 of a second). + bool Wait(unsigned delay = OVR_WAIT_INFINITE); + + // Set an event, releasing objects waiting on it + void SetEvent() + { updateState(true, false, true); } + + // Reset an event, un-signaling it + void ResetEvent() + { updateState(false, false, false); } + + // Set and then reset an event once a waiter is released. + // If threads are already waiting, they will be notified and released + // If threads are not waiting, the event is set until the first thread comes in + void PulseEvent() + { updateState(true, true, true); } +}; + + +//----------------------------------------------------------------------------------- +// ***** Thread class + +// ThreadId uniquely identifies a thread; returned by GetCurrentThreadId() and +// Thread::GetThreadId. +typedef void* ThreadId; + + +// *** Thread flags + +// Indicates that the thread is has been started, i.e. Start method has been called, and threads +// OnExit() method has not yet been called/returned. +#define OVR_THREAD_STARTED 0x01 +// This flag is set once the thread has ran, and finished. +#define OVR_THREAD_FINISHED 0x02 +// This flag is set temporarily if this thread was started suspended. It is used internally. +#define OVR_THREAD_START_SUSPENDED 0x08 +// This flag is used to ask a thread to exit. Message driven threads will usually check this flag +// and finish once it is set. +#define OVR_THREAD_EXIT 0x10 + + +class Thread : public RefCountBase +{ // NOTE: Waitable must be the first base since it implements RefCountImpl. + +public: + + // *** Callback functions, can be used instead of overriding Run + + // Run function prototypes. + // Thread function and user handle passed to it, executed by the default + // Thread::Run implementation if not null. + typedef int (*ThreadFn)(Thread *pthread, void* h); + + // Thread ThreadFunction1 is executed if not 0, otherwise ThreadFunction2 is tried + ThreadFn ThreadFunction; + // User handle passes to a thread + void* UserHandle; + + // Thread state to start a thread with + enum ThreadState + { + NotRunning = 0, + Running = 1, + Suspended = 2 + }; + + // Thread priority + enum ThreadPriority + { + CriticalPriority, + HighestPriority, + AboveNormalPriority, + NormalPriority, + BelowNormalPriority, + LowestPriority, + IdlePriority, + }; + + // Thread constructor parameters + struct CreateParams + { + CreateParams(ThreadFn func = 0, void* hand = 0, UPInt ssize = 128 * 1024, + int proc = -1, ThreadState state = NotRunning, ThreadPriority prior = NormalPriority) + : threadFunction(func), userHandle(hand), stackSize(ssize), + processor(proc), initialState(state), priority(prior) {} + ThreadFn threadFunction; // Thread function + void* userHandle; // User handle passes to a thread + UPInt stackSize; // Thread stack size + int processor; // Thread hardware processor + ThreadState initialState; // + ThreadPriority priority; // Thread priority + }; + + // *** Constructors + + // A default constructor always creates a thread in NotRunning state, because + // the derived class has not yet been initialized. The derived class can call Start explicitly. + // "processor" parameter specifies which hardware processor this thread will be run on. + // -1 means OS decides this. Implemented only on Win32 + Thread(UPInt stackSize = 128 * 1024, int processor = -1); + // Constructors that initialize the thread with a pointer to function. + // An option to start a thread is available, but it should not be used if classes are derived from Thread. + // "processor" parameter specifies which hardware processor this thread will be run on. + // -1 means OS decides this. Implemented only on Win32 + Thread(ThreadFn threadFunction, void* userHandle = 0, UPInt stackSize = 128 * 1024, + int processor = -1, ThreadState initialState = NotRunning); + // Constructors that initialize the thread with a create parameters structure. + explicit Thread(const CreateParams& params); + + // Destructor. + virtual ~Thread(); + + // Waits for all Threads to finish; should be called only from the root + // application thread. Once this function returns, we know that all other + // thread's references to Thread object have been released. + static void OVR_CDECL FinishAllThreads(); + + + // *** Overridable Run function for thread processing + + // - returning from this method will end the execution of the thread + // - return value is usually 0 for success + virtual int Run(); + // Called after return/exit function + virtual void OnExit(); + + + // *** Thread management + + // Starts the thread if its not already running + // - internally sets up the threading and calls Run() + // - initial state can either be Running or Suspended, NotRunning will just fail and do nothing + // - returns the exit code + virtual bool Start(ThreadState initialState = Running); + + // Quits with an exit code + virtual void Exit(int exitCode=0); + + // Suspend the thread until resumed + // Returns 1 for success, 0 for failure. + bool Suspend(); + // Resumes currently suspended thread + // Returns 1 for success, 0 for failure. + bool Resume(); + + // Static function to return a pointer to the current thread + //static Thread* GetThread(); + + + // *** Thread status query functions + + bool GetExitFlag() const; + void SetExitFlag(bool exitFlag); + + // Determines whether the thread was running and is now finished + bool IsFinished() const; + // Determines if the thread is currently suspended + bool IsSuspended() const; + // Returns current thread state + ThreadState GetThreadState() const; + + // Returns the number of available CPUs on the system + static int GetCPUCount(); + + // Returns the thread exit code. Exit code is initialized to 0, + // and set to the return value if Run function after the thread is finished. + inline int GetExitCode() const { return ExitCode; } + // Returns an OS handle +#if defined(OVR_OS_WIN32) + void* GetOSHandle() const { return ThreadHandle; } +#else + pthread_t GetOSHandle() const { return ThreadHandle; } +#endif + +#if defined(OVR_OS_WIN32) + ThreadId GetThreadId() const { return IdValue; } +#else + ThreadId GetThreadId() const { return (ThreadId)GetOSHandle(); } +#endif + + static int GetOSPriority(ThreadPriority); + // *** Sleep + + // Sleep secs seconds + static bool Sleep(unsigned secs); + // Sleep msecs milliseconds + static bool MSleep(unsigned msecs); + + + // *** Debugging functionality +#if defined(OVR_OS_WIN32) + virtual void SetThreadName( const char* name ); +#else + virtual void SetThreadName( const char* name ) { OVR_UNUSED(name); } +#endif + +private: +#if defined(OVR_OS_WIN32) + friend unsigned WINAPI Thread_Win32StartFn(void *pthread); + +#else + friend void *Thread_PthreadStartFn(void * phandle); + + static int InitAttr; + static pthread_attr_t Attr; +#endif + +protected: + // Thread state flags + AtomicInt ThreadFlags; + AtomicInt SuspendCount; + UPInt StackSize; + + // Hardware processor which this thread is running on. + int Processor; + ThreadPriority Priority; + +#if defined(OVR_OS_WIN32) + void* ThreadHandle; + volatile ThreadId IdValue; + + // System-specific cleanup function called from destructor + void CleanupSystemThread(); + +#else + pthread_t ThreadHandle; +#endif + + // Exit code of the thread, as returned by Run. + int ExitCode; + + // Internal run function. + int PRun(); + // Finishes the thread and releases internal reference to it. + void FinishAndRelease(); + + void Init(const CreateParams& params); + + // Protected copy constructor + Thread(const Thread &source) { OVR_UNUSED(source); } + +}; + +// Returns the unique Id of a thread it is called on, intended for +// comparison purposes. +ThreadId GetCurrentThreadId(); + + +} // OVR + +#endif // OVR_ENABLE_THREADS +#endif // OVR_Threads_h diff --git a/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp b/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp new file mode 100644 index 0000000..c40d9d8 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp @@ -0,0 +1,795 @@ + +#include "OVR_Threads.h" +#include "OVR_Hash.h" + +#ifdef OVR_ENABLE_THREADS + +#include "OVR_Timer.h" +#include "OVR_Log.h" + +#include +#include + +#ifdef OVR_OS_PS3 +#include +#include +#include +#define sleep(x) sys_timer_sleep(x) +#define usleep(x) sys_timer_usleep(x) +using std::timespec; +#else +#include +#include +#include +#endif + +namespace OVR { + +// ***** Mutex implementation + + +// *** Internal Mutex implementation structure + +class MutexImpl : public NewOverrideBase +{ + // System mutex or semaphore + pthread_mutex_t SMutex; + bool Recursive; + unsigned LockCount; + pthread_t LockedBy; + + friend class WaitConditionImpl; + +public: + // Constructor/destructor + MutexImpl(Mutex* pmutex, bool recursive = 1); + ~MutexImpl(); + + // Locking functions + void DoLock(); + bool TryLock(); + void Unlock(Mutex* pmutex); + // Returns 1 if the mutes is currently locked + bool IsLockedByAnotherThread(Mutex* pmutex); + bool IsSignaled() const; +}; + +pthread_mutexattr_t Lock::RecursiveAttr; +bool Lock::RecursiveAttrInit = 0; + +// *** Constructor/destructor +MutexImpl::MutexImpl(Mutex* pmutex, bool recursive) +{ + Recursive = recursive; + LockCount = 0; + + if (Recursive) + { + if (!Lock::RecursiveAttrInit) + { + pthread_mutexattr_init(&Lock::RecursiveAttr); + pthread_mutexattr_settype(&Lock::RecursiveAttr, PTHREAD_MUTEX_RECURSIVE); + Lock::RecursiveAttrInit = 1; + } + + pthread_mutex_init(&SMutex, &Lock::RecursiveAttr); + } + else + pthread_mutex_init(&SMutex, 0); +} + +MutexImpl::~MutexImpl() +{ + pthread_mutex_destroy(&SMutex); +} + + +// Lock and try lock +void MutexImpl::DoLock() +{ + while (pthread_mutex_lock(&SMutex)); + LockCount++; + LockedBy = pthread_self(); +} + +bool MutexImpl::TryLock() +{ + if (!pthread_mutex_trylock(&SMutex)) + { + LockCount++; + LockedBy = pthread_self(); + return 1; + } + + return 0; +} + +void MutexImpl::Unlock(Mutex* pmutex) +{ + OVR_ASSERT(pthread_self() == LockedBy && LockCount > 0); + + unsigned lockCount; + LockCount--; + lockCount = LockCount; + + pthread_mutex_unlock(&SMutex); +} + +bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex) +{ + // There could be multiple interpretations of IsLocked with respect to current thread + if (LockCount == 0) + return 0; + if (pthread_self() != LockedBy) + return 1; + return 0; +} + +bool MutexImpl::IsSignaled() const +{ + // An mutex is signaled if it is not locked ANYWHERE + // Note that this is different from IsLockedByAnotherThread function, + // that takes current thread into account + return LockCount == 0; +} + + +// *** Actual Mutex class implementation + +Mutex::Mutex(bool recursive) +{ + // NOTE: RefCount mode already thread-safe for all waitables. + pImpl = new MutexImpl(this, recursive); +} + +Mutex::~Mutex() +{ + delete pImpl; +} + +// Lock and try lock +void Mutex::DoLock() +{ + pImpl->DoLock(); +} +bool Mutex::TryLock() +{ + return pImpl->TryLock(); +} +void Mutex::Unlock() +{ + pImpl->Unlock(this); +} +bool Mutex::IsLockedByAnotherThread() +{ + return pImpl->IsLockedByAnotherThread(this); +} + + + +//----------------------------------------------------------------------------------- +// ***** Event + +bool Event::Wait(unsigned delay) +{ + Mutex::Locker lock(&StateMutex); + + // Do the correct amount of waiting + if (delay == OVR_WAIT_INFINITE) + { + while(!State) + StateWaitCondition.Wait(&StateMutex); + } + else if (delay) + { + if (!State) + StateWaitCondition.Wait(&StateMutex, delay); + } + + bool state = State; + // Take care of temporary 'pulsing' of a state + if (Temporary) + { + Temporary = false; + State = false; + } + return state; +} + +void Event::updateState(bool newState, bool newTemp, bool mustNotify) +{ + Mutex::Locker lock(&StateMutex); + State = newState; + Temporary = newTemp; + if (mustNotify) + StateWaitCondition.NotifyAll(); +} + + + +// ***** Wait Condition Implementation + +// Internal implementation class +class WaitConditionImpl : public NewOverrideBase +{ + pthread_mutex_t SMutex; + pthread_cond_t Condv; + +public: + + // Constructor/destructor + WaitConditionImpl(); + ~WaitConditionImpl(); + + // Release mutex and wait for condition. The mutex is re-aqured after the wait. + bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE); + + // Notify a condition, releasing at one object waiting + void Notify(); + // Notify a condition, releasing all objects waiting + void NotifyAll(); +}; + + +WaitConditionImpl::WaitConditionImpl() +{ + pthread_mutex_init(&SMutex, 0); + pthread_cond_init(&Condv, 0); +} + +WaitConditionImpl::~WaitConditionImpl() +{ + pthread_mutex_destroy(&SMutex); + pthread_cond_destroy(&Condv); +} + +bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay) +{ + bool result = 1; + unsigned lockCount = pmutex->pImpl->LockCount; + + // Mutex must have been locked + if (lockCount == 0) + return 0; + + pthread_mutex_lock(&SMutex); + + // Finally, release a mutex or semaphore + if (pmutex->pImpl->Recursive) + { + // Release the recursive mutex N times + pmutex->pImpl->LockCount = 0; + for(unsigned i=0; ipImpl->SMutex); + } + else + { + pmutex->pImpl->LockCount = 0; + pthread_mutex_unlock(&pmutex->pImpl->SMutex); + } + + // Note that there is a gap here between mutex.Unlock() and Wait(). + // The other mutex protects this gap. + + if (delay == OVR_WAIT_INFINITE) + pthread_cond_wait(&Condv,&SMutex); + else + { + timespec ts; +#ifdef OVR_OS_PS3 + sys_time_sec_t s; + sys_time_nsec_t ns; + sys_time_get_current_time(&s, &ns); + + ts.tv_sec = s + (delay / 1000); + ts.tv_nsec = ns + (delay % 1000) * 1000000; + +#else + struct timeval tv; + gettimeofday(&tv, 0); + + ts.tv_sec = tv.tv_sec + (delay / 1000); + ts.tv_nsec = (tv.tv_usec + (delay % 1000) * 1000) * 1000; +#endif + if (ts.tv_nsec > 999999999) + { + ts.tv_sec++; + ts.tv_nsec -= 1000000000; + } + int r = pthread_cond_timedwait(&Condv,&SMutex, &ts); + OVR_ASSERT(r == 0 || r == ETIMEDOUT); + if (r) + result = 0; + } + + pthread_mutex_unlock(&SMutex); + + // Re-aquire the mutex + for(unsigned i=0; iDoLock(); + + // Return the result + return result; +} + +// Notify a condition, releasing the least object in a queue +void WaitConditionImpl::Notify() +{ + pthread_mutex_lock(&SMutex); + pthread_cond_signal(&Condv); + pthread_mutex_unlock(&SMutex); +} + +// Notify a condition, releasing all objects waiting +void WaitConditionImpl::NotifyAll() +{ + pthread_mutex_lock(&SMutex); + pthread_cond_broadcast(&Condv); + pthread_mutex_unlock(&SMutex); +} + + + +// *** Actual implementation of WaitCondition + +WaitCondition::WaitCondition() +{ + pImpl = new WaitConditionImpl; +} +WaitCondition::~WaitCondition() +{ + delete pImpl; +} + +bool WaitCondition::Wait(Mutex *pmutex, unsigned delay) +{ + return pImpl->Wait(pmutex, delay); +} +// Notification +void WaitCondition::Notify() +{ + pImpl->Notify(); +} +void WaitCondition::NotifyAll() +{ + pImpl->NotifyAll(); +} + + +// ***** Current thread + +// Per-thread variable +/* +static __thread Thread* pCurrentThread = 0; + +// Static function to return a pointer to the current thread +void Thread::InitCurrentThread(Thread *pthread) +{ + pCurrentThread = pthread; +} + +// Static function to return a pointer to the current thread +Thread* Thread::GetThread() +{ + return pCurrentThread; +} +*/ + + +// *** Thread constructors. + +Thread::Thread(UPInt stackSize, int processor) +{ + // NOTE: RefCount mode already thread-safe for all Waitable objects. + CreateParams params; + params.stackSize = stackSize; + params.processor = processor; + Init(params); +} + +Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize, + int processor, Thread::ThreadState initialState) +{ + CreateParams params(threadFunction, userHandle, stackSize, processor, initialState); + Init(params); +} + +Thread::Thread(const CreateParams& params) +{ + Init(params); +} + +void Thread::Init(const CreateParams& params) +{ + // Clear the variables + ThreadFlags = 0; + ThreadHandle = 0; + ExitCode = 0; + SuspendCount = 0; + StackSize = params.stackSize; + Processor = params.processor; + Priority = params.priority; + + // Clear Function pointers + ThreadFunction = params.threadFunction; + UserHandle = params.userHandle; + if (params.initialState != NotRunning) + Start(params.initialState); +} + +Thread::~Thread() +{ + // Thread should not running while object is being destroyed, + // this would indicate ref-counting issue. + //OVR_ASSERT(IsRunning() == 0); + + // Clean up thread. + ThreadHandle = 0; +} + + + +// *** Overridable User functions. + +// Default Run implementation +int Thread::Run() +{ + // Call pointer to function, if available. + return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0; +} +void Thread::OnExit() +{ +} + + +// Finishes the thread and releases internal reference to it. +void Thread::FinishAndRelease() +{ + // Note: thread must be US. + ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED); + ThreadFlags |= OVR_THREAD_FINISHED; + + // Release our reference; this is equivalent to 'delete this' + // from the point of view of our thread. + Release(); +} + + + +// *** ThreadList - used to track all created threads + +class ThreadList : public NewOverrideBase +{ + //------------------------------------------------------------------------ + struct ThreadHashOp + { + size_t operator()(const Thread* ptr) + { + return (((size_t)ptr) >> 6) ^ (size_t)ptr; + } + }; + + HashSet ThreadSet; + Mutex ThreadMutex; + WaitCondition ThreadsEmpty; + // Track the root thread that created us. + pthread_t RootThreadId; + + static ThreadList* volatile pRunningThreads; + + void addThread(Thread *pthread) + { + Mutex::Locker lock(&ThreadMutex); + ThreadSet.Add(pthread); + } + + void removeThread(Thread *pthread) + { + Mutex::Locker lock(&ThreadMutex); + ThreadSet.Remove(pthread); + if (ThreadSet.GetSize() == 0) + ThreadsEmpty.Notify(); + } + + void finishAllThreads() + { + // Only original root thread can call this. + OVR_ASSERT(pthread_self() == RootThreadId); + + Mutex::Locker lock(&ThreadMutex); + while (ThreadSet.GetSize() != 0) + ThreadsEmpty.Wait(&ThreadMutex); + } + +public: + + ThreadList() + { + RootThreadId = pthread_self(); + } + ~ThreadList() { } + + + static void AddRunningThread(Thread *pthread) + { + // Non-atomic creation ok since only the root thread + if (!pRunningThreads) + { + pRunningThreads = new ThreadList; + OVR_ASSERT(pRunningThreads); + } + pRunningThreads->addThread(pthread); + } + + // NOTE: 'pthread' might be a dead pointer when this is + // called so it should not be accessed; it is only used + // for removal. + static void RemoveRunningThread(Thread *pthread) + { + OVR_ASSERT(pRunningThreads); + pRunningThreads->removeThread(pthread); + } + + static void FinishAllThreads() + { + // This is ok because only root thread can wait for other thread finish. + if (pRunningThreads) + { + pRunningThreads->finishAllThreads(); + delete pRunningThreads; + pRunningThreads = 0; + } + } +}; + +// By default, we have no thread list. +ThreadList* volatile ThreadList::pRunningThreads = 0; + + +// FinishAllThreads - exposed publicly in Thread. +void Thread::FinishAllThreads() +{ + ThreadList::FinishAllThreads(); +} + +// *** Run override + +int Thread::PRun() +{ + // Suspend us on start, if requested + if (ThreadFlags & OVR_THREAD_START_SUSPENDED) + { + Suspend(); + ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED; + } + + // Call the virtual run function + ExitCode = Run(); + return ExitCode; +} + + + + +// *** User overridables + +bool Thread::GetExitFlag() const +{ + return (ThreadFlags & OVR_THREAD_EXIT) != 0; +} + +void Thread::SetExitFlag(bool exitFlag) +{ + // The below is atomic since ThreadFlags is AtomicInt. + if (exitFlag) + ThreadFlags |= OVR_THREAD_EXIT; + else + ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT; +} + + +// Determines whether the thread was running and is now finished +bool Thread::IsFinished() const +{ + return (ThreadFlags & OVR_THREAD_FINISHED) != 0; +} +// Determines whether the thread is suspended +bool Thread::IsSuspended() const +{ + return SuspendCount > 0; +} +// Returns current thread state +Thread::ThreadState Thread::GetThreadState() const +{ + if (IsSuspended()) + return Suspended; + if (ThreadFlags & OVR_THREAD_STARTED) + return Running; + return NotRunning; +} +/* +static const char* mapsched_policy(int policy) +{ + switch(policy) + { + case SCHED_OTHER: + return "SCHED_OTHER"; + case SCHED_RR: + return "SCHED_RR"; + case SCHED_FIFO: + return "SCHED_FIFO"; + + } + return "UNKNOWN"; +} + int policy; + sched_param sparam; + pthread_getschedparam(pthread_self(), &policy, &sparam); + int max_prior = sched_get_priority_max(policy); + int min_prior = sched_get_priority_min(policy); + printf(" !!!! policy: %s, priority: %d, max priority: %d, min priority: %d\n", mapsched_policy(policy), sparam.sched_priority, max_prior, min_prior); +#include +*/ +// ***** Thread management + +// The actual first function called on thread start +void* Thread_PthreadStartFn(void* phandle) +{ + Thread* pthread = (Thread*)phandle; + int result = pthread->PRun(); + // Signal the thread as done and release it atomically. + pthread->FinishAndRelease(); + // At this point Thread object might be dead; however we can still pass + // it to RemoveRunningThread since it is only used as a key there. + ThreadList::RemoveRunningThread(pthread); + return (void*) result; +} + +int Thread::InitAttr = 0; +pthread_attr_t Thread::Attr; + +/* static */ +int Thread::GetOSPriority(ThreadPriority p) +//static inline int MapToSystemPrority(Thread::ThreadPriority p) +{ +#ifdef OVR_OS_PS3 + switch(p) + { + case Thread::CriticalPriority: return 0; + case Thread::HighestPriority: return 300; + case Thread::AboveNormalPriority: return 600; + case Thread::NormalPriority: return 1000; + case Thread::BelowNormalPriority: return 1500; + case Thread::LowestPriority: return 2500; + case Thread::IdlePriority: return 3071; + } return 1000; +#else + OVR_UNUSED(p); + return -1; +#endif +} + +bool Thread::Start(ThreadState initialState) +{ + if (initialState == NotRunning) + return 0; + if (GetThreadState() != NotRunning) + { + OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this)); + return 0; + } + + if (!InitAttr) + { + pthread_attr_init(&Attr); + pthread_attr_setdetachstate(&Attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&Attr, 128 * 1024); + sched_param sparam; + sparam.sched_priority = Thread::GetOSPriority(NormalPriority); + pthread_attr_setschedparam(&Attr, &sparam); + InitAttr = 1; + } + + ExitCode = 0; + SuspendCount = 0; + ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED; + + // AddRef to us until the thread is finished + AddRef(); + ThreadList::AddRunningThread(this); + + int result; + if (StackSize != 128 * 1024 || Priority != NormalPriority) + { + pthread_attr_t attr; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, StackSize); + sched_param sparam; + sparam.sched_priority = Thread::GetOSPriority(Priority); + pthread_attr_setschedparam(&attr, &sparam); + result = pthread_create(&ThreadHandle, &attr, Thread_PthreadStartFn, this); + pthread_attr_destroy(&attr); + } + else + result = pthread_create(&ThreadHandle, &Attr, Thread_PthreadStartFn, this); + + if (result) + { + ThreadFlags = 0; + Release(); + ThreadList::RemoveRunningThread(this); + return 0; + } + return 1; +} + + +// Suspend the thread until resumed +bool Thread::Suspend() +{ + OVR_DEBUG_LOG(("Thread::Suspend - cannot suspend threads on this system")); + return 0; +} + +// Resumes currently suspended thread +bool Thread::Resume() +{ + return 0; +} + + +// Quits with an exit code +void Thread::Exit(int exitCode) +{ + // Can only exist the current thread + // if (GetThread() != this) + // return; + + // Call the virtual OnExit function + OnExit(); + + // Signal this thread object as done and release it's references. + FinishAndRelease(); + ThreadList::RemoveRunningThread(this); + + pthread_exit((void *) exitCode); +} + +ThreadId GetCurrentThreadId() +{ + return (void*)pthread_self(); +} + +// *** Sleep functions + +/* static */ +bool Thread::Sleep(unsigned secs) +{ + sleep(secs); + return 1; +} +/* static */ +bool Thread::MSleep(unsigned msecs) +{ + usleep(msecs*1000); + return 1; +} + +/* static */ +int Thread::GetCPUCount() +{ + return 1; +} + + +#ifdef OVR_OS_PS3 + +sys_lwmutex_attribute_t Lock::LockAttr = { SYS_SYNC_PRIORITY, SYS_SYNC_RECURSIVE }; + +#endif + +} + +#endif // OVR_ENABLE_THREADS diff --git a/LibOVR/Src/Kernel/OVR_ThreadsWinAPI.cpp b/LibOVR/Src/Kernel/OVR_ThreadsWinAPI.cpp new file mode 100644 index 0000000..fa3962e --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_ThreadsWinAPI.cpp @@ -0,0 +1,994 @@ +/************************************************************************************ + +Filename : OVR_ThreadsWinAPI.cpp +Platform : WinAPI +Content : Windows specific thread-related (safe) functionality +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_Threads.h" +#include "OVR_Hash.h" +#include "OVR_Log.h" + +#ifdef OVR_ENABLE_THREADS + +// For _beginthreadex / _endtheadex +#include + +namespace OVR { + + +//----------------------------------------------------------------------------------- +// *** Internal Mutex implementation class + +class MutexImpl : public NewOverrideBase +{ + // System mutex or semaphore + HANDLE hMutexOrSemaphore; + bool Recursive; + volatile unsigned LockCount; + + friend class WaitConditionImpl; + +public: + // Constructor/destructor + MutexImpl(bool recursive = 1); + ~MutexImpl(); + + // Locking functions + void DoLock(); + bool TryLock(); + void Unlock(Mutex* pmutex); + // Returns 1 if the mutes is currently locked + bool IsLockedByAnotherThread(Mutex* pmutex); +}; + +// *** Constructor/destructor +MutexImpl::MutexImpl(bool recursive) +{ + Recursive = recursive; + LockCount = 0; + hMutexOrSemaphore = Recursive ? CreateMutex(NULL, 0, NULL) : CreateSemaphore(NULL, 1, 1, NULL); +} +MutexImpl::~MutexImpl() +{ + CloseHandle(hMutexOrSemaphore); +} + + +// Lock and try lock +void MutexImpl::DoLock() +{ + if (::WaitForSingleObject(hMutexOrSemaphore, INFINITE) != WAIT_OBJECT_0) + return; + LockCount++; +} + +bool MutexImpl::TryLock() +{ + DWORD ret; + if ((ret=::WaitForSingleObject(hMutexOrSemaphore, 0)) != WAIT_OBJECT_0) + return 0; + LockCount++; + return 1; +} + +void MutexImpl::Unlock(Mutex* pmutex) +{ + OVR_UNUSED(pmutex); + + unsigned lockCount; + LockCount--; + lockCount = LockCount; + + // Release mutex + if ((Recursive ? ReleaseMutex(hMutexOrSemaphore) : + ReleaseSemaphore(hMutexOrSemaphore, 1, NULL)) != 0) + { + // This used to call Wait handlers if lockCount == 0. + } +} + +bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex) +{ + // There could be multiple interpretations of IsLocked with respect to current thread + if (LockCount == 0) + return 0; + if (!TryLock()) + return 1; + Unlock(pmutex); + return 0; +} + +/* +bool MutexImpl::IsSignaled() const +{ + // An mutex is signaled if it is not locked ANYWHERE + // Note that this is different from IsLockedByAnotherThread function, + // that takes current thread into account + return LockCount == 0; +} +*/ + + +// *** Actual Mutex class implementation + +Mutex::Mutex(bool recursive) +{ + pImpl = new MutexImpl(recursive); +} +Mutex::~Mutex() +{ + delete pImpl; +} + +// Lock and try lock +void Mutex::DoLock() +{ + pImpl->DoLock(); +} +bool Mutex::TryLock() +{ + return pImpl->TryLock(); +} +void Mutex::Unlock() +{ + pImpl->Unlock(this); +} +bool Mutex::IsLockedByAnotherThread() +{ + return pImpl->IsLockedByAnotherThread(this); +} + +//----------------------------------------------------------------------------------- +// ***** Event + +bool Event::Wait(unsigned delay) +{ + Mutex::Locker lock(&StateMutex); + + // Do the correct amount of waiting + if (delay == OVR_WAIT_INFINITE) + { + while(!State) + StateWaitCondition.Wait(&StateMutex); + } + else if (delay) + { + if (!State) + StateWaitCondition.Wait(&StateMutex, delay); + } + + bool state = State; + // Take care of temporary 'pulsing' of a state + if (Temporary) + { + Temporary = false; + State = false; + } + return state; +} + +void Event::updateState(bool newState, bool newTemp, bool mustNotify) +{ + Mutex::Locker lock(&StateMutex); + State = newState; + Temporary = newTemp; + if (mustNotify) + StateWaitCondition.NotifyAll(); +} + + +//----------------------------------------------------------------------------------- +// ***** Win32 Wait Condition Implementation + +// Internal implementation class +class WaitConditionImpl : public NewOverrideBase +{ + // Event pool entries for extra events + struct EventPoolEntry : public NewOverrideBase + { + HANDLE hEvent; + EventPoolEntry *pNext; + EventPoolEntry *pPrev; + }; + + Lock WaitQueueLoc; + // Stores free events that can be used later + EventPoolEntry * pFreeEventList; + + // A queue of waiting objects to be signaled + EventPoolEntry* pQueueHead; + EventPoolEntry* pQueueTail; + + // Allocation functions for free events + EventPoolEntry* GetNewEvent(); + void ReleaseEvent(EventPoolEntry* pevent); + + // Queue operations + void QueuePush(EventPoolEntry* pentry); + EventPoolEntry* QueuePop(); + void QueueFindAndRemove(EventPoolEntry* pentry); + +public: + + // Constructor/destructor + WaitConditionImpl(); + ~WaitConditionImpl(); + + // Release mutex and wait for condition. The mutex is re-acqured after the wait. + bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE); + + // Notify a condition, releasing at one object waiting + void Notify(); + // Notify a condition, releasing all objects waiting + void NotifyAll(); +}; + + + +WaitConditionImpl::WaitConditionImpl() +{ + pFreeEventList = 0; + pQueueHead = + pQueueTail = 0; +} + +WaitConditionImpl::~WaitConditionImpl() +{ + // Free all the resources + EventPoolEntry* p = pFreeEventList; + EventPoolEntry* pentry; + + while(p) + { + // Move to next + pentry = p; + p = p->pNext; + // Delete old + ::CloseHandle(pentry->hEvent); + delete pentry; + } + // Shouldn't we also consider the queue? + + // To be safe + pFreeEventList = 0; + pQueueHead = + pQueueTail = 0; +} + + +// Allocation functions for free events +WaitConditionImpl::EventPoolEntry* WaitConditionImpl::GetNewEvent() +{ + EventPoolEntry* pentry; + + // If there are any free nodes, use them + if (pFreeEventList) + { + pentry = pFreeEventList; + pFreeEventList = pFreeEventList->pNext; + } + else + { + // Allocate a new node + pentry = new EventPoolEntry; + pentry->pNext = 0; + pentry->pPrev = 0; + // Non-signaled manual event + pentry->hEvent = ::CreateEvent(NULL, TRUE, 0, NULL); + } + + return pentry; +} + +void WaitConditionImpl::ReleaseEvent(EventPoolEntry* pevent) +{ + // Mark event as non-signaled + ::ResetEvent(pevent->hEvent); + // And add it to free pool + pevent->pNext = pFreeEventList; + pevent->pPrev = 0; + pFreeEventList = pevent; +} + +// Queue operations +void WaitConditionImpl::QueuePush(EventPoolEntry* pentry) +{ + // Items already exist? Just add to tail + if (pQueueTail) + { + pentry->pPrev = pQueueTail; + pQueueTail->pNext = pentry; + pentry->pNext = 0; + pQueueTail = pentry; + } + else + { + // No items in queue + pentry->pNext = + pentry->pPrev = 0; + pQueueHead = + pQueueTail = pentry; + } +} + +WaitConditionImpl::EventPoolEntry* WaitConditionImpl::QueuePop() +{ + EventPoolEntry* pentry = pQueueHead; + + // No items, null pointer + if (pentry) + { + // More items after this one? just grab the first item + if (pQueueHead->pNext) + { + pQueueHead = pentry->pNext; + pQueueHead->pPrev = 0; + } + else + { + // Last item left + pQueueTail = + pQueueHead = 0; + } + } + return pentry; +} + +void WaitConditionImpl::QueueFindAndRemove(EventPoolEntry* pentry) +{ + // Do an exhaustive search looking for an entry + EventPoolEntry* p = pQueueHead; + + while(p) + { + // Entry found? Remove. + if (p == pentry) + { + + // Remove the node form the list + // Prev link + if (pentry->pPrev) + pentry->pPrev->pNext = pentry->pNext; + else + pQueueHead = pentry->pNext; + // Next link + if (pentry->pNext) + pentry->pNext->pPrev = pentry->pPrev; + else + pQueueTail = pentry->pPrev; + // Done + return; + } + + // Move to next item + p = p->pNext; + } +} + + +bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay) +{ + bool result = 0; + unsigned i; + unsigned lockCount = pmutex->pImpl->LockCount; + EventPoolEntry* pentry; + + // Mutex must have been locked + if (lockCount == 0) + return 0; + + // Add an object to the wait queue + WaitQueueLoc.DoLock(); + QueuePush(pentry = GetNewEvent()); + WaitQueueLoc.Unlock(); + + // Finally, release a mutex or semaphore + if (pmutex->pImpl->Recursive) + { + // Release the recursive mutex N times + pmutex->pImpl->LockCount = 0; + for(i=0; ipImpl->hMutexOrSemaphore); + } + else + { + pmutex->pImpl->LockCount = 0; + ::ReleaseSemaphore(pmutex->pImpl->hMutexOrSemaphore, 1, NULL); + } + + // Note that there is a gap here between mutex.Unlock() and Wait(). However, + // if notify() comes in at this point in the other thread it will set our + // corresponding event so wait will just fall through, as expected. + + // Block and wait on the event + DWORD waitResult = ::WaitForSingleObject(pentry->hEvent, + (delay == OVR_WAIT_INFINITE) ? INFINITE : delay); + /* +repeat_wait: + DWORD waitResult = + + ::MsgWaitForMultipleObjects(1, &pentry->hEvent, FALSE, + (delay == OVR_WAIT_INFINITE) ? INFINITE : delay, + QS_ALLINPUT); + */ + + WaitQueueLoc.DoLock(); + switch(waitResult) + { + case WAIT_ABANDONED: + case WAIT_OBJECT_0: + result = 1; + // Wait was successful, therefore the event entry should already be removed + // So just add entry back to a free list + ReleaseEvent(pentry); + break; + /* + case WAIT_OBJECT_0 + 1: + // Messages in WINDOWS queue + { + MSG msg; + PeekMessage(&msg, NULL, 0U, 0U, PM_NOREMOVE); + WaitQueueLoc.Unlock(); + goto repeat_wait; + } + break; */ + default: + // Timeout, our entry should still be in a queue + QueueFindAndRemove(pentry); + ReleaseEvent(pentry); + } + WaitQueueLoc.Unlock(); + + // Re-aquire the mutex + for(i=0; iDoLock(); + + // Return the result + return result; +} + +// Notify a condition, releasing the least object in a queue +void WaitConditionImpl::Notify() +{ + Lock::Locker lock(&WaitQueueLoc); + + // Pop last entry & signal it + EventPoolEntry* pentry = QueuePop(); + if (pentry) + ::SetEvent(pentry->hEvent); +} + +// Notify a condition, releasing all objects waiting +void WaitConditionImpl::NotifyAll() +{ + Lock::Locker lock(&WaitQueueLoc); + + // Pop and signal all events + // NOTE : There is no need to release the events, it's the waiters job to do so + EventPoolEntry* pentry = QueuePop(); + while (pentry) + { + ::SetEvent(pentry->hEvent); + pentry = QueuePop(); + } +} + + + +// *** Actual implementation of WaitCondition + +WaitCondition::WaitCondition() +{ + pImpl = new WaitConditionImpl; +} +WaitCondition::~WaitCondition() +{ + delete pImpl; +} + +// Wait without a mutex +bool WaitCondition::Wait(Mutex *pmutex, unsigned delay) +{ + return pImpl->Wait(pmutex, delay); +} +// Notification +void WaitCondition::Notify() +{ + pImpl->Notify(); +} +void WaitCondition::NotifyAll() +{ + pImpl->NotifyAll(); +} + + + +//----------------------------------------------------------------------------------- +// ***** Thread Class + +// Per-thread variable +// MA: Don't use TLS for now - portability issues with DLLs, etc. +/* +#if !defined(OVR_CC_MSVC) || (OVR_CC_MSVC < 1300) +__declspec(thread) Thread* pCurrentThread = 0; +#else +#pragma data_seg(".tls$") +__declspec(thread) Thread* pCurrentThread = 0; +#pragma data_seg(".rwdata") +#endif +*/ + +// *** Thread constructors. + +Thread::Thread(UPInt stackSize, int processor) +{ + CreateParams params; + params.stackSize = stackSize; + params.processor = processor; + Init(params); +} + +Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize, + int processor, Thread::ThreadState initialState) +{ + CreateParams params(threadFunction, userHandle, stackSize, processor, initialState); + Init(params); +} + +Thread::Thread(const CreateParams& params) +{ + Init(params); +} +void Thread::Init(const CreateParams& params) +{ + // Clear the variables + ThreadFlags = 0; + ThreadHandle = 0; + IdValue = 0; + ExitCode = 0; + SuspendCount = 0; + StackSize = params.stackSize; + Processor = params.processor; + Priority = params.priority; + + // Clear Function pointers + ThreadFunction = params.threadFunction; + UserHandle = params.userHandle; + if (params.initialState != NotRunning) + Start(params.initialState); + +} + +Thread::~Thread() +{ + // Thread should not running while object is being destroyed, + // this would indicate ref-counting issue. + //OVR_ASSERT(IsRunning() == 0); + + // Clean up thread. + CleanupSystemThread(); + ThreadHandle = 0; +} + + +// *** Overridable User functions. + +// Default Run implementation +int Thread::Run() +{ + // Call pointer to function, if available. + return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0; +} +void Thread::OnExit() +{ +} + +// Finishes the thread and releases internal reference to it. +void Thread::FinishAndRelease() +{ + // Note: thread must be US. + ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED); + ThreadFlags |= OVR_THREAD_FINISHED; + + // Release our reference; this is equivalent to 'delete this' + // from the point of view of our thread. + Release(); +} + + +// *** ThreadList - used to tack all created threads + +class ThreadList : public NewOverrideBase +{ + //------------------------------------------------------------------------ + struct ThreadHashOp + { + UPInt operator()(const Thread* ptr) + { + return (((UPInt)ptr) >> 6) ^ (UPInt)ptr; + } + }; + + HashSet ThreadSet; + Mutex ThreadMutex; + WaitCondition ThreadsEmpty; + // Track the root thread that created us. + ThreadId RootThreadId; + + static ThreadList* volatile pRunningThreads; + + void addThread(Thread *pthread) + { + Mutex::Locker lock(&ThreadMutex); + ThreadSet.Add(pthread); + } + + void removeThread(Thread *pthread) + { + Mutex::Locker lock(&ThreadMutex); + ThreadSet.Remove(pthread); + if (ThreadSet.GetSize() == 0) + ThreadsEmpty.Notify(); + } + + void finishAllThreads() + { + // Only original root thread can call this. + OVR_ASSERT(GetCurrentThreadId() == RootThreadId); + + Mutex::Locker lock(&ThreadMutex); + while (ThreadSet.GetSize() != 0) + ThreadsEmpty.Wait(&ThreadMutex); + } + +public: + + ThreadList() + { + RootThreadId = GetCurrentThreadId(); + } + ~ThreadList() { } + + + static void AddRunningThread(Thread *pthread) + { + // Non-atomic creation ok since only the root thread + if (!pRunningThreads) + { + pRunningThreads = new ThreadList; + OVR_ASSERT(pRunningThreads); + } + pRunningThreads->addThread(pthread); + } + + // NOTE: 'pthread' might be a dead pointer when this is + // called so it should not be accessed; it is only used + // for removal. + static void RemoveRunningThread(Thread *pthread) + { + OVR_ASSERT(pRunningThreads); + pRunningThreads->removeThread(pthread); + } + + static void FinishAllThreads() + { + // This is ok because only root thread can wait for other thread finish. + if (pRunningThreads) + { + pRunningThreads->finishAllThreads(); + delete pRunningThreads; + pRunningThreads = 0; + } + } +}; + +// By default, we have no thread list. +ThreadList* volatile ThreadList::pRunningThreads = 0; + + +// FinishAllThreads - exposed publicly in Thread. +void Thread::FinishAllThreads() +{ + ThreadList::FinishAllThreads(); +} + + +// *** Run override + +int Thread::PRun() +{ + // Suspend us on start, if requested + if (ThreadFlags & OVR_THREAD_START_SUSPENDED) + { + Suspend(); + ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED; + } + + // Call the virtual run function + ExitCode = Run(); + return ExitCode; +} + + + +/* MA: Don't use TLS for now. + +// Static function to return a pointer to the current thread +void Thread::InitCurrentThread(Thread *pthread) +{ + pCurrentThread = pthread; +} + +// Static function to return a pointer to the current thread +Thread* Thread::GetThread() +{ + return pCurrentThread; +} +*/ + + +// *** User overridables + +bool Thread::GetExitFlag() const +{ + return (ThreadFlags & OVR_THREAD_EXIT) != 0; +} + +void Thread::SetExitFlag(bool exitFlag) +{ + // The below is atomic since ThreadFlags is AtomicInt. + if (exitFlag) + ThreadFlags |= OVR_THREAD_EXIT; + else + ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT; +} + + +// Determines whether the thread was running and is now finished +bool Thread::IsFinished() const +{ + return (ThreadFlags & OVR_THREAD_FINISHED) != 0; +} +// Determines whether the thread is suspended +bool Thread::IsSuspended() const +{ + return SuspendCount > 0; +} +// Returns current thread state +Thread::ThreadState Thread::GetThreadState() const +{ + if (IsSuspended()) + return Suspended; + if (ThreadFlags & OVR_THREAD_STARTED) + return Running; + return NotRunning; +} + + + +// ***** Thread management +/* static */ +int Thread::GetOSPriority(ThreadPriority p) +{ + switch(p) + { + case Thread::CriticalPriority: return THREAD_PRIORITY_TIME_CRITICAL; + case Thread::HighestPriority: return THREAD_PRIORITY_HIGHEST; + case Thread::AboveNormalPriority: return THREAD_PRIORITY_ABOVE_NORMAL; + case Thread::NormalPriority: return THREAD_PRIORITY_NORMAL; + case Thread::BelowNormalPriority: return THREAD_PRIORITY_BELOW_NORMAL; + case Thread::LowestPriority: return THREAD_PRIORITY_LOWEST; + case Thread::IdlePriority: return THREAD_PRIORITY_IDLE; + } + return THREAD_PRIORITY_NORMAL; +} + +// The actual first function called on thread start +unsigned WINAPI Thread_Win32StartFn(void * phandle) +{ + Thread * pthread = (Thread*)phandle; + if (pthread->Processor != -1) + { + DWORD_PTR ret = SetThreadAffinityMask(GetCurrentThread(), (DWORD)pthread->Processor); + if (ret == 0) + OVR_DEBUG_LOG(("Could not set hardware processor for the thread")); + } + BOOL ret = ::SetThreadPriority(GetCurrentThread(), Thread::GetOSPriority(pthread->Priority)); + if (ret == 0) + OVR_DEBUG_LOG(("Could not set thread priority")); + OVR_UNUSED(ret); + + // Ensure that ThreadId is assigned once thread is running, in case + // beginthread hasn't filled it in yet. + pthread->IdValue = (ThreadId)::GetCurrentThreadId(); + + DWORD result = pthread->PRun(); + // Signal the thread as done and release it atomically. + pthread->FinishAndRelease(); + // At this point Thread object might be dead; however we can still pass + // it to RemoveRunningThread since it is only used as a key there. + ThreadList::RemoveRunningThread(pthread); + return (unsigned) result; +} + +bool Thread::Start(ThreadState initialState) +{ + if (initialState == NotRunning) + return 0; + if (GetThreadState() != NotRunning) + { + OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this)); + return 0; + } + + // Free old thread handle before creating the new one + CleanupSystemThread(); + + // AddRef to us until the thread is finished. + AddRef(); + ThreadList::AddRunningThread(this); + + ExitCode = 0; + SuspendCount = 0; + ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED; + ThreadHandle = (HANDLE) _beginthreadex(0, (unsigned)StackSize, + Thread_Win32StartFn, this, 0, (unsigned*)&IdValue); + + // Failed? Fail the function + if (ThreadHandle == 0) + { + ThreadFlags = 0; + Release(); + ThreadList::RemoveRunningThread(this); + return 0; + } + return 1; +} + + +// Suspend the thread until resumed +bool Thread::Suspend() +{ + // Can't suspend a thread that wasn't started + if (!(ThreadFlags & OVR_THREAD_STARTED)) + return 0; + + if (::SuspendThread(ThreadHandle) != 0xFFFFFFFF) + { + SuspendCount++; + return 1; + } + return 0; +} + +// Resumes currently suspended thread +bool Thread::Resume() +{ + // Can't suspend a thread that wasn't started + if (!(ThreadFlags & OVR_THREAD_STARTED)) + return 0; + + // Decrement count, and resume thread if it is 0 + SInt32 oldCount = SuspendCount.ExchangeAdd_Acquire(-1); + if (oldCount >= 1) + { + if (oldCount == 1) + { + if (::ResumeThread(ThreadHandle) != 0xFFFFFFFF) + return 1; + } + else + { + return 1; + } + } + return 0; +} + + +// Quits with an exit code +void Thread::Exit(int exitCode) +{ + // Can only exist the current thread. + // MA: Don't use TLS for now. + //if (GetThread() != this) + // return; + + // Call the virtual OnExit function. + OnExit(); + + // Signal this thread object as done and release it's references. + FinishAndRelease(); + ThreadList::RemoveRunningThread(this); + + // Call the exit function. + _endthreadex((unsigned)exitCode); +} + + +void Thread::CleanupSystemThread() +{ + if (ThreadHandle != 0) + { + ::CloseHandle(ThreadHandle); + ThreadHandle = 0; + } +} + +// *** Sleep functions +// static +bool Thread::Sleep(unsigned secs) +{ + ::Sleep(secs*1000); + return 1; +} + +// static +bool Thread::MSleep(unsigned msecs) +{ + ::Sleep(msecs); + return 1; +} + +void Thread::SetThreadName( const char* name ) +{ +#if !defined(OVR_BUILD_SHIPPING) || defined(OVR_BUILD_PROFILING) + // Looks ugly, but it is the recommended way to name a thread. + typedef struct tagTHREADNAME_INFO { + DWORD dwType; // Must be 0x1000 + LPCSTR szName; // Pointer to name (in user address space) + DWORD dwThreadID; // Thread ID (-1 for caller thread) + DWORD dwFlags; // Reserved for future use; must be zero + } THREADNAME_INFO; + + THREADNAME_INFO info; + + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = reinterpret_cast(GetThreadId()); + info.dwFlags = 0; + + __try + { +#ifdef _WIN64 + RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (const ULONG_PTR *)&info ); +#else + RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD *)&info ); +#endif + } + __except( GetExceptionCode()==0x406D1388 ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER ) + { + } +#endif // OVR_BUILD_SHIPPING +} + +// static +int Thread::GetCPUCount() +{ + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + return (int) sysInfo.dwNumberOfProcessors; +} + +// Returns the unique Id of a thread it is called on, intended for +// comparison purposes. +ThreadId GetCurrentThreadId() +{ + return (ThreadId)::GetCurrentThreadId(); +} + +} // OVR + +#endif + + diff --git a/LibOVR/Src/Kernel/OVR_Timer.cpp b/LibOVR/Src/Kernel/OVR_Timer.cpp new file mode 100644 index 0000000..db25b8f --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Timer.cpp @@ -0,0 +1,156 @@ +/************************************************************************************ + +Filename : OVR_Timer.cpp +Content : Provides static functions for precise timing +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_Timer.h" + +#if defined (OVR_OS_WIN32) +#include + +#else +#include +#endif + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** Timer Class + +UInt64 Timer::GetProfileTicks() +{ + return (GetRawTicks() * MksPerSecond) / GetRawFrequency(); +} +double Timer::GetProfileSeconds() +{ + static UInt64 StartTime = GetProfileTicks(); + return TicksToSeconds(GetProfileTicks()-StartTime); +} + + +//------------------------------------------------------------------------ +// *** Win32 Specific Timer + +#if (defined (OVR_OS_WIN32)) + +CRITICAL_SECTION WinAPI_GetTimeCS; +volatile UInt32 WinAPI_OldTime = 0; +volatile UInt32 WinAPI_WrapCounter = 0; + + +UInt32 Timer::GetTicksMs() +{ + return timeGetTime(); +} + +UInt64 Timer::GetTicks() +{ + DWORD ticks = timeGetTime(); + UInt64 result; + + // On Win32 QueryPerformanceFrequency is unreliable due to SMP and + // performance levels, so use this logic to detect wrapping and track + // high bits. + ::EnterCriticalSection(&WinAPI_GetTimeCS); + + if (WinAPI_OldTime > ticks) + WinAPI_WrapCounter++; + WinAPI_OldTime = ticks; + + result = (UInt64(WinAPI_WrapCounter) << 32) | ticks; + ::LeaveCriticalSection(&WinAPI_GetTimeCS); + + return result * MksPerMs; +} + +UInt64 Timer::GetRawTicks() +{ + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + return li.QuadPart; +} + +UInt64 Timer::GetRawFrequency() +{ + static UInt64 perfFreq = 0; + if (perfFreq == 0) + { + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + perfFreq = freq.QuadPart; + } + return perfFreq; +} + +void Timer::initializeTimerSystem() +{ + timeBeginPeriod(1); + InitializeCriticalSection(&WinAPI_GetTimeCS); + +} +void Timer::shutdownTimerSystem() +{ + DeleteCriticalSection(&WinAPI_GetTimeCS); + timeEndPeriod(1); +} + +#else // !OVR_OS_WIN32 + + +//------------------------------------------------------------------------ +// *** Standard OS Timer + +UInt32 Timer::GetTicksMs() +{ + return (UInt32)(GetProfileTicks() / 1000); +} +// The profile ticks implementation is just fine for a normal timer. +UInt64 Timer::GetTicks() +{ + return GetProfileTicks(); +} + +void Timer::initializeTimerSystem() +{ +} +void Timer::shutdownTimerSystem() +{ +} + +UInt64 Timer::GetRawTicks() +{ + // TODO: prefer rdtsc when available? + + // Return microseconds. + struct timeval tv; + UInt64 result; + + gettimeofday(&tv, 0); + + result = (UInt64)tv.tv_sec * 1000000; + result += tv.tv_usec; + + return result; +} + +UInt64 Timer::GetRawFrequency() +{ + return MksPerSecond; +} + +#endif // !OVR_OS_WIN32 + + + +} // OVR + diff --git a/LibOVR/Src/Kernel/OVR_Timer.h b/LibOVR/Src/Kernel/OVR_Timer.h new file mode 100644 index 0000000..0b71912 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Timer.h @@ -0,0 +1,100 @@ +/************************************************************************************ + +PublicHeader: OVR +Filename : OVR_Timer.h +Content : Provides static functions for precise timing +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_Timer_h +#define OVR_Timer_h + +#include "OVR_Types.h" + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** Timer + +// Timer class defines a family of static functions used for application +// timing and profiling. + +class Timer +{ +public: + enum { + MsPerSecond = 1000, // Milliseconds in one second. + MksPerMs = 1000, // Microseconds in one millisecond. + MksPerSecond = MsPerSecond * MksPerMs + }; + + + // ***** Timing APIs for Application + // These APIs should be used to guide animation and other program functions + // that require precision. + + // Returns ticks in milliseconds, as a 32-bit number. May wrap around every + // 49.2 days. Use either time difference of two values of GetTicks to avoid + // wrap-around. GetTicksMs may perform better then GetTicks. + static UInt32 OVR_STDCALL GetTicksMs(); + + // GetTicks returns general-purpose high resolution application timer value, + // measured in microseconds (mks, or 1/1000000 of a second). The actual precision + // is system-specific and may be much lower, such as 1 ms. + static UInt64 OVR_STDCALL GetTicks(); + + + // ***** Profiling APIs. + // These functions should be used for profiling, but may have system specific + // artifacts that make them less appropriate for general system use. + // On Win32, for example these rely on QueryPerformanceConter may have + // problems with thread-core switching and power modes. + + // Return a hi-res timer value in mks (1/1000000 of a sec). + // Generally you want to call this at the start and end of an + // operation, and pass the difference to + // TicksToSeconds() to find out how long the operation took. + static UInt64 OVR_STDCALL GetProfileTicks(); + + // More convenient zero-based profile timer in seconds. First call initializes + // the "zero" value; future calls return the difference. Not thread safe for first call. + // Due to low precision of Double, may malfunction after long runtime. + static double OVR_STDCALL GetProfileSeconds(); + + // Get the raw cycle counter value, providing the maximum possible timer resolution. + static UInt64 OVR_STDCALL GetRawTicks(); + static UInt64 OVR_STDCALL GetRawFrequency(); + + + // ***** Tick and time unit conversion. + + // Convert micro-second ticks value into seconds value. + static inline double TicksToSeconds(UInt64 ticks) + { + return static_cast(ticks) * (1.0 / (double)MksPerSecond); + } + // Convert Raw or frequency-unit ticks to seconds based on specified frequency. + static inline double RawTicksToSeconds(UInt64 rawTicks, UInt64 rawFrequency) + { + return static_cast(rawTicks) * rawFrequency; + } + +private: + friend class System; + // System called during program startup/shutdown. + static void initializeTimerSystem(); + static void shutdownTimerSystem(); +}; + + +} // Scaleform::Timer + +#endif diff --git a/LibOVR/Src/Kernel/OVR_Types.h b/LibOVR/Src/Kernel/OVR_Types.h new file mode 100644 index 0000000..40eb156 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Types.h @@ -0,0 +1,456 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Types.h +Content : Standard library defines and simple types +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_Types_H +#define OVR_Types_H + +//----------------------------------------------------------------------------------- +// ****** Operating System +// +// Type definitions exist for the following operating systems: (OVR_OS_x) +// +// WIN32 - Win32 (Windows 95/98/ME and Windows NT/2000/XP) +// DARWIN - Darwin OS (Mac OS X) +// LINUX - Linux +// ANDROID - Android +// IPHONE - iPhone + +#if (defined(__APPLE__) && (defined(__GNUC__) ||\ + defined(__xlC__) || defined(__xlc__))) || defined(__MACOS__) +# if (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) || defined(__IPHONE_OS_VERSION_MIN_REQUIRED)) +# define OVR_OS_IPHONE +# else +# define OVR_OS_DARWIN +# define OVR_OS_MAC +# endif +#elif (defined(WIN64) || defined(_WIN64) || defined(__WIN64__)) +# define OVR_OS_WIN32 +#elif (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)) +# define OVR_OS_WIN32 +#elif defined(__linux__) || defined(__linux) +# define OVR_OS_LINUX +#else +# define OVR_OS_OTHER +#endif + +#if defined(ANDROID) +# define OVR_OS_ANDROID +#endif + + +//----------------------------------------------------------------------------------- +// ***** CPU Architecture +// +// The following CPUs are defined: (OVR_CPU_x) +// +// X86 - x86 (IA-32) +// X86_64 - x86_64 (amd64) +// PPC - PowerPC +// PPC64 - PowerPC64 +// MIPS - MIPS +// OTHER - CPU for which no special support is present or needed + + +#if defined(__x86_64__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) +# define OVR_CPU_X86_64 +# define OVR_64BIT_POINTERS +#elif defined(__i386__) || defined(OVR_OS_WIN32) +# define OVR_CPU_X86 +#elif defined(__powerpc64__) +# define OVR_CPU_PPC64 +#elif defined(__ppc__) +# define OVR_CPU_PPC +#elif defined(__mips__) || defined(__MIPSEL__) +# define OVR_CPU_MIPS +#elif defined(__arm__) +# define OVR_CPU_ARM +#else +# define OVR_CPU_OTHER +#endif + +//----------------------------------------------------------------------------------- +// ***** Co-Processor Architecture +// +// The following co-processors are defined: (OVR_CPU_x) +// +// SSE - Available on all modern x86 processors. +// Altivec - Available on all modern ppc processors. +// Neon - Available on some armv7+ processors. + +#if defined(__SSE__) || defined(OVR_OS_WIN32) +# define OVR_CPU_SSE +#endif // __SSE__ + +#if defined( __ALTIVEC__ ) +# define OVR_CPU_ALTIVEC +#endif // __ALTIVEC__ + +#if defined(__ARM_NEON__) +# define OVR_CPU_ARM_NEON +#endif // __ARM_NEON__ + + +//----------------------------------------------------------------------------------- +// ***** Compiler +// +// The following compilers are defined: (OVR_CC_x) +// +// MSVC - Microsoft Visual C/C++ +// INTEL - Intel C++ for Linux / Windows +// GNU - GNU C++ +// ARM - ARM C/C++ + +#if defined(__INTEL_COMPILER) +// Intel 4.0 = 400 +// Intel 5.0 = 500 +// Intel 6.0 = 600 +// Intel 8.0 = 800 +// Intel 9.0 = 900 +# define OVR_CC_INTEL __INTEL_COMPILER + +#elif defined(_MSC_VER) +// MSVC 5.0 = 1100 +// MSVC 6.0 = 1200 +// MSVC 7.0 (VC2002) = 1300 +// MSVC 7.1 (VC2003) = 1310 +// MSVC 8.0 (VC2005) = 1400 +// MSVC 9.0 (VC2008) = 1500 +// MSVC 10.0 (VC2010) = 1600 +# define OVR_CC_MSVC _MSC_VER + +#elif defined(__GNUC__) +# define OVR_CC_GNU + +#elif defined(__CC_ARM) +# define OVR_CC_ARM + +#else +# error "Oculus does not support this Compiler" +#endif + + +//----------------------------------------------------------------------------------- +// ***** Compiler Warnings + +// Disable MSVC warnings +#if defined(OVR_CC_MSVC) +# pragma warning(disable : 4127) // Inconsistent dll linkage +# pragma warning(disable : 4530) // Exception handling +# if (OVR_CC_MSVC<1300) +# pragma warning(disable : 4514) // Unreferenced inline function has been removed +# pragma warning(disable : 4710) // Function not inlined +# pragma warning(disable : 4714) // _force_inline not inlined +# pragma warning(disable : 4786) // Debug variable name longer than 255 chars +# endif // (OVR_CC_MSVC<1300) +#endif // (OVR_CC_MSVC) + + + +// *** Linux Unicode - must come before Standard Includes + +#ifdef OVR_OS_LINUX +// Use glibc unicode functions on linux. +# ifndef _GNU_SOURCE +# define _GNU_SOURCE +# endif +#endif + +//----------------------------------------------------------------------------------- +// ***** Standard Includes +// +#include +#include +#include + + +// MSVC Based Memory Leak checking - for now +#if defined(OVR_CC_MSVC) && defined(OVR_BUILD_DEBUG) +# define _CRTDBG_MAP_ALLOC +# include +# include + +// Uncomment this to help debug memory leaks under Visual Studio in OVR apps only. +// This shouldn't be defined in customer releases. +# ifndef OVR_DEFINE_NEW +# define OVR_DEFINE_NEW new(__FILE__, __LINE__) +# define new OVR_DEFINE_NEW +# endif + +#endif + + +//----------------------------------------------------------------------------------- +// ***** Type definitions for Common Systems + +namespace OVR { + +typedef char Char; + +// Pointer-sized integer +typedef size_t UPInt; +typedef ptrdiff_t SPInt; + + +#if defined(OVR_OS_WIN32) + +typedef char SByte; // 8 bit Integer (Byte) +typedef unsigned char UByte; +typedef short SInt16; // 16 bit Integer (Word) +typedef unsigned short UInt16; +typedef long SInt32; // 32 bit Integer +typedef unsigned long UInt32; +typedef __int64 SInt64; // 64 bit Integer (QWord) +typedef unsigned __int64 UInt64; + + +#elif defined(OVR_OS_MAC) || defined(OVR_OS_IPHONE) || defined(OVR_CC_GNU) + +typedef int SByte __attribute__((__mode__ (__QI__))); +typedef unsigned int UByte __attribute__((__mode__ (__QI__))); +typedef int SInt16 __attribute__((__mode__ (__HI__))); +typedef unsigned int UInt16 __attribute__((__mode__ (__HI__))); +typedef int SInt32 __attribute__((__mode__ (__SI__))); +typedef unsigned int UInt32 __attribute__((__mode__ (__SI__))); +typedef int SInt64 __attribute__((__mode__ (__DI__))); +typedef unsigned int UInt64 __attribute__((__mode__ (__DI__))); + +#else + +#include +typedef int8_t SByte; +typedef uint8_t UByte; +typedef int16_t SInt16; +typedef uint16_t UInt16; +typedef int32_t SInt32; +typedef uint32_t UInt32; +typedef int64_t SInt64; +typedef uint64_t UInt64; + +#endif + + +// ***** BaseTypes Namespace + +// BaseTypes namespace is explicitly declared to allow base types to be used +// by customers directly without other contents of OVR namespace. +// +// Its is expected that GFx samples will declare 'using namespace OVR::BaseTypes' +// to allow using these directly without polluting the target scope with other +// OVR declarations, such as Ptr<>, String or Mutex. +namespace BaseTypes +{ + using OVR::UPInt; + using OVR::SPInt; + using OVR::UByte; + using OVR::SByte; + using OVR::UInt16; + using OVR::SInt16; + using OVR::UInt32; + using OVR::SInt32; + using OVR::UInt64; + using OVR::SInt64; +} // OVR::BaseTypes + +} // OVR + + +//----------------------------------------------------------------------------------- +// ***** Macro Definitions +// +// We define the following: +// +// OVR_BYTE_ORDER - Defined to either OVR_LITTLE_ENDIAN or OVR_BIG_ENDIAN +// OVR_FORCE_INLINE - Forces inline expansion of function +// OVR_ASM - Assembly language prefix +// OVR_STR - Prefixes string with L"" if building unicode +// +// OVR_STDCALL - Use stdcall calling convention (Pascal arg order) +// OVR_CDECL - Use cdecl calling convention (C argument order) +// OVR_FASTCALL - Use fastcall calling convention (registers) +// + +// Byte order constants, OVR_BYTE_ORDER is defined to be one of these. +#define OVR_LITTLE_ENDIAN 1 +#define OVR_BIG_ENDIAN 2 + + +// Force inline substitute - goes before function declaration +#if defined(OVR_CC_MSVC) +# define OVR_FORCE_INLINE __forceinline +#elif defined(OVR_CC_GNU) +# define OVR_FORCE_INLINE __attribute__((always_inline)) +#else +# define OVR_FORCE_INLINE inline +#endif // OVR_CC_MSVC + + +#if defined(OVR_OS_WIN32) + + // ***** Win32 + + // Byte order + #define OVR_BYTE_ORDER OVR_LITTLE_ENDIAN + + // Calling convention - goes after function return type but before function name + #ifdef __cplusplus_cli + # define OVR_FASTCALL __stdcall + #else + # define OVR_FASTCALL __fastcall + #endif + + #define OVR_STDCALL __stdcall + #define OVR_CDECL __cdecl + + + // Assembly macros + #if defined(OVR_CC_MSVC) + # define OVR_ASM _asm + #else + # define OVR_ASM asm + #endif // (OVR_CC_MSVC) + + #ifdef UNICODE + # define OVR_STR(str) L##str + #else + # define OVR_STR(str) str + #endif // UNICODE + +#else + + // **** Standard systems + + #if (defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN))|| \ + (defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN)) + # define OVR_BYTE_ORDER OVR_BIG_ENDIAN + #elif (defined(__ARMEB__) || defined(OVR_CPU_PPC) || defined(OVR_CPU_PPC64)) + # define OVR_BYTE_ORDER OVR_BIG_ENDIAN + #else + # define OVR_BYTE_ORDER OVR_LITTLE_ENDIAN + #endif + + // Assembly macros + #define OVR_ASM __asm__ + #define OVR_ASM_PROC(procname) OVR_ASM + #define OVR_ASM_END OVR_ASM + + // Calling convention - goes after function return type but before function name + #define OVR_FASTCALL + #define OVR_STDCALL + #define OVR_CDECL + +#endif // defined(OVR_OS_WIN32) + + + +//----------------------------------------------------------------------------------- +// ***** OVR_DEBUG_BREAK, OVR_ASSERT +// +// If not in debug build, macros do nothing +#ifndef OVR_BUILD_DEBUG + +# define OVR_DEBUG_BREAK ((void)0) +# define OVR_ASSERT(p) ((void)0) + +#else + +// Microsoft Win32 specific debugging support +#if defined(OVR_OS_WIN32) +# ifdef OVR_CPU_X86 +# if defined(__cplusplus_cli) +# define OVR_DEBUG_BREAK do { __debugbreak(); } while(0) +# elif defined(OVR_CC_GNU) +# define OVR_DEBUG_BREAK do { OVR_ASM("int $3\n\t"); } while(0) +# else +# define OVR_DEBUG_BREAK do { OVR_ASM int 3 } while (0) +# endif +# else +# define OVR_DEBUG_BREAK do { __debugbreak(); } while(0) +# endif +// Unix specific debugging support +#elif defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64) +# define OVR_DEBUG_BREAK do { OVR_ASM("int $3\n\t"); } while(0) +#else +# define OVR_DEBUG_BREAK do { *((int *) 0) = 1; } while(0) +#endif + +// This will cause compiler breakpoint +#define OVR_ASSERT(p) do { if (!(p)) { OVR_DEBUG_BREAK; } } while(0) + +#endif // OVR_BUILD_DEBUG + + +// Compile-time assert; produces compiler error if condition is false +#define OVR_COMPILER_ASSERT(x) { int zero = 0; switch(zero) {case 0: case x:;} } + + + +//----------------------------------------------------------------------------------- +// ***** OVR_UNUSED - Unused Argument handling + +// Macro to quiet compiler warnings about unused parameters/variables. +#if defined(OVR_CC_GNU) +# define OVR_UNUSED(a) do {__typeof__ (&a) __attribute__ ((unused)) __tmp = &a; } while(0) +#else +# define OVR_UNUSED(a) (a) +#endif + +#define OVR_UNUSED1(a1) OVR_UNUSED(a1) +#define OVR_UNUSED2(a1,a2) OVR_UNUSED(a1); OVR_UNUSED(a2) +#define OVR_UNUSED3(a1,a2,a3) OVR_UNUSED2(a1,a2); OVR_UNUSED(a3) +#define OVR_UNUSED4(a1,a2,a3,a4) OVR_UNUSED3(a1,a2,a3); OVR_UNUSED(a4) +#define OVR_UNUSED5(a1,a2,a3,a4,a5) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED(a5) +#define OVR_UNUSED6(a1,a2,a3,a4,a5,a6) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED2(a5,a6) +#define OVR_UNUSED7(a1,a2,a3,a4,a5,a6,a7) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED3(a5,a6,a7) +#define OVR_UNUSED8(a1,a2,a3,a4,a5,a6,a7,a8) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED4(a5,a6,a7,a8) +#define OVR_UNUSED9(a1,a2,a3,a4,a5,a6,a7,a8,a9) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED5(a5,a6,a7,a8,a9) + + +//----------------------------------------------------------------------------------- +// ***** Configuration Macros + +// SF Build type +#ifdef OVR_BUILD_DEBUG +# define OVR_BUILD_STRING "Debug" +#else +# define OVR_BUILD_STRING "Release" +#endif + + +//// Enables SF Debugging information +//# define OVR_BUILD_DEBUG + +// OVR_DEBUG_STATEMENT injects a statement only in debug builds. +// OVR_DEBUG_SELECT injects first argument in debug builds, second argument otherwise. +#ifdef OVR_BUILD_DEBUG +#define OVR_DEBUG_STATEMENT(s) s +#define OVR_DEBUG_SELECT(d, nd) d +#else +#define OVR_DEBUG_STATEMENT(s) +#define OVR_DEBUG_SELECT(d, nd) nd +#endif + + +#define OVR_ENABLE_THREADS +// +// Prevents OVR from defining new within +// type macros, so developers can override +// new using the #define new new(...) trick +// - used with OVR_DEFINE_NEW macro +//# define OVR_BUILD_DEFINE_NEW +// + + +#endif // OVR_Types_h diff --git a/LibOVR/Src/Kernel/OVR_UTF8Util.cpp b/LibOVR/Src/Kernel/OVR_UTF8Util.cpp new file mode 100644 index 0000000..21485df --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_UTF8Util.cpp @@ -0,0 +1,545 @@ +/************************************************************************** + +Filename : OVR_UTF8Util.cpp +Content : UTF8 Unicode character encoding/decoding support +Created : September 19, 2012 +Notes : +Notes : Much useful info at "UTF-8 and Unicode FAQ" + http://www.cl.cam.ac.uk/~mgk25/unicode.html + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_UTF8Util.h" + +namespace OVR { namespace UTF8Util { + +SPInt OVR_STDCALL GetLength(const char* buf, SPInt buflen) +{ + const char* p = buf; + SPInt length = 0; + + if (buflen != -1) + { + while (p - buf < buflen) + { + // We should be able to have ASStrings with 0 in the middle. + UTF8Util::DecodeNextChar_Advance0(&p); + length++; + } + } + else + { + while (UTF8Util::DecodeNextChar_Advance0(&p)) + length++; + } + + return length; +} + +UInt32 OVR_STDCALL GetCharAt(SPInt index, const char* putf8str, SPInt length) +{ + const char* buf = putf8str; + UInt32 c = 0; + + if (length != -1) + { + while (buf - putf8str < length) + { + c = UTF8Util::DecodeNextChar_Advance0(&buf); + if (index == 0) + return c; + index--; + } + + return c; + } + + do + { + c = UTF8Util::DecodeNextChar_Advance0(&buf); + index--; + + if (c == 0) + { + // We've hit the end of the string; don't go further. + OVR_ASSERT(index == 0); + return c; + } + } while (index >= 0); + + return c; +} + +SPInt OVR_STDCALL GetByteIndex(SPInt index, const char *putf8str, SPInt length) +{ + const char* buf = putf8str; + + if (length != -1) + { + while ((buf - putf8str) < length && index > 0) + { + UTF8Util::DecodeNextChar_Advance0(&buf); + index--; + } + + return buf-putf8str; + } + + while (index > 0) + { + UInt32 c = UTF8Util::DecodeNextChar_Advance0(&buf); + index--; + + if (c == 0) + return buf-putf8str; + }; + + return buf-putf8str; +} + +int OVR_STDCALL GetEncodeCharSize(UInt32 ucs_character) +{ + if (ucs_character <= 0x7F) + return 1; + else if (ucs_character <= 0x7FF) + return 2; + else if (ucs_character <= 0xFFFF) + return 3; + else if (ucs_character <= 0x1FFFFF) + return 4; + else if (ucs_character <= 0x3FFFFFF) + return 5; + else if (ucs_character <= 0x7FFFFFFF) + return 6; + else + return 0; +} + +UInt32 OVR_STDCALL DecodeNextChar_Advance0(const char** putf8Buffer) +{ + UInt32 uc; + char c; + + // Security considerations: + // + // Changed, this is now only the case for DecodeNextChar: + // - If we hit a zero byte, we want to return 0 without stepping + // the buffer pointer past the 0. th + // + // If we hit an "overlong sequence"; i.e. a character encoded + // in a longer multibyte string than is necessary, then we + // need to discard the character. This is so attackers can't + // disguise dangerous characters or character sequences -- + // there is only one valid encoding for each character. + // + // If we decode characters { 0xD800 .. 0xDFFF } or { 0xFFFE, + // 0xFFFF } then we ignore them; they are not valid in UTF-8. + + // This isn't actually an invalid character; it's a valid char that + // looks like an inverted question mark. +#define INVALID_CHAR 0x0FFFD + +#define FIRST_BYTE(mask, shift) \ + uc = (c & (mask)) << (shift); + +#define NEXT_BYTE(shift) \ + c = **putf8Buffer; \ + if (c == 0) return 0; /* end of buffer, do not advance */ \ + if ((c & 0xC0) != 0x80) return INVALID_CHAR; /* standard check */ \ + (*putf8Buffer)++; \ + uc |= (c & 0x3F) << shift; + + c = **putf8Buffer; + (*putf8Buffer)++; + if (c == 0) + return 0; // End of buffer. + + if ((c & 0x80) == 0) return (UInt32) c; // Conventional 7-bit ASCII. + + // Multi-byte sequences. + if ((c & 0xE0) == 0xC0) + { + // Two-byte sequence. + FIRST_BYTE(0x1F, 6); + NEXT_BYTE(0); + if (uc < 0x80) return INVALID_CHAR; // overlong + return uc; + } + else if ((c & 0xF0) == 0xE0) + { + // Three-byte sequence. + FIRST_BYTE(0x0F, 12); + NEXT_BYTE(6); + NEXT_BYTE(0); + if (uc < 0x800) return INVALID_CHAR; // overlong + // Not valid ISO 10646, but Flash requires these to work + // see AS3 test e15_5_3_2_3 for String.fromCharCode().charCodeAt(0) + // if (uc >= 0x0D800 && uc <= 0x0DFFF) return INVALID_CHAR; + // if (uc == 0x0FFFE || uc == 0x0FFFF) return INVALID_CHAR; // not valid ISO 10646 + return uc; + } + else if ((c & 0xF8) == 0xF0) + { + // Four-byte sequence. + FIRST_BYTE(0x07, 18); + NEXT_BYTE(12); + NEXT_BYTE(6); + NEXT_BYTE(0); + if (uc < 0x010000) return INVALID_CHAR; // overlong + return uc; + } + else if ((c & 0xFC) == 0xF8) + { + // Five-byte sequence. + FIRST_BYTE(0x03, 24); + NEXT_BYTE(18); + NEXT_BYTE(12); + NEXT_BYTE(6); + NEXT_BYTE(0); + if (uc < 0x0200000) return INVALID_CHAR; // overlong + return uc; + } + else if ((c & 0xFE) == 0xFC) + { + // Six-byte sequence. + FIRST_BYTE(0x01, 30); + NEXT_BYTE(24); + NEXT_BYTE(18); + NEXT_BYTE(12); + NEXT_BYTE(6); + NEXT_BYTE(0); + if (uc < 0x04000000) return INVALID_CHAR; // overlong + return uc; + } + else + { + // Invalid. + return INVALID_CHAR; + } +} + + +void OVR_STDCALL EncodeChar(char* pbuffer, SPInt* pindex, UInt32 ucs_character) +{ + if (ucs_character <= 0x7F) + { + // Plain single-byte ASCII. + pbuffer[(*pindex)++] = (char) ucs_character; + } + else if (ucs_character <= 0x7FF) + { + // Two bytes. + pbuffer[(*pindex)++] = 0xC0 | (char)(ucs_character >> 6); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); + } + else if (ucs_character <= 0xFFFF) + { + // Three bytes. + pbuffer[(*pindex)++] = 0xE0 | (char)(ucs_character >> 12); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); + } + else if (ucs_character <= 0x1FFFFF) + { + // Four bytes. + pbuffer[(*pindex)++] = 0xF0 | (char)(ucs_character >> 18); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); + } + else if (ucs_character <= 0x3FFFFFF) + { + // Five bytes. + pbuffer[(*pindex)++] = 0xF8 | (char)(ucs_character >> 24); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 18) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); + } + else if (ucs_character <= 0x7FFFFFFF) + { + // Six bytes. + pbuffer[(*pindex)++] = 0xFC | (char)(ucs_character >> 30); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 24) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 18) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); + } + else + { + // Invalid char; don't encode anything. + } +} + +SPInt OVR_STDCALL GetEncodeStringSize(const wchar_t* pchar, SPInt length) +{ + SPInt len = 0; + if (length != -1) + for (int i = 0; i < length; i++) + { + len += GetEncodeCharSize(pchar[i]); + } + else + for (int i = 0;; i++) + { + if (pchar[i] == 0) + return len; + len += GetEncodeCharSize(pchar[i]); + } + return len; +} + +void OVR_STDCALL EncodeString(char *pbuff, const wchar_t* pchar, SPInt length) +{ + SPInt ofs = 0; + if (length != -1) + { + for (int i = 0; i < length; i++) + { + EncodeChar(pbuff, &ofs, pchar[i]); + } + } + else + { + for (int i = 0;; i++) + { + if (pchar[i] == 0) + break; + EncodeChar(pbuff, &ofs, pchar[i]); + } + } + pbuff[ofs] = 0; +} + +UPInt OVR_STDCALL DecodeString(wchar_t *pbuff, const char* putf8str, SPInt bytesLen) +{ + wchar_t *pbegin = pbuff; + if (bytesLen == -1) + { + while (1) + { + UInt32 ch = DecodeNextChar_Advance0(&putf8str); + if (ch == 0) + break; + else if (ch >= 0xFFFF) + ch = 0xFFFD; + *pbuff++ = wchar_t(ch); + } + } + else + { + const char* p = putf8str; + while ((p - putf8str) < bytesLen) + { + UInt32 ch = DecodeNextChar_Advance0(&p); + if (ch >= 0xFFFF) + ch = 0xFFFD; + *pbuff++ = wchar_t(ch); + } + } + + *pbuff = 0; + return pbuff - pbegin; +} + + +#ifdef UTF8_UNIT_TEST + +// Compile this test case with something like: +// +// gcc utf8.cpp -g -I.. -DUTF8_UNIT_TEST -lstdc++ -o utf8_test +// +// or +// +// cl utf8.cpp -Zi -Od -DUTF8_UNIT_TEST -I.. +// +// If possible, try running the test program with the first arg +// pointing at the file: +// +// http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt +// +// and examine the results by eye to make sure they are acceptable to +// you. + + +#include "base/utility.h" +#include + + +bool check_equal(const char* utf8_in, const UInt32* ucs_in) +{ + for (;;) + { + UInt32 next_ucs = *ucs_in++; + UInt32 next_ucs_from_utf8 = utf8::decode_next_unicode_character(&utf8_in); + if (next_ucs != next_ucs_from_utf8) + { + return false; + } + if (next_ucs == 0) + { + OVR_ASSERT(next_ucs_from_utf8 == 0); + break; + } + } + + return true; +} + + +void log_ascii(const char* line) +{ + for (;;) + { + unsigned char c = (unsigned char) *line++; + if (c == 0) + { + // End of line. + return; + } + else if (c != '\n' + && (c < 32 || c > 127)) + { + // Non-printable as plain ASCII. + printf("<0x%02X>", (int) c); + } + else + { + printf("%c", c); + } + } +} + + +void log_ucs(const UInt32* line) +{ + for (;;) + { + UInt32 uc = *line++; + if (uc == 0) + { + // End of line. + return; + } + else if (uc != '\n' + && (uc < 32 || uc > 127)) + { + // Non-printable as plain ASCII. + printf("", uc); + } + else + { + printf("%c", (char) uc); + } + } +} + + +// Simple canned test. +int main(int argc, const char* argv[]) +{ + { + const char* test8 = "Ignacio Castaño"; + const UInt32 test32[] = + { + 0x49, 0x67, 0x6E, 0x61, 0x63, + 0x69, 0x6F, 0x20, 0x43, 0x61, + 0x73, 0x74, 0x61, 0xF1, 0x6F, + 0x00 + }; + + OVR_ASSERT(check_equal(test8, test32)); + } + + // If user passed an arg, try reading the file as UTF-8 encoded text. + if (argc > 1) + { + const char* filename = argv[1]; + FILE* fp = fopen(filename, "rb"); + if (fp == NULL) + { + printf("Can't open file '%s'\n", filename); + return 1; + } + + // Read lines from the file, encode/decode them, and highlight discrepancies. + const int LINE_SIZE = 200; // max line size + char line_buffer_utf8[LINE_SIZE]; + char reencoded_utf8[6 * LINE_SIZE]; + UInt32 line_buffer_ucs[LINE_SIZE]; + + int byte_counter = 0; + for (;;) + { + int c = fgetc(fp); + if (c == EOF) + { + // Done. + break; + } + line_buffer_utf8[byte_counter++] = c; + if (c == '\n' || byte_counter >= LINE_SIZE - 2) + { + // End of line. Process the line. + line_buffer_utf8[byte_counter++] = 0; // terminate. + + // Decode into UCS. + const char* p = line_buffer_utf8; + UInt32* q = line_buffer_ucs; + for (;;) + { + UInt32 uc = UTF8Util::DecodeNextChar(&p); + *q++ = uc; + + OVR_ASSERT(q < line_buffer_ucs + LINE_SIZE); + OVR_ASSERT(p < line_buffer_utf8 + LINE_SIZE); + + if (uc == 0) break; + } + + // Encode back into UTF-8. + q = line_buffer_ucs; + int index = 0; + for (;;) + { + UInt32 uc = *q++; + OVR_ASSERT(index < LINE_SIZE * 6 - 6); + int last_index = index; + UTF8Util::EncodeChar(reencoded_utf8, &index, uc); + OVR_ASSERT(index <= last_index + 6); + if (uc == 0) break; + } + + // This can be useful for debugging. +#if 0 + // Show the UCS and the re-encoded UTF-8. + log_ucs(line_buffer_ucs); + log_ascii(reencoded_utf8); +#endif // 0 + + OVR_ASSERT(check_equal(line_buffer_utf8, line_buffer_ucs)); + OVR_ASSERT(check_equal(reencoded_utf8, line_buffer_ucs)); + + // Start next line. + byte_counter = 0; + } + } + + fclose(fp); + } + + return 0; +} + + +#endif // UTF8_UNIT_TEST + +}} // namespace UTF8Util::OVR + diff --git a/LibOVR/Src/Kernel/OVR_UTF8Util.h b/LibOVR/Src/Kernel/OVR_UTF8Util.h new file mode 100644 index 0000000..bf5af46 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_UTF8Util.h @@ -0,0 +1,88 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_UTF8Util.h +Content : UTF8 Unicode character encoding/decoding support +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_UTF8Util_h +#define OVR_UTF8Util_h + +#include "OVR_Types.h" + +namespace OVR { namespace UTF8Util { + +//----------------------------------------------------------------------------------- + +// *** UTF8 string length and indexing. + +// Determines the length of UTF8 string in characters. +// If source length is specified (in bytes), null 0 character is counted properly. +SPInt OVR_STDCALL GetLength(const char* putf8str, SPInt length = -1); + +// Gets a decoded UTF8 character at index; you can access up to the index returned +// by GetLength. 0 will be returned for out of bounds access. +UInt32 OVR_STDCALL GetCharAt(SPInt index, const char* putf8str, SPInt length = -1); + +// Converts UTF8 character index into byte offset. +// -1 is returned if index was out of bounds. +SPInt OVR_STDCALL GetByteIndex(SPInt index, const char* putf8str, SPInt length = -1); + + +// *** 16-bit Unicode string Encoding/Decoding routines. + +// Determines the number of bytes necessary to encode a string. +// Does not count the terminating 0 (null) character. +SPInt OVR_STDCALL GetEncodeStringSize(const wchar_t* pchar, SPInt length = -1); + +// Encodes a unicode (UCS-2 only) string into a buffer. The size of buffer must be at +// least GetEncodeStringSize() + 1. +void OVR_STDCALL EncodeString(char *pbuff, const wchar_t* pchar, SPInt length = -1); + +// Decode UTF8 into a wchar_t buffer. Must have GetLength()+1 characters available. +// Characters over 0xFFFF are replaced with 0xFFFD. +// Returns the length of resulting string (number of characters) +UPInt OVR_STDCALL DecodeString(wchar_t *pbuff, const char* putf8str, SPInt bytesLen = -1); + + +// *** Individual character Encoding/Decoding. + +// Determined the number of bytes necessary to encode a UCS character. +int OVR_STDCALL GetEncodeCharSize(UInt32 ucsCharacter); + +// Encodes the given UCS character into the given UTF-8 buffer. +// Writes the data starting at buffer[offset], and +// increments offset by the number of bytes written. +// May write up to 6 bytes, so make sure there's room in the buffer +void OVR_STDCALL EncodeChar(char* pbuffer, SPInt* poffset, UInt32 ucsCharacter); + +// Return the next Unicode character in the UTF-8 encoded buffer. +// Invalid UTF-8 sequences produce a U+FFFD character as output. +// Advances *utf8_buffer past the character returned. Pointer advance +// occurs even if the terminating 0 character is hit, since that allows +// strings with middle '\0' characters to be supported. +UInt32 OVR_STDCALL DecodeNextChar_Advance0(const char** putf8Buffer); + +// Safer version of DecodeNextChar, which doesn't advance pointer if +// null character is hit. +inline UInt32 DecodeNextChar(const char** putf8Buffer) +{ + UInt32 ch = DecodeNextChar_Advance0(putf8Buffer); + if (ch == 0) + (*putf8Buffer)--; + return ch; +} + + +}} // OVR::UTF8Util + +#endif diff --git a/LibOVR/Src/OVR_Device.h b/LibOVR/Src/OVR_Device.h new file mode 100644 index 0000000..4434916 --- /dev/null +++ b/LibOVR/Src/OVR_Device.h @@ -0,0 +1,627 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Device.h +Content : Definition of HMD-related Device interfaces +Created : September 21, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Device_h +#define OVR_Device_h + +#include "OVR_DeviceConstants.h" +#include "OVR_DeviceHandle.h" +#include "OVR_DeviceMessages.h" +#include "OVR_HIDDeviceBase.h" + +#include "Kernel/OVR_Atomic.h" +#include "Kernel/OVR_RefCount.h" +#include "Kernel/OVR_String.h" + +namespace OVR { + +class SensorDevice; +class DeviceCommon; +class DeviceManager; + +// MessageHandler is a base class from which users derive to receive messages, +// its OnMessage handler will be called for messages once it is installed on +// a device. Same message handler can be installed on multiple devices. +class MessageHandler +{ + friend class MessageHandlerImpl; +public: + MessageHandler(); + virtual ~MessageHandler(); + + // Returns 'true' if handler is currently installed on any devices. + bool IsHandlerInstalled() const; + + // Should be called from derived class destructor to avoid handler + // being called after it exits. + void RemoveHandlerFromDevices(); + + // Returns a pointer to the internal lock object that is locked by a + // background thread while OnMessage() is called. + // This lock guaranteed to survive until ~MessageHandler. + Lock* GetHandlerLock() const; + + + virtual void OnMessage(const Message&) { } + + // Determines if handler supports a specific message type. Can + // be used to filter out entire message groups. The result + // returned by this function shouldn't change after handler creation. + virtual bool SupportsMessageType(MessageType) const { return true; } + +private: + UPInt Internal[4]; +}; + + +//------------------------------------------------------------------------------------- +// ***** DeviceBase + +// DeviceBase is the base class for all OVR Devices. It provides the following basic +// functionality: +// - Reports device type, manager, and associated parent (if any). +// - Supports installable message handlers, which are notified of device events. +// - Device objects are created through DeviceHandle::CreateDevice or more commonly +// through DeviceEnumerator<>::CreateDevice. +// - Created devices are reference counted, starting with RefCount of 1. +// - Device is resources are cleaned up when it is Released, although its handles +// may survive longer if referenced. + +class DeviceBase : public NewOverrideBase +{ + friend class DeviceHandle; + friend class DeviceManagerImpl; +public: + + // Enumerating DeviceBase enumerates all devices. + enum { EnumDeviceType = Device_All }; + + virtual ~DeviceBase() { } + virtual void AddRef(); + virtual void Release(); + + virtual DeviceBase* GetParent() const; + virtual DeviceManager* GetManager() const; + + virtual void SetMessageHandler(MessageHandler* handler); + virtual MessageHandler* GetMessageHandler() const; + + virtual DeviceType GetType() const; + virtual bool GetDeviceInfo(DeviceInfo* info) const; + + // returns the MessageHandler's lock + Lock* GetHandlerLock() const; +protected: + // Internal + virtual DeviceCommon* getDeviceCommon() const = 0; +}; + + +//------------------------------------------------------------------------------------- +// ***** DeviceInfo + +// DeviceInfo describes a device and its capabilities, obtained by calling +// GetDeviceInfo. This base class only contains device-independent functionality; +// users will normally use a derived HMDInfo or SensorInfo classes for more +// extensive device info. + +class DeviceInfo +{ +public: + DeviceInfo() : InfoClassType(Device_None), Type(Device_None), Version(0) + { ProductName[0] = Manufacturer[0] = 0; } + + enum { MaxNameLength = 32 }; + + // Type of device for which DeviceInfo is intended. + // This will be set to Device_HMD for HMDInfo structure, note that this may be + // different form the actual device type since (Device_None) is valid. + const DeviceType InfoClassType; + // Type of device this describes. This must be the same as InfoClassType when + // InfoClassType != Device_None. + DeviceType Type; + // Name string describing the product: "Oculus Rift DK1", etc. + char ProductName[MaxNameLength]; + char Manufacturer[MaxNameLength]; + unsigned Version; + +protected: + DeviceInfo(DeviceType type) : InfoClassType(type), Type(type), Version(0) + { ProductName[0] = Manufacturer[0] = 0; } + void operator = (const DeviceInfo&) { OVR_ASSERT(0); } // Assignment not allowed. +}; + + +//------------------------------------------------------------------------------------- +// DeviceEnumerationArgs provides device enumeration argumenrs for DeviceManager::EnumerateDevicesEx. +class DeviceEnumerationArgs +{ +public: + DeviceEnumerationArgs(DeviceType enumType, bool availableOnly) + : EnumType(enumType), AvailableOnly(availableOnly) + { } + + // Helper; returns true if args match our enumeration criteria. + bool MatchRule(DeviceType type, bool available) const + { + return ((EnumType == type) || (EnumType == Device_All)) && + (available || !AvailableOnly); + } + +protected: + DeviceType EnumType; + bool AvailableOnly; +}; + + +// DeviceEnumerator<> is used to enumerate and create devices of specified class, +// it is returned by calling MeviceManager::EnumerateDevices. Initially, the enumerator will +// refer to the first device of specified type. Additional devices can be accessed by +// calling Next(). + +template +class DeviceEnumerator : public DeviceHandle +{ + friend class DeviceManager; + friend class DeviceManagerImpl; +public: + DeviceEnumerator() + : DeviceHandle(), EnumArgs(Device_None, true) { } + + // Next advances enumeration to the next device that first criteria. + // Returns false if no more devices exist that match enumeration criteria. + bool Next() { return enumerateNext(EnumArgs); } + + // Creates an instance of the device referenced by enumerator; returns null + // if enumerator does not refer to a valid device or device is unavailable. + // If device was already created, the same object with incremented ref-count is returned. + T* CreateDevice() { return static_cast(DeviceHandle::CreateDevice()); } + +protected: + DeviceEnumerator(const DeviceHandle &dev, const DeviceEnumerationArgs& args) + : DeviceHandle(dev), EnumArgs(args) + { } + + DeviceEnumerationArgs EnumArgs; +}; + +//------------------------------------------------------------------------------------- +// ***** DeviceManager + +// DeviceManager maintains and provides access to devices supported by OVR, such as +// HMDs and sensors. A single instance of DeviceManager is normally created at +// program startup, allowing devices to be enumerated and created. DeviceManager is +// reference counted and is AddRefed by its created child devices, causing it to +// always be the last object that is released. +// +// Install MessageHandler on DeviceManager to detect when devices are inserted or removed. +// +// The following code will create the manager and its first available HMDDevice, +// and then release it when not needed: +// +// DeviceManager* manager = DeviceManager::Create(); +// HMDDevice* hmd = manager->EnumerateDevices().CreateDevice(); +// +// if (hmd) hmd->Release(); +// if (manager) manager->Release(); + + +class DeviceManager : public DeviceBase +{ +public: + + DeviceManager() + { } + + // DeviceBase implementation. + virtual DeviceType GetType() const { return Device_Manager; } + virtual DeviceManager* GetManager() const { return const_cast(this); } + + + // EnumerateDevices enumerates all of the available devices of the specified class, + // returning an enumerator that references the first device. An empty enumerator is + // returned if no devices are available. The following APIs are exposed through + // DeviceEnumerator: + // DeviceEnumerator::GetType() - Check device type. Returns Device_None + // if no device was found/pointed to. + // DeviceEnumerator::GetDeviceInfo() - Get more information on device. + // DeviceEnumerator::CreateDevice() - Create an instance of device. + // DeviceEnumerator::Next() - Move onto next device. + template + DeviceEnumerator EnumerateDevices(bool availableOnly = true) + { + // TBD: A cleaner (but less efficient) alternative is though enumeratorFromHandle. + DeviceEnumerator<> e = EnumerateDevicesEx(DeviceEnumerationArgs((DeviceType)D::EnumDeviceType, availableOnly)); + return *reinterpret_cast*>(&e); + } + + // EnumerateDevicesEx provides internal implementation for device enumeration, enumerating + // devices based on dynamically specified DeviceType in DeviceEnumerationArgs. + // End users should call DeumerateDevices<>() instead. + virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args) = 0; + + // Adds a device (DeviceCreateDesc*) into Devices. Returns NULL, + // if unsuccessful or device is already in the list. + virtual Ptr AddDevice_NeedsLock(const DeviceCreateDesc& createDesc) = 0; + + // Creates a new DeviceManager. Only one instance of DeviceManager should be created at a time. + static DeviceManager* Create(); + + // Static constant for this device type, used in template cast type checks. + enum { EnumDeviceType = Device_Manager }; + +protected: + DeviceEnumerator<> enumeratorFromHandle(const DeviceHandle& h, const DeviceEnumerationArgs& args) + { return DeviceEnumerator<>(h, args); } + + DeviceManager* getThis() { return this; } +}; + + + +//------------------------------------------------------------------------------------- +// ***** HMDInfo + +// This structure describes various aspects of the HMD allowing us to configure rendering. +// +// Currently included data: +// - Physical screen dimensions, resolution, and eye distances. +// (some of these will be configurable with a tool in the future). +// These arguments allow us to properly setup projection across HMDs. +// - DisplayDeviceName for identifying HMD screen; system-specific interpretation. +// +// TBD: +// - Power on/ off? +// - Sensor rates and capabilities +// - Distortion radius/variables +// - Screen update frequency +// - Distortion needed flag +// - Update modes: +// Set update mode: Stereo (both sides together), mono (same in both eyes), +// Alternating, Alternating scan-lines. + +class HMDInfo : public DeviceInfo +{ +public: + // Size of the entire screen, in pixels. + unsigned HResolution, VResolution; + // Physical dimensions of the active screen in meters. Can be used to calculate + // projection center while considering IPD. + float HScreenSize, VScreenSize; + // Physical offset from the top of the screen to the eye center, in meters. + // This will usually, but not necessarily be half of VScreenSize. + float VScreenCenter; + // Distance from the eye to screen surface, in meters. + // Useful for calculating FOV and projection. + float EyeToScreenDistance; + // Distance between physical lens centers useful for calculating distortion center. + float LensSeparationDistance; + // Configured distance between the user's eye centers, in meters. Defaults to 0.064. + float InterpupillaryDistance; + + // Radial distortion correction coefficients. + // The distortion assumes that the input texture coordinates will be scaled + // by the following equation: + // uvResult = uvInput * (K0 + K1 * uvLength^2 + K2 * uvLength^4) + // Where uvInput is the UV vector from the center of distortion in direction + // of the mapped pixel, uvLength is the magnitude of that vector, and uvResult + // the corresponding location after distortion. + float DistortionK[4]; + + float ChromaAbCorrection[4]; + + // Desktop coordinate position of the screen (can be negative; may not be present on all platforms) + int DesktopX, DesktopY; + + // Windows: + // "\\\\.\\DISPLAY3", etc. Can be used in EnumDisplaySettings/CreateDC. + char DisplayDeviceName[32]; + + // MacOS: + long DisplayId; + + + HMDInfo() + : DeviceInfo(Device_HMD), + HResolution(0), VResolution(0), HScreenSize(0), VScreenSize(0), + VScreenCenter(0), EyeToScreenDistance(0), + LensSeparationDistance(0), InterpupillaryDistance(0), + DesktopX(0), DesktopY(0), DisplayId(0) + { + DisplayDeviceName[0] = 0; + memset(DistortionK, 0, sizeof(DistortionK)); + DistortionK[0] = 1; + ChromaAbCorrection[0] = ChromaAbCorrection[2] = 1; + ChromaAbCorrection[1] = ChromaAbCorrection[3] = 0; + } + + // Operator = copies local fields only (base class must be correct already) + void operator = (const HMDInfo& src) + { + HResolution = src.HResolution; + VResolution = src.VResolution; + HScreenSize = src.HScreenSize; + VScreenSize = src.VScreenSize; + VScreenCenter = src.VScreenCenter; + EyeToScreenDistance = src.EyeToScreenDistance; + LensSeparationDistance = src.LensSeparationDistance; + InterpupillaryDistance = src.InterpupillaryDistance; + DistortionK[0] = src.DistortionK[0]; + DistortionK[1] = src.DistortionK[1]; + DistortionK[2] = src.DistortionK[2]; + DistortionK[3] = src.DistortionK[3]; + ChromaAbCorrection[0] = src.ChromaAbCorrection[0]; + ChromaAbCorrection[1] = src.ChromaAbCorrection[1]; + ChromaAbCorrection[2] = src.ChromaAbCorrection[2]; + ChromaAbCorrection[3] = src.ChromaAbCorrection[3]; + DesktopX = src.DesktopX; + DesktopY = src.DesktopY; + memcpy(DisplayDeviceName, src.DisplayDeviceName, sizeof(DisplayDeviceName)); + DisplayId = src.DisplayId; + } + + bool IsSameDisplay(const HMDInfo& o) const + { + return DisplayId == o.DisplayId && + String::CompareNoCase(DisplayDeviceName, + o.DisplayDeviceName) == 0; + } + +}; + + +// HMDDevice represents an Oculus HMD device unit. An instance of this class +// is typically created from the DeviceManager. +// After HMD device is created, we its sensor data can be obtained by +// first creating a Sensor object and then. + +// TBD: +// - Configure Sensor +// - APIs to set On-Screen message, other states? + +class HMDDevice : public DeviceBase +{ +public: + HMDDevice() + { } + + // Static constant for this device type, used in template cast type checks. + enum { EnumDeviceType = Device_HMD }; + + virtual DeviceType GetType() const { return Device_HMD; } + + // Creates a sensor associated with this HMD. + virtual SensorDevice* GetSensor() = 0; + + // Disconnects from real HMD device. This HMDDevice remains as 'fake' HMD. + // SensorDevice ptr is used to restore the 'fake' HMD (can be NULL). + HMDDevice* Disconnect(SensorDevice*); + + // Returns 'true' if HMD device is a 'fake' HMD (was created this way or + // 'Disconnect' method was called). + bool IsDisconnected() const; +}; + + +//------------------------------------------------------------------------------------- +// ***** SensorRange & SensorInfo + +// SensorRange specifies maximum value ranges that SensorDevice hardware is configured +// to detect. Although this range doesn't affect the scale of MessageBodyFrame values, +// physical motions whose positive or negative magnitude is outside the specified range +// may get clamped or misreported. Setting lower values may result in higher precision +// tracking. +struct SensorRange +{ + SensorRange(float maxAcceleration = 0.0f, float maxRotationRate = 0.0f, + float maxMagneticField = 0.0f) + : MaxAcceleration(maxAcceleration), MaxRotationRate(maxRotationRate), + MaxMagneticField(maxMagneticField) + { } + + // Maximum detected acceleration in m/s^2. Up to 8*G equivalent support guaranteed, + // where G is ~9.81 m/s^2. + // Oculus DK1 HW has thresholds near: 2, 4 (default), 8, 16 G. + float MaxAcceleration; + // Maximum detected angular velocity in rad/s. Up to 8*Pi support guaranteed. + // Oculus DK1 HW thresholds near: 1, 2, 4, 8 Pi (default). + float MaxRotationRate; + // Maximum detectable Magnetic field strength in Gauss. Up to 2.5 Gauss support guaranteed. + // Oculus DK1 HW thresholds near: 0.88, 1.3, 1.9, 2.5 gauss. + float MaxMagneticField; +}; + +// SensorInfo describes capabilities of the sensor device. +class SensorInfo : public DeviceInfo +{ +public: + SensorInfo() : DeviceInfo(Device_Sensor), VendorId(0), ProductId(0) + { + SerialNumber[0] = 0; + } + + // HID Vendor and ProductId of the device. + UInt16 VendorId; + UInt16 ProductId; + // MaxRanges report maximum sensor range values supported by HW. + SensorRange MaxRanges; + // Sensor (and display) serial number. + char SerialNumber[20]; + +private: + void operator = (const SensorInfo&) { OVR_ASSERT(0); } // Assignment not allowed. +}; + + +//------------------------------------------------------------------------------------- +// ***** SensorDevice + +// SensorDevice is an interface to sensor data. +// Install a MessageHandler of SensorDevice instance to receive MessageBodyFrame +// notifications. +// +// TBD: Add Polling API? More HID interfaces? + +class SensorDevice : public HIDDeviceBase, public DeviceBase +{ +public: + SensorDevice() + { } + + // Static constant for this device type, used in template cast type checks. + enum { EnumDeviceType = Device_Sensor }; + + virtual DeviceType GetType() const { return Device_Sensor; } + + + // CoordinateFrame defines whether messages come in the coordinate frame + // of the sensor device or HMD, which has a different internal sensor. + // Sensors obtained form the HMD will automatically use HMD coordinates. + enum CoordinateFrame + { + Coord_Sensor = 0, + Coord_HMD = 1 + }; + + virtual void SetCoordinateFrame(CoordinateFrame coordframe) = 0; + virtual CoordinateFrame GetCoordinateFrame() const = 0; + + // Sets report rate (in Hz) of MessageBodyFrame messages (delivered through MessageHandler::OnMessage call). + // Currently supported maximum rate is 1000Hz. If the rate is set to 500 or 333 Hz then OnMessage will be + // called twice or thrice at the same 'tick'. + // If the rate is < 333 then the OnMessage / MessageBodyFrame will be called three + // times for each 'tick': the first call will contain averaged values, the second + // and third calls will provide with most recent two recorded samples. + virtual void SetReportRate(unsigned rateHz) = 0; + // Returns currently set report rate, in Hz. If 0 - error occurred. + // Note, this value may be different from the one provided for SetReportRate. The return + // value will contain the actual rate. + virtual unsigned GetReportRate() const = 0; + + // Sets maximum range settings for the sensor described by SensorRange. + // The function will fail if you try to pass values outside Maximum supported + // by the HW, as described by SensorInfo. + // Pass waitFlag == true to wait for command completion. For waitFlag == true, + // returns true if the range was applied successfully (no HW error). + // For waitFlag = false, return 'true' means that command was enqueued successfully. + virtual bool SetRange(const SensorRange& range, bool waitFlag = false) = 0; + + // Return the current sensor range settings for the device. These may not exactly + // match the values applied through SetRange. + virtual void GetRange(SensorRange* range) const = 0; +}; + +//------------------------------------------------------------------------------------- +// ***** LatencyTestConfiguration +// LatencyTestConfiguration specifies configuration information for the Oculus Latency Tester device. +struct LatencyTestConfiguration +{ + LatencyTestConfiguration(const Color& threshold, bool sendSamples = false) + : Threshold(threshold), SendSamples(sendSamples) + { + } + + // The color threshold for triggering a detected display change. + Color Threshold; + // Flag specifying whether we wish to receive a stream of color values from the sensor. + bool SendSamples; +}; + +//------------------------------------------------------------------------------------- +// ***** LatencyTestCalibrate +// LatencyTestCalibrate specifies colors used for Latency Tester calibration. +struct LatencyTestCalibrate +{ + LatencyTestCalibrate(const Color& value) + : Value(value) + { + } + + // The color being calibrated to. + Color Value; +}; + +//------------------------------------------------------------------------------------- +// ***** LatencyTestStartTest +// LatencyTestStartTest specifies values used when starting the Latency Tester test. +struct LatencyTestStartTest +{ + LatencyTestStartTest(const Color& targetValue) + : TargetValue(targetValue) + { + } + + // The color value that the display is being set to. + Color TargetValue; +}; + +//------------------------------------------------------------------------------------- +// ***** LatencyTestDisplay +// LatencyTestDisplay sets the mode and contents of the Latency Tester LED display. +// See the 'Latency Tester Specification' document for more details. +struct LatencyTestDisplay +{ + LatencyTestDisplay(UByte mode, UInt32 value) + : Mode(mode), Value(value) + { + } + + UByte Mode; // The display mode that we wish to select. + UInt32 Value; // The value to display. +}; + +//------------------------------------------------------------------------------------- +// ***** LatencyTestDevice + +// LatencyTestDevice provides an interface to the Oculus Latency Tester which is used to test 'motion to photon' latency. +class LatencyTestDevice : public HIDDeviceBase, public DeviceBase +{ +public: + LatencyTestDevice() + { } + + // Static constant for this device type, used in template cast type checks. + enum { EnumDeviceType = Device_LatencyTester }; + + virtual DeviceType GetType() const { return Device_LatencyTester; } + + // Specifies configuration information including the threshold for triggering a detected color change, + // and a flag to enable a stream of sensor values (typically used for debugging). + virtual bool SetConfiguration(const LatencyTestConfiguration& configuration, bool waitFlag = false) = 0; + + // Get configuration information from device. + virtual bool GetConfiguration(LatencyTestConfiguration* configuration) = 0; + + // Used to calibrate the latency tester at the start of a test. Calibration information is lost + // when power is removed from the device. + virtual bool SetCalibrate(const LatencyTestCalibrate& calibrate, bool waitFlag = false) = 0; + + // Get calibration information from device. + virtual bool GetCalibrate(LatencyTestCalibrate* calibrate) = 0; + + // Triggers the start of a measurement. This starts the millisecond timer on the device and + // causes it to respond with the 'MessageLatencyTestStarted' message. + virtual bool SetStartTest(const LatencyTestStartTest& start, bool waitFlag = false) = 0; + + // Used to set the value displayed on the LED display panel. + virtual bool SetDisplay(const LatencyTestDisplay& display, bool waitFlag = false) = 0; + + virtual DeviceBase* GetDevice() { return this; } +}; + +} // namespace OVR + +#endif diff --git a/LibOVR/Src/OVR_DeviceConstants.h b/LibOVR/Src/OVR_DeviceConstants.h new file mode 100644 index 0000000..b729d5f --- /dev/null +++ b/LibOVR/Src/OVR_DeviceConstants.h @@ -0,0 +1,38 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_DeviceConstants.h +Content : Device constants +Created : February 5, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_DeviceConstants_h +#define OVR_DeviceConstants_h + +namespace OVR { + + +//------------------------------------------------------------------------------------- +// Different device types supported by OVR; this type is reported by DeviceBase::GetType. +// +enum DeviceType +{ + Device_None = 0, + Device_Manager = 1, + Device_HMD = 2, + Device_Sensor = 3, + Device_LatencyTester = 4, + Device_All = 0xFF // Set for enumeration only, to enumerate all device types. +}; + +} // namespace OVR + +#endif diff --git a/LibOVR/Src/OVR_DeviceHandle.cpp b/LibOVR/Src/OVR_DeviceHandle.cpp new file mode 100644 index 0000000..5a0fce6 --- /dev/null +++ b/LibOVR/Src/OVR_DeviceHandle.cpp @@ -0,0 +1,174 @@ +/************************************************************************************ + +Filename : OVR_DeviceHandle.cpp +Content : Implementation of device handle class +Created : February 5, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_DeviceHandle.h" + +#include "OVR_DeviceImpl.h" + +namespace OVR { + +//------------------------------------------------------------------------------------- +// ***** DeviceHandle + +DeviceHandle::DeviceHandle(DeviceCreateDesc* impl) : pImpl(impl) +{ + if (pImpl) + pImpl->AddRef(); +} + +DeviceHandle::DeviceHandle(const DeviceHandle& src) : pImpl(src.pImpl) +{ + if (pImpl) + pImpl->AddRef(); +} + +DeviceHandle::~DeviceHandle() +{ + if (pImpl) + pImpl->Release(); +} + +void DeviceHandle::operator = (const DeviceHandle& src) +{ + if (src.pImpl) + src.pImpl->AddRef(); + if (pImpl) + pImpl->Release(); + pImpl = src.pImpl; +} + +DeviceBase* DeviceHandle::GetDevice_AddRef() const +{ + if (pImpl && pImpl->pDevice) + { + pImpl->pDevice->AddRef(); + return pImpl->pDevice; + } + return NULL; +} + +// Returns true, if the handle contains the same device ptr +// as specified in the parameter. +bool DeviceHandle::IsDevice(DeviceBase* pdev) const +{ + return (pdev && pImpl && pImpl->pDevice) ? + pImpl->pDevice == pdev : false; +} + +DeviceType DeviceHandle::GetType() const +{ + return pImpl ? pImpl->Type : Device_None; +} + +bool DeviceHandle::GetDeviceInfo(DeviceInfo* info) const +{ + return pImpl ? pImpl->GetDeviceInfo(info) : false; +} +bool DeviceHandle::IsAvailable() const +{ + // This isn't "atomically safe", but the function only returns the + // recent state that may change. + return pImpl ? (pImpl->Enumerated && pImpl->pLock->pManager) : false; +} + +bool DeviceHandle::IsCreated() const +{ + return pImpl ? (pImpl->pDevice != 0) : false; +} + +DeviceBase* DeviceHandle::CreateDevice() +{ + if (!pImpl) + return 0; + + DeviceBase* device = 0; + Ptr manager= 0; + + // Since both manager and device pointers can only be destroyed during a lock, + // hold it while checking for availability. + // AddRef to manager so that it doesn't get released on us. + { + Lock::Locker deviceLockScope(pImpl->GetLock()); + + if (pImpl->pDevice) + { + pImpl->pDevice->AddRef(); + return pImpl->pDevice; + } + manager = pImpl->GetManagerImpl(); + } + + if (manager) + { + if (manager->GetThreadId() != OVR::GetCurrentThreadId()) + { + // Queue up a CreateDevice request. This fills in '&device' with AddRefed value, + // or keep it at null. + manager->GetThreadQueue()->PushCallAndWaitResult( + manager.GetPtr(), &DeviceManagerImpl::CreateDevice_MgrThread, + &device, pImpl, (DeviceBase*)0); + } + else + device = manager->CreateDevice_MgrThread(pImpl, (DeviceBase*)0); + } + return device; +} + +void DeviceHandle::Clear() +{ + if (pImpl) + { + pImpl->Release(); + pImpl = 0; + } +} + +bool DeviceHandle::enumerateNext(const DeviceEnumerationArgs& args) +{ + if (GetType() == Device_None) + return false; + + Ptr managerKeepAlive; + Lock::Locker lockScope(pImpl->GetLock()); + + DeviceCreateDesc* next = pImpl; + // If manager was destroyed, we get removed from the list. + if (!pImpl->pNext) + return false; + + managerKeepAlive = next->GetManagerImpl(); + OVR_ASSERT(managerKeepAlive); + + do { + next = next->pNext; + + if (managerKeepAlive->Devices.IsNull(next)) + { + pImpl->Release(); + pImpl = 0; + return false; + } + + } while(!args.MatchRule(next->Type, next->Enumerated)); + + next->AddRef(); + pImpl->Release(); + pImpl = next; + + return true; +} + +} // namespace OVR + diff --git a/LibOVR/Src/OVR_DeviceHandle.h b/LibOVR/Src/OVR_DeviceHandle.h new file mode 100644 index 0000000..4ac4387 --- /dev/null +++ b/LibOVR/Src/OVR_DeviceHandle.h @@ -0,0 +1,97 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_DeviceHandle.h +Content : Handle to a device that was enumerated +Created : February 5, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_DeviceHandle_h +#define OVR_DeviceHandle_h + +#include "OVR_DeviceConstants.h" + +namespace OVR { + +class DeviceBase; +class DeviceInfo; + +// Internal +class DeviceCreateDesc; +class DeviceEnumerationArgs; + + +//------------------------------------------------------------------------------------- +// ***** DeviceHandle + +// DeviceHandle references a specific device that was enumerated; it can be assigned +// directly from DeviceEnumerator. +// +// Devices represented by DeviceHandle are not necessarily created or available. +// A device may become unavailable if, for example, it its unplugged. If the device +// is available, it can be created by calling CreateDevice. +// + +class DeviceHandle +{ + friend class DeviceManager; + friend class DeviceManagerImpl; + template friend class HIDDeviceImpl; + +public: + DeviceHandle() : pImpl(0) { } + DeviceHandle(const DeviceHandle& src); + ~DeviceHandle(); + + void operator = (const DeviceHandle& src); + + bool operator == (const DeviceHandle& other) const { return pImpl == other.pImpl; } + bool operator != (const DeviceHandle& other) const { return pImpl != other.pImpl; } + + // operator bool() returns true if Handle/Enumerator points to a valid device. + operator bool () const { return GetType() != Device_None; } + + // Returns existing device, or NULL if !IsCreated. The returned ptr is + // addref-ed. + DeviceBase* GetDevice_AddRef() const; + DeviceType GetType() const; + bool GetDeviceInfo(DeviceInfo* info) const; + bool IsAvailable() const; + bool IsCreated() const; + // Returns true, if the handle contains the same device ptr + // as specified in the parameter. + bool IsDevice(DeviceBase*) const; + + // Creates a device, or returns AddRefed pointer if one is already created. + // New devices start out with RefCount of 1. + DeviceBase* CreateDevice(); + + // Creates a device, or returns AddRefed pointer if one is already created. + // New devices start out with RefCount of 1. DeviceT is used to cast the + // DeviceBase* to a concreete type. + template + DeviceT* CreateDeviceTyped() const + { + return static_cast(DeviceHandle(*this).CreateDevice()); + } + + // Resets the device handle to uninitialized state. + void Clear(); + +protected: + explicit DeviceHandle(DeviceCreateDesc* impl); + bool enumerateNext(const DeviceEnumerationArgs& args); + DeviceCreateDesc* pImpl; +}; + +} // namespace OVR + +#endif diff --git a/LibOVR/Src/OVR_DeviceImpl.cpp b/LibOVR/Src/OVR_DeviceImpl.cpp new file mode 100644 index 0000000..97cc2d4 --- /dev/null +++ b/LibOVR/Src/OVR_DeviceImpl.cpp @@ -0,0 +1,784 @@ +/************************************************************************************ + +Filename : OVR_DeviceImpl.h +Content : Partial back-end independent implementation of Device interfaces +Created : October 10, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_DeviceImpl.h" +#include "Kernel/OVR_Atomic.h" +#include "Kernel/OVR_Log.h" +#include "Kernel/OVR_System.h" + +#include "OVR_DeviceImpl.h" +#include "OVR_SensorImpl.h" + +namespace OVR { + + +//------------------------------------------------------------------------------------- +// ***** SharedLock + +// This is a general purpose globally shared Lock implementation that should probably be +// moved to Kernel. +// May in theory busy spin-wait if we hit contention on first lock creation, +// but this shouldn't matter in practice since Lock* should be cached. + + +enum { LockInitMarker = 0xFFFFFFFF }; + +Lock* SharedLock::GetLockAddRef() +{ + int oldUseCount; + + do { + oldUseCount = UseCount; + if (oldUseCount == LockInitMarker) + continue; + + if (oldUseCount == 0) + { + // Initialize marker + if (AtomicOps::CompareAndSet_Sync(&UseCount, 0, LockInitMarker)) + { + Construct(Buffer); + do { } + while (!AtomicOps::CompareAndSet_Sync(&UseCount, LockInitMarker, 1)); + return toLock(); + } + continue; + } + + } while (!AtomicOps::CompareAndSet_NoSync(&UseCount, oldUseCount, oldUseCount + 1)); + + return toLock(); +} + +void SharedLock::ReleaseLock(Lock* plock) +{ + OVR_UNUSED(plock); + OVR_ASSERT((plock = toLock()) != 0); + + int oldUseCount; + + do { + oldUseCount = UseCount; + OVR_ASSERT(oldUseCount != LockInitMarker); + + if (oldUseCount == 1) + { + // Initialize marker + if (AtomicOps::CompareAndSet_Sync(&UseCount, 1, LockInitMarker)) + { + Destruct(toLock()); + + do { } + while (!AtomicOps::CompareAndSet_Sync(&UseCount, LockInitMarker, 0)); + + return; + } + continue; + } + + } while (!AtomicOps::CompareAndSet_NoSync(&UseCount, oldUseCount, oldUseCount - 1)); +} + + + +//------------------------------------------------------------------------------------- +// ***** MessageHandler + +// Threading notes: +// The OnMessage() handler and SetMessageHandler are currently synchronized +// through a separately stored shared Lock object to avoid calling the handler +// from background thread while it's being removed. + +static SharedLock MessageHandlerSharedLock; + + +class MessageHandlerImpl +{ +public: + MessageHandlerImpl() + : pLock(MessageHandlerSharedLock.GetLockAddRef()) + { + } + ~MessageHandlerImpl() + { + MessageHandlerSharedLock.ReleaseLock(pLock); + pLock = 0; + } + + static MessageHandlerImpl* FromHandler(MessageHandler* handler) + { return (MessageHandlerImpl*)&handler->Internal; } + static const MessageHandlerImpl* FromHandler(const MessageHandler* handler) + { return (const MessageHandlerImpl*)&handler->Internal; } + + // This lock is held while calling a handler and when we are applied/ + // removed from a device. + Lock* pLock; + // List of device we are applied to. + List UseList; +}; + + +MessageHandlerRef::MessageHandlerRef(DeviceBase* device) + : pLock(MessageHandlerSharedLock.GetLockAddRef()), pDevice(device), pHandler(0) +{ +} + +MessageHandlerRef::~MessageHandlerRef() +{ + { + Lock::Locker lockScope(pLock); + if (pHandler) + { + pHandler = 0; + RemoveNode(); + } + } + MessageHandlerSharedLock.ReleaseLock(pLock); + pLock = 0; +} + +void MessageHandlerRef::SetHandler(MessageHandler* handler) +{ + OVR_ASSERT(!handler || + MessageHandlerImpl::FromHandler(handler)->pLock == pLock); + Lock::Locker lockScope(pLock); + SetHandler_NTS(handler); +} + +void MessageHandlerRef::SetHandler_NTS(MessageHandler* handler) +{ + if (pHandler != handler) + { + if (pHandler) + RemoveNode(); + pHandler = handler; + + if (handler) + { + MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(handler); + handlerImpl->UseList.PushBack(this); + } + // TBD: Call notifier on device? + } +} + + +MessageHandler::MessageHandler() +{ + OVR_COMPILER_ASSERT(sizeof(Internal) > sizeof(MessageHandlerImpl)); + Construct(Internal); +} + +MessageHandler::~MessageHandler() +{ + MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this); + { + Lock::Locker lockedScope(handlerImpl->pLock); + OVR_ASSERT_LOG(handlerImpl->UseList.IsEmpty(), + ("~MessageHandler %p - Handler still active; call RemoveHandlerFromDevices", this)); + } + + Destruct(handlerImpl); +} + +bool MessageHandler::IsHandlerInstalled() const +{ + const MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this); + Lock::Locker lockedScope(handlerImpl->pLock); + return handlerImpl->UseList.IsEmpty() != true; +} + + +void MessageHandler::RemoveHandlerFromDevices() +{ + MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this); + Lock::Locker lockedScope(handlerImpl->pLock); + + while(!handlerImpl->UseList.IsEmpty()) + { + MessageHandlerRef* use = handlerImpl->UseList.GetFirst(); + use->SetHandler_NTS(0); + } +} + +Lock* MessageHandler::GetHandlerLock() const +{ + const MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this); + return handlerImpl->pLock; +} + + +//------------------------------------------------------------------------------------- +// ***** DeviceBase + + +// Delegate relevant implementation to DeviceRectord to avoid re-implementation in +// every derived Device. +void DeviceBase::AddRef() +{ + getDeviceCommon()->DeviceAddRef(); +} +void DeviceBase::Release() +{ + getDeviceCommon()->DeviceRelease(); +} +DeviceBase* DeviceBase::GetParent() const +{ + return getDeviceCommon()->pParent.GetPtr(); +} +DeviceManager* DeviceBase::GetManager() const +{ + return getDeviceCommon()->pCreateDesc->GetManagerImpl(); +} + +void DeviceBase::SetMessageHandler(MessageHandler* handler) +{ + getDeviceCommon()->HandlerRef.SetHandler(handler); +} +MessageHandler* DeviceBase::GetMessageHandler() const +{ + return getDeviceCommon()->HandlerRef.GetHandler(); +} + +DeviceType DeviceBase::GetType() const +{ + return getDeviceCommon()->pCreateDesc->Type; +} + +bool DeviceBase::GetDeviceInfo(DeviceInfo* info) const +{ + return getDeviceCommon()->pCreateDesc->GetDeviceInfo(info); + //info->Name[0] = 0; + //return false; +} + +// returns the MessageHandler's lock +Lock* DeviceBase::GetHandlerLock() const +{ + return getDeviceCommon()->HandlerRef.GetLock(); +} + +// Derive DeviceManagerCreateDesc to provide abstract function implementation. +class DeviceManagerCreateDesc : public DeviceCreateDesc +{ +public: + DeviceManagerCreateDesc(DeviceFactory* factory) + : DeviceCreateDesc(factory, Device_Manager) { } + + // We don't need there on Manager since it isn't assigned to DeviceHandle. + virtual DeviceCreateDesc* Clone() const { return 0; } + virtual MatchResult MatchDevice(const DeviceCreateDesc&, + DeviceCreateDesc**) const { return Match_None; } + virtual DeviceBase* NewDeviceInstance() { return 0; } + virtual bool GetDeviceInfo(DeviceInfo*) const { return false; } +}; + +//------------------------------------------------------------------------------------- +// ***** DeviceManagerImpl + +DeviceManagerImpl::DeviceManagerImpl() + : DeviceImpl(CreateManagerDesc(), 0) + //,DeviceCreateDescList(pCreateDesc ? pCreateDesc->pLock : 0) +{ + if (pCreateDesc) + { + pCreateDesc->pLock->pManager = this; + } +} + +DeviceManagerImpl::~DeviceManagerImpl() +{ + // Shutdown must've been called. + OVR_ASSERT(!pCreateDesc->pDevice); + + // Remove all factories + while(!Factories.IsEmpty()) + { + DeviceFactory* factory = Factories.GetFirst(); + factory->RemovedFromManager(); + factory->RemoveNode(); + } +} + +DeviceCreateDesc* DeviceManagerImpl::CreateManagerDesc() +{ + DeviceCreateDesc* managerDesc = new DeviceManagerCreateDesc(0); + if (managerDesc) + { + managerDesc->pLock = *new DeviceManagerLock; + } + return managerDesc; +} + +bool DeviceManagerImpl::Initialize(DeviceBase* parent) +{ + OVR_UNUSED(parent); + if (!pCreateDesc || !pCreateDesc->pLock) + return false; + return true; +} + +void DeviceManagerImpl::Shutdown() +{ + // Remove all device descriptors from list while the lock is held. + // Some descriptors may survive longer due to handles. + while(!Devices.IsEmpty()) + { + DeviceCreateDesc* devDesc = Devices.GetFirst(); + OVR_ASSERT(!devDesc->pDevice); // Manager shouldn't be dying while Device exists. + devDesc->Enumerated = false; + devDesc->RemoveNode(); + devDesc->pNext = devDesc->pPrev = 0; + + if (devDesc->HandleCount == 0) + { + delete devDesc; + } + } + Devices.Clear(); + + // These must've been cleared by caller. + OVR_ASSERT(pCreateDesc->pDevice == 0); + OVR_ASSERT(pCreateDesc->pLock->pManager == 0); +} + + +// Callbacks for DeviceCreation/Release +DeviceBase* DeviceManagerImpl::CreateDevice_MgrThread(DeviceCreateDesc* createDesc, DeviceBase* parent) +{ + // Calls to DeviceManagerImpl::CreateDevice are enqueued with wait while holding pManager, + // so 'this' must remain valid. + OVR_ASSERT(createDesc->pLock->pManager); + + Lock::Locker devicesLock(GetLock()); + + // If device already exists, just AddRef to it. + if (createDesc->pDevice) + { + createDesc->pDevice->AddRef(); + return createDesc->pDevice; + } + + if (!parent) + parent = this; + + DeviceBase* device = createDesc->NewDeviceInstance(); + + if (device) + { + if (device->getDeviceCommon()->Initialize(parent)) + { + createDesc->pDevice = device; + } + else + { + // Don't go through Release() to avoid PushCall behaviour, + // as it is not needed here. + delete device; + device = 0; + } + } + + return device; +} + +Void DeviceManagerImpl::ReleaseDevice_MgrThread(DeviceBase* device) +{ + // descKeepAlive will keep ManagerLock object alive as well, + // allowing us to exit gracefully. + Ptr descKeepAlive; + Lock::Locker devicesLock(GetLock()); + DeviceCommon* devCommon = device->getDeviceCommon(); + + while(1) + { + UInt32 refCount = devCommon->RefCount; + + if (refCount > 1) + { + if (devCommon->RefCount.CompareAndSet_NoSync(refCount, refCount-1)) + { + // We decreented from initial count higher then 1; + // nothing else to do. + return 0; + } + } + else if (devCommon->RefCount.CompareAndSet_NoSync(1, 0)) + { + // { 1 -> 0 } decrement succeded. Destroy this device. + break; + } + } + + // At this point, may be releasing the device manager itself. + // This does not matter, however, since shutdown logic is the same + // in both cases. DeviceManager::Shutdown with begin shutdown process for + // the internal manager thread, which will eventually destroy itself. + // TBD: Clean thread shutdown. + descKeepAlive = devCommon->pCreateDesc; + descKeepAlive->pDevice = 0; + devCommon->Shutdown(); + delete device; + return 0; +} + + + +Void DeviceManagerImpl::EnumerateAllFactoryDevices() +{ + // 1. Mark matching devices as NOT enumerated. + // 2. Call factory to enumerate all HW devices, adding any device that + // was not matched. + // 3. Remove non-matching devices. + + Lock::Locker deviceLock(GetLock()); + + DeviceCreateDesc* devDesc, *nextdevDesc; + + // 1. + for(devDesc = Devices.GetFirst(); + !Devices.IsNull(devDesc); devDesc = devDesc->pNext) + { + //if (devDesc->pFactory == factory) + devDesc->Enumerated = false; + } + + // 2. + DeviceFactory* factory = Factories.GetFirst(); + while(!Factories.IsNull(factory)) + { + EnumerateFactoryDevices(factory); + factory = factory->pNext; + } + + + // 3. + for(devDesc = Devices.GetFirst(); + !Devices.IsNull(devDesc); devDesc = nextdevDesc) + { + // In case 'devDesc' gets removed. + nextdevDesc = devDesc->pNext; + + // Note, device might be not enumerated since it is opened and + // in use! Do NOT notify 'device removed' in this case (!AB) + if (!devDesc->Enumerated) + { + // This deletes the devDesc for HandleCount == 0 due to Release in DeviceHandle. + CallOnDeviceRemoved(devDesc); + + /* + if (devDesc->HandleCount == 0) + { + // Device must be dead if it ever existed, since it AddRefs to us. + // ~DeviceCreateDesc removes its node from list. + OVR_ASSERT(!devDesc->pDevice); + delete devDesc; + } + */ + } + } + + return 0; +} + +Ptr DeviceManagerImpl::AddDevice_NeedsLock( + const DeviceCreateDesc& createDesc) +{ + // If found, mark as enumerated and we are done. + DeviceCreateDesc* descCandidate = 0; + + for(DeviceCreateDesc* devDesc = Devices.GetFirst(); + !Devices.IsNull(devDesc); devDesc = devDesc->pNext) + { + DeviceCreateDesc::MatchResult mr = devDesc->MatchDevice(createDesc, &descCandidate); + if (mr == DeviceCreateDesc::Match_Found) + { + devDesc->Enumerated = true; + if (!devDesc->pDevice) + CallOnDeviceAdded(devDesc); + return devDesc; + } + } + + // Update candidate (this may involve writing fields to HMDDevice createDesc). + if (descCandidate) + { + bool newDevice = false; + if (descCandidate->UpdateMatchedCandidate(createDesc, &newDevice)) + { + descCandidate->Enumerated = true; + if (!descCandidate->pDevice || newDevice) + CallOnDeviceAdded(descCandidate); + return descCandidate; + } + } + + // If not found, add new device. + // - This stores a new descriptor with + // {pDevice = 0, HandleCount = 1, Enumerated = true} + DeviceCreateDesc* desc = createDesc.Clone(); + desc->pLock = pCreateDesc->pLock; + Devices.PushBack(desc); + desc->Enumerated = true; + + CallOnDeviceAdded(desc); + + return desc; +} + +Ptr DeviceManagerImpl::FindDevice( + const String& path, + DeviceType deviceType) +{ + Lock::Locker deviceLock(GetLock()); + DeviceCreateDesc* devDesc; + + for (devDesc = Devices.GetFirst(); + !Devices.IsNull(devDesc); devDesc = devDesc->pNext) + { + if ((deviceType == Device_None || deviceType == devDesc->Type) && + devDesc->MatchDevice(path)) + return devDesc; + } + return NULL; +} + +Ptr DeviceManagerImpl::FindHIDDevice(const HIDDeviceDesc& hidDevDesc) +{ + Lock::Locker deviceLock(GetLock()); + DeviceCreateDesc* devDesc; + + for (devDesc = Devices.GetFirst(); + !Devices.IsNull(devDesc); devDesc = devDesc->pNext) + { + if (devDesc->MatchHIDDevice(hidDevDesc)) + return devDesc; + } + return NULL; +} + +void DeviceManagerImpl::DetectHIDDevice(const HIDDeviceDesc& hidDevDesc) +{ + Lock::Locker deviceLock(GetLock()); + DeviceFactory* factory = Factories.GetFirst(); + while(!Factories.IsNull(factory)) + { + if (factory->DetectHIDDevice(this, hidDevDesc)) + break; + factory = factory->pNext; + } + +} + +// Enumerates devices for a particular factory. +Void DeviceManagerImpl::EnumerateFactoryDevices(DeviceFactory* factory) +{ + + class FactoryEnumerateVisitor : public DeviceFactory::EnumerateVisitor + { + DeviceManagerImpl* pManager; + DeviceFactory* pFactory; + public: + FactoryEnumerateVisitor(DeviceManagerImpl* manager, DeviceFactory* factory) + : pManager(manager), pFactory(factory) { } + + virtual void Visit(const DeviceCreateDesc& createDesc) + { + pManager->AddDevice_NeedsLock(createDesc); + } + }; + + FactoryEnumerateVisitor newDeviceVisitor(this, factory); + factory->EnumerateDevices(newDeviceVisitor); + + + return 0; +} + + +DeviceEnumerator<> DeviceManagerImpl::EnumerateDevicesEx(const DeviceEnumerationArgs& args) +{ + Lock::Locker deviceLock(GetLock()); + + if (Devices.IsEmpty()) + return DeviceEnumerator<>(); + + DeviceCreateDesc* firstDeviceDesc = Devices.GetFirst(); + DeviceEnumerator<> e = enumeratorFromHandle(DeviceHandle(firstDeviceDesc), args); + + if (!args.MatchRule(firstDeviceDesc->Type, firstDeviceDesc->Enumerated)) + { + e.Next(); + } + + return e; +} + +//------------------------------------------------------------------------------------- +// ***** DeviceCommon + +void DeviceCommon::DeviceAddRef() +{ + RefCount++; +} + +void DeviceCommon::DeviceRelease() +{ + while(1) + { + UInt32 refCount = RefCount; + OVR_ASSERT(refCount > 0); + + if (refCount == 1) + { + DeviceManagerImpl* manager = pCreateDesc->GetManagerImpl(); + ThreadCommandQueue* queue = manager->GetThreadQueue(); + + // Enqueue ReleaseDevice for {1 -> 0} transition with no wait. + // We pass our reference ownership into the queue to destroy. + // It's in theory possible for another thread to re-steal our device reference, + // but that is checked for atomically in DeviceManagerImpl::ReleaseDevice. + if (!queue->PushCall(manager, &DeviceManagerImpl::ReleaseDevice_MgrThread, + pCreateDesc->pDevice)) + { + // PushCall shouldn't fail because background thread runs while manager is + // alive and we are holding Manager alive through pParent chain. + OVR_ASSERT(false); + } + + // Warning! At his point everything, including manager, may be dead. + break; + } + else if (RefCount.CompareAndSet_NoSync(refCount, refCount-1)) + { + break; + } + } +} + + + +//------------------------------------------------------------------------------------- +// ***** DeviceCreateDesc + + +void DeviceCreateDesc::AddRef() +{ + // Technically, HandleCount { 0 -> 1 } transition can only happen during Lock, + // but we leave this to caller to worry about (happens during enumeration). + HandleCount++; +} + +void DeviceCreateDesc::Release() +{ + while(1) + { + UInt32 handleCount = HandleCount; + // HandleCount must obviously be >= 1, since we are releasing it. + OVR_ASSERT(handleCount > 0); + + // {1 -> 0} transition may cause us to be destroyed, so require a lock. + if (handleCount == 1) + { + Ptr lockKeepAlive; + Lock::Locker deviceLockScope(GetLock()); + + if (!HandleCount.CompareAndSet_NoSync(handleCount, 0)) + continue; + + OVR_ASSERT(pDevice == 0); + + // Destroy *this if the manager was destroyed already, or Enumerated + // is false (device no longer available). + if (!GetManagerImpl() || !Enumerated) + { + lockKeepAlive = pLock; + + // Remove from manager list (only matters for !Enumerated). + if (pNext) + { + RemoveNode(); + pNext = pPrev = 0; + } + + delete this; + } + + // Available DeviceCreateDesc may survive with { HandleCount == 0 }, + // in case it might be enumerated again later. + break; + } + else if (HandleCount.CompareAndSet_NoSync(handleCount, handleCount-1)) + { + break; + } + } +} + +HMDDevice* HMDDevice::Disconnect(SensorDevice* psensor) +{ + if (!psensor) + return NULL; + + OVR::DeviceManager* manager = GetManager(); + if (manager) + { + //DeviceManagerImpl* mgrImpl = static_cast(manager); + Ptr desc = getDeviceCommon()->pCreateDesc; + if (desc) + { + class Visitor : public DeviceFactory::EnumerateVisitor + { + Ptr Desc; + public: + Visitor(DeviceCreateDesc* desc) : Desc(desc) {} + virtual void Visit(const DeviceCreateDesc& createDesc) + { + Lock::Locker lock(Desc->GetLock()); + Desc->UpdateMatchedCandidate(createDesc); + } + } visitor(desc); + //SensorDeviceImpl* sImpl = static_cast(psensor); + + SensorDisplayInfoImpl displayInfo; + + if (psensor->GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize)) + { + displayInfo.Unpack(); + + // If we got display info, try to match / create HMDDevice as well + // so that sensor settings give preference. + if (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) + { + SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo(displayInfo, visitor); + } + } + } + } + return this; +} + +bool HMDDevice::IsDisconnected() const +{ + OVR::HMDInfo info; + GetDeviceInfo(&info); + // if strlen(info.DisplayDeviceName) == 0 then + // this HMD is 'fake' (created using sensor). + return (strlen(info.DisplayDeviceName) == 0); +} + + +} // namespace OVR + diff --git a/LibOVR/Src/OVR_DeviceImpl.h b/LibOVR/Src/OVR_DeviceImpl.h new file mode 100644 index 0000000..8b7bf0a --- /dev/null +++ b/LibOVR/Src/OVR_DeviceImpl.h @@ -0,0 +1,425 @@ +/************************************************************************************ + +Filename : OVR_DeviceImpl.h +Content : Partial back-end independent implementation of Device interfaces +Created : October 10, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_DeviceImpl_h +#define OVR_DeviceImpl_h + +#include "OVR_Device.h" +#include "Kernel/OVR_Atomic.h" +#include "Kernel/OVR_Log.h" +#include "Kernel/OVR_System.h" + +#include "Kernel/OVR_Threads.h" +#include "OVR_ThreadCommandQueue.h" +#include "OVR_HIDDevice.h" + +namespace OVR { + +class DeviceManagerImpl; +class DeviceFactory; + +enum +{ + Oculus_VendorId = 0x2833 +}; + +//------------------------------------------------------------------------------------- +// Globally shared Lock implementation used for MessageHandlers. + +class SharedLock +{ +public: + + Lock* GetLockAddRef(); + void ReleaseLock(Lock* plock); + +private: + Lock* toLock() { return (Lock*)Buffer; } + + // UseCount and max alignment. + volatile int UseCount; + UInt64 Buffer[(sizeof(Lock)+sizeof(UInt64)-1)/sizeof(UInt64)]; +}; + + +// Wrapper for MessageHandler that includes synchronization logic. +// References to MessageHandlers are organized in a list to allow for them to +// easily removed with MessageHandler::RemoveAllHandlers. +class MessageHandlerRef : public ListNode +{ +public: + MessageHandlerRef(DeviceBase* device); + ~MessageHandlerRef(); + + void SetHandler(MessageHandler* hander); + + // Not-thread-safe version + void SetHandler_NTS(MessageHandler* hander); + + void Call(const Message& msg) + { + Lock::Locker lockScope(pLock); + if (pHandler) + pHandler->OnMessage(msg); + } + + Lock* GetLock() const { return pLock; } + + // GetHandler() is not thread safe if used out of order across threads; nothing can be done + // about that. + MessageHandler* GetHandler() const { return pHandler; } + DeviceBase* GetDevice() const { return pDevice; } + +private: + Lock* pLock; // Cached global handler lock. + DeviceBase* pDevice; + MessageHandler* pHandler; +}; + + + +//------------------------------------------------------------------------------------- + +// DeviceManagerLock is a synchronization lock used by DeviceManager for Devices +// and is allocated separately for potentially longer lifetime. +// +// DeviceManagerLock is used for all of the following: +// - Adding/removing devices +// - Reporting manager lifetime (pManager != 0) for DeviceHandles +// - Protecting device creation/shutdown. + +class DeviceManagerLock : public RefCountBase +{ +public: + Lock CreateLock; + DeviceManagerImpl* pManager; + + DeviceManagerLock() : pManager(0) { } +}; + + +// DeviceCreateDesc provides all of the information needed to create any device, a derived +// instance of this class is created by DeviceFactory during enumeration. +// - DeviceCreateDesc may or may not be a part of DeviceManager::Devices list (check pNext != 0). +// - Referenced and kept alive by DeviceHandle. + +class DeviceCreateDesc : public ListNode, public NewOverrideBase +{ + void operator = (const DeviceCreateDesc&) { } // Assign not supported; suppress MSVC warning. +public: + DeviceCreateDesc(DeviceFactory* factory, DeviceType type) + : pFactory(factory), Type(type), pLock(0), HandleCount(0), pDevice(0), Enumerated(true) + { + pNext = pPrev = 0; + } + + virtual ~DeviceCreateDesc() + { + OVR_ASSERT(!pDevice); + if (pNext) + RemoveNode(); + } + + DeviceManagerImpl* GetManagerImpl() { return pLock->pManager; } + Lock* GetLock() const { return &pLock->CreateLock; } + + // DeviceCreateDesc reference counting is tied to Devices list management, + // see comments for HandleCount. + void AddRef(); + void Release(); + + + // *** Device creation/matching Interface + + + // Cloning copies us to an allocated object when new device is enumerated. + virtual DeviceCreateDesc* Clone() const = 0; + // Creates a new device instance without Initializing it; the + // later is done my Initialize()/Shutdown() methods of the device itself. + virtual DeviceBase* NewDeviceInstance() = 0; + // Override to return device-specific info. + virtual bool GetDeviceInfo(DeviceInfo* info) const = 0; + + + enum MatchResult + { + Match_None, + Match_Found, + Match_Candidate + }; + + // Override to return Match_Found if descriptor matches our device. + // Match_Candidate can be returned, with pcandicate update, if this may be a match + // but more searching is necessary. If this is the case UpdateMatchedCandidate will be called. + virtual MatchResult MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc** pcandidate) const = 0; + + // Called for matched candidate after all potential matches are iterated. + // Used to update HMDevice creation arguments from Sensor. + // Optional return param 'newDeviceFlag' will be set to true if the + // 'desc' refers to a new device; false, otherwise. + // Return 'false' to create new object, 'true' if done with this argument. + virtual bool UpdateMatchedCandidate( + const DeviceCreateDesc& desc, bool* newDeviceFlag = NULL) + { OVR_UNUSED2(desc, newDeviceFlag); return false; } + + // Matches HID device to the descriptor. + virtual bool MatchHIDDevice(const HIDDeviceDesc&) const { return false; } + + // Matches device by path. + virtual bool MatchDevice(const String& /*path*/) { return false; } +//protected: + DeviceFactory* const pFactory; + const DeviceType Type; + + // List in which this descriptor lives. pList->CreateLock required if added/removed. + Ptr pLock; + + // Strong references to us: Incremented by Device, DeviceHandles & Enumerators. + // May be 0 if device not created and there are no handles. + // Following transitions require pList->CreateLock: + // {1 -> 0}: May delete & remove handle if no longer available. + // {0 -> 1}: Device creation is only possible if manager is still alive. + AtomicInt HandleCount; + // If not null, points to our created device instance. Modified during lock only. + DeviceBase* pDevice; + // True if device is marked as available during enumeration. + bool Enumerated; +}; + + + +// Common data present in the implementation of every DeviceBase. +// Injected by DeviceImpl. +class DeviceCommon +{ +public: + AtomicInt RefCount; + Ptr pCreateDesc; + Ptr pParent; + MessageHandlerRef HandlerRef; + + DeviceCommon(DeviceCreateDesc* createDesc, DeviceBase* device, DeviceBase* parent) + : RefCount(1), pCreateDesc(createDesc), pParent(parent), HandlerRef(device) + { + } + + // Device reference counting delegates to Manager thread to actually kill devices. + void DeviceAddRef(); + void DeviceRelease(); + + Lock* GetLock() const { return pCreateDesc->GetLock(); } + + virtual bool Initialize(DeviceBase* parent) = 0; + virtual void Shutdown() = 0; +}; + + +//------------------------------------------------------------------------------------- +// DeviceImpl address DeviceRecord implementation to a device base class B. +// B must be derived form DeviceBase. + +template +class DeviceImpl : public B, public DeviceCommon +{ +public: + DeviceImpl(DeviceCreateDesc* createDesc, DeviceBase* parent) + : DeviceCommon(createDesc, getThis(), parent) + { + } + + // Convenience method to avoid manager access typecasts. + DeviceManagerImpl* GetManagerImpl() const { return pCreateDesc->pLock->pManager; } + + // Inline to avoid warnings. + DeviceImpl* getThis() { return this; } + + // Common implementation delegate to avoid virtual inheritance and dynamic casts. + virtual DeviceCommon* getDeviceCommon() const { return (DeviceCommon*)this; } + + /* + virtual void AddRef() { pCreateDesc->DeviceAddRef(); } + virtual void Release() { pCreateDesc->DeviceRelease(); } + virtual DeviceBase* GetParent() const { return pParent.GetPtr(); } + virtual DeviceManager* GetManager() const { return pCreateDesc->pLock->pManager;} + virtual void SetMessageHandler(MessageHandler* handler) { HanderRef.SetHandler(handler); } + virtual MessageHandler* GetMessageHandler() const { return HanderRef.GetHandler(); } + virtual DeviceType GetType() const { return pCreateDesc->Type; } + virtual DeviceType GetType() const { return pCreateDesc->Type; } + */ +}; + + +//------------------------------------------------------------------------------------- +// ***** DeviceFactory + +// DeviceFactory is maintained in DeviceManager for each separately-enumerable +// device type; factories allow separation of unrelated enumeration code. + +class DeviceFactory : public ListNode, public NewOverrideBase +{ +public: + + DeviceFactory() : pManager(0) + { + pNext = pPrev = 0; + } + virtual ~DeviceFactory() { } + + DeviceManagerImpl* GetManagerImpl() { return pManager; } + + // Notifiers called when we are added to/removed from a device. + virtual bool AddedToManager(DeviceManagerImpl* manager) + { + OVR_ASSERT(pManager == 0); + pManager = manager; + return true; + } + + virtual void RemovedFromManager() + { + pManager = 0; + } + + + // *** Device Enumeration/Creation Support + + // Passed to EnumerateDevices to be informed of every device detected. + class EnumerateVisitor + { + public: + virtual void Visit(const DeviceCreateDesc& createDesc) = 0; + }; + + // Enumerates factory devices by notifying EnumerateVisitor about every + // device that is present. + virtual void EnumerateDevices(EnumerateVisitor& visitor) = 0; + + // Matches vendorId/productId pair with the factory; returns 'true' + // if the factory can handle the device. + virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const + { + OVR_UNUSED2(vendorId, productId); + return false; + } + + // Detects the HID device and adds the DeviceCreateDesc into Devices list, if + // the device belongs to this factory. Returns 'false', if not. + virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc) + { + OVR_UNUSED2(pdevMgr, desc); + return false; + } + +protected: + DeviceManagerImpl* pManager; +}; + + +//------------------------------------------------------------------------------------- +// ***** DeviceManagerImpl + +// DeviceManagerImpl is a partial default DeviceManager implementation that +// maintains a list of devices and supports their enumeration. + +class DeviceManagerImpl : public DeviceImpl, public ThreadCommandQueue +{ +public: + DeviceManagerImpl(); + ~DeviceManagerImpl(); + + // Constructor helper function to create Descriptor and manager lock during initialization. + static DeviceCreateDesc* CreateManagerDesc(); + + // DeviceManagerImpl provides partial implementation of Initialize/Shutdown that must + // be called by the platform-specific derived class. + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + // Override to return ThreadCommandQueue implementation used to post commands + // to the background device manager thread (that must be created by Initialize). + virtual ThreadCommandQueue* GetThreadQueue() = 0; + + // Returns the thread id of the DeviceManager. + virtual ThreadId GetThreadId() const = 0; + + virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args); + + + // + void AddFactory(DeviceFactory* factory) + { + // This lock is only needed if we call AddFactory after manager thread creation. + Lock::Locker scopeLock(GetLock()); + Factories.PushBack(factory); + factory->AddedToManager(this); + } + + void CallOnDeviceAdded(DeviceCreateDesc* desc) + { + HandlerRef.Call(MessageDeviceStatus(Message_DeviceAdded, this, DeviceHandle(desc))); + } + void CallOnDeviceRemoved(DeviceCreateDesc* desc) + { + HandlerRef.Call(MessageDeviceStatus(Message_DeviceRemoved, this, DeviceHandle(desc))); + } + + // Helper to access Common data for a device. + static DeviceCommon* GetDeviceCommon(DeviceBase* device) + { + return device->getDeviceCommon(); + } + + + // Background-thread callbacks for DeviceCreation/Release. These + DeviceBase* CreateDevice_MgrThread(DeviceCreateDesc* createDesc, DeviceBase* parent = 0); + Void ReleaseDevice_MgrThread(DeviceBase* device); + + + // Calls EnumerateDevices() on all factories + virtual Void EnumerateAllFactoryDevices(); + // Enumerates devices for a particular factory. + virtual Void EnumerateFactoryDevices(DeviceFactory* factory); + + virtual HIDDeviceManager* GetHIDDeviceManager() const + { + return HidDeviceManager; + } + + // Adds device (DeviceCreateDesc*) into Devices. Returns NULL, + // if unsuccessful or device is already in the list. + virtual Ptr AddDevice_NeedsLock(const DeviceCreateDesc& createDesc); + + // Finds a device descriptor by path and optional type. + Ptr FindDevice(const String& path, DeviceType = Device_None); + + // Finds HID device by HIDDeviceDesc. + Ptr FindHIDDevice(const HIDDeviceDesc&); + void DetectHIDDevice(const HIDDeviceDesc&); + + // Manager Lock-protected list of devices. + List Devices; + + // Factories used to detect and manage devices. + List Factories; + +protected: + Ptr HidDeviceManager; +}; + + +} // namespace OVR + +#endif diff --git a/LibOVR/Src/OVR_DeviceMessages.h b/LibOVR/Src/OVR_DeviceMessages.h new file mode 100644 index 0000000..97eb907 --- /dev/null +++ b/LibOVR/Src/OVR_DeviceMessages.h @@ -0,0 +1,162 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_DeviceMessages.h +Content : Definition of messages generated by devices +Created : February 5, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_DeviceMessages_h +#define OVR_DeviceMessages_h + +#include "OVR_DeviceConstants.h" +#include "OVR_DeviceHandle.h" + +#include "Kernel/OVR_Math.h" +#include "Kernel/OVR_Array.h" +#include "Kernel/OVR_Color.h" + +namespace OVR { + +class DeviceBase; +class DeviceHandle; + + +#define OVR_MESSAGETYPE(devName, msgIndex) ((Device_##devName << 8) | msgIndex) + +// MessageType identifies the structure of the Message class; based on the message, +// casting can be used to obtain the exact value. +enum MessageType +{ + // Used for unassigned message types. + Message_None = 0, + + // Device Manager Messages + Message_DeviceAdded = OVR_MESSAGETYPE(Manager, 0), // A new device is detected by manager. + Message_DeviceRemoved = OVR_MESSAGETYPE(Manager, 1), // Existing device has been plugged/unplugged. + // Sensor Messages + Message_BodyFrame = OVR_MESSAGETYPE(Sensor, 0), // Emitted by sensor at regular intervals. + // Latency Tester Messages + Message_LatencyTestSamples = OVR_MESSAGETYPE(LatencyTester, 0), + Message_LatencyTestColorDetected = OVR_MESSAGETYPE(LatencyTester, 1), + Message_LatencyTestStarted = OVR_MESSAGETYPE(LatencyTester, 2), + Message_LatencyTestButton = OVR_MESSAGETYPE(LatencyTester, 3), + +}; + +//------------------------------------------------------------------------------------- +// Base class for all messages. +class Message +{ +public: + Message(MessageType type = Message_None, + DeviceBase* pdev = 0) : Type(type), pDevice(pdev) + { } + + MessageType Type; // What kind of message this is. + DeviceBase* pDevice; // Device that emitted the message. +}; + + +// Sensor BodyFrame notification. +// Sensor uses Right-Handed coordinate system to return results, with the following +// axis definitions: +// - Y Up positive +// - X Right Positive +// - Z Back Positive +// Rotations a counter-clockwise (CCW) while looking in the negative direction +// of the axis. This means they are interpreted as follows: +// - Roll is rotation around Z, counter-clockwise (tilting left) in XY plane. +// - Yaw is rotation around Y, positive for turning left. +// - Pitch is rotation around X, positive for pitching up. + +class MessageBodyFrame : public Message +{ +public: + MessageBodyFrame(DeviceBase* dev) + : Message(Message_BodyFrame, dev), Temperature(0.0f), TimeDelta(0.0f) + { + } + + Vector3f Acceleration; // Acceleration in m/s^2. + Vector3f RotationRate; // Angular velocity in rad/s^2. + Vector3f MagneticField; // Magnetic field strength in Gauss. + float Temperature; // Temperature reading on sensor surface, in degrees Celsius. + float TimeDelta; // Time passed since last Body Frame, in seconds. +}; + +// Sent when we receive a device status changes (e.g.: +// Message_DeviceAdded, Message_DeviceRemoved). +class MessageDeviceStatus : public Message +{ +public: + MessageDeviceStatus(MessageType type, DeviceBase* dev, const DeviceHandle &hdev) + : Message(type, dev), Handle(hdev) { } + + DeviceHandle Handle; +}; + +//------------------------------------------------------------------------------------- +// ***** Latency Tester + +// Sent when we receive Latency Tester samples. +class MessageLatencyTestSamples : public Message +{ +public: + MessageLatencyTestSamples(DeviceBase* dev) + : Message(Message_LatencyTestSamples, dev) + { + } + + Array Samples; +}; + +// Sent when a Latency Tester 'color detected' event occurs. +class MessageLatencyTestColorDetected : public Message +{ +public: + MessageLatencyTestColorDetected(DeviceBase* dev) + : Message(Message_LatencyTestColorDetected, dev) + { + } + + UInt16 Elapsed; + Color DetectedValue; + Color TargetValue; +}; + +// Sent when a Latency Tester 'change color' event occurs. +class MessageLatencyTestStarted : public Message +{ +public: + MessageLatencyTestStarted(DeviceBase* dev) + : Message(Message_LatencyTestStarted, dev) + { + } + + Color TargetValue; +}; + +// Sent when a Latency Tester 'button' event occurs. +class MessageLatencyTestButton : public Message +{ +public: + MessageLatencyTestButton(DeviceBase* dev) + : Message(Message_LatencyTestButton, dev) + { + } + +}; + + +} // namespace OVR + +#endif diff --git a/LibOVR/Src/OVR_HIDDevice.h b/LibOVR/Src/OVR_HIDDevice.h new file mode 100644 index 0000000..b7ef089 --- /dev/null +++ b/LibOVR/Src/OVR_HIDDevice.h @@ -0,0 +1,143 @@ +/************************************************************************************ + +Filename : OVR_HIDDevice.h +Content : Cross platform HID device interface. +Created : February 22, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_HIDDevice_h +#define OVR_HIDDevice_h + +#include "OVR_HIDDeviceBase.h" + +#include "Kernel/OVR_RefCount.h" +#include "Kernel/OVR_String.h" +#include "Kernel/OVR_Timer.h" + +namespace OVR { + +class HIDDevice; +class DeviceManager; + +// HIDDeviceDesc contains interesting attributes of a HID device, including a Path +// that can be used to create it. +struct HIDDeviceDesc +{ + UInt16 VendorId; + UInt16 ProductId; + UInt16 VersionNumber; + UInt16 Usage; + UInt16 UsagePage; + String Path; // Platform specific. + String Manufacturer; + String Product; + String SerialNumber; +}; + +// HIDEnumerateVisitor exposes a Visit interface called for every detected device +// by HIDDeviceManager::Enumerate. +class HIDEnumerateVisitor +{ +public: + + // Should return true if we are interested in supporting + // this HID VendorId and ProductId pair. + virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) + { OVR_UNUSED2(vendorId, productId); return true; } + + // Override to get notified about available device. Will only be called for + // devices that matched MatchVendorProduct. + virtual void Visit(HIDDevice&, const HIDDeviceDesc&) { } +}; + + +//------------------------------------------------------------------------------------- +// ***** HIDDeviceManager + +// Internal manager for enumerating and opening HID devices. +// If an OVR::DeviceManager is created then an OVR::HIDDeviceManager will automatically be created and can be accessed from the +// DeviceManager by calling 'GetHIDDeviceManager()'. When using HIDDeviceManager in standalone mode, the client must call +// 'Create' below. +class HIDDeviceManager : public RefCountBase +{ +public: + + // Creates a new HIDDeviceManager. Only one instance of HIDDeviceManager should be created at a time. + static HIDDeviceManager* Create(); + + // Enumerate HID devices using a HIDEnumerateVisitor derived visitor class. + virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor) = 0; + + // Open a HID device with the specified path. + virtual HIDDevice* Open(const String& path) = 0; + +protected: + HIDDeviceManager() + { } +}; + +//------------------------------------------------------------------------------------- +// ***** HIDDevice + +// HID device object. This is designed to be operated in synchronous +// and asynchronous modes. With no handler set, input messages will be +// stored and can be retrieved by calling 'Read' or 'ReadBlocking'. +class HIDDevice : public RefCountBase, public HIDDeviceBase +{ +public: + + HIDDevice() + : Handler(NULL) + { + } + + virtual ~HIDDevice() {} + + virtual bool SetFeatureReport(UByte* data, UInt32 length) = 0; + virtual bool GetFeatureReport(UByte* data, UInt32 length) = 0; + +// Not yet implemented. +/* + virtual bool Write(UByte* data, UInt32 length) = 0; + + virtual bool Read(UByte* pData, UInt32 length, UInt32 timeoutMilliS) = 0; + virtual bool ReadBlocking(UByte* pData, UInt32 length) = 0; +*/ + + class HIDHandler + { + public: + virtual void OnInputReport(UByte* pData, UInt32 length) + { OVR_UNUSED2(pData, length); } + + virtual UInt64 OnTicks(UInt64 ticksMks) + { OVR_UNUSED1(ticksMks); return Timer::MksPerSecond * 1000; ; } + + enum HIDDeviceMessageType + { + HIDDeviceMessage_DeviceAdded = 0, + HIDDeviceMessage_DeviceRemoved = 1 + }; + + virtual void OnDeviceMessage(HIDDeviceMessageType messageType) + { OVR_UNUSED1(messageType); } + }; + + void SetHandler(HIDHandler* handler) + { Handler = handler; } + +protected: + HIDHandler* Handler; +}; + +} // namespace OVR + +#endif diff --git a/LibOVR/Src/OVR_HIDDeviceBase.h b/LibOVR/Src/OVR_HIDDeviceBase.h new file mode 100644 index 0000000..42fdadc --- /dev/null +++ b/LibOVR/Src/OVR_HIDDeviceBase.h @@ -0,0 +1,40 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_HIDDeviceBase.h +Content : Definition of HID device interface. +Created : March 11, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_HIDDeviceBase_h +#define OVR_HIDDeviceBase_h + +#include "Kernel/OVR_Types.h" + +namespace OVR { + +//------------------------------------------------------------------------------------- +// ***** HIDDeviceBase + +// Base interface for HID devices. +class HIDDeviceBase +{ +public: + + virtual ~HIDDeviceBase() { } + + virtual bool SetFeatureReport(UByte* data, UInt32 length) = 0; + virtual bool GetFeatureReport(UByte* data, UInt32 length) = 0; +}; + +} // namespace OVR + +#endif diff --git a/LibOVR/Src/OVR_HIDDeviceImpl.h b/LibOVR/Src/OVR_HIDDeviceImpl.h new file mode 100644 index 0000000..c5163b4 --- /dev/null +++ b/LibOVR/Src/OVR_HIDDeviceImpl.h @@ -0,0 +1,203 @@ +/************************************************************************************ + +Filename : OVR_HIDDeviceImpl.h +Content : Implementation of HIDDevice. +Created : March 7, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_HIDDeviceImpl_h +#define OVR_HIDDeviceImpl_h + +//#include "OVR_Device.h" +#include "OVR_DeviceImpl.h" + +namespace OVR { + +//------------------------------------------------------------------------------------- +class HIDDeviceCreateDesc : public DeviceCreateDesc +{ +public: + HIDDeviceCreateDesc(DeviceFactory* factory, DeviceType type, const HIDDeviceDesc& hidDesc) + : DeviceCreateDesc(factory, type), HIDDesc(hidDesc) { } + HIDDeviceCreateDesc(const HIDDeviceCreateDesc& other) + : DeviceCreateDesc(other.pFactory, other.Type), HIDDesc(other.HIDDesc) { } + + virtual bool MatchDevice(const String& path) + { + // should it be case insensitive? + return HIDDesc.Path.CompareNoCase(path) == 0; + } + + HIDDeviceDesc HIDDesc; +}; + +//------------------------------------------------------------------------------------- +template +class HIDDeviceImpl : public DeviceImpl, public HIDDevice::HIDHandler +{ +public: + HIDDeviceImpl(HIDDeviceCreateDesc* createDesc, DeviceBase* parent) + : DeviceImpl(createDesc, parent) + { + } + + // HIDDevice::Handler interface. + virtual void OnDeviceMessage(HIDDeviceMessageType messageType) + { + MessageType handlerMessageType; + switch (messageType) { + case HIDDeviceMessage_DeviceAdded: + handlerMessageType = Message_DeviceAdded; + break; + + case HIDDeviceMessage_DeviceRemoved: + handlerMessageType = Message_DeviceRemoved; + break; + + default: OVR_ASSERT(0); return; + } + + // Do device notification. + { + Lock::Locker scopeLock(this->HandlerRef.GetLock()); + + if (this->HandlerRef.GetHandler()) + { + MessageDeviceStatus status(handlerMessageType, this, OVR::DeviceHandle(this->pCreateDesc)); + this->HandlerRef.GetHandler()->OnMessage(status); + } + } + + // Do device manager notification. + DeviceManagerImpl* manager = this->GetManagerImpl(); + switch (handlerMessageType) { + case Message_DeviceAdded: + manager->CallOnDeviceAdded(this->pCreateDesc); + break; + + case Message_DeviceRemoved: + manager->CallOnDeviceRemoved(this->pCreateDesc); + break; + + default:; + } + } + + virtual bool Initialize(DeviceBase* parent) + { + // Open HID device. + HIDDeviceDesc& hidDesc = *getHIDDesc(); + HIDDeviceManager* pManager = GetHIDDeviceManager(); + + + HIDDevice* device = pManager->Open(hidDesc.Path); + if (!device) + { + return false; + } + + InternalDevice = *device; + InternalDevice->SetHandler(this); + + // AddRef() to parent, forcing chain to stay alive. + DeviceImpl::pParent = parent; + + return true; + } + + virtual void Shutdown() + { + InternalDevice->SetHandler(NULL); + + // Remove the handler, if any. + this->HandlerRef.SetHandler(0); + + DeviceImpl::pParent.Clear(); + } + + DeviceManager* GetDeviceManager() + { + return DeviceImpl::pCreateDesc->GetManagerImpl(); + } + + HIDDeviceManager* GetHIDDeviceManager() + { + return DeviceImpl::pCreateDesc->GetManagerImpl()->GetHIDDeviceManager(); + } + + + struct WriteData + { + enum { BufferSize = 64 }; + UByte Buffer[64]; + UPInt Size; + + WriteData(UByte* data, UPInt size) : Size(size) + { + OVR_ASSERT(size <= BufferSize); + memcpy(Buffer, data, size); + } + }; + + bool SetFeatureReport(UByte* data, UInt32 length) + { + WriteData writeData(data, length); + + // Push call with wait. + bool result = false; + + ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue(); + if (!pQueue->PushCallAndWaitResult(this, &HIDDeviceImpl::setFeatureReport, &result, writeData)) + return false; + + return result; + } + + bool setFeatureReport(const WriteData& data) + { + return InternalDevice->SetFeatureReport((UByte*) data.Buffer, (UInt32) data.Size); + } + + bool GetFeatureReport(UByte* data, UInt32 length) + { + bool result = false; + + ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue(); + if (!pQueue->PushCallAndWaitResult(this, &HIDDeviceImpl::getFeatureReport, &result, data, length)) + return false; + + return result; + } + + bool getFeatureReport(UByte* data, UInt32 length) + { + return InternalDevice->GetFeatureReport(data, length); + } + +protected: + HIDDevice* GetInternalDevice() const + { + return InternalDevice; + } + + HIDDeviceDesc* getHIDDesc() const + { return &getCreateDesc()->HIDDesc; } + + HIDDeviceCreateDesc* getCreateDesc() const + { return (HIDDeviceCreateDesc*) &(*DeviceImpl::pCreateDesc); } + +private: + Ptr InternalDevice; +}; + +} // namespace OVR + +#endif diff --git a/LibOVR/Src/OVR_LatencyTestImpl.cpp b/LibOVR/Src/OVR_LatencyTestImpl.cpp new file mode 100644 index 0000000..ff420a2 --- /dev/null +++ b/LibOVR/Src/OVR_LatencyTestImpl.cpp @@ -0,0 +1,798 @@ +/************************************************************************************ + +Filename : OVR_LatencyTestImpl.cpp +Content : Oculus Latency Tester device implementation. +Created : March 7, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_LatencyTestImpl.h" + +namespace OVR { + +//------------------------------------------------------------------------------------- +// ***** Oculus Latency Tester specific packet data structures + +enum { + LatencyTester_VendorId = Oculus_VendorId, + LatencyTester_ProductId = 0x0101, +}; + +// Reported data is little-endian now +static UInt16 DecodeUInt16(const UByte* buffer) +{ + return (UInt16(buffer[1]) << 8) | UInt16(buffer[0]); +} + +/* Unreferenced +static SInt16 DecodeSInt16(const UByte* buffer) +{ + return (SInt16(buffer[1]) << 8) | SInt16(buffer[0]); +}*/ + +static void UnpackSamples(const UByte* buffer, UByte* r, UByte* g, UByte* b) +{ + *r = buffer[0]; + *g = buffer[1]; + *b = buffer[2]; +} + +// Messages we handle. +enum LatencyTestMessageType +{ + LatencyTestMessage_None = 0, + LatencyTestMessage_Samples = 1, + LatencyTestMessage_ColorDetected = 2, + LatencyTestMessage_TestStarted = 3, + LatencyTestMessage_Button = 4, + LatencyTestMessage_Unknown = 0x100, + LatencyTestMessage_SizeError = 0x101, +}; + +struct LatencyTestSample +{ + UByte Value[3]; +}; + +struct LatencyTestSamples +{ + UByte SampleCount; + UInt16 Timestamp; + + LatencyTestSample Samples[20]; + + LatencyTestMessageType Decode(const UByte* buffer, int size) + { + if (size < 64) + { + return LatencyTestMessage_SizeError; + } + + SampleCount = buffer[1]; + Timestamp = DecodeUInt16(buffer + 2); + + for (UByte i = 0; i < SampleCount; i++) + { + UnpackSamples(buffer + 4 + (3 * i), &Samples[i].Value[0], &Samples[i].Value[1], &Samples[i].Value[2]); + } + + return LatencyTestMessage_Samples; + } +}; + +struct LatencyTestSamplesMessage +{ + LatencyTestMessageType Type; + LatencyTestSamples Samples; +}; + +bool DecodeLatencyTestSamplesMessage(LatencyTestSamplesMessage* message, UByte* buffer, int size) +{ + memset(message, 0, sizeof(LatencyTestSamplesMessage)); + + if (size < 64) + { + message->Type = LatencyTestMessage_SizeError; + return false; + } + + switch (buffer[0]) + { + case LatencyTestMessage_Samples: + message->Type = message->Samples.Decode(buffer, size); + break; + + default: + message->Type = LatencyTestMessage_Unknown; + break; + } + + return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None); +} + +struct LatencyTestColorDetected +{ + UInt16 CommandID; + UInt16 Timestamp; + UInt16 Elapsed; + UByte TriggerValue[3]; + UByte TargetValue[3]; + + LatencyTestMessageType Decode(const UByte* buffer, int size) + { + if (size < 13) + return LatencyTestMessage_SizeError; + + CommandID = DecodeUInt16(buffer + 1); + Timestamp = DecodeUInt16(buffer + 3); + Elapsed = DecodeUInt16(buffer + 5); + memcpy(TriggerValue, buffer + 7, 3); + memcpy(TargetValue, buffer + 10, 3); + + return LatencyTestMessage_ColorDetected; + } +}; + +struct LatencyTestColorDetectedMessage +{ + LatencyTestMessageType Type; + LatencyTestColorDetected ColorDetected; +}; + +bool DecodeLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message, UByte* buffer, int size) +{ + memset(message, 0, sizeof(LatencyTestColorDetectedMessage)); + + if (size < 13) + { + message->Type = LatencyTestMessage_SizeError; + return false; + } + + switch (buffer[0]) + { + case LatencyTestMessage_ColorDetected: + message->Type = message->ColorDetected.Decode(buffer, size); + break; + + default: + message->Type = LatencyTestMessage_Unknown; + break; + } + + return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None); +} + +struct LatencyTestStarted +{ + UInt16 CommandID; + UInt16 Timestamp; + UByte TargetValue[3]; + + LatencyTestMessageType Decode(const UByte* buffer, int size) + { + if (size < 8) + return LatencyTestMessage_SizeError; + + CommandID = DecodeUInt16(buffer + 1); + Timestamp = DecodeUInt16(buffer + 3); + memcpy(TargetValue, buffer + 5, 3); + + return LatencyTestMessage_TestStarted; + } +}; + +struct LatencyTestStartedMessage +{ + LatencyTestMessageType Type; + LatencyTestStarted TestStarted; +}; + +bool DecodeLatencyTestStartedMessage(LatencyTestStartedMessage* message, UByte* buffer, int size) +{ + memset(message, 0, sizeof(LatencyTestStartedMessage)); + + if (size < 8) + { + message->Type = LatencyTestMessage_SizeError; + return false; + } + + switch (buffer[0]) + { + case LatencyTestMessage_TestStarted: + message->Type = message->TestStarted.Decode(buffer, size); + break; + + default: + message->Type = LatencyTestMessage_Unknown; + break; + } + + return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None); +} + +struct LatencyTestButton +{ + UInt16 CommandID; + UInt16 Timestamp; + + LatencyTestMessageType Decode(const UByte* buffer, int size) + { + if (size < 5) + return LatencyTestMessage_SizeError; + + CommandID = DecodeUInt16(buffer + 1); + Timestamp = DecodeUInt16(buffer + 3); + + return LatencyTestMessage_Button; + } +}; + +struct LatencyTestButtonMessage +{ + LatencyTestMessageType Type; + LatencyTestButton Button; +}; + +bool DecodeLatencyTestButtonMessage(LatencyTestButtonMessage* message, UByte* buffer, int size) +{ + memset(message, 0, sizeof(LatencyTestButtonMessage)); + + if (size < 5) + { + message->Type = LatencyTestMessage_SizeError; + return false; + } + + switch (buffer[0]) + { + case LatencyTestMessage_Button: + message->Type = message->Button.Decode(buffer, size); + break; + + default: + message->Type = LatencyTestMessage_Unknown; + break; + } + + return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None); +} + +struct LatencyTestConfigurationImpl +{ + enum { PacketSize = 5 }; + UByte Buffer[PacketSize]; + + OVR::LatencyTestConfiguration Configuration; + + LatencyTestConfigurationImpl(const OVR::LatencyTestConfiguration& configuration) + : Configuration(configuration) + { + Pack(); + } + + void Pack() + { + Buffer[0] = 5; + Buffer[1] = UByte(Configuration.SendSamples); + Buffer[2] = Configuration.Threshold.R; + Buffer[3] = Configuration.Threshold.G; + Buffer[4] = Configuration.Threshold.B; + } + + void Unpack() + { + Configuration.SendSamples = Buffer[1] != 0 ? true : false; + Configuration.Threshold.R = Buffer[2]; + Configuration.Threshold.G = Buffer[3]; + Configuration.Threshold.B = Buffer[4]; + } +}; + +struct LatencyTestCalibrateImpl +{ + enum { PacketSize = 4 }; + UByte Buffer[PacketSize]; + + OVR::LatencyTestCalibrate Calibrate; + + LatencyTestCalibrateImpl(const OVR::LatencyTestCalibrate& calibrate) + : Calibrate(calibrate) + { + Pack(); + } + + void Pack() + { + Buffer[0] = 7; + Buffer[1] = Calibrate.Value.R; + Buffer[2] = Calibrate.Value.G; + Buffer[3] = Calibrate.Value.B; + } + + void Unpack() + { + Calibrate.Value.R = Buffer[1]; + Calibrate.Value.G = Buffer[2]; + Calibrate.Value.B = Buffer[3]; + } +}; + +struct LatencyTestStartTestImpl +{ + enum { PacketSize = 6 }; + UByte Buffer[PacketSize]; + + OVR::LatencyTestStartTest StartTest; + + LatencyTestStartTestImpl(const OVR::LatencyTestStartTest& startTest) + : StartTest(startTest) + { + Pack(); + } + + void Pack() + { + UInt16 commandID = 1; + + Buffer[0] = 8; + Buffer[1] = UByte(commandID & 0xFF); + Buffer[2] = UByte(commandID >> 8); + Buffer[3] = StartTest.TargetValue.R; + Buffer[4] = StartTest.TargetValue.G; + Buffer[5] = StartTest.TargetValue.B; + } + + void Unpack() + { +// UInt16 commandID = Buffer[1] | (UInt16(Buffer[2]) << 8); + StartTest.TargetValue.R = Buffer[3]; + StartTest.TargetValue.G = Buffer[4]; + StartTest.TargetValue.B = Buffer[5]; + } +}; + +struct LatencyTestDisplayImpl +{ + enum { PacketSize = 6 }; + UByte Buffer[PacketSize]; + + OVR::LatencyTestDisplay Display; + + LatencyTestDisplayImpl(const OVR::LatencyTestDisplay& display) + : Display(display) + { + Pack(); + } + + void Pack() + { + Buffer[0] = 9; + Buffer[1] = Display.Mode; + Buffer[2] = UByte(Display.Value & 0xFF); + Buffer[3] = UByte((Display.Value >> 8) & 0xFF); + Buffer[4] = UByte((Display.Value >> 16) & 0xFF); + Buffer[5] = UByte((Display.Value >> 24) & 0xFF); + } + + void Unpack() + { + Display.Mode = Buffer[1]; + Display.Value = UInt32(Buffer[2]) | + (UInt32(Buffer[3]) << 8) | + (UInt32(Buffer[4]) << 16) | + (UInt32(Buffer[5]) << 24); + } +}; + +//------------------------------------------------------------------------------------- +// ***** LatencyTestDeviceFactory + +LatencyTestDeviceFactory LatencyTestDeviceFactory::Instance; + +void LatencyTestDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor) +{ + + class LatencyTestEnumerator : public HIDEnumerateVisitor + { + // Assign not supported; suppress MSVC warning. + void operator = (const LatencyTestEnumerator&) { } + + DeviceFactory* pFactory; + EnumerateVisitor& ExternalVisitor; + public: + LatencyTestEnumerator(DeviceFactory* factory, EnumerateVisitor& externalVisitor) + : pFactory(factory), ExternalVisitor(externalVisitor) { } + + virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) + { + return pFactory->MatchVendorProduct(vendorId, productId); + } + + virtual void Visit(HIDDevice& device, const HIDDeviceDesc& desc) + { + OVR_UNUSED(device); + + LatencyTestDeviceCreateDesc createDesc(pFactory, desc); + ExternalVisitor.Visit(createDesc); + } + }; + + LatencyTestEnumerator latencyTestEnumerator(this, visitor); + GetManagerImpl()->GetHIDDeviceManager()->Enumerate(&latencyTestEnumerator); +} + +bool LatencyTestDeviceFactory::MatchVendorProduct(UInt16 vendorId, UInt16 productId) const +{ + return ((vendorId == LatencyTester_VendorId) && (productId == LatencyTester_ProductId)); +} + +bool LatencyTestDeviceFactory::DetectHIDDevice(DeviceManager* pdevMgr, + const HIDDeviceDesc& desc) +{ + if (MatchVendorProduct(desc.VendorId, desc.ProductId)) + { + LatencyTestDeviceCreateDesc createDesc(this, desc); + return pdevMgr->AddDevice_NeedsLock(createDesc).GetPtr() != NULL; + } + return false; +} + +//------------------------------------------------------------------------------------- +// ***** LatencyTestDeviceCreateDesc + +DeviceBase* LatencyTestDeviceCreateDesc::NewDeviceInstance() +{ + return new LatencyTestDeviceImpl(this); +} + +bool LatencyTestDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const +{ + if ((info->InfoClassType != Device_LatencyTester) && + (info->InfoClassType != Device_None)) + return false; + + OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, HIDDesc.Product.ToCStr()); + OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, HIDDesc.Manufacturer.ToCStr()); + info->Type = Device_LatencyTester; + info->Version = 0; + + if (info->InfoClassType == Device_LatencyTester) + { + SensorInfo* sinfo = (SensorInfo*)info; + sinfo->VendorId = HIDDesc.VendorId; + sinfo->ProductId = HIDDesc.ProductId; + OVR_strcpy(sinfo->SerialNumber, sizeof(sinfo->SerialNumber),HIDDesc.SerialNumber.ToCStr()); + } + return true; +} + +//------------------------------------------------------------------------------------- +// ***** LatencyTestDevice + +LatencyTestDeviceImpl::LatencyTestDeviceImpl(LatencyTestDeviceCreateDesc* createDesc) + : OVR::HIDDeviceImpl(createDesc, 0) +{ +} + +LatencyTestDeviceImpl::~LatencyTestDeviceImpl() +{ + // Check that Shutdown() was called. + OVR_ASSERT(!pCreateDesc->pDevice); +} + +// Internal creation APIs. +bool LatencyTestDeviceImpl::Initialize(DeviceBase* parent) +{ + if (HIDDeviceImpl::Initialize(parent)) + { + LogText("OVR::LatencyTestDevice initialized.\n"); + return true; + } + + return false; +} + +void LatencyTestDeviceImpl::Shutdown() +{ + HIDDeviceImpl::Shutdown(); + + LogText("OVR::LatencyTestDevice - Closed '%s'\n", getHIDDesc()->Path.ToCStr()); +} + +void LatencyTestDeviceImpl::OnInputReport(UByte* pData, UInt32 length) +{ + + bool processed = false; + if (!processed) + { + LatencyTestSamplesMessage message; + if (DecodeLatencyTestSamplesMessage(&message, pData, length)) + { + processed = true; + onLatencyTestSamplesMessage(&message); + } + } + + if (!processed) + { + LatencyTestColorDetectedMessage message; + if (DecodeLatencyTestColorDetectedMessage(&message, pData, length)) + { + processed = true; + onLatencyTestColorDetectedMessage(&message); + } + } + + if (!processed) + { + LatencyTestStartedMessage message; + if (DecodeLatencyTestStartedMessage(&message, pData, length)) + { + processed = true; + onLatencyTestStartedMessage(&message); + } + } + + if (!processed) + { + LatencyTestButtonMessage message; + if (DecodeLatencyTestButtonMessage(&message, pData, length)) + { + processed = true; + onLatencyTestButtonMessage(&message); + } + } +} + +bool LatencyTestDeviceImpl::SetConfiguration(const OVR::LatencyTestConfiguration& configuration, bool waitFlag) +{ + bool result = false; + ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue(); + + if (GetManagerImpl()->GetThreadId() != OVR::GetCurrentThreadId()) + { + if (!waitFlag) + { + return queue->PushCall(this, &LatencyTestDeviceImpl::setConfiguration, configuration); + } + + if (!queue->PushCallAndWaitResult( this, + &LatencyTestDeviceImpl::setConfiguration, + &result, + configuration)) + { + return false; + } + } + else + return setConfiguration(configuration); + + return result; +} + +bool LatencyTestDeviceImpl::setConfiguration(const OVR::LatencyTestConfiguration& configuration) +{ + LatencyTestConfigurationImpl ltc(configuration); + return GetInternalDevice()->SetFeatureReport(ltc.Buffer, LatencyTestConfigurationImpl::PacketSize); +} + +bool LatencyTestDeviceImpl::GetConfiguration(OVR::LatencyTestConfiguration* configuration) +{ + bool result = false; + + ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue(); + if (!pQueue->PushCallAndWaitResult(this, &LatencyTestDeviceImpl::getConfiguration, &result, configuration)) + return false; + + return result; +} + +bool LatencyTestDeviceImpl::getConfiguration(OVR::LatencyTestConfiguration* configuration) +{ + LatencyTestConfigurationImpl ltc(*configuration); + if (GetInternalDevice()->GetFeatureReport(ltc.Buffer, LatencyTestConfigurationImpl::PacketSize)) + { + ltc.Unpack(); + *configuration = ltc.Configuration; + return true; + } + + return false; +} + +bool LatencyTestDeviceImpl::SetCalibrate(const OVR::LatencyTestCalibrate& calibrate, bool waitFlag) +{ + bool result = false; + ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue(); + + if (!waitFlag) + { + return queue->PushCall(this, &LatencyTestDeviceImpl::setCalibrate, calibrate); + } + + if (!queue->PushCallAndWaitResult( this, + &LatencyTestDeviceImpl::setCalibrate, + &result, + calibrate)) + { + return false; + } + + return result; +} + +bool LatencyTestDeviceImpl::setCalibrate(const OVR::LatencyTestCalibrate& calibrate) +{ + LatencyTestCalibrateImpl ltc(calibrate); + return GetInternalDevice()->SetFeatureReport(ltc.Buffer, LatencyTestCalibrateImpl::PacketSize); +} + +bool LatencyTestDeviceImpl::GetCalibrate(OVR::LatencyTestCalibrate* calibrate) +{ + bool result = false; + + ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue(); + if (!pQueue->PushCallAndWaitResult(this, &LatencyTestDeviceImpl::getCalibrate, &result, calibrate)) + return false; + + return result; +} + +bool LatencyTestDeviceImpl::getCalibrate(OVR::LatencyTestCalibrate* calibrate) +{ + LatencyTestCalibrateImpl ltc(*calibrate); + if (GetInternalDevice()->GetFeatureReport(ltc.Buffer, LatencyTestCalibrateImpl::PacketSize)) + { + ltc.Unpack(); + *calibrate = ltc.Calibrate; + return true; + } + + return false; +} + +bool LatencyTestDeviceImpl::SetStartTest(const OVR::LatencyTestStartTest& start, bool waitFlag) +{ + bool result = false; + ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue(); + + if (!waitFlag) + { + return queue->PushCall(this, &LatencyTestDeviceImpl::setStartTest, start); + } + + if (!queue->PushCallAndWaitResult( this, + &LatencyTestDeviceImpl::setStartTest, + &result, + start)) + { + return false; + } + + return result; +} + +bool LatencyTestDeviceImpl::setStartTest(const OVR::LatencyTestStartTest& start) +{ + LatencyTestStartTestImpl ltst(start); + return GetInternalDevice()->SetFeatureReport(ltst.Buffer, LatencyTestStartTestImpl::PacketSize); +} + +bool LatencyTestDeviceImpl::SetDisplay(const OVR::LatencyTestDisplay& display, bool waitFlag) +{ + bool result = false; + ThreadCommandQueue * queue = GetManagerImpl()->GetThreadQueue(); + + if (!waitFlag) + { + return queue->PushCall(this, &LatencyTestDeviceImpl::setDisplay, display); + } + + if (!queue->PushCallAndWaitResult( this, + &LatencyTestDeviceImpl::setDisplay, + &result, + display)) + { + return false; + } + + return result; +} + +bool LatencyTestDeviceImpl::setDisplay(const OVR::LatencyTestDisplay& display) +{ + LatencyTestDisplayImpl ltd(display); + return GetInternalDevice()->SetFeatureReport(ltd.Buffer, LatencyTestDisplayImpl::PacketSize); +} + +void LatencyTestDeviceImpl::onLatencyTestSamplesMessage(LatencyTestSamplesMessage* message) +{ + + if (message->Type != LatencyTestMessage_Samples) + return; + + LatencyTestSamples& s = message->Samples; + + // Call OnMessage() within a lock to avoid conflicts with handlers. + Lock::Locker scopeLock(HandlerRef.GetLock()); + + if (HandlerRef.GetHandler()) + { + MessageLatencyTestSamples samples(this); + for (UByte i = 0; i < s.SampleCount; i++) + { + samples.Samples.PushBack(Color(s.Samples[i].Value[0], s.Samples[i].Value[1], s.Samples[i].Value[2])); + } + + HandlerRef.GetHandler()->OnMessage(samples); + } +} + +void LatencyTestDeviceImpl::onLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message) +{ + if (message->Type != LatencyTestMessage_ColorDetected) + return; + + LatencyTestColorDetected& s = message->ColorDetected; + + // Call OnMessage() within a lock to avoid conflicts with handlers. + Lock::Locker scopeLock(HandlerRef.GetLock()); + + if (HandlerRef.GetHandler()) + { + MessageLatencyTestColorDetected detected(this); + detected.Elapsed = s.Elapsed; + detected.DetectedValue = Color(s.TriggerValue[0], s.TriggerValue[1], s.TriggerValue[2]); + detected.TargetValue = Color(s.TargetValue[0], s.TargetValue[1], s.TargetValue[2]); + + HandlerRef.GetHandler()->OnMessage(detected); + } +} + +void LatencyTestDeviceImpl::onLatencyTestStartedMessage(LatencyTestStartedMessage* message) +{ + if (message->Type != LatencyTestMessage_TestStarted) + return; + + LatencyTestStarted& ts = message->TestStarted; + + // Call OnMessage() within a lock to avoid conflicts with handlers. + Lock::Locker scopeLock(HandlerRef.GetLock()); + + if (HandlerRef.GetHandler()) + { + MessageLatencyTestStarted started(this); + started.TargetValue = Color(ts.TargetValue[0], ts.TargetValue[1], ts.TargetValue[2]); + + HandlerRef.GetHandler()->OnMessage(started); + } +} + +void LatencyTestDeviceImpl::onLatencyTestButtonMessage(LatencyTestButtonMessage* message) +{ + if (message->Type != LatencyTestMessage_Button) + return; + +// LatencyTestButton& s = message->Button; + + // Call OnMessage() within a lock to avoid conflicts with handlers. + Lock::Locker scopeLock(HandlerRef.GetLock()); + + if (HandlerRef.GetHandler()) + { + MessageLatencyTestButton button(this); + + HandlerRef.GetHandler()->OnMessage(button); + } +} + +} // namespace OVR diff --git a/LibOVR/Src/OVR_LatencyTestImpl.h b/LibOVR/Src/OVR_LatencyTestImpl.h new file mode 100644 index 0000000..9217f57 --- /dev/null +++ b/LibOVR/Src/OVR_LatencyTestImpl.h @@ -0,0 +1,135 @@ +/************************************************************************************ + +Filename : OVR_LatencyTestImpl.h +Content : Latency Tester specific implementation. +Created : March 7, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_LatencyTestImpl_h +#define OVR_LatencyTestImpl_h + +#include "OVR_HIDDeviceImpl.h" + +namespace OVR { + +struct LatencyTestSamplesMessage; +struct LatencyTestButtonMessage; +struct LatencyTestStartedMessage; +struct LatencyTestColorDetectedMessage; + +//------------------------------------------------------------------------------------- +// LatencyTestDeviceFactory enumerates Oculus Latency Tester devices. +class LatencyTestDeviceFactory : public DeviceFactory +{ +public: + static LatencyTestDeviceFactory Instance; + + // Enumerates devices, creating and destroying relevant objects in manager. + virtual void EnumerateDevices(EnumerateVisitor& visitor); + + virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const; + virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc); + +protected: + DeviceManager* getManager() const { return (DeviceManager*) pManager; } +}; + + +// Describes a single a Oculus Latency Tester device and supports creating its instance. +class LatencyTestDeviceCreateDesc : public HIDDeviceCreateDesc +{ +public: + LatencyTestDeviceCreateDesc(DeviceFactory* factory, const HIDDeviceDesc& hidDesc) + : HIDDeviceCreateDesc(factory, Device_LatencyTester, hidDesc) { } + + virtual DeviceCreateDesc* Clone() const + { + return new LatencyTestDeviceCreateDesc(*this); + } + + virtual DeviceBase* NewDeviceInstance(); + + virtual MatchResult MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc**) const + { + if ((other.Type == Device_LatencyTester) && (pFactory == other.pFactory)) + { + const LatencyTestDeviceCreateDesc& s2 = (const LatencyTestDeviceCreateDesc&) other; + if (MatchHIDDevice(s2.HIDDesc)) + return Match_Found; + } + return Match_None; + } + + virtual bool MatchHIDDevice(const HIDDeviceDesc& hidDesc) const + { + // should paths comparison be case insensitive? + return ((HIDDesc.Path.CompareNoCase(hidDesc.Path) == 0) && + (HIDDesc.SerialNumber == hidDesc.SerialNumber)); + } + virtual bool GetDeviceInfo(DeviceInfo* info) const; +}; + + +//------------------------------------------------------------------------------------- +// ***** OVR::LatencyTestDeviceImpl + +// Oculus Latency Tester interface. + +class LatencyTestDeviceImpl : public HIDDeviceImpl +{ +public: + LatencyTestDeviceImpl(LatencyTestDeviceCreateDesc* createDesc); + ~LatencyTestDeviceImpl(); + + // DeviceCommon interface. + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + // DeviceManagerThread::Notifier interface. + virtual void OnInputReport(UByte* pData, UInt32 length); + + // LatencyTesterDevice interface + virtual bool SetConfiguration(const OVR::LatencyTestConfiguration& configuration, bool waitFlag = false); + virtual bool GetConfiguration(OVR::LatencyTestConfiguration* configuration); + + virtual bool SetCalibrate(const OVR::LatencyTestCalibrate& calibrate, bool waitFlag = false); + virtual bool GetCalibrate(OVR::LatencyTestCalibrate* calibrate); + + virtual bool SetStartTest(const OVR::LatencyTestStartTest& start, bool waitFlag = false); + virtual bool SetDisplay(const LatencyTestDisplay& display, bool waitFlag = false); + +protected: + bool openDevice(const char** errorFormatString); + void closeDevice(); + void closeDeviceOnIOError(); + + bool initializeRead(); + bool processReadResult(); + + bool setConfiguration(const OVR::LatencyTestConfiguration& configuration); + bool getConfiguration(OVR::LatencyTestConfiguration* configuration); + bool setCalibrate(const OVR::LatencyTestCalibrate& calibrate); + bool getCalibrate(OVR::LatencyTestCalibrate* calibrate); + bool setStartTest(const OVR::LatencyTestStartTest& start); + bool setDisplay(const OVR::LatencyTestDisplay& display); + + // Called for decoded messages + void onLatencyTestSamplesMessage(LatencyTestSamplesMessage* message); + void onLatencyTestButtonMessage(LatencyTestButtonMessage* message); + void onLatencyTestStartedMessage(LatencyTestStartedMessage* message); + void onLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message); + +}; + +} // namespace OVR + +#endif // OVR_LatencyTestImpl_h diff --git a/LibOVR/Src/OVR_OSX_DeviceManager.cpp b/LibOVR/Src/OVR_OSX_DeviceManager.cpp new file mode 100644 index 0000000..b2403ed --- /dev/null +++ b/LibOVR/Src/OVR_OSX_DeviceManager.cpp @@ -0,0 +1,349 @@ +/************************************************************************************ + +Filename : OVR_OSX_DeviceManager.cpp +Content : OSX specific DeviceManager implementation. +Created : March 14, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_OSX_DeviceManager.h" + +// Sensor & HMD Factories +#include "OVR_LatencyTestImpl.h" +#include "OVR_SensorImpl.h" +#include "OVR_OSX_HMDDevice.h" +#include "OVR_OSX_HIDDevice.h" + +#include "Kernel/OVR_Timer.h" +#include "Kernel/OVR_Std.h" +#include "Kernel/OVR_Log.h" + +#include +#include + + +namespace OVR { namespace OSX { + +//------------------------------------------------------------------------------------- +// **** OSX::DeviceManager + +DeviceManager::DeviceManager() +{ +} + +DeviceManager::~DeviceManager() +{ + OVR_DEBUG_LOG(("OSX::DeviceManager::~DeviceManager was called")); +} + +bool DeviceManager::Initialize(DeviceBase*) +{ + if (!DeviceManagerImpl::Initialize(0)) + return false; + + // Start the background thread. + pThread = *new DeviceManagerThread(); + if (!pThread || !pThread->Start()) + return false; + + // Wait for the thread to be fully up and running. + pThread->StartupEvent.Wait(); + + // Do this now that we know the thread's run loop. + HidDeviceManager = *HIDDeviceManager::CreateInternal(this); + + CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallBack, this); + + pCreateDesc->pDevice = this; + LogText("OVR::DeviceManager - initialized.\n"); + + return true; +} + +void DeviceManager::Shutdown() +{ + LogText("OVR::DeviceManager - shutting down.\n"); + + CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallBack, this); + + // Set Manager shutdown marker variable; this prevents + // any existing DeviceHandle objects from accessing device. + pCreateDesc->pLock->pManager = 0; + + // Push for thread shutdown *WITH NO WAIT*. + // This will have the following effect: + // - Exit command will get enqueued, which will be executed later on the thread itself. + // - Beyond this point, this DeviceManager object may be deleted by our caller. + // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will + // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued + // after pManager is null. + // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last + // reference to the thread object. + pThread->Shutdown(); + pThread.Clear(); + + DeviceManagerImpl::Shutdown(); +} + +ThreadCommandQueue* DeviceManager::GetThreadQueue() +{ + return pThread; +} + +ThreadId DeviceManager::GetThreadId() const +{ + return pThread->GetThreadId(); +} + +bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const +{ + if ((info->InfoClassType != Device_Manager) && + (info->InfoClassType != Device_None)) + return false; + + info->Type = Device_Manager; + info->Version = 0; + OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, "DeviceManager"); + OVR_strcpy(info->Manufacturer,DeviceInfo::MaxNameLength, "Oculus VR, Inc."); + return true; +} + +DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args) +{ + // TBD: Can this be avoided in the future, once proper device notification is in place? + pThread->PushCall((DeviceManagerImpl*)this, + &DeviceManager::EnumerateAllFactoryDevices, true); + + return DeviceManagerImpl::EnumerateDevicesEx(args); +} + +void DeviceManager::displayReconfigurationCallBack (CGDirectDisplayID display, + CGDisplayChangeSummaryFlags flags, + void *userInfo) +{ + DeviceManager* manager = reinterpret_cast(userInfo); + OVR_UNUSED(manager); + + if (flags & kCGDisplayAddFlag) + { + LogText("Display Added, id = %d\n", int(display)); + manager->EnumerateDevices(); + } + else if (flags & kCGDisplayRemoveFlag) + { + LogText("Display Removed, id = %d\n", int(display)); + manager->EnumerateDevices(); + } +} + +//------------------------------------------------------------------------------------- +// ***** DeviceManager Thread + +DeviceManagerThread::DeviceManagerThread() + : Thread(ThreadStackSize) +{ +} + +DeviceManagerThread::~DeviceManagerThread() +{ +} + +int DeviceManagerThread::Run() +{ + + SetThreadName("OVR::DeviceManagerThread"); + LogText("OVR::DeviceManagerThread - running (ThreadId=0x%p).\n", GetThreadId()); + + // Store out the run loop ref. + RunLoop = CFRunLoopGetCurrent(); + + // Create a 'source' to enable us to signal the run loop to process the command queue. + CFRunLoopSourceContext sourceContext; + memset(&sourceContext, 0, sizeof(sourceContext)); + sourceContext.version = 0; + sourceContext.info = this; + sourceContext.perform = &staticCommandQueueSourceCallback; + + CommandQueueSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0 , &sourceContext); + + CFRunLoopAddSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode); + + + // Signal to the parent thread that initialization has finished. + StartupEvent.SetEvent(); + + + ThreadCommand::PopBuffer command; + + while(!IsExiting()) + { + // PopCommand will reset event on empty queue. + if (PopCommand(&command)) + { + command.Execute(); + } + else + { + SInt32 exitReason = 0; + do { + + UInt32 waitMs = INT_MAX; + + // If devices have time-dependent logic registered, get the longest wait + // allowed based on current ticks. + if (!TicksNotifiers.IsEmpty()) + { + UInt64 ticksMks = Timer::GetTicks(); + UInt32 waitAllowed; + + for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++) + { + waitAllowed = (UInt32)(TicksNotifiers[j]->OnTicks(ticksMks) / Timer::MksPerMs); + if (waitAllowed < waitMs) + waitMs = waitAllowed; + } + } + + // Enter blocking run loop. We may continue until we timeout in which + // case it's time to service the ticks. Or if commands arrive in the command + // queue then the source callback will call 'CFRunLoopStop' causing this + // to return. + CFTimeInterval blockInterval = 0.001 * (double) waitMs; + exitReason = CFRunLoopRunInMode(kCFRunLoopDefaultMode, blockInterval, false); + + if (exitReason == kCFRunLoopRunFinished) + { + // Maybe this will occur during shutdown? + break; + } + else if (exitReason == kCFRunLoopRunStopped ) + { + // Commands need processing or we're being shutdown. + break; + } + else if (exitReason == kCFRunLoopRunTimedOut) + { + // Timed out so that we can service our ticks callbacks. + continue; + } + else if (exitReason == kCFRunLoopRunHandledSource) + { + // Should never occur since the last param when we call + // 'CFRunLoopRunInMode' is false. + OVR_ASSERT(false); + break; + } + else + { + OVR_ASSERT_LOG(false, ("CFRunLoopRunInMode returned unexpected code")); + break; + } + } + while(true); + } + } + + + CFRunLoopRemoveSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode); + CFRelease(CommandQueueSource); + + LogText("OVR::DeviceManagerThread - exiting (ThreadId=0x%p).\n", GetThreadId()); + + return 0; +} + +void DeviceManagerThread::staticCommandQueueSourceCallback(void* pContext) +{ + DeviceManagerThread* pThread = (DeviceManagerThread*) pContext; + pThread->commandQueueSourceCallback(); +} + +void DeviceManagerThread::commandQueueSourceCallback() +{ + CFRunLoopStop(RunLoop); +} + +bool DeviceManagerThread::AddTicksNotifier(Notifier* notify) +{ + TicksNotifiers.PushBack(notify); + return true; +} + +bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify) +{ + for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++) + { + if (TicksNotifiers[i] == notify) + { + TicksNotifiers.RemoveAt(i); + return true; + } + } + return false; +} + +void DeviceManagerThread::Shutdown() +{ + // Push for thread shutdown *WITH NO WAIT*. + // This will have the following effect: + // - Exit command will get enqueued, which will be executed later on the thread itself. + // - Beyond this point, this DeviceManager object may be deleted by our caller. + // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will + // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued + // after pManager is null. + // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last + // reference to the thread object. + PushExitCommand(false); + + // make sure CFRunLoopRunInMode is woken up + CFRunLoopSourceSignal(CommandQueueSource); + CFRunLoopWakeUp(RunLoop); +} + +} // namespace OSX + + +//------------------------------------------------------------------------------------- +// ***** Creation + +// Creates a new DeviceManager and initializes OVR. +DeviceManager* DeviceManager::Create() +{ + + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr manager = *new OSX::DeviceManager; + + if (manager) + { + if (manager->Initialize(0)) + { + manager->AddFactory(&LatencyTestDeviceFactory::Instance); + manager->AddFactory(&SensorDeviceFactory::Instance); + manager->AddFactory(&OSX::HMDDeviceFactory::Instance); + + manager->AddRef(); + } + else + { + manager.Clear(); + } + } + + return manager.GetPtr(); +} + +} // namespace OVR diff --git a/LibOVR/Src/OVR_OSX_DeviceManager.h b/LibOVR/Src/OVR_OSX_DeviceManager.h new file mode 100644 index 0000000..02ba661 --- /dev/null +++ b/LibOVR/Src/OVR_OSX_DeviceManager.h @@ -0,0 +1,119 @@ +/************************************************************************************ + +Filename : OVR_OSX_DeviceManager.h +Content : OSX specific DeviceManager header. +Created : March 14, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_OSX_DeviceManager_h +#define OVR_OSX_DeviceManager_h + +#include "OVR_DeviceImpl.h" + +#include "Kernel/OVR_Timer.h" + +#include +#include +#include + + +namespace OVR { namespace OSX { + +class DeviceManagerThread; + +//------------------------------------------------------------------------------------- +// ***** OSX DeviceManager + +class DeviceManager : public DeviceManagerImpl +{ +public: + DeviceManager(); + ~DeviceManager(); + + // Initialize/Shutdown manager thread. + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + virtual ThreadCommandQueue* GetThreadQueue(); + virtual ThreadId GetThreadId() const; + + virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args); + + virtual bool GetDeviceInfo(DeviceInfo* info) const; + +protected: + static void displayReconfigurationCallBack (CGDirectDisplayID display, + CGDisplayChangeSummaryFlags flags, + void *userInfo); + +public: // data + Ptr pThread; +}; + +//------------------------------------------------------------------------------------- +// ***** Device Manager Background Thread + +class DeviceManagerThread : public Thread, public ThreadCommandQueue +{ + friend class DeviceManager; + enum { ThreadStackSize = 32 * 1024 }; +public: + DeviceManagerThread(); + ~DeviceManagerThread(); + + virtual int Run(); + + // ThreadCommandQueue notifications for CommandEvent handling. + virtual void OnPushNonEmpty_Locked() + { + CFRunLoopSourceSignal(CommandQueueSource); + CFRunLoopWakeUp(RunLoop); + } + + virtual void OnPopEmpty_Locked() {} + + + // Notifier used for different updates (EVENT or regular timing or messages). + class Notifier + { + public: + + // Called when timing ticks are updated. // Returns the largest number of microseconds + // this function can wait till next call. + virtual UInt64 OnTicks(UInt64 ticksMks) + { OVR_UNUSED1(ticksMks); return Timer::MksPerSecond * 1000; } + }; + + // Add notifier that will be called at regular intervals. + bool AddTicksNotifier(Notifier* notify); + bool RemoveTicksNotifier(Notifier* notify); + + CFRunLoopRef GetRunLoop() + { return RunLoop; } + + void Shutdown(); +private: + CFRunLoopRef RunLoop; + + CFRunLoopSourceRef CommandQueueSource; + + static void staticCommandQueueSourceCallback(void* pContext); + void commandQueueSourceCallback(); + + Event StartupEvent; + + // Ticks notifiers. Used for time-dependent events such as keep-alive. + Array TicksNotifiers; +}; + +}} // namespace OSX::OVR + +#endif // OVR_OSX_DeviceManager_h diff --git a/LibOVR/Src/OVR_OSX_HIDDevice.cpp b/LibOVR/Src/OVR_OSX_HIDDevice.cpp new file mode 100644 index 0000000..40b63c9 --- /dev/null +++ b/LibOVR/Src/OVR_OSX_HIDDevice.cpp @@ -0,0 +1,899 @@ +/************************************************************************************ +Filename : OVR_OSX_HIDDevice.cpp +Content : OSX HID device implementation. +Created : February 26, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_OSX_HIDDevice.h" + +#include + +namespace OVR { namespace OSX { + +static const UInt32 MAX_QUEUED_INPUT_REPORTS = 5; + +//------------------------------------------------------------------------------------- +// **** OSX::DeviceManager + +HIDDeviceManager::HIDDeviceManager(DeviceManager* manager) + : DevManager(manager) +{ + HIDManager = NULL; +} + +HIDDeviceManager::~HIDDeviceManager() +{ +} + +CFRunLoopRef HIDDeviceManager::getRunLoop() +{ + if (DevManager != NULL) + { + return DevManager->pThread->GetRunLoop(); + } + + return CFRunLoopGetCurrent(); +} + +bool HIDDeviceManager::initializeManager() +{ + if (HIDManager != NULL) + { + return true; + } + + HIDManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + + if (!HIDManager) + { + return false; + } + + // Create a Matching Dictionary + CFMutableDictionaryRef matchDict = + CFDictionaryCreateMutable(kCFAllocatorDefault, + 2, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + // Specify a device manufacturer in the Matching Dictionary + UInt32 vendorId = Oculus_VendorId; + CFNumberRef vendorIdRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendorId); + CFDictionarySetValue(matchDict, + CFSTR(kIOHIDVendorIDKey), + vendorIdRef); + // Register the Matching Dictionary to the HID Manager + IOHIDManagerSetDeviceMatching(HIDManager, matchDict); + CFRelease(vendorIdRef); + CFRelease(matchDict); + + // Register a callback for USB device detection with the HID Manager + IOHIDManagerRegisterDeviceMatchingCallback(HIDManager, &staticDeviceMatchingCallback, this); + + IOHIDManagerScheduleWithRunLoop(HIDManager, getRunLoop(), kCFRunLoopDefaultMode); + + return true; +} + +bool HIDDeviceManager::Initialize() +{ + return initializeManager(); +} + +void HIDDeviceManager::Shutdown() +{ + OVR_ASSERT_LOG(HIDManager, ("Should have called 'Initialize' before 'Shutdown'.")); + CFRelease(HIDManager); + + LogText("OVR::OSX::HIDDeviceManager - shutting down.\n"); +} + +bool HIDDeviceManager::getIntProperty(IOHIDDeviceRef device, CFStringRef propertyName, SInt32* pResult) +{ + + CFTypeRef ref = IOHIDDeviceGetProperty(device, propertyName); + + if (!ref) + { + return false; + } + + if (CFGetTypeID(ref) != CFNumberGetTypeID()) + { + return false; + } + + CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, pResult); + + return true; +} + +bool HIDDeviceManager::initVendorProductVersion(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc) +{ + + if (!getVendorId(device, &(pDevDesc->VendorId))) + { + return false; + } + + if (!getProductId(device, &(pDevDesc->ProductId))) + { + return false; + } + + return true; +} + +bool HIDDeviceManager::initUsage(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc) +{ + + SInt32 result; + + if (!getIntProperty(device, CFSTR(kIOHIDPrimaryUsagePageKey), &result)) + { + return false; + } + + pDevDesc->UsagePage = result; + + + if (!getIntProperty(device, CFSTR(kIOHIDPrimaryUsageKey), &result)) + { + return false; + } + + pDevDesc->Usage = result; + + return true; +} + +bool HIDDeviceManager::initSerialNumber(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc) +{ + return getSerialNumberString(device, &(pDevDesc->SerialNumber)); +} + +bool HIDDeviceManager::initStrings(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc) +{ + + // Regardless of whether they fail we'll try and get the remaining. + getStringProperty(device, CFSTR(kIOHIDManufacturerKey), &(pDevDesc->Manufacturer)); + getStringProperty(device, CFSTR(kIOHIDProductKey), &(pDevDesc->Product)); + + return true; +} + +bool HIDDeviceManager::getStringProperty(IOHIDDeviceRef device, + CFStringRef propertyName, + String* pResult) +{ + + CFStringRef str = (CFStringRef) IOHIDDeviceGetProperty(device, propertyName); + + if (!str) + { + return false; + } + + CFIndex length = CFStringGetLength(str); + CFRange range = CFRangeMake(0, length); + + // Test the conversion first to get required buffer size. + CFIndex bufferLength; + CFIndex numberOfChars = CFStringGetBytes(str, + range, + kCFStringEncodingUTF8, + (char) '?', + FALSE, + NULL, + 0, + &bufferLength); + + if (numberOfChars == 0) + { + return false; + } + + // Now allocate buffer. + char* buffer = new char[bufferLength+1]; + + numberOfChars = CFStringGetBytes(str, + range, + kCFStringEncodingUTF8, + (char) '?', + FALSE, + (UInt8*) buffer, + bufferLength, + NULL); + OVR_ASSERT_LOG(numberOfChars != 0, ("CFStringGetBytes failed.")); + + buffer[bufferLength] = '\0'; + *pResult = String(buffer); + + return true; +} + +bool HIDDeviceManager::getVendorId(IOHIDDeviceRef device, UInt16* pResult) +{ + SInt32 result; + + if (!getIntProperty(device, CFSTR(kIOHIDVendorIDKey), &result)) + { + return false; + } + + *pResult = result; + + return true; +} + +bool HIDDeviceManager::getProductId(IOHIDDeviceRef device, UInt16* pResult) +{ + SInt32 result; + + if (!getIntProperty(device, CFSTR(kIOHIDProductIDKey), &result)) + { + return false; + } + + *pResult = result; + + return true; +} + +bool HIDDeviceManager::getLocationId(IOHIDDeviceRef device, SInt32* pResult) +{ + SInt32 result; + + if (!getIntProperty(device, CFSTR(kIOHIDLocationIDKey), &result)) + { + return false; + } + + *pResult = result; + + return true; +} + +bool HIDDeviceManager::getSerialNumberString(IOHIDDeviceRef device, String* pResult) +{ + + if (!getStringProperty(device, CFSTR(kIOHIDSerialNumberKey), pResult)) + { + return false; + } + + return true; +} + +bool HIDDeviceManager::getPath(IOHIDDeviceRef device, String* pPath) +{ + + String transport; + if (!getStringProperty(device, CFSTR(kIOHIDTransportKey), &transport)) + { + return false; + } + + UInt16 vendorId; + if (!getVendorId(device, &vendorId)) + { + return false; + } + + UInt16 productId; + if (!getProductId(device, &productId)) + { + return false; + } + + String serialNumber; + if (!getSerialNumberString(device, &serialNumber)) + { + return false; + } + + + StringBuffer buffer; + buffer.AppendFormat("%s:vid=%04hx:pid=%04hx:ser=%s", + transport.ToCStr(), + vendorId, + productId, + serialNumber.ToCStr()); + + *pPath = String(buffer); + + return true; +} + +bool HIDDeviceManager::Enumerate(HIDEnumerateVisitor* enumVisitor) +{ + if (!initializeManager()) + { + return false; + } + + + CFSetRef deviceSet = IOHIDManagerCopyDevices(HIDManager); + if (!deviceSet) + return false; + + CFIndex deviceCount = CFSetGetCount(deviceSet); + + // Allocate a block of memory and read the set into it. + IOHIDDeviceRef* devices = (IOHIDDeviceRef*) OVR_ALLOC(sizeof(IOHIDDeviceRef) * deviceCount); + CFSetGetValues(deviceSet, (const void **) devices); + + + // Iterate over devices. + for (CFIndex deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++) + { + IOHIDDeviceRef hidDev = devices[deviceIndex]; + + if (!hidDev) + { + continue; + } + + HIDDeviceDesc devDesc; + + if (getPath(hidDev, &(devDesc.Path)) && + initVendorProductVersion(hidDev, &devDesc) && + enumVisitor->MatchVendorProduct(devDesc.VendorId, devDesc.ProductId) && + initUsage(hidDev, &devDesc)) + { + initStrings(hidDev, &devDesc); + initSerialNumber(hidDev, &devDesc); + + // Look for the device to check if it is already opened. + Ptr existingDevice = DevManager->FindHIDDevice(devDesc); + // if device exists and it is opened then most likely the CreateHIDFile + // will fail; therefore, we just set Enumerated to 'true' and continue. + if (existingDevice && existingDevice->pDevice) + { + existingDevice->Enumerated = true; + continue; + } + + // Construct minimal device that the visitor callback can get feature reports from. + OSX::HIDDevice device(this, hidDev); + + enumVisitor->Visit(device, devDesc); + } + } + + OVR_FREE(devices); + CFRelease(deviceSet); + + return true; +} + +OVR::HIDDevice* HIDDeviceManager::Open(const String& path) +{ + + Ptr device = *new OSX::HIDDevice(this); + + if (!device->HIDInitialize(path)) + { + return NULL; + } + + device->AddRef(); + + return device; +} + +bool HIDDeviceManager::getFullDesc(IOHIDDeviceRef device, HIDDeviceDesc* desc) +{ + + if (!initVendorProductVersion(device, desc)) + { + return false; + } + + if (!initUsage(device, desc)) + { + return false; + } + + if (!initSerialNumber(device, desc)) + { + return false; + } + + initStrings(device, desc); + + return true; +} + +// New USB device specified in the matching dictionary has been added (callback function) +void HIDDeviceManager::staticDeviceMatchingCallback(void *inContext, + IOReturn inResult, + void *inSender, + IOHIDDeviceRef inIOHIDDeviceRef) +{ + HIDDeviceManager* hidMgr = static_cast(inContext); + HIDDeviceDesc hidDevDesc; + hidMgr->getPath(inIOHIDDeviceRef, &hidDevDesc.Path); + hidMgr->getFullDesc(inIOHIDDeviceRef, &hidDevDesc); + + hidMgr->DevManager->DetectHIDDevice(hidDevDesc); +} + +//------------------------------------------------------------------------------------- +// **** OSX::HIDDevice + +HIDDevice::HIDDevice(HIDDeviceManager* manager) + : HIDManager(manager), InMinimalMode(false) +{ + Device = NULL; + RepluggedNotificationPort = 0; +} + +// This is a minimal constructor used during enumeration for us to pass +// a HIDDevice to the visit function (so that it can query feature reports). +HIDDevice::HIDDevice(HIDDeviceManager* manager, IOHIDDeviceRef device) +: HIDManager(manager), Device(device), InMinimalMode(true) +{ + RepluggedNotificationPort = 0; +} + +HIDDevice::~HIDDevice() +{ + if (!InMinimalMode) + { + HIDShutdown(); + } +} + +bool HIDDevice::HIDInitialize(const String& path) +{ + + DevDesc.Path = path; + + if (!openDevice()) + { + LogText("OVR::OSX::HIDDevice - Failed to open HIDDevice: %s", path.ToCStr()); + return false; + } + + // Setup notification for when a device is unplugged and plugged back in. + if (!setupDevicePluggedInNotification()) + { + LogText("OVR::OSX::HIDDevice - Failed to setup notification for when device plugged back in."); + closeDevice(false); + return false; + } + + HIDManager->DevManager->pThread->AddTicksNotifier(this); + + + LogText("OVR::OSX::HIDDevice - Opened '%s'\n" + " Manufacturer:'%s' Product:'%s' Serial#:'%s'\n", + DevDesc.Path.ToCStr(), + DevDesc.Manufacturer.ToCStr(), DevDesc.Product.ToCStr(), + DevDesc.SerialNumber.ToCStr()); + + return true; +} + +bool HIDDevice::initInfo() +{ + // Device must have been successfully opened. + OVR_ASSERT(Device); + + + // Get report lengths. + SInt32 bufferLength; + bool getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxInputReportSizeKey), &bufferLength); + OVR_ASSERT(getResult); + InputReportBufferLength = (UInt16) bufferLength; + + getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxOutputReportSizeKey), &bufferLength); + OVR_ASSERT(getResult); + OutputReportBufferLength = (UInt16) bufferLength; + + getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxFeatureReportSizeKey), &bufferLength); + OVR_ASSERT(getResult); + FeatureReportBufferLength = (UInt16) bufferLength; + + + if (ReadBufferSize < InputReportBufferLength) + { + OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer.")); + return false; + } + + // Get device desc. + if (!HIDManager->getFullDesc(Device, &DevDesc)) + { + OVR_ASSERT_LOG(false, ("Failed to get device desc while initializing device.")); + return false; + } + + return true; +} + +void HIDDevice::staticDeviceAddedCallback(void* pContext, io_iterator_t iterator) +{ + HIDDevice* pDevice = (HIDDevice*) pContext; + pDevice->deviceAddedCallback(iterator); +} + +void HIDDevice::deviceAddedCallback(io_iterator_t iterator) +{ + + if (Device == NULL) + { + if (openDevice()) + { + LogText("OVR::OSX::HIDDevice - Reopened device : %s", DevDesc.Path.ToCStr()); + + Ptr existingHIDDev = HIDManager->DevManager->FindHIDDevice(DevDesc); + if (existingHIDDev && existingHIDDev->pDevice) + { + HIDManager->DevManager->CallOnDeviceAdded(existingHIDDev); + } + } + } + + // Reset callback. + while (IOIteratorNext(iterator)) + ; +} + +bool HIDDevice::openDevice() +{ + + // Have to iterate through devices again to generate paths. + CFSetRef deviceSet = IOHIDManagerCopyDevices(HIDManager->HIDManager); + CFIndex deviceCount = CFSetGetCount(deviceSet); + + // Allocate a block of memory and read the set into it. + IOHIDDeviceRef* devices = (IOHIDDeviceRef*) OVR_ALLOC(sizeof(IOHIDDeviceRef) * deviceCount); + CFSetGetValues(deviceSet, (const void **) devices); + + + // Iterate over devices. + IOHIDDeviceRef device = NULL; + + for (CFIndex deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++) + { + IOHIDDeviceRef tmpDevice = devices[deviceIndex]; + + if (!tmpDevice) + { + continue; + } + + String path; + if (!HIDManager->getPath(tmpDevice, &path)) + { + continue; + } + + if (path == DevDesc.Path) + { + device = tmpDevice; + break; + } + } + + + OVR_FREE(devices); + + if (!device) + { + CFRelease(deviceSet); + return false; + } + + // Attempt to open device. + if (IOHIDDeviceOpen(device, kIOHIDOptionsTypeSeizeDevice) + != kIOReturnSuccess) + { + CFRelease(deviceSet); + return false; + } + + // Retain the device before we release the set. + CFRetain(device); + CFRelease(deviceSet); + + + Device = device; + + + if (!initInfo()) + { + IOHIDDeviceClose(Device, kIOHIDOptionsTypeSeizeDevice); + CFRelease(Device); + Device = NULL; + return false; + } + + + // Setup the Run Loop and callbacks. + IOHIDDeviceScheduleWithRunLoop(Device, + HIDManager->getRunLoop(), + kCFRunLoopDefaultMode); + + IOHIDDeviceRegisterInputReportCallback(Device, + ReadBuffer, + ReadBufferSize, + staticHIDReportCallback, + this); + + IOHIDDeviceRegisterRemovalCallback(Device, + staticDeviceRemovedCallback, + this); + + return true; +} + +void HIDDevice::HIDShutdown() +{ + + HIDManager->DevManager->pThread->RemoveTicksNotifier(this); + + if (Device != NULL) // Device may already have been closed if unplugged. + { + closeDevice(false); + } + + IOObjectRelease(RepluggedNotification); + if (RepluggedNotificationPort) + IONotificationPortDestroy(RepluggedNotificationPort); + + LogText("OVR::OSX::HIDDevice - HIDShutdown '%s'\n", DevDesc.Path.ToCStr()); +} + +bool HIDDevice::setupDevicePluggedInNotification() +{ + + // Setup notification when devices are plugged in. + RepluggedNotificationPort = IONotificationPortCreate(kIOMasterPortDefault); + + CFRunLoopSourceRef notificationRunLoopSource = + IONotificationPortGetRunLoopSource(RepluggedNotificationPort); + + CFRunLoopAddSource(HIDManager->getRunLoop(), + notificationRunLoopSource, + kCFRunLoopDefaultMode); + + CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName); + + // Have to specify vendorId and productId. Doesn't seem to accept additional + // things like serial number. + SInt32 vendorId = DevDesc.VendorId; + CFNumberRef numberRef = CFNumberCreate(kCFAllocatorDefault, + kCFNumberSInt32Type, + &vendorId); + CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), numberRef); + CFRelease(numberRef); + + SInt32 deviceProductId = DevDesc.ProductId; + numberRef = CFNumberCreate(kCFAllocatorDefault, + kCFNumberSInt32Type, + &deviceProductId); + CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), numberRef); + CFRelease(numberRef); + + kern_return_t result = + IOServiceAddMatchingNotification(RepluggedNotificationPort, + kIOMatchedNotification, + matchingDict, + staticDeviceAddedCallback, + this, + &RepluggedNotification); + + if (result != KERN_SUCCESS) + { + CFRelease(RepluggedNotificationPort); + RepluggedNotificationPort = 0; + return false; + } + + // Iterate through to arm. + while (IOIteratorNext(RepluggedNotification)) + { + } + + return true; +} + +void HIDDevice::closeDevice(bool wasUnplugged) +{ + OVR_ASSERT(Device != NULL); + + if (!wasUnplugged) + { + // Clear the registered callbacks. + IOHIDDeviceRegisterInputReportCallback(Device, + ReadBuffer, + InputReportBufferLength, + NULL, + this); + + IOHIDDeviceRegisterRemovalCallback(Device, NULL, this); + + IOHIDDeviceUnscheduleFromRunLoop(Device, + HIDManager->getRunLoop(), + kCFRunLoopDefaultMode); + IOHIDDeviceClose(Device, kIOHIDOptionsTypeNone); + } + + CFRelease(Device); + Device = NULL; + + LogText("OVR::OSX::HIDDevice - HID Device Closed '%s'\n", DevDesc.Path.ToCStr()); +} + +void HIDDevice::staticHIDReportCallback(void* pContext, + IOReturn result, + void* pSender, + IOHIDReportType reportType, + uint32_t reportId, + uint8_t* pReport, + CFIndex reportLength) +{ + HIDDevice* pDevice = (HIDDevice*) pContext; + return pDevice->hidReportCallback(pReport, (UInt32)reportLength); +} + +void HIDDevice::hidReportCallback(UByte* pData, UInt32 length) +{ + + // We got data. + if (Handler) + { + Handler->OnInputReport(pData, length); + } +} + +void HIDDevice::staticDeviceRemovedCallback(void* pContext, IOReturn result, void* pSender) +{ + HIDDevice* pDevice = (HIDDevice*) pContext; + pDevice->deviceRemovedCallback(); +} + +void HIDDevice::deviceRemovedCallback() +{ + Ptr _this(this); // prevent from release + + Ptr existingHIDDev = HIDManager->DevManager->FindHIDDevice(DevDesc); + if (existingHIDDev && existingHIDDev->pDevice) + { + HIDManager->DevManager->CallOnDeviceRemoved(existingHIDDev); + } + closeDevice(true); +} + +CFStringRef HIDDevice::generateRunLoopModeString(IOHIDDeviceRef device) +{ + const UInt32 safeBuffSize = 256; + char nameBuff[safeBuffSize]; + OVR_sprintf(nameBuff, safeBuffSize, "%016lX", device); + + return CFStringCreateWithCString(NULL, nameBuff, kCFStringEncodingASCII); +} + +bool HIDDevice::SetFeatureReport(UByte* data, UInt32 length) +{ + + if (!Device) + return false; + + UByte reportID = data[0]; + + if (reportID == 0) + { + // Not using reports so remove from data packet. + data++; + length--; + } + + IOReturn result = IOHIDDeviceSetReport( Device, + kIOHIDReportTypeFeature, + reportID, + data, + length); + + return (result == kIOReturnSuccess); +} + +bool HIDDevice::GetFeatureReport(UByte* data, UInt32 length) +{ + if (!Device) + return false; + + CFIndex bufferLength = length; + + // Report id is in first byte of the buffer. + IOReturn result = IOHIDDeviceGetReport(Device, kIOHIDReportTypeFeature, data[0], data, &bufferLength); + + return (result == kIOReturnSuccess); +} + +UInt64 HIDDevice::OnTicks(UInt64 ticksMks) +{ + + if (Handler) + { + return Handler->OnTicks(ticksMks); + } + + return DeviceManagerThread::Notifier::OnTicks(ticksMks); +} + +HIDDeviceManager* HIDDeviceManager::CreateInternal(OSX::DeviceManager* devManager) +{ + + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr manager = *new OSX::HIDDeviceManager(devManager); + + if (manager) + { + if (manager->Initialize()) + { + manager->AddRef(); + } + else + { + manager.Clear(); + } + } + + return manager.GetPtr(); +} + +} // namespace OSX + +//------------------------------------------------------------------------------------- +// ***** Creation + +// Creates a new HIDDeviceManager and initializes OVR. +HIDDeviceManager* HIDDeviceManager::Create() +{ + OVR_ASSERT_LOG(false, ("Standalone mode not implemented yet.")); + + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr manager = *new OSX::HIDDeviceManager(NULL); + + if (manager) + { + if (manager->Initialize()) + { + manager->AddRef(); + } + else + { + manager.Clear(); + } + } + + return manager.GetPtr(); +} + +} // namespace OVR diff --git a/LibOVR/Src/OVR_OSX_HIDDevice.h b/LibOVR/Src/OVR_OSX_HIDDevice.h new file mode 100644 index 0000000..b9b3fc5 --- /dev/null +++ b/LibOVR/Src/OVR_OSX_HIDDevice.h @@ -0,0 +1,149 @@ +/************************************************************************************ +Filename : OVR_OSX_HIDDevice.h +Content : OSX HID device implementation. +Created : February 26, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_OSX_HIDDevice_h +#define OVR_OSX_HIDDevice_h + +#include "OVR_HIDDevice.h" + +#include "OVR_OSX_DeviceManager.h" + +#include + +namespace OVR { namespace OSX { + +class HIDDeviceManager; + +//------------------------------------------------------------------------------------- +// ***** OSX HIDDevice + +class HIDDevice : public OVR::HIDDevice, public DeviceManagerThread::Notifier +{ +private: + friend class HIDDeviceManager; + +public: + HIDDevice(HIDDeviceManager* manager); + + // This is a minimal constructor used during enumeration for us to pass + // a HIDDevice to the visit function (so that it can query feature reports). + HIDDevice(HIDDeviceManager* manager, IOHIDDeviceRef device); + + virtual ~HIDDevice(); + + bool HIDInitialize(const String& path); + void HIDShutdown(); + + virtual bool SetFeatureReport(UByte* data, UInt32 length); + virtual bool GetFeatureReport(UByte* data, UInt32 length); + + bool Write(UByte* data, UInt32 length); + + bool Read(UByte* pData, UInt32 length, UInt32 timeoutMilliS); + bool ReadBlocking(UByte* pData, UInt32 length); + + + // DeviceManagerThread::Notifier + UInt64 OnTicks(UInt64 ticksMks); + +private: + bool initInfo(); + bool openDevice(); + void closeDevice(bool wasUnplugged); + bool setupDevicePluggedInNotification(); + CFStringRef generateRunLoopModeString(IOHIDDeviceRef device); + + static void staticHIDReportCallback(void* pContext, + IOReturn result, + void* pSender, + IOHIDReportType reportType, + uint32_t reportId, + uint8_t* pReport, + CFIndex reportLength); + void hidReportCallback(UByte* pData, UInt32 length); + + static void staticDeviceRemovedCallback(void* pContext, + IOReturn result, + void* pSender); + void deviceRemovedCallback(); + + static void staticDeviceAddedCallback(void* pContext, + io_iterator_t iterator); + void deviceAddedCallback(io_iterator_t iterator); + + bool InMinimalMode; + HIDDeviceManager* HIDManager; + IOHIDDeviceRef Device; + HIDDeviceDesc DevDesc; + + enum { ReadBufferSize = 96 }; + UByte ReadBuffer[ReadBufferSize]; + + UInt16 InputReportBufferLength; + UInt16 OutputReportBufferLength; + UInt16 FeatureReportBufferLength; + + IONotificationPortRef RepluggedNotificationPort; + io_iterator_t RepluggedNotification; +}; + + +//------------------------------------------------------------------------------------- +// ***** OSX HIDDeviceManager + +class HIDDeviceManager : public OVR::HIDDeviceManager +{ + friend class HIDDevice; + +public: + HIDDeviceManager(OSX::DeviceManager* Manager); + virtual ~HIDDeviceManager(); + + virtual bool Initialize(); + virtual void Shutdown(); + + virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor); + virtual OVR::HIDDevice* Open(const String& path); + + static HIDDeviceManager* CreateInternal(DeviceManager* manager); + +private: + CFRunLoopRef getRunLoop(); + bool initializeManager(); + bool initVendorProductVersion(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc); + bool initUsage(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc); + bool initStrings(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc); + bool initSerialNumber(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc); + bool getVendorId(IOHIDDeviceRef device, UInt16* pResult); + bool getProductId(IOHIDDeviceRef device, UInt16* pResult); + bool getLocationId(IOHIDDeviceRef device, SInt32* pResult); + bool getSerialNumberString(IOHIDDeviceRef device, String* pResult); + bool getPath(IOHIDDeviceRef device, String* pPath); + bool getIntProperty(IOHIDDeviceRef device, CFStringRef key, int32_t* pResult); + bool getStringProperty(IOHIDDeviceRef device, CFStringRef propertyName, String* pResult); + bool getFullDesc(IOHIDDeviceRef device, HIDDeviceDesc* desc); + + static void staticDeviceMatchingCallback(void *inContext, + IOReturn inResult, + void *inSender, + IOHIDDeviceRef inIOHIDDeviceRef); + + DeviceManager* DevManager; + + IOHIDManagerRef HIDManager; +}; + +}} // namespace OVR::OSX + +#endif // OVR_OSX_HIDDevice_h diff --git a/LibOVR/Src/OVR_OSX_HMDDevice.cpp b/LibOVR/Src/OVR_OSX_HMDDevice.cpp new file mode 100644 index 0000000..df9003d --- /dev/null +++ b/LibOVR/Src/OVR_OSX_HMDDevice.cpp @@ -0,0 +1,326 @@ +/************************************************************************************ + +Filename : OVR_OSX_HMDDevice.cpp +Content : OSX Interface to HMD - detects HMD display +Created : September 21, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_OSX_HMDDevice.h" +#include +#include +#include +#include +#include + +namespace OVR { namespace OSX { + +//------------------------------------------------------------------------------------- + +HMDDeviceCreateDesc::HMDDeviceCreateDesc(DeviceFactory* factory, + UInt32 vend, UInt32 prod, const String& displayDeviceName, long dispId) + : DeviceCreateDesc(factory, Device_HMD), + DisplayDeviceName(displayDeviceName), + DesktopX(0), DesktopY(0), Contents(0), + HResolution(0), VResolution(0), HScreenSize(0), VScreenSize(0), + DisplayId(dispId) +{ + /* //?????????? + char idstring[9]; + idstring[0] = 'A'-1+((vend>>10) & 31); + idstring[1] = 'A'-1+((vend>>5) & 31); + idstring[2] = 'A'-1+((vend>>0) & 31); + snprintf(idstring+3, 5, "%04d", prod); + DeviceId = idstring;*/ + DeviceId = DisplayDeviceName; +} + +HMDDeviceCreateDesc::HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other) + : DeviceCreateDesc(other.pFactory, Device_HMD), + DeviceId(other.DeviceId), DisplayDeviceName(other.DisplayDeviceName), + DesktopX(other.DesktopX), DesktopY(other.DesktopY), Contents(other.Contents), + HResolution(other.HResolution), VResolution(other.VResolution), + HScreenSize(other.HScreenSize), VScreenSize(other.VScreenSize), + DisplayId(other.DisplayId) +{ +} + +HMDDeviceCreateDesc::MatchResult HMDDeviceCreateDesc::MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc** pcandidate) const +{ + if ((other.Type != Device_HMD) || (other.pFactory != pFactory)) + return Match_None; + + // There are several reasons we can come in here: + // a) Matching this HMD Monitor created desc to OTHER HMD Monitor desc + // - Require exact device DeviceId/DeviceName match + // b) Matching SensorDisplayInfo created desc to OTHER HMD Monitor desc + // - This DeviceId is empty; becomes candidate + // c) Matching this HMD Monitor created desc to SensorDisplayInfo desc + // - This other.DeviceId is empty; becomes candidate + + const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; + + if ((DeviceId == s2.DeviceId) && + (DisplayId == s2.DisplayId)) + { + // Non-null DeviceId may match while size is different if screen size was overwritten + // by SensorDisplayInfo in prior iteration. + if (!DeviceId.IsEmpty() || + ((HScreenSize == s2.HScreenSize) && + (VScreenSize == s2.VScreenSize)) ) + { + *pcandidate = 0; + return Match_Found; + } + } + + + // DisplayInfo takes precedence, although we try to match it first. + if ((HResolution == s2.HResolution) && + (VResolution == s2.VResolution) && + (HScreenSize == s2.HScreenSize) && + (VScreenSize == s2.VScreenSize)) + { + if (DeviceId.IsEmpty() && !s2.DeviceId.IsEmpty()) + { + *pcandidate = const_cast((const DeviceCreateDesc*)this); + return Match_Candidate; + } + + *pcandidate = 0; + return Match_Found; + } + + // SensorDisplayInfo may override resolution settings, so store as candidiate. + if (s2.DeviceId.IsEmpty() && s2.DisplayId == 0) + { + *pcandidate = const_cast((const DeviceCreateDesc*)this); + return Match_Candidate; + } + // OTHER HMD Monitor desc may initialize DeviceName/Id + else if (DeviceId.IsEmpty() && DisplayId == 0) + { + *pcandidate = const_cast((const DeviceCreateDesc*)this); + return Match_Candidate; + } + + return Match_None; +} + + +bool HMDDeviceCreateDesc::UpdateMatchedCandidate(const DeviceCreateDesc& other, bool* newDeviceFlag) +{ + // This candidate was the the "best fit" to apply sensor DisplayInfo to. + OVR_ASSERT(other.Type == Device_HMD); + + const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; + + // Force screen size on resolution from SensorDisplayInfo. + // We do this because USB detection is more reliable as compared to HDMI EDID, + // which may be corrupted by splitter reporting wrong monitor + if (s2.DeviceId.IsEmpty() && s2.DisplayId == 0) + { + // disconnected HMD: replace old descriptor by the 'fake' one. + HScreenSize = s2.HScreenSize; + VScreenSize = s2.VScreenSize; + Contents |= Contents_Screen; + + if (s2.Contents & HMDDeviceCreateDesc::Contents_Distortion) + { + memcpy(DistortionK, s2.DistortionK, sizeof(float)*4); + Contents |= Contents_Distortion; + } + DeviceId = s2.DeviceId; + DisplayId = s2.DisplayId; + DisplayDeviceName = s2.DisplayDeviceName; + if (newDeviceFlag) *newDeviceFlag = true; + } + else if (DeviceId.IsEmpty()) + { + // This branch is executed when 'fake' HMD descriptor is being replaced by + // the real one. + DeviceId = s2.DeviceId; + DisplayId = s2.DisplayId; + DisplayDeviceName = s2.DisplayDeviceName; + if (newDeviceFlag) *newDeviceFlag = true; + } + else + { + if (newDeviceFlag) *newDeviceFlag = false; + } + + return true; +} + + +//------------------------------------------------------------------------------------- + + +//------------------------------------------------------------------------------------- +// ***** HMDDeviceFactory + +HMDDeviceFactory HMDDeviceFactory::Instance; + +void HMDDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor) +{ + CGDirectDisplayID Displays[32]; + uint32_t NDisplays = 0; + CGGetOnlineDisplayList(32, Displays, &NDisplays); + + for (int i = 0; i < NDisplays; i++) + { + io_service_t port = CGDisplayIOServicePort(Displays[i]); + CFDictionaryRef DispInfo = IODisplayCreateInfoDictionary(port, kIODisplayMatchingInfo); + + uint32_t vendor = CGDisplayVendorNumber(Displays[i]); + uint32_t product = CGDisplayModelNumber(Displays[i]); + unsigned mwidth = (unsigned)CGDisplayPixelsWide(Displays[i]); + unsigned mheight = (unsigned)CGDisplayPixelsHigh(Displays[i]); + CGRect desktop = CGDisplayBounds(Displays[i]); + + if (vendor == 16082 && product == 1) + { + char idstring[9]; + idstring[0] = 'A'-1+((vendor>>10) & 31); + idstring[1] = 'A'-1+((vendor>>5) & 31); + idstring[2] = 'A'-1+((vendor>>0) & 31); + snprintf(idstring+3, 5, "%04d", product); + + HMDDeviceCreateDesc hmdCreateDesc(this, vendor, product, idstring, Displays[i]); + + if (hmdCreateDesc.Is7Inch()) + { + // Physical dimension of SLA screen. + hmdCreateDesc.SetScreenParameters(desktop.origin.x, desktop.origin.y, mwidth, mheight, 0.14976f, 0.0936f); + } + else + { + hmdCreateDesc.SetScreenParameters(desktop.origin.x, desktop.origin.y, mwidth, mheight, 0.12096f, 0.0756f); + } + OVR_DEBUG_LOG_TEXT(("DeviceManager - HMD Found %x:%x\n", vendor, product)); + + // Notify caller about detected device. This will call EnumerateAddDevice + // if the this is the first time device was detected. + visitor.Visit(hmdCreateDesc); + } + CFRelease(DispInfo); + } +} + +DeviceBase* HMDDeviceCreateDesc::NewDeviceInstance() +{ + return new HMDDevice(this); +} + +bool HMDDeviceCreateDesc::Is7Inch() const +{ + return (strstr(DeviceId.ToCStr(), "OVR0001") != 0) || (Contents & Contents_7Inch); +} + +bool HMDDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const +{ + if ((info->InfoClassType != Device_HMD) && + (info->InfoClassType != Device_None)) + return false; + + bool is7Inch = Is7Inch(); + + OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, + is7Inch ? "Oculus Rift DK1" : "Oculus Rift DK1-Prototype"); + OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, "Oculus VR"); + info->Type = Device_HMD; + info->Version = 0; + + // Display detection. + if (info->InfoClassType == Device_HMD) + { + HMDInfo* hmdInfo = static_cast(info); + + hmdInfo->DesktopX = DesktopX; + hmdInfo->DesktopY = DesktopY; + hmdInfo->HResolution = HResolution; + hmdInfo->VResolution = VResolution; + hmdInfo->HScreenSize = HScreenSize; + hmdInfo->VScreenSize = VScreenSize; + hmdInfo->VScreenCenter = VScreenSize * 0.5f; + hmdInfo->InterpupillaryDistance = 0.064f; // Default IPD; should be configurable. + hmdInfo->LensSeparationDistance = 0.0635f; + + if (Contents & Contents_Distortion) + { + memcpy(hmdInfo->DistortionK, DistortionK, sizeof(float)*4); + } + else + { + if (is7Inch) + { + // 7" screen. + hmdInfo->DistortionK[0] = 1.0f; + hmdInfo->DistortionK[1] = 0.22f; + hmdInfo->DistortionK[2] = 0.24f; + hmdInfo->EyeToScreenDistance = 0.041f; + + hmdInfo->ChromaAbCorrection[0] = 0.996f; + hmdInfo->ChromaAbCorrection[1] = -0.004f; + hmdInfo->ChromaAbCorrection[2] = 1.014f; + hmdInfo->ChromaAbCorrection[3] = 0.0f; + } + else + { + hmdInfo->DistortionK[0] = 1.0f; + hmdInfo->DistortionK[1] = 0.18f; + hmdInfo->DistortionK[2] = 0.115f; + hmdInfo->EyeToScreenDistance = 0.0387f; + } + } + + OVR_strcpy(hmdInfo->DisplayDeviceName, sizeof(hmdInfo->DisplayDeviceName), + DisplayDeviceName.ToCStr()); + hmdInfo->DisplayId = DisplayId; + } + + return true; +} + +//------------------------------------------------------------------------------------- +// ***** HMDDevice + +HMDDevice::HMDDevice(HMDDeviceCreateDesc* createDesc) + : OVR::DeviceImpl(createDesc, 0) +{ +} +HMDDevice::~HMDDevice() +{ +} + +bool HMDDevice::Initialize(DeviceBase* parent) +{ + pParent = parent; + return true; +} +void HMDDevice::Shutdown() +{ + pParent.Clear(); +} + +OVR::SensorDevice* HMDDevice::GetSensor() +{ + // Just return first sensor found since we have no way to match it yet. + OVR::SensorDevice* sensor = GetManager()->EnumerateDevices().CreateDevice(); + if (sensor) + sensor->SetCoordinateFrame(SensorDevice::Coord_HMD); + return sensor; +} + + +}} // namespace OVR::OSX + + diff --git a/LibOVR/Src/OVR_OSX_HMDDevice.h b/LibOVR/Src/OVR_OSX_HMDDevice.h new file mode 100644 index 0000000..37c34ed --- /dev/null +++ b/LibOVR/Src/OVR_OSX_HMDDevice.h @@ -0,0 +1,136 @@ +/************************************************************************************ + +Filename : OVR_OSX_HMDDevice.h +Content : OSX HMDDevice implementation +Created : September 21, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_OSX_HMDDevice_h +#define OVR_OSX_HMDDevice_h + +#include "OVR_DeviceImpl.h" +#include + +namespace OVR { namespace OSX { + +class HMDDevice; + + +//------------------------------------------------------------------------------------- + +// HMDDeviceFactory enumerates attached Oculus HMD devices. +// +// This is currently done by matching monitor device strings. + +class HMDDeviceFactory : public DeviceFactory +{ +public: + static HMDDeviceFactory Instance; + + // Enumerates devices, creating and destroying relevant objects in manager. + virtual void EnumerateDevices(EnumerateVisitor& visitor); + +protected: + DeviceManager* getManager() const { return (DeviceManager*) pManager; } +}; + + +class HMDDeviceCreateDesc : public DeviceCreateDesc +{ + friend class HMDDevice; + +protected: + enum + { + Contents_Screen = 1, + Contents_Distortion = 2, + Contents_7Inch = 4, + }; + +public: + + HMDDeviceCreateDesc(DeviceFactory* factory, + UInt32 vendor, UInt32 product, const String& displayDeviceName, long dispId); + HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other); + + virtual DeviceCreateDesc* Clone() const + { + return new HMDDeviceCreateDesc(*this); + } + + virtual DeviceBase* NewDeviceInstance(); + + virtual MatchResult MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc**) const; + + virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&, bool* newDeviceFlag = NULL); + + virtual bool GetDeviceInfo(DeviceInfo* info) const; + + void SetScreenParameters(int x, int y, unsigned hres, unsigned vres, float hsize, float vsize) + { + DesktopX = x; + DesktopY = y; + HResolution = hres; + VResolution = vres; + HScreenSize = hsize; + VScreenSize = vsize; + Contents |= Contents_Screen; + } + + void SetDistortion(const float* dks) + { + for (int i = 0; i < 4; i++) + DistortionK[i] = dks[i]; + Contents |= Contents_Distortion; + } + + void Set7Inch() { Contents |= Contents_7Inch; } + + bool Is7Inch() const; + +protected: + String DeviceId; + String DisplayDeviceName; + int DesktopX, DesktopY; + unsigned Contents; + unsigned HResolution, VResolution; + float HScreenSize, VScreenSize; + long DisplayId; + float DistortionK[4]; +}; + + +//------------------------------------------------------------------------------------- + +// HMDDevice represents an Oculus HMD device unit. An instance of this class +// is typically created from the DeviceManager. +// After HMD device is created, we its sensor data can be obtained by +// first creating a Sensor object and then wrappig it in SensorFusion. + +class HMDDevice : public DeviceImpl +{ +public: + HMDDevice(HMDDeviceCreateDesc* createDesc); + ~HMDDevice(); + + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + // Query associated sensor. + virtual OVR::SensorDevice* GetSensor(); +}; + + +}} // namespace OVR::OSX + +#endif // OVR_OSX_HMDDevice_h + diff --git a/LibOVR/Src/OVR_OSX_SensorDevice.cpp b/LibOVR/Src/OVR_OSX_SensorDevice.cpp new file mode 100644 index 0000000..da0726c --- /dev/null +++ b/LibOVR/Src/OVR_OSX_SensorDevice.cpp @@ -0,0 +1,45 @@ +/************************************************************************************ + +Filename : OVR_OSX_SensorDevice.cpp +Content : OSX SensorDevice implementation +Created : March 14, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_OSX_HMDDevice.h" +#include "OVR_SensorImpl.h" +#include "OVR_DeviceImpl.h" + +namespace OVR { namespace OSX { + +} // namespace OSX + +//------------------------------------------------------------------------------------- +void SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo( const SensorDisplayInfoImpl& displayInfo, + DeviceFactory::EnumerateVisitor& visitor) +{ + + OSX::HMDDeviceCreateDesc hmdCreateDesc(&OSX::HMDDeviceFactory::Instance, 1, 1, "", 0); + + hmdCreateDesc.SetScreenParameters( 0, 0, + displayInfo.HResolution, displayInfo.VResolution, + displayInfo.HScreenSize, displayInfo.VScreenSize); + + if ((displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) == SensorDisplayInfoImpl::Base_Distortion) + hmdCreateDesc.SetDistortion(displayInfo.DistortionK); + if (displayInfo.HScreenSize > 0.14f) + hmdCreateDesc.Set7Inch(); + + visitor.Visit(hmdCreateDesc); +} + +} // namespace OVR + + diff --git a/LibOVR/Src/OVR_SensorFilter.cpp b/LibOVR/Src/OVR_SensorFilter.cpp new file mode 100644 index 0000000..6bd71b5 --- /dev/null +++ b/LibOVR/Src/OVR_SensorFilter.cpp @@ -0,0 +1,201 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_SensorFilter.cpp +Content : Basic filtering of sensor data +Created : March 7, 2013 +Authors : Steve LaValle, Anna Yershova + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_SensorFilter.h" + +namespace OVR { + +Vector3f SensorFilter::Total() const +{ + Vector3f total = Vector3f(0.0f, 0.0f, 0.0f); + for (int i = 0; i < Size; i++) + total += Elements[i]; + return total; +} + +Vector3f SensorFilter::Mean() const +{ + Vector3f total = Vector3f(0.0f, 0.0f, 0.0f); + for (int i = 0; i < Size; i++) + total += Elements[i]; + return total / (float) Size; +} + +Vector3f SensorFilter::Median() const +{ + int half_window = (int) Size / 2; + float sortx[MaxFilterSize]; + float resultx = 0.0f; + + float sorty[MaxFilterSize]; + float resulty = 0.0f; + + float sortz[MaxFilterSize]; + float resultz = 0.0f; + + for (int i = 0; i < Size; i++) + { + sortx[i] = Elements[i].x; + sorty[i] = Elements[i].y; + sortz[i] = Elements[i].z; + } + for (int j = 0; j <= half_window; j++) + { + int minx = j; + int miny = j; + int minz = j; + for (int k = j + 1; k < Size; k++) + { + if (sortx[k] < sortx[minx]) minx = k; + if (sorty[k] < sorty[miny]) miny = k; + if (sortz[k] < sortz[minz]) minz = k; + } + const float tempx = sortx[j]; + const float tempy = sorty[j]; + const float tempz = sortz[j]; + sortx[j] = sortx[minx]; + sortx[minx] = tempx; + + sorty[j] = sorty[miny]; + sorty[miny] = tempy; + + sortz[j] = sortz[minz]; + sortz[minz] = tempz; + } + resultx = sortx[half_window]; + resulty = sorty[half_window]; + resultz = sortz[half_window]; + + return Vector3f(resultx, resulty, resultz); +} + +// Only the diagonal of the covariance matrix. +Vector3f SensorFilter::Variance() const +{ + Vector3f mean = Mean(); + Vector3f total = Vector3f(0.0f, 0.0f, 0.0f); + for (int i = 0; i < Size; i++) + { + total.x += (Elements[i].x - mean.x) * (Elements[i].x - mean.x); + total.y += (Elements[i].y - mean.y) * (Elements[i].y - mean.y); + total.z += (Elements[i].z - mean.z) * (Elements[i].z - mean.z); + } + return total / (float) Size; +} + +// Should be a 3x3 matrix returned, but OVR_math.h doesn't have one +Matrix4f SensorFilter::Covariance() const +{ + Vector3f mean = Mean(); + Matrix4f total = Matrix4f(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0); + for (int i = 0; i < Size; i++) + { + total.M[0][0] += (Elements[i].x - mean.x) * (Elements[i].x - mean.x); + total.M[1][0] += (Elements[i].y - mean.y) * (Elements[i].x - mean.x); + total.M[2][0] += (Elements[i].z - mean.z) * (Elements[i].x - mean.x); + total.M[1][1] += (Elements[i].y - mean.y) * (Elements[i].y - mean.y); + total.M[2][1] += (Elements[i].z - mean.z) * (Elements[i].y - mean.y); + total.M[2][2] += (Elements[i].z - mean.z) * (Elements[i].z - mean.z); + } + total.M[0][1] = total.M[1][0]; + total.M[0][2] = total.M[2][0]; + total.M[1][2] = total.M[2][1]; + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + total.M[i][j] *= 1.0f / Size; + return total; +} + +Vector3f SensorFilter::PearsonCoefficient() const +{ + Matrix4f cov = Covariance(); + Vector3f pearson = Vector3f(); + pearson.x = cov.M[0][1]/(sqrt(cov.M[0][0])*sqrt(cov.M[1][1])); + pearson.y = cov.M[1][2]/(sqrt(cov.M[1][1])*sqrt(cov.M[2][2])); + pearson.z = cov.M[2][0]/(sqrt(cov.M[2][2])*sqrt(cov.M[0][0])); + + return pearson; +} + + +Vector3f SensorFilter::SavitzkyGolaySmooth8() const +{ + OVR_ASSERT(Size >= 8); + return GetPrev(0)*0.41667f + + GetPrev(1)*0.33333f + + GetPrev(2)*0.25f + + GetPrev(3)*0.16667f + + GetPrev(4)*0.08333f - + GetPrev(6)*0.08333f - + GetPrev(7)*0.16667f; +} + + +Vector3f SensorFilter::SavitzkyGolayDerivative4() const +{ + OVR_ASSERT(Size >= 4); + return GetPrev(0)*0.3f + + GetPrev(1)*0.1f - + GetPrev(2)*0.1f - + GetPrev(3)*0.3f; +} + +Vector3f SensorFilter::SavitzkyGolayDerivative5() const +{ + OVR_ASSERT(Size >= 5); + return GetPrev(0)*0.2f + + GetPrev(1)*0.1f - + GetPrev(3)*0.1f - + GetPrev(4)*0.2f; +} + +Vector3f SensorFilter::SavitzkyGolayDerivative12() const +{ + OVR_ASSERT(Size >= 12); + return GetPrev(0)*0.03846f + + GetPrev(1)*0.03147f + + GetPrev(2)*0.02448f + + GetPrev(3)*0.01748f + + GetPrev(4)*0.01049f + + GetPrev(5)*0.0035f - + GetPrev(6)*0.0035f - + GetPrev(7)*0.01049f - + GetPrev(8)*0.01748f - + GetPrev(9)*0.02448f - + GetPrev(10)*0.03147f - + GetPrev(11)*0.03846f; +} + +Vector3f SensorFilter::SavitzkyGolayDerivativeN(int n) const +{ + OVR_ASSERT(Size >= n); + int m = (n-1)/2; + Vector3f result = Vector3f(); + for (int k = 1; k <= m; k++) + { + int ind1 = m - k; + int ind2 = n - m + k - 1; + result += (GetPrev(ind1) - GetPrev(ind2)) * (float) k; + } + float coef = 3.0f/(m*(m+1.0f)*(2.0f*m+1.0f)); + result = result*coef; + return result; +} + + + + +} //namespace OVR \ No newline at end of file diff --git a/LibOVR/Src/OVR_SensorFilter.h b/LibOVR/Src/OVR_SensorFilter.h new file mode 100644 index 0000000..18665b1 --- /dev/null +++ b/LibOVR/Src/OVR_SensorFilter.h @@ -0,0 +1,99 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_SensorFilter.h +Content : Basic filtering of sensor data +Created : March 7, 2013 +Authors : Steve LaValle, Anna Yershova + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_SensorFilter_h +#define OVR_SensorFilter_h + +#include "Kernel/OVR_Math.h" + + +namespace OVR { + +// This class maintains a sliding window of sensor data taken over time and implements +// various simple filters, most of which are linear functions of the data history. +class SensorFilter +{ + enum + { + MaxFilterSize = 100, + DefaultFilterSize = 20 + }; + +private: + int LastIdx; // The index of the last element that was added to the array + int Size; // The window size (number of elements) + Vector3f Elements[MaxFilterSize]; + +public: + // Create a new filter with default size + SensorFilter() + { + LastIdx = -1; + Size = DefaultFilterSize; + }; + + // Create a new filter with size i + SensorFilter(int i) + { + OVR_ASSERT(i <= MaxFilterSize); + LastIdx = -1; + Size = i; + }; + + + // Create a new element to the filter + void AddElement (const Vector3f &e) + { + if (LastIdx == Size - 1) + LastIdx = 0; + else + LastIdx++; + + Elements[LastIdx] = e; + }; + + // Get element i. 0 is the most recent, 1 is one step ago, 2 is two steps ago, ... + Vector3f GetPrev(int i) const + { + OVR_ASSERT(i >= 0); // + int idx = (LastIdx - i); + if (idx < 0) // Fix the wraparound case + idx += Size; + OVR_ASSERT(idx >= 0); // Multiple wraparounds not allowed + return Elements[idx]; + }; + + // Simple statistics + Vector3f Total() const; + Vector3f Mean() const; + Vector3f Median() const; + Vector3f Variance() const; // The diagonal of covariance matrix + Matrix4f Covariance() const; + Vector3f PearsonCoefficient() const; + + // A popular family of smoothing filters and smoothed derivatives + Vector3f SavitzkyGolaySmooth8() const; + Vector3f SavitzkyGolayDerivative4() const; + Vector3f SavitzkyGolayDerivative5() const; + Vector3f SavitzkyGolayDerivative12() const; + Vector3f SavitzkyGolayDerivativeN(int n) const; + + ~SensorFilter() {}; +}; + +} //namespace OVR + +#endif // OVR_SensorFilter_h diff --git a/LibOVR/Src/OVR_SensorFusion.cpp b/LibOVR/Src/OVR_SensorFusion.cpp new file mode 100644 index 0000000..78dd128 --- /dev/null +++ b/LibOVR/Src/OVR_SensorFusion.cpp @@ -0,0 +1,378 @@ +/************************************************************************************ + +Filename : OVR_SensorFusion.cpp +Content : Methods that determine head orientation from sensor data over time +Created : October 9, 2012 +Authors : Michael Antonov, Steve LaValle + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_SensorFusion.h" +#include "Kernel/OVR_Log.h" +#include "Kernel/OVR_System.h" + +namespace OVR { + +//------------------------------------------------------------------------------------- +// ***** Sensor Fusion + +SensorFusion::SensorFusion(SensorDevice* sensor) + : Handler(getThis()), pDelegate(0), + Gain(0.05f), YawMult(1), EnableGravity(true), Stage(0), DeltaT(0.001f), + EnablePrediction(false), PredictionDT(0.03f), + FRawMag(10), FAccW(20), FAngV(20), + TiltCondCount(0), TiltErrorAngle(0), + TiltErrorAxis(0,1,0), + MagCondCount(0), MagReady(false), MagCalibrated(false), MagReferenced(false), + MagRefQ(0, 0, 0, 1), MagRefM(0), MagRefYaw(0), YawErrorAngle(0), MagRefDistance(0.15f), + YawErrorCount(0), YawCorrectionActivated(false), YawCorrectionInProgress(false), + EnableYawCorrection(false) +{ + if (sensor) + AttachToSensor(sensor); + MagCalibrationMatrix.SetIdentity(); +} + +SensorFusion::~SensorFusion() +{ +} + + +bool SensorFusion::AttachToSensor(SensorDevice* sensor) +{ + + if (sensor != NULL) + { + MessageHandler* pCurrentHandler = sensor->GetMessageHandler(); + + if (pCurrentHandler == &Handler) + { + Reset(); + return true; + } + + if (pCurrentHandler != NULL) + { + OVR_DEBUG_LOG( + ("SensorFusion::AttachToSensor failed - sensor %p already has handler", sensor)); + return false; + } + } + + if (Handler.IsHandlerInstalled()) + { + Handler.RemoveHandlerFromDevices(); + } + + if (sensor != NULL) + { + sensor->SetMessageHandler(&Handler); + } + + Reset(); + return true; +} + + + + +void SensorFusion::handleMessage(const MessageBodyFrame& msg) +{ + if (msg.Type != Message_BodyFrame) + return; + + // Put the sensor readings into convenient local variables + Vector3f angVel = msg.RotationRate; + Vector3f rawAccel = msg.Acceleration; + Vector3f mag = msg.MagneticField; + + // Set variables accessible through the class API + DeltaT = msg.TimeDelta; + AngV = msg.RotationRate; + AngV.y *= YawMult; // Warning: If YawMult != 1, then AngV is not true angular velocity + A = rawAccel; + + // Allow external access to uncalibrated magnetometer values + RawMag = mag; + + // Apply the calibration parameters to raw mag + if (HasMagCalibration()) + { + mag.x += MagCalibrationMatrix.M[0][3]; + mag.y += MagCalibrationMatrix.M[1][3]; + mag.z += MagCalibrationMatrix.M[2][3]; + } + + // Provide external access to calibrated mag values + // (if the mag is not calibrated, then the raw value is returned) + CalMag = mag; + + float angVelLength = angVel.Length(); + float accLength = rawAccel.Length(); + + + // Acceleration in the world frame (Q is current HMD orientation) + Vector3f accWorld = Q.Rotate(rawAccel); + + // Keep track of time + Stage++; + float currentTime = Stage * DeltaT; // Assumes uniform time spacing + + // Insert current sensor data into filter history + FRawMag.AddElement(RawMag); + FAccW.AddElement(accWorld); + FAngV.AddElement(angVel); + + // Update orientation Q based on gyro outputs. This technique is + // based on direct properties of the angular velocity vector: + // Its direction is the current rotation axis, and its magnitude + // is the rotation rate (rad/sec) about that axis. Our sensor + // sampling rate is so fast that we need not worry about integral + // approximation error (not yet, anyway). + if (angVelLength > 0.0f) + { + Vector3f rotAxis = angVel / angVelLength; + float halfRotAngle = angVelLength * DeltaT * 0.5f; + float sinHRA = sin(halfRotAngle); + Quatf deltaQ(rotAxis.x*sinHRA, rotAxis.y*sinHRA, rotAxis.z*sinHRA, cos(halfRotAngle)); + + Q = Q * deltaQ; + } + + // The quaternion magnitude may slowly drift due to numerical error, + // so it is periodically normalized. + if (Stage % 5000 == 0) + Q.Normalize(); + + // Maintain the uncorrected orientation for later use by predictive filtering + QUncorrected = Q; + + // Perform tilt correction using the accelerometer data. This enables + // drift errors in pitch and roll to be corrected. Note that yaw cannot be corrected + // because the rotation axis is parallel to the gravity vector. + if (EnableGravity) + { + // Correcting for tilt error by using accelerometer data + const float gravityEpsilon = 0.4f; + const float angVelEpsilon = 0.1f; // Relatively slow rotation + const int tiltPeriod = 50; // Req'd time steps of stability + const float maxTiltError = 0.05f; + const float minTiltError = 0.01f; + + // This condition estimates whether the only measured acceleration is due to gravity + // (the Rift is not linearly accelerating). It is often wrong, but tends to average + // out well over time. + if ((fabs(accLength - 9.81f) < gravityEpsilon) && + (angVelLength < angVelEpsilon)) + TiltCondCount++; + else + TiltCondCount = 0; + + // After stable measurements have been taken over a sufficiently long period, + // estimate the amount of tilt error and calculate the tilt axis for later correction. + if (TiltCondCount >= tiltPeriod) + { // Update TiltErrorEstimate + TiltCondCount = 0; + // Use an average value to reduce noice (could alternatively use an LPF) + Vector3f accWMean = FAccW.Mean(); + // Project the acceleration vector into the XZ plane + Vector3f xzAcc = Vector3f(accWMean.x, 0.0f, accWMean.z); + // The unit normal of xzAcc will be the rotation axis for tilt correction + Vector3f tiltAxis = Vector3f(xzAcc.z, 0.0f, -xzAcc.x).Normalized(); + Vector3f yUp = Vector3f(0.0f, 1.0f, 0.0f); + // This is the amount of rotation + float tiltAngle = yUp.Angle(accWMean); + // Record values if the tilt error is intolerable + if (tiltAngle > maxTiltError) + { + TiltErrorAngle = tiltAngle; + TiltErrorAxis = tiltAxis; + } + } + + // This part performs the actual tilt correction as needed + if (TiltErrorAngle > minTiltError) + { + if ((TiltErrorAngle > 0.4f)&&(Stage < 8000)) + { // Tilt completely to correct orientation + Q = Quatf(TiltErrorAxis, -TiltErrorAngle) * Q; + TiltErrorAngle = 0.0f; + } + else + { + //LogText("Performing tilt correction - Angle: %f Axis: %f %f %f\n", + // TiltErrorAngle,TiltErrorAxis.x,TiltErrorAxis.y,TiltErrorAxis.z); + //float deltaTiltAngle = -Gain*TiltErrorAngle*0.005f; + // This uses agressive correction steps while your head is moving fast + float deltaTiltAngle = -Gain*TiltErrorAngle*0.005f*(5.0f*angVelLength+1.0f); + // Incrementally "untilt" by a small step size + Q = Quatf(TiltErrorAxis, deltaTiltAngle) * Q; + TiltErrorAngle += deltaTiltAngle; + } + } + } + + // Yaw drift correction based on magnetometer data. This corrects the part of the drift + // that the accelerometer cannot handle. + // This will only work if the magnetometer has been enabled, calibrated, and a reference + // point has been set. + const float maxAngVelLength = 3.0f; + const int magWindow = 5; + const float yawErrorMax = 0.1f; + const float yawErrorMin = 0.01f; + const int yawErrorCountLimit = 50; + const float yawRotationStep = 0.00002f; + + if (angVelLength < maxAngVelLength) + MagCondCount++; + else + MagCondCount = 0; + + YawCorrectionInProgress = false; + if (EnableYawCorrection && MagReady && (currentTime > 2.0f) && (MagCondCount >= magWindow) && + (Q.Distance(MagRefQ) < MagRefDistance)) + { + // Use rotational invariance to bring reference mag value into global frame + Vector3f grefmag = MagRefQ.Rotate(GetCalibratedMagValue(MagRefM)); + // Bring current (averaged) mag reading into global frame + Vector3f gmag = Q.Rotate(GetCalibratedMagValue(FRawMag.Mean())); + // Calculate the reference yaw in the global frame + float gryaw = atan2(grefmag.x,grefmag.z); + // Calculate the current yaw in the global frame + float gyaw = atan2(gmag.x,gmag.z); + //LogText("Yaw error estimate: %f\n",YawErrorAngle); + // The difference between reference and current yaws is the perceived error + YawErrorAngle = AngleDifference(gyaw,gryaw); + // If the perceived error is large, keep count + if ((fabs(YawErrorAngle) > yawErrorMax) && (!YawCorrectionActivated)) + YawErrorCount++; + // After enough iterations of high perceived error, start the correction process + if (YawErrorCount > yawErrorCountLimit) + YawCorrectionActivated = true; + // If the perceived error becomes small, turn off the yaw correction + if ((fabs(YawErrorAngle) < yawErrorMin) && YawCorrectionActivated) + { + YawCorrectionActivated = false; + YawErrorCount = 0; + } + // Perform the actual yaw correction, due to previously detected, large yaw error + if (YawCorrectionActivated) + { + YawCorrectionInProgress = true; + int sign = (YawErrorAngle > 0.0f) ? 1 : -1; + // Incrementally "unyaw" by a small step size + Q = Quatf(Vector3f(0.0f,1.0f,0.0f), -yawRotationStep * sign) * Q; + } + } +} + + + // This is a simple predictive filter based only on extrapolating the smoothed, current angular velocity. + // Note that both QP (the predicted future orientation) and Q (the current orientation) are both maintained. +Quatf SensorFusion::GetPredictedOrientation() +{ + Lock::Locker lockScope(Handler.GetHandlerLock()); + Quatf qP = QUncorrected; + if (EnablePrediction) { +#if 1 + Vector3f angVelF = FAngV.SavitzkyGolaySmooth8(); + float angVelFL = angVelF.Length(); + + if (angVelFL > 0.001f) + { + Vector3f rotAxisP = angVelF / angVelFL; + float halfRotAngleP = angVelFL * PredictionDT * 0.5f; + float sinaHRAP = sin(halfRotAngleP); + Quatf deltaQP(rotAxisP.x*sinaHRAP, rotAxisP.y*sinaHRAP, + rotAxisP.z*sinaHRAP, cos(halfRotAngleP)); + qP = QUncorrected * deltaQP; + } +#else + Quatd qpd = Quatd(Q.x,Q.y,Q.z,Q.w); + int predictionStages = (int)(PredictionDT / DeltaT); + Vector3f aa = FAngV.SavitzkyGolayDerivative12(); + Vector3d aad = Vector3d(aa.x,aa.y,aa.z); + Vector3f angVelF = FAngV.SavitzkyGolaySmooth8(); + Vector3d avkd = Vector3d(angVelF.x,angVelF.y,angVelF.z); + for (int i = 0; i < predictionStages; i++) + { + double angVelLengthd = avkd.Length(); + Vector3d rotAxisd = avkd / angVelLengthd; + double halfRotAngled = angVelLengthd * DeltaT * 0.5; + double sinHRAd = sin(halfRotAngled); + Quatd deltaQd = Quatd(rotAxisd.x*sinHRAd, rotAxisd.y*sinHRAd, rotAxisd.z*sinHRAd, + cos(halfRotAngled)); + qpd = qpd * deltaQd; + // Update vel + avkd += aad; + } + qP = Quatf((float)qpd.x,(float)qpd.y,(float)qpd.z,(float)qpd.w); +#endif + } + return qP; +} + + +Vector3f SensorFusion::GetCalibratedMagValue(const Vector3f& rawMag) const +{ + Vector3f mag = rawMag; + OVR_ASSERT(HasMagCalibration()); + mag.x += MagCalibrationMatrix.M[0][3]; + mag.y += MagCalibrationMatrix.M[1][3]; + mag.z += MagCalibrationMatrix.M[2][3]; + return mag; +} + + +void SensorFusion::SetMagReference(const Quatf& q, const Vector3f& rawMag) +{ + MagRefQ = q; + MagRefM = rawMag; + + float pitch, roll, yaw; + Q.GetEulerAngles(&pitch, &roll, &yaw); + MagRefYaw = yaw; + MagReferenced = true; + if (MagCalibrated) + MagReady = true; +} + + +float SensorFusion::AngleDifference(float theta1, float theta2) +{ + float x = theta1 - theta2; + if (x > Math::Pi) + return x - Math::TwoPi; + if (x < -Math::Pi) + return x + Math::TwoPi; + return x; +} + + +SensorFusion::BodyFrameHandler::~BodyFrameHandler() +{ + RemoveHandlerFromDevices(); +} + +void SensorFusion::BodyFrameHandler::OnMessage(const Message& msg) +{ + if (msg.Type == Message_BodyFrame) + pFusion->handleMessage(static_cast(msg)); + if (pFusion->pDelegate) + pFusion->pDelegate->OnMessage(msg); +} + +bool SensorFusion::BodyFrameHandler::SupportsMessageType(MessageType type) const +{ + return (type == Message_BodyFrame); +} + + +} // namespace OVR + diff --git a/LibOVR/Src/OVR_SensorFusion.h b/LibOVR/Src/OVR_SensorFusion.h new file mode 100644 index 0000000..8b88ea2 --- /dev/null +++ b/LibOVR/Src/OVR_SensorFusion.h @@ -0,0 +1,268 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_SensorFusion.h +Content : Methods that determine head orientation from sensor data over time +Created : October 9, 2012 +Authors : Michael Antonov, Steve LaValle + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_SensorFusion_h +#define OVR_SensorFusion_h + +#include "OVR_Device.h" +#include "OVR_SensorFilter.h" + +namespace OVR { + +//------------------------------------------------------------------------------------- +// ***** SensorFusion + +// SensorFusion class accumulates Sensor notification messages to keep track of +// orientation, which involves integrating the gyro and doing correction with gravity. +// Orientation is reported as a quaternion, from which users can obtain either the +// rotation matrix or Euler angles. +// +// The class can operate in two ways: +// - By user manually passing MessageBodyFrame messages to the OnMessage() function. +// - By attaching SensorFusion to a SensorDevice, in which case it will +// automatically handle notifications from that device. + +class SensorFusion : public NewOverrideBase +{ +public: + SensorFusion(SensorDevice* sensor = 0); + ~SensorFusion(); + + // Attaches this SensorFusion to a sensor device, from which it will receive + // notification messages. If a sensor is attached, manual message notification + // is not necessary. Calling this function also resets SensorFusion state. + bool AttachToSensor(SensorDevice* sensor); + + // Returns true if this Sensor fusion object is attached to a sensor. + bool IsAttachedToSensor() const { return Handler.IsHandlerInstalled(); } + + void SetGravityEnabled(bool enableGravity) { EnableGravity = enableGravity; } + + bool IsGravityEnabled() const { return EnableGravity;} + + void SetYawCorrectionEnabled(bool enableYawCorrection) { EnableYawCorrection = enableYawCorrection; } + + // Yaw correction is set up to work + bool IsYawCorrectionEnabled() const { return EnableYawCorrection;} + + // Yaw correction is currently working (forcing a corrective yaw rotation) + bool IsYawCorrectionInProgress() const { return YawCorrectionInProgress;} + + // Store the calibration matrix for the magnetometer + void SetMagCalibration(const Matrix4f& m) + { + MagCalibrationMatrix = m; + MagCalibrated = true; + if (MagReferenced) + MagReady = true; + } + + // True only if the mag has calibration values stored + bool HasMagCalibration() const { return MagCalibrated;} + + // Force the mag into the uncalibrated state + void ClearMagCalibration() + { + MagCalibrated = false; + MagReady = false; + } + + // Set the magnetometer's reference orientation for use in yaw correction + // The supplied mag is an uncalibrated value + void SetMagReference(const Quatf& q, const Vector3f& rawMag); + // Default to current HMD orientation + void SetMagReference() { SetMagReference(Q, RawMag); } + + bool HasMagReference() const { return MagReferenced; } + + void ClearMagReference() + { + MagReferenced = false; + MagReady = false; + } + + bool IsMagReady() const { return MagReady; } + + void SetMagRefDistance(const float d) { MagRefDistance = d; } + + // Notifies SensorFusion object about a new BodyFrame message from a sensor. + // Should be called by user if not attaching to a sensor. + void OnMessage(const MessageBodyFrame& msg) + { + OVR_ASSERT(!IsAttachedToSensor()); + handleMessage(msg); + } + + // Obtain the current accumulated orientation. + Quatf GetOrientation() const + { + Lock::Locker lockScope(Handler.GetHandlerLock()); + return Q; + } + + // Use a predictive filter to estimate the future orientation + Quatf GetPredictedOrientation(); + + // Obtain the last absolute acceleration reading, in m/s^2. + Vector3f GetAcceleration() const + { + Lock::Locker lockScope(Handler.GetHandlerLock()); + return A; + } + + // Obtain the last angular velocity reading, in rad/s. + Vector3f GetAngularVelocity() const + { + Lock::Locker lockScope(Handler.GetHandlerLock()); + return AngV; + } + // Obtain the last magnetometer reading, in Gauss + Vector3f GetMagnetometer() const + { + Lock::Locker lockScope(Handler.GetHandlerLock()); + return RawMag; + } + // Obtain the filtered magnetometer reading, in Gauss + Vector3f GetFilteredMagnetometer() const + { + Lock::Locker lockScope(Handler.GetHandlerLock()); + return FRawMag.Mean(); + } + // Obtain the calibrated magnetometer reading (direction and field strength) + Vector3f GetCalibratedMagnetometer() const + { + OVR_ASSERT(MagCalibrated); + Lock::Locker lockScope(Handler.GetHandlerLock()); + return CalMag; + } + + Vector3f GetCalibratedMagValue(const Vector3f& rawMag) const; + + float GetMagRefYaw() const + { + return MagRefYaw; + } + + float GetYawErrorAngle() const + { + return YawErrorAngle; + } + // For later + //Vector3f GetGravity() const; + + // Resets the current orientation + void Reset() + { + Lock::Locker lockScope(Handler.GetHandlerLock()); + Q = Quatf(); + QUncorrected = Quatf(); + + Stage = 0; + } + + // Configuration + + // Gain used to correct gyro with accel. Default value is appropriate for typical use. + float GetAccelGain() const { return Gain; } + void SetAccelGain(float ag) { Gain = ag; } + + // Multiplier for yaw rotation (turning); setting this higher than 1 (the default) can allow the game + // to be played without auxillary rotation controls, possibly making it more immersive. Whether this is more + // or less likely to cause motion sickness is unknown. + float GetYawMultiplier() const { return YawMult; } + void SetYawMultiplier(float y) { YawMult = y; } + + void SetDelegateMessageHandler(MessageHandler* handler) + { pDelegate = handler; } + + // Prediction functions. + // Prediction delta specifes how much prediction should be applied in seconds; it should in + // general be under the average rendering latency. Call GetPredictedOrientation() to get + // predicted orientation. + float GetPredictionDelta() const { return PredictionDT; } + void SetPrediction(float dt, bool enable = true) { PredictionDT = dt; EnablePrediction = enable; } + void SetPredictionEnabled(bool enable = true) { EnablePrediction = enable; } + bool IsPredictionEnabled() { return EnablePrediction; } + + // Methods for magnetometer calibration + static float AngleDifference(float theta1, float theta2); + static Vector3f CalculateSphereCenter(Vector3f p1, Vector3f p2, + Vector3f p3, Vector3f p4); + + +private: + SensorFusion* getThis() { return this; } + + // Internal handler for messages; bypasses error checking. + void handleMessage(const MessageBodyFrame& msg); + + class BodyFrameHandler : public MessageHandler + { + SensorFusion* pFusion; + public: + BodyFrameHandler(SensorFusion* fusion) : pFusion(fusion) { } + ~BodyFrameHandler(); + + virtual void OnMessage(const Message& msg); + virtual bool SupportsMessageType(MessageType type) const; + }; + + Quatf Q; + Quatf QUncorrected; + Vector3f A; + Vector3f AngV; + Vector3f CalMag; + Vector3f RawMag; + unsigned int Stage; + float DeltaT; + BodyFrameHandler Handler; + MessageHandler* pDelegate; + float Gain; + float YawMult; + volatile bool EnableGravity; + + bool EnablePrediction; + float PredictionDT; + + SensorFilter FRawMag; + SensorFilter FAccW; + SensorFilter FAngV; + + int TiltCondCount; + float TiltErrorAngle; + Vector3f TiltErrorAxis; + + bool EnableYawCorrection; + Matrix4f MagCalibrationMatrix; + bool MagCalibrated; + int MagCondCount; + bool MagReferenced; + float MagRefDistance; + bool MagReady; + Quatf MagRefQ; + Vector3f MagRefM; + float MagRefYaw; + float YawErrorAngle; + int YawErrorCount; + bool YawCorrectionInProgress; + bool YawCorrectionActivated; + +}; + + +} // namespace OVR + +#endif diff --git a/LibOVR/Src/OVR_SensorImpl.cpp b/LibOVR/Src/OVR_SensorImpl.cpp new file mode 100644 index 0000000..fb322df --- /dev/null +++ b/LibOVR/Src/OVR_SensorImpl.cpp @@ -0,0 +1,882 @@ +/************************************************************************************ + +Filename : OVR_SensorImpl.cpp +Content : Oculus Sensor device implementation. +Created : March 7, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_SensorImpl.h" + +// HMDDeviceDesc can be created/updated through Sensor carrying DisplayInfo. + +#include "Kernel/OVR_Timer.h" + +namespace OVR { + +//------------------------------------------------------------------------------------- +// ***** Oculus Sensor-specific packet data structures + +enum { + Sensor_VendorId = Oculus_VendorId, + Sensor_ProductId = 0x0001, + + // ST's VID used originally; should be removed in the future + Sensor_OldVendorId = 0x0483, + Sensor_OldProductId = 0x5750, + + Sensor_DefaultReportRate = 500, // Hz + Sensor_MaxReportRate = 1000 // Hz +}; + +// Reported data is little-endian now +static UInt16 DecodeUInt16(const UByte* buffer) +{ + return (UInt16(buffer[1]) << 8) | UInt16(buffer[0]); +} + +static SInt16 DecodeSInt16(const UByte* buffer) +{ + return (SInt16(buffer[1]) << 8) | SInt16(buffer[0]); +} + +static UInt32 DecodeUInt32(const UByte* buffer) +{ + return (buffer[0]) | UInt32(buffer[1] << 8) | UInt32(buffer[2] << 16) | UInt32(buffer[3] << 24); +} + +static float DecodeFloat(const UByte* buffer) +{ + union { + UInt32 U; + float F; + }; + + U = DecodeUInt32(buffer); + return F; +} + + +static void UnpackSensor(const UByte* buffer, SInt32* x, SInt32* y, SInt32* z) +{ + // Sign extending trick + // from http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend + struct {SInt32 x:21;} s; + + *x = s.x = (buffer[0] << 13) | (buffer[1] << 5) | ((buffer[2] & 0xF8) >> 3); + *y = s.x = ((buffer[2] & 0x07) << 18) | (buffer[3] << 10) | (buffer[4] << 2) | + ((buffer[5] & 0xC0) >> 6); + *z = s.x = ((buffer[5] & 0x3F) << 15) | (buffer[6] << 7) | (buffer[7] >> 1); +} + +// Messages we care for +enum TrackerMessageType +{ + TrackerMessage_None = 0, + TrackerMessage_Sensors = 1, + TrackerMessage_Unknown = 0x100, + TrackerMessage_SizeError = 0x101, +}; + +struct TrackerSample +{ + SInt32 AccelX, AccelY, AccelZ; + SInt32 GyroX, GyroY, GyroZ; +}; + + +struct TrackerSensors +{ + UByte SampleCount; + UInt16 Timestamp; + UInt16 LastCommandID; + SInt16 Temperature; + + TrackerSample Samples[3]; + + SInt16 MagX, MagY, MagZ; + + TrackerMessageType Decode(const UByte* buffer, int size) + { + if (size < 62) + return TrackerMessage_SizeError; + + SampleCount = buffer[1]; + Timestamp = DecodeUInt16(buffer + 2); + LastCommandID = DecodeUInt16(buffer + 4); + Temperature = DecodeSInt16(buffer + 6); + + //if (SampleCount > 2) + // OVR_DEBUG_LOG_TEXT(("TackerSensor::Decode SampleCount=%d\n", SampleCount)); + + // Only unpack as many samples as there actually are + UByte iterationCount = (SampleCount > 2) ? 3 : SampleCount; + + for (UByte i = 0; i < iterationCount; i++) + { + UnpackSensor(buffer + 8 + 16 * i, &Samples[i].AccelX, &Samples[i].AccelY, &Samples[i].AccelZ); + UnpackSensor(buffer + 16 + 16 * i, &Samples[i].GyroX, &Samples[i].GyroY, &Samples[i].GyroZ); + } + + MagX = DecodeSInt16(buffer + 56); + MagY = DecodeSInt16(buffer + 58); + MagZ = DecodeSInt16(buffer + 60); + + return TrackerMessage_Sensors; + } +}; + +struct TrackerMessage +{ + TrackerMessageType Type; + TrackerSensors Sensors; +}; + +bool DecodeTrackerMessage(TrackerMessage* message, UByte* buffer, int size) +{ + memset(message, 0, sizeof(TrackerMessage)); + + if (size < 4) + { + message->Type = TrackerMessage_SizeError; + return false; + } + + switch (buffer[0]) + { + case TrackerMessage_Sensors: + message->Type = message->Sensors.Decode(buffer, size); + break; + + default: + message->Type = TrackerMessage_Unknown; + break; + } + + return (message->Type < TrackerMessage_Unknown) && (message->Type != TrackerMessage_None); +} + + +// ***** SensorRangeImpl Implementation + +// Sensor HW only accepts specific maximum range values, used to maximize +// the 16-bit sensor outputs. Use these ramps to specify and report appropriate values. +static const UInt16 AccelRangeRamp[] = { 2, 4, 8, 16 }; +static const UInt16 GyroRangeRamp[] = { 250, 500, 1000, 2000 }; +static const UInt16 MagRangeRamp[] = { 880, 1300, 1900, 2500 }; + +static UInt16 SelectSensorRampValue(const UInt16* ramp, unsigned count, + float val, float factor, const char* label) +{ + UInt16 threshold = (UInt16)(val * factor); + + for (unsigned i = 0; i= threshold) + return ramp[i]; + } + OVR_DEBUG_LOG(("SensorDevice::SetRange - %s clamped to %0.4f", + label, float(ramp[count-1]) / factor)); + OVR_UNUSED2(factor, label); + return ramp[count-1]; +} + +// SensorScaleImpl provides buffer packing logic for the Sensor Range +// record that can be applied to DK1 sensor through Get/SetFeature. We expose this +// through SensorRange class, which has different units. +struct SensorRangeImpl +{ + enum { PacketSize = 8 }; + UByte Buffer[PacketSize]; + + UInt16 CommandId; + UInt16 AccelScale; + UInt16 GyroScale; + UInt16 MagScale; + + SensorRangeImpl(const SensorRange& r, UInt16 commandId = 0) + { + SetSensorRange(r, commandId); + } + + void SetSensorRange(const SensorRange& r, UInt16 commandId = 0) + { + CommandId = commandId; + AccelScale = SelectSensorRampValue(AccelRangeRamp, sizeof(AccelRangeRamp)/sizeof(AccelRangeRamp[0]), + r.MaxAcceleration, (1.0f / 9.81f), "MaxAcceleration"); + GyroScale = SelectSensorRampValue(GyroRangeRamp, sizeof(GyroRangeRamp)/sizeof(GyroRangeRamp[0]), + r.MaxRotationRate, Math::RadToDegreeFactor, "MaxRotationRate"); + MagScale = SelectSensorRampValue(MagRangeRamp, sizeof(MagRangeRamp)/sizeof(MagRangeRamp[0]), + r.MaxMagneticField, 1000.0f, "MaxMagneticField"); + Pack(); + } + + void GetSensorRange(SensorRange* r) + { + r->MaxAcceleration = AccelScale * 9.81f; + r->MaxRotationRate = DegreeToRad((float)GyroScale); + r->MaxMagneticField= MagScale * 0.001f; + } + + static SensorRange GetMaxSensorRange() + { + return SensorRange(AccelRangeRamp[sizeof(AccelRangeRamp)/sizeof(AccelRangeRamp[0]) - 1] * 9.81f, + GyroRangeRamp[sizeof(GyroRangeRamp)/sizeof(GyroRangeRamp[0]) - 1] * + Math::DegreeToRadFactor, + MagRangeRamp[sizeof(MagRangeRamp)/sizeof(MagRangeRamp[0]) - 1] * 0.001f); + } + + void Pack() + { + Buffer[0] = 4; + Buffer[1] = UByte(CommandId & 0xFF); + Buffer[2] = UByte(CommandId >> 8); + Buffer[3] = UByte(AccelScale); + Buffer[4] = UByte(GyroScale & 0xFF); + Buffer[5] = UByte(GyroScale >> 8); + Buffer[6] = UByte(MagScale & 0xFF); + Buffer[7] = UByte(MagScale >> 8); + } + + void Unpack() + { + CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8); + AccelScale= Buffer[3]; + GyroScale = Buffer[4] | (UInt16(Buffer[5]) << 8); + MagScale = Buffer[6] | (UInt16(Buffer[7]) << 8); + } +}; + + +// Sensor configuration command, ReportId == 2. + +struct SensorConfigImpl +{ + enum { PacketSize = 7 }; + UByte Buffer[PacketSize]; + + // Flag values for Flags. + enum { + Flag_RawMode = 0x01, + Flag_CallibrationTest = 0x02, // Internal test mode + Flag_UseCallibration = 0x04, + Flag_AutoCallibration = 0x08, + Flag_MotionKeepAlive = 0x10, + Flag_CommandKeepAlive = 0x20, + Flag_SensorCoordinates = 0x40 + }; + + UInt16 CommandId; + UByte Flags; + UInt16 PacketInterval; + UInt16 KeepAliveIntervalMs; + + SensorConfigImpl() : CommandId(0), Flags(0), PacketInterval(0), KeepAliveIntervalMs(0) + { + memset(Buffer, 0, PacketSize); + Buffer[0] = 2; + } + + void SetSensorCoordinates(bool sensorCoordinates) + { Flags = (Flags & ~Flag_SensorCoordinates) | (sensorCoordinates ? Flag_SensorCoordinates : 0); } + bool IsUsingSensorCoordinates() const + { return (Flags & Flag_SensorCoordinates) != 0; } + + void Pack() + { + Buffer[0] = 2; + Buffer[1] = UByte(CommandId & 0xFF); + Buffer[2] = UByte(CommandId >> 8); + Buffer[3] = Flags; + Buffer[4] = UByte(PacketInterval); + Buffer[5] = UByte(KeepAliveIntervalMs & 0xFF); + Buffer[6] = UByte(KeepAliveIntervalMs >> 8); + } + + void Unpack() + { + CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8); + Flags = Buffer[3]; + PacketInterval = Buffer[4]; + KeepAliveIntervalMs= Buffer[5] | (UInt16(Buffer[6]) << 8); + } + +}; + + +// SensorKeepAlive - feature report that needs to be sent at regular intervals for sensor +// to receive commands. +struct SensorKeepAliveImpl +{ + enum { PacketSize = 5 }; + UByte Buffer[PacketSize]; + + UInt16 CommandId; + UInt16 KeepAliveIntervalMs; + + SensorKeepAliveImpl(UInt16 interval = 0, UInt16 commandId = 0) + : CommandId(commandId), KeepAliveIntervalMs(interval) + { + Pack(); + } + + void Pack() + { + Buffer[0] = 8; + Buffer[1] = UByte(CommandId & 0xFF); + Buffer[2] = UByte(CommandId >> 8); + Buffer[3] = UByte(KeepAliveIntervalMs & 0xFF); + Buffer[4] = UByte(KeepAliveIntervalMs >> 8); + } + + void Unpack() + { + CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8); + KeepAliveIntervalMs= Buffer[3] | (UInt16(Buffer[4]) << 8); + } +}; + + +//------------------------------------------------------------------------------------- +// ***** SensorDisplayInfoImpl +SensorDisplayInfoImpl::SensorDisplayInfoImpl() + : CommandId(0), DistortionType(Base_None) +{ + memset(Buffer, 0, PacketSize); + Buffer[0] = 9; +} + +void SensorDisplayInfoImpl::Unpack() +{ + CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8); + DistortionType = Buffer[3]; + HResolution = DecodeUInt16(Buffer+4); + VResolution = DecodeUInt16(Buffer+6); + HScreenSize = DecodeUInt32(Buffer+8) * (1/1000000.f); + VScreenSize = DecodeUInt32(Buffer+12) * (1/1000000.f); + VCenter = DecodeUInt32(Buffer+16) * (1/1000000.f); + LensSeparation = DecodeUInt32(Buffer+20) * (1/1000000.f); + EyeToScreenDistance[0] = DecodeUInt32(Buffer+24) * (1/1000000.f); + EyeToScreenDistance[1] = DecodeUInt32(Buffer+28) * (1/1000000.f); + DistortionK[0] = DecodeFloat(Buffer+32); + DistortionK[1] = DecodeFloat(Buffer+36); + DistortionK[2] = DecodeFloat(Buffer+40); + DistortionK[3] = DecodeFloat(Buffer+44); + DistortionK[4] = DecodeFloat(Buffer+48); + DistortionK[5] = DecodeFloat(Buffer+52); +} + + +//------------------------------------------------------------------------------------- +// ***** SensorDeviceFactory + +SensorDeviceFactory SensorDeviceFactory::Instance; + +void SensorDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor) +{ + + class SensorEnumerator : public HIDEnumerateVisitor + { + // Assign not supported; suppress MSVC warning. + void operator = (const SensorEnumerator&) { } + + DeviceFactory* pFactory; + EnumerateVisitor& ExternalVisitor; + public: + SensorEnumerator(DeviceFactory* factory, EnumerateVisitor& externalVisitor) + : pFactory(factory), ExternalVisitor(externalVisitor) { } + + virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) + { + return pFactory->MatchVendorProduct(vendorId, productId); + } + + virtual void Visit(HIDDevice& device, const HIDDeviceDesc& desc) + { + SensorDeviceCreateDesc createDesc(pFactory, desc); + ExternalVisitor.Visit(createDesc); + + // Check if the sensor returns DisplayInfo. If so, try to use it to override potentially + // mismatching monitor information (in case wrong EDID is reported by splitter), + // or to create a new "virtualized" HMD Device. + + SensorDisplayInfoImpl displayInfo; + + if (device.GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize)) + { + displayInfo.Unpack(); + + // If we got display info, try to match / create HMDDevice as well + // so that sensor settings give preference. + if (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) + { + SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo(displayInfo, ExternalVisitor); + } + } + } + }; + + //double start = Timer::GetProfileSeconds(); + + SensorEnumerator sensorEnumerator(this, visitor); + GetManagerImpl()->GetHIDDeviceManager()->Enumerate(&sensorEnumerator); + + //double totalSeconds = Timer::GetProfileSeconds() - start; +} + +bool SensorDeviceFactory::MatchVendorProduct(UInt16 vendorId, UInt16 productId) const +{ + return ((vendorId == Sensor_VendorId) && (productId == Sensor_ProductId)) || + ((vendorId == Sensor_OldVendorId) && (productId == Sensor_OldProductId)); +} + +bool SensorDeviceFactory::DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc) +{ + if (MatchVendorProduct(desc.VendorId, desc.ProductId)) + { + SensorDeviceCreateDesc createDesc(this, desc); + return pdevMgr->AddDevice_NeedsLock(createDesc).GetPtr() != NULL; + } + return false; +} + +//------------------------------------------------------------------------------------- +// ***** SensorDeviceCreateDesc + +DeviceBase* SensorDeviceCreateDesc::NewDeviceInstance() +{ + return new SensorDeviceImpl(this); +} + +bool SensorDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const +{ + if ((info->InfoClassType != Device_Sensor) && + (info->InfoClassType != Device_None)) + return false; + + OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, HIDDesc.Product.ToCStr()); + OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, HIDDesc.Manufacturer.ToCStr()); + info->Type = Device_Sensor; + info->Version = 0; + + if (info->InfoClassType == Device_Sensor) + { + SensorInfo* sinfo = (SensorInfo*)info; + sinfo->VendorId = HIDDesc.VendorId; + sinfo->ProductId = HIDDesc.ProductId; + sinfo->MaxRanges = SensorRangeImpl::GetMaxSensorRange(); + OVR_strcpy(sinfo->SerialNumber, sizeof(sinfo->SerialNumber),HIDDesc.SerialNumber.ToCStr()); + } + return true; +} + + +//------------------------------------------------------------------------------------- +// ***** SensorDevice + +SensorDeviceImpl::SensorDeviceImpl(SensorDeviceCreateDesc* createDesc) + : OVR::HIDDeviceImpl(createDesc, 0), + Coordinates(SensorDevice::Coord_Sensor), + HWCoordinates(SensorDevice::Coord_HMD), // HW reports HMD coordinates by default. + NextKeepAliveTicks(0), + MaxValidRange(SensorRangeImpl::GetMaxSensorRange()) +{ + SequenceValid = false; + LastSampleCount= 0; + LastTimestamp = 0; + + OldCommandId = 0; +} + +SensorDeviceImpl::~SensorDeviceImpl() +{ + // Check that Shutdown() was called. + OVR_ASSERT(!pCreateDesc->pDevice); +} + +// Internal creation APIs. +bool SensorDeviceImpl::Initialize(DeviceBase* parent) +{ + if (HIDDeviceImpl::Initialize(parent)) + { + openDevice(); + + LogText("OVR::SensorDevice initialized.\n"); + + return true; + } + + return false; +} + +void SensorDeviceImpl::openDevice() +{ + + // Read the currently configured range from sensor. + SensorRangeImpl sr(SensorRange(), 0); + + if (GetInternalDevice()->GetFeatureReport(sr.Buffer, SensorRangeImpl::PacketSize)) + { + sr.Unpack(); + sr.GetSensorRange(&CurrentRange); + } + + + // If the sensor has "DisplayInfo" data, use HMD coordinate frame by default. + SensorDisplayInfoImpl displayInfo; + if (GetInternalDevice()->GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize)) + { + displayInfo.Unpack(); + Coordinates = (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) ? + Coord_HMD : Coord_Sensor; + } + + // Read/Apply sensor config. + setCoordinateFrame(Coordinates); + setReportRate(Sensor_DefaultReportRate); + + // Set Keep-alive at 10 seconds. + SensorKeepAliveImpl skeepAlive(10 * 1000); + GetInternalDevice()->SetFeatureReport(skeepAlive.Buffer, SensorKeepAliveImpl::PacketSize); +} + +void SensorDeviceImpl::closeDeviceOnError() +{ + LogText("OVR::SensorDevice - Lost connection to '%s'\n", getHIDDesc()->Path.ToCStr()); + NextKeepAliveTicks = 0; +} + +void SensorDeviceImpl::Shutdown() +{ + HIDDeviceImpl::Shutdown(); + + LogText("OVR::SensorDevice - Closed '%s'\n", getHIDDesc()->Path.ToCStr()); +} + + +void SensorDeviceImpl::OnInputReport(UByte* pData, UInt32 length) +{ + + bool processed = false; + if (!processed) + { + + TrackerMessage message; + if (DecodeTrackerMessage(&message, pData, length)) + { + processed = true; + onTrackerMessage(&message); + } + } +} + +UInt64 SensorDeviceImpl::OnTicks(UInt64 ticksMks) +{ + + if (ticksMks >= NextKeepAliveTicks) + { + // Use 3-seconds keep alive by default. + UInt64 keepAliveDelta = Timer::MksPerSecond * 3; + + // Set Keep-alive at 10 seconds. + SensorKeepAliveImpl skeepAlive(10 * 1000); + // OnTicks is called from background thread so we don't need to add this to the command queue. + GetInternalDevice()->SetFeatureReport(skeepAlive.Buffer, SensorKeepAliveImpl::PacketSize); + + // Emit keep-alive every few seconds. + NextKeepAliveTicks = ticksMks + keepAliveDelta; + } + return NextKeepAliveTicks - ticksMks; +} + +bool SensorDeviceImpl::SetRange(const SensorRange& range, bool waitFlag) +{ + bool result = 0; + ThreadCommandQueue * threadQueue = GetManagerImpl()->GetThreadQueue(); + + if (!waitFlag) + { + return threadQueue->PushCall(this, &SensorDeviceImpl::setRange, range); + } + + if (!threadQueue->PushCallAndWaitResult(this, + &SensorDeviceImpl::setRange, + &result, + range)) + { + return false; + } + + return result; +} + +void SensorDeviceImpl::GetRange(SensorRange* range) const +{ + Lock::Locker lockScope(GetLock()); + *range = CurrentRange; +} + +bool SensorDeviceImpl::setRange(const SensorRange& range) +{ + SensorRangeImpl sr(range); + + if (GetInternalDevice()->SetFeatureReport(sr.Buffer, SensorRangeImpl::PacketSize)) + { + Lock::Locker lockScope(GetLock()); + sr.GetSensorRange(&CurrentRange); + return true; + } + + return false; +} + +void SensorDeviceImpl::SetCoordinateFrame(CoordinateFrame coordframe) +{ + // Push call with wait. + GetManagerImpl()->GetThreadQueue()-> + PushCall(this, &SensorDeviceImpl::setCoordinateFrame, coordframe, true); +} + +SensorDevice::CoordinateFrame SensorDeviceImpl::GetCoordinateFrame() const +{ + return Coordinates; +} + +Void SensorDeviceImpl::setCoordinateFrame(CoordinateFrame coordframe) +{ + + Coordinates = coordframe; + + // Read the original coordinate frame, then try to change it. + SensorConfigImpl scfg; + if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize)) + { + scfg.Unpack(); + } + + scfg.SetSensorCoordinates(coordframe == Coord_Sensor); + scfg.Pack(); + + GetInternalDevice()->SetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize); + + // Re-read the state, in case of older firmware that doesn't support Sensor coordinates. + if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize)) + { + scfg.Unpack(); + HWCoordinates = scfg.IsUsingSensorCoordinates() ? Coord_Sensor : Coord_HMD; + } + else + { + HWCoordinates = Coord_HMD; + } + return 0; +} + +void SensorDeviceImpl::SetReportRate(unsigned rateHz) +{ + // Push call with wait. + GetManagerImpl()->GetThreadQueue()-> + PushCall(this, &SensorDeviceImpl::setReportRate, rateHz, true); +} + +unsigned SensorDeviceImpl::GetReportRate() const +{ + // Read the original configuration + SensorConfigImpl scfg; + if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize)) + { + scfg.Unpack(); + return Sensor_MaxReportRate / (scfg.PacketInterval + 1); + } + return 0; // error +} + +Void SensorDeviceImpl::setReportRate(unsigned rateHz) +{ + // Read the original configuration + SensorConfigImpl scfg; + if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize)) + { + scfg.Unpack(); + } + + if (rateHz > Sensor_MaxReportRate) + rateHz = Sensor_MaxReportRate; + else if (rateHz == 0) + rateHz = Sensor_DefaultReportRate; + + scfg.PacketInterval = UInt16((Sensor_MaxReportRate / rateHz) - 1); + + scfg.Pack(); + + GetInternalDevice()->SetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize); + return 0; +} + +void SensorDeviceImpl::SetMessageHandler(MessageHandler* handler) +{ + if (handler) + { + SequenceValid = false; + DeviceBase::SetMessageHandler(handler); + } + else + { + DeviceBase::SetMessageHandler(handler); + } +} + +// Sensor reports data in the following coordinate system: +// Accelerometer: 10^-4 m/s^2; X forward, Y right, Z Down. +// Gyro: 10^-4 rad/s; X positive roll right, Y positive pitch up; Z positive yaw right. + + +// We need to convert it to the following RHS coordinate system: +// X right, Y Up, Z Back (out of screen) +// +Vector3f AccelFromBodyFrameUpdate(const TrackerSensors& update, UByte sampleNumber, + bool convertHMDToSensor = false) +{ + const TrackerSample& sample = update.Samples[sampleNumber]; + float ax = (float)sample.AccelX; + float ay = (float)sample.AccelY; + float az = (float)sample.AccelZ; + + Vector3f val = convertHMDToSensor ? Vector3f(ax, az, -ay) : Vector3f(ax, ay, az); + return val * 0.0001f; +} + + +Vector3f MagFromBodyFrameUpdate(const TrackerSensors& update, + bool convertHMDToSensor = false) +{ + // Note: Y and Z are swapped in comparison to the Accel. + // This accounts for DK1 sensor firmware axis swap, which should be undone in future releases. + if (!convertHMDToSensor) + { + return Vector3f( (float)update.MagX, + (float)update.MagZ, + (float)update.MagY) * 0.0001f; + } + + return Vector3f( (float)update.MagX, + (float)update.MagY, + -(float)update.MagZ) * 0.0001f; +} + +Vector3f EulerFromBodyFrameUpdate(const TrackerSensors& update, UByte sampleNumber, + bool convertHMDToSensor = false) +{ + const TrackerSample& sample = update.Samples[sampleNumber]; + float gx = (float)sample.GyroX; + float gy = (float)sample.GyroY; + float gz = (float)sample.GyroZ; + + Vector3f val = convertHMDToSensor ? Vector3f(gx, gz, -gy) : Vector3f(gx, gy, gz); + return val * 0.0001f; +} + + +void SensorDeviceImpl::onTrackerMessage(TrackerMessage* message) +{ + if (message->Type != TrackerMessage_Sensors) + return; + + const float timeUnit = (1.0f / 1000.f); + TrackerSensors& s = message->Sensors; + + + // Call OnMessage() within a lock to avoid conflicts with handlers. + Lock::Locker scopeLock(HandlerRef.GetLock()); + + + if (SequenceValid) + { + unsigned timestampDelta; + + if (s.Timestamp < LastTimestamp) + timestampDelta = ((((int)s.Timestamp) + 0x10000) - (int)LastTimestamp); + else + timestampDelta = (s.Timestamp - LastTimestamp); + + // If we missed a small number of samples, replicate the last sample. + if ((timestampDelta > LastSampleCount) && (timestampDelta <= 254)) + { + if (HandlerRef.GetHandler()) + { + MessageBodyFrame sensors(this); + sensors.TimeDelta = (timestampDelta - LastSampleCount) * timeUnit; + sensors.Acceleration = LastAcceleration; + sensors.RotationRate = LastRotationRate; + sensors.MagneticField = LastMagneticField; + sensors.Temperature = LastTemperature; + + HandlerRef.GetHandler()->OnMessage(sensors); + } + } + } + else + { + LastAcceleration = Vector3f(0); + LastRotationRate = Vector3f(0); + LastMagneticField= Vector3f(0); + LastTemperature = 0; + SequenceValid = true; + } + + LastSampleCount = s.SampleCount; + LastTimestamp = s.Timestamp; + + bool convertHMDToSensor = (Coordinates == Coord_Sensor) && (HWCoordinates == Coord_HMD); + + if (HandlerRef.GetHandler()) + { + MessageBodyFrame sensors(this); + UByte iterations = s.SampleCount; + + if (s.SampleCount > 3) + { + iterations = 3; + sensors.TimeDelta = (s.SampleCount - 2) * timeUnit; + } + else + { + sensors.TimeDelta = timeUnit; + } + + for (UByte i = 0; i < iterations; i++) + { + sensors.Acceleration = AccelFromBodyFrameUpdate(s, i, convertHMDToSensor); + sensors.RotationRate = EulerFromBodyFrameUpdate(s, i, convertHMDToSensor); + sensors.MagneticField= MagFromBodyFrameUpdate(s, convertHMDToSensor); + sensors.Temperature = s.Temperature * 0.01f; + HandlerRef.GetHandler()->OnMessage(sensors); + // TimeDelta for the last two sample is always fixed. + sensors.TimeDelta = timeUnit; + } + + LastAcceleration = sensors.Acceleration; + LastRotationRate = sensors.RotationRate; + LastMagneticField= sensors.MagneticField; + LastTemperature = sensors.Temperature; + } + else + { + UByte i = (s.SampleCount > 3) ? 2 : (s.SampleCount - 1); + LastAcceleration = AccelFromBodyFrameUpdate(s, i, convertHMDToSensor); + LastRotationRate = EulerFromBodyFrameUpdate(s, i, convertHMDToSensor); + LastMagneticField = MagFromBodyFrameUpdate(s, convertHMDToSensor); + LastTemperature = s.Temperature * 0.01f; + } +} + +} // namespace OVR + + diff --git a/LibOVR/Src/OVR_SensorImpl.h b/LibOVR/Src/OVR_SensorImpl.h new file mode 100644 index 0000000..890d621 --- /dev/null +++ b/LibOVR/Src/OVR_SensorImpl.h @@ -0,0 +1,208 @@ +/************************************************************************************ + +Filename : OVR_SensorImpl.h +Content : Sensor device specific implementation. +Created : March 7, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_SensorImpl_h +#define OVR_SensorImpl_h + +#include "OVR_HIDDeviceImpl.h" + +namespace OVR { + +struct TrackerMessage; +class ExternalVisitor; + +//------------------------------------------------------------------------------------- +// SensorDeviceFactory enumerates Oculus Sensor devices. +class SensorDeviceFactory : public DeviceFactory +{ +public: + static SensorDeviceFactory Instance; + + // Enumerates devices, creating and destroying relevant objects in manager. + virtual void EnumerateDevices(EnumerateVisitor& visitor); + + virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const; + virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc); +protected: + DeviceManager* getManager() const { return (DeviceManager*) pManager; } +}; + + +// Describes a single a Oculus Sensor device and supports creating its instance. +class SensorDeviceCreateDesc : public HIDDeviceCreateDesc +{ +public: + SensorDeviceCreateDesc(DeviceFactory* factory, const HIDDeviceDesc& hidDesc) + : HIDDeviceCreateDesc(factory, Device_Sensor, hidDesc) { } + + virtual DeviceCreateDesc* Clone() const + { + return new SensorDeviceCreateDesc(*this); + } + + virtual DeviceBase* NewDeviceInstance(); + + virtual MatchResult MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc**) const + { + if ((other.Type == Device_Sensor) && (pFactory == other.pFactory)) + { + const SensorDeviceCreateDesc& s2 = (const SensorDeviceCreateDesc&) other; + if (MatchHIDDevice(s2.HIDDesc)) + return Match_Found; + } + return Match_None; + } + + virtual bool MatchHIDDevice(const HIDDeviceDesc& hidDesc) const + { + // should paths comparison be case insensitive? + return ((HIDDesc.Path.CompareNoCase(hidDesc.Path) == 0) && + (HIDDesc.SerialNumber == hidDesc.SerialNumber)); + } + + virtual bool GetDeviceInfo(DeviceInfo* info) const; +}; + + +//------------------------------------------------------------------------------------- +// ***** OVR::SensorDisplayInfoImpl + +// DisplayInfo obtained from sensor; these values are used to report distortion +// settings and other coefficients. +// Older SensorDisplayInfo will have all zeros, causing the library to apply hard-coded defaults. +// Currently, only resolutions and sizes are used. +struct SensorDisplayInfoImpl +{ + enum { PacketSize = 56 }; + UByte Buffer[PacketSize]; + + enum + { + Mask_BaseFmt = 0x0f, + Mask_OptionFmts = 0xf0, + Base_None = 0, + Base_ScreenOnly = 1, + Base_Distortion = 2, + }; + + UInt16 CommandId; + UByte DistortionType; + UInt16 HResolution, VResolution; + float HScreenSize, VScreenSize; + float VCenter; + float LensSeparation; + float EyeToScreenDistance[2]; + float DistortionK[6]; + + SensorDisplayInfoImpl(); + + void Unpack(); +}; + + +//------------------------------------------------------------------------------------- +// ***** OVR::SensorDeviceImpl + +// Oculus Sensor interface. + +class SensorDeviceImpl : public HIDDeviceImpl +{ +public: + SensorDeviceImpl(SensorDeviceCreateDesc* createDesc); + ~SensorDeviceImpl(); + + + // DeviceCommaon interface + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + virtual void SetMessageHandler(MessageHandler* handler); + + // HIDDevice::Notifier interface. + virtual void OnInputReport(UByte* pData, UInt32 length); + virtual UInt64 OnTicks(UInt64 ticksMks); + + // HMD-Mounted sensor has a different coordinate frame. + virtual void SetCoordinateFrame(CoordinateFrame coordframe); + virtual CoordinateFrame GetCoordinateFrame() const; + + // SensorDevice interface + virtual bool SetRange(const SensorRange& range, bool waitFlag); + virtual void GetRange(SensorRange* range) const; + + // Sets report rate (in Hz) of MessageBodyFrame messages (delivered through MessageHandler::OnMessage call). + // Currently supported maximum rate is 1000Hz. If the rate is set to 500 or 333 Hz then OnMessage will be + // called twice or thrice at the same 'tick'. + // If the rate is < 333 then the OnMessage / MessageBodyFrame will be called three + // times for each 'tick': the first call will contain averaged values, the second + // and third calls will provide with most recent two recorded samples. + virtual void SetReportRate(unsigned rateHz); + // Returns currently set report rate, in Hz. If 0 - error occurred. + // Note, this value may be different from the one provided for SetReportRate. The return + // value will contain the actual rate. + virtual unsigned GetReportRate() const; + + // Hack to create HMD device from sensor display info. + static void EnumerateHMDFromSensorDisplayInfo(const SensorDisplayInfoImpl& displayInfo, + DeviceFactory::EnumerateVisitor& visitor); +protected: + + void openDevice(); + void closeDeviceOnError(); + + Void setCoordinateFrame(CoordinateFrame coordframe); + bool setRange(const SensorRange& range); + + Void setReportRate(unsigned rateHz); + + // Called for decoded messages + void onTrackerMessage(TrackerMessage* message); + + // Helpers to reduce casting. +/* + SensorDeviceCreateDesc* getCreateDesc() const + { return (SensorDeviceCreateDesc*)pCreateDesc.GetPtr(); } + + HIDDeviceDesc* getHIDDesc() const + { return &getCreateDesc()->HIDDesc; } +*/ + + // Set if the sensor is located on the HMD. + // Older prototype firmware doesn't support changing HW coordinates, + // so we track its state. + CoordinateFrame Coordinates; + CoordinateFrame HWCoordinates; + UInt64 NextKeepAliveTicks; + + bool SequenceValid; + SInt16 LastTimestamp; + UByte LastSampleCount; + float LastTemperature; + Vector3f LastAcceleration; + Vector3f LastRotationRate; + Vector3f LastMagneticField; + + // Current sensor range obtained from device. + SensorRange MaxValidRange; + SensorRange CurrentRange; + + UInt16 OldCommandId; +}; + + +} // namespace OVR + +#endif // OVR_SensorImpl_h diff --git a/LibOVR/Src/OVR_ThreadCommandQueue.cpp b/LibOVR/Src/OVR_ThreadCommandQueue.cpp new file mode 100644 index 0000000..d9dbab3 --- /dev/null +++ b/LibOVR/Src/OVR_ThreadCommandQueue.cpp @@ -0,0 +1,370 @@ +/************************************************************************************ + +PublicHeader: None +Filename : OVR_ThreadCommandQueue.cpp +Content : Command queue for operations executed on a thread +Created : October 29, 2012 + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_ThreadCommandQueue.h" + +namespace OVR { + + +//------------------------------------------------------------------------ +// ***** CircularBuffer + +// CircularBuffer is a FIFO buffer implemented in a single block of memory, +// which allows writing and reading variable-size data chucks. Write fails +// if buffer is full. + +class CircularBuffer +{ + enum { + AlignSize = 16, + AlignMask = AlignSize - 1 + }; + + UByte* pBuffer; + UPInt Size; + UPInt Tail; // Byte offset of next item to be popped. + UPInt Head; // Byte offset of where next push will take place. + UPInt End; // When Head < Tail, this is used instead of Size. + + inline UPInt roundUpSize(UPInt size) + { return (size + AlignMask) & ~(UPInt)AlignMask; } + +public: + + CircularBuffer(UPInt size) + : Size(size), Tail(0), Head(0), End(0) + { + pBuffer = (UByte*)OVR_ALLOC_ALIGNED(roundUpSize(size), AlignSize); + } + ~CircularBuffer() + { + // For ThreadCommands, we must consume everything before shutdown. + OVR_ASSERT(IsEmpty()); + OVR_FREE_ALIGNED(pBuffer); + } + + bool IsEmpty() const { return (Head == Tail); } + + // Allocates a state block of specified size and advances pointers, + // returning 0 if buffer is full. + UByte* Write(UPInt size); + + // Returns a pointer to next available data block; 0 if none available. + UByte* ReadBegin() + { return (Head != Tail) ? (pBuffer + Tail) : 0; } + // Consumes data of specified size; this must match size passed to Write. + void ReadEnd(UPInt size); +}; + + +// Allocates a state block of specified size and advances pointers, +// returning 0 if buffer is full. +UByte* CircularBuffer::Write(UPInt size) +{ + UByte* p = 0; + + size = roundUpSize(size); + // Since this is circular buffer, always allow at least one item. + OVR_ASSERT(size < Size/2); + + if (Head >= Tail) + { + OVR_ASSERT(End == 0); + + if (size <= (Size - Head)) + { + p = pBuffer + Head; + Head += size; + } + else if (size < Tail) + { + p = pBuffer; + End = Head; + Head = size; + OVR_ASSERT(Head != Tail); + } + } + else + { + OVR_ASSERT(End != 0); + + if ((Tail - Head) > size) + { + p = pBuffer + Head; + Head += size; + OVR_ASSERT(Head != Tail); + } + } + + return p; +} + +void CircularBuffer::ReadEnd(UPInt size) +{ + OVR_ASSERT(Head != Tail); + size = roundUpSize(size); + + Tail += size; + if (Tail == End) + { + Tail = End = 0; + } + else if (Tail == Head) + { + OVR_ASSERT(End == 0); + Tail = Head = 0; + } +} + + +//------------------------------------------------------------------------------------- +// ***** ThreadCommand + +ThreadCommand::PopBuffer::~PopBuffer() +{ + if (Size) + Destruct(toCommand()); +} + +void ThreadCommand::PopBuffer::InitFromBuffer(void* data) +{ + ThreadCommand* cmd = (ThreadCommand*)data; + OVR_ASSERT(cmd->Size <= MaxSize); + + if (Size) + Destruct(toCommand()); + Size = cmd->Size; + memcpy(Buffer, (void*)cmd, Size); +} + +void ThreadCommand::PopBuffer::Execute() +{ + ThreadCommand* command = toCommand(); + OVR_ASSERT(command); + command->Execute(); + if (NeedsWait()) + GetEvent()->PulseEvent(); +} + +//------------------------------------------------------------------------------------- + +class ThreadCommandQueueImpl : public NewOverrideBase +{ + typedef ThreadCommand::NotifyEvent NotifyEvent; + friend class ThreadCommandQueue; + +public: + + ThreadCommandQueueImpl(ThreadCommandQueue* queue) + : pQueue(queue), CommandBuffer(2048), + ExitEnqueued(false), ExitProcessed(false) + { + } + ~ThreadCommandQueueImpl(); + + + bool PushCommand(const ThreadCommand& command); + bool PopCommand(ThreadCommand::PopBuffer* popBuffer); + + + // ExitCommand is used by notify us that Thread is shutting down. + struct ExitCommand : public ThreadCommand + { + ThreadCommandQueueImpl* pImpl; + + ExitCommand(ThreadCommandQueueImpl* impl, bool wait) + : ThreadCommand(sizeof(ExitCommand), wait, true), pImpl(impl) { } + + virtual void Execute() const + { + Lock::Locker lock(&pImpl->QueueLock); + pImpl->ExitProcessed = true; + } + virtual ThreadCommand* CopyConstruct(void* p) const + { return Construct(p, *this); } + }; + + + NotifyEvent* AllocNotifyEvent_NTS() + { + NotifyEvent* p = AvailableEvents.GetFirst(); + + if (!AvailableEvents.IsNull(p)) + p->RemoveNode(); + else + p = new NotifyEvent; + return p; + } + + void FreeNotifyEvent_NTS(NotifyEvent* p) + { + AvailableEvents.PushBack(p); + } + + void FreeNotifyEvents_NTS() + { + while(!AvailableEvents.IsEmpty()) + { + NotifyEvent* p = AvailableEvents.GetFirst(); + p->RemoveNode(); + delete p; + } + } + + ThreadCommandQueue* pQueue; + Lock QueueLock; + volatile bool ExitEnqueued; + volatile bool ExitProcessed; + List AvailableEvents; + List BlockedProducers; + CircularBuffer CommandBuffer; +}; + + + +ThreadCommandQueueImpl::~ThreadCommandQueueImpl() +{ + Lock::Locker lock(&QueueLock); + OVR_ASSERT(BlockedProducers.IsEmpty()); + FreeNotifyEvents_NTS(); +} + +bool ThreadCommandQueueImpl::PushCommand(const ThreadCommand& command) +{ + ThreadCommand::NotifyEvent* completeEvent = 0; + ThreadCommand::NotifyEvent* queueAvailableEvent = 0; + + // Repeat writing command into buffer until it is available. + do { + + { // Lock Scope + Lock::Locker lock(&QueueLock); + + if (queueAvailableEvent) + { + FreeNotifyEvent_NTS(queueAvailableEvent); + queueAvailableEvent = 0; + } + + // Don't allow any commands after PushExitCommand() is called. + if (ExitEnqueued && !command.ExitFlag) + return false; + + + bool bufferWasEmpty = CommandBuffer.IsEmpty(); + UByte* buffer = CommandBuffer.Write(command.GetSize()); + if (buffer) + { + ThreadCommand* c = command.CopyConstruct(buffer); + if (c->NeedsWait()) + completeEvent = c->pEvent = AllocNotifyEvent_NTS(); + // Signal-waker consumer when we add data to buffer. + if (bufferWasEmpty) + pQueue->OnPushNonEmpty_Locked(); + break; + } + + queueAvailableEvent = AllocNotifyEvent_NTS(); + BlockedProducers.PushBack(queueAvailableEvent); + } // Lock Scope + + queueAvailableEvent->Wait(); + + } while(1); + + // Command was enqueued, wait if necessary. + if (completeEvent) + { + completeEvent->Wait(); + Lock::Locker lock(&QueueLock); + FreeNotifyEvent_NTS(completeEvent); + } + + return true; +} + + +// Pops the next command from the thread queue, if any is available. +bool ThreadCommandQueueImpl::PopCommand(ThreadCommand::PopBuffer* popBuffer) +{ + Lock::Locker lock(&QueueLock); + + UByte* buffer = CommandBuffer.ReadBegin(); + if (!buffer) + { + // Notify thread while in lock scope, enabling initialization of wait. + pQueue->OnPopEmpty_Locked(); + return false; + } + + popBuffer->InitFromBuffer(buffer); + CommandBuffer.ReadEnd(popBuffer->GetSize()); + + if (!BlockedProducers.IsEmpty()) + { + ThreadCommand::NotifyEvent* queueAvailableEvent = BlockedProducers.GetFirst(); + queueAvailableEvent->RemoveNode(); + queueAvailableEvent->PulseEvent(); + // Event is freed later by waiter. + } + return true; +} + + +//------------------------------------------------------------------------------------- + +ThreadCommandQueue::ThreadCommandQueue() +{ + pImpl = new ThreadCommandQueueImpl(this); +} +ThreadCommandQueue::~ThreadCommandQueue() +{ + delete pImpl; +} + +bool ThreadCommandQueue::PushCommand(const ThreadCommand& command) +{ + return pImpl->PushCommand(command); +} + +bool ThreadCommandQueue::PopCommand(ThreadCommand::PopBuffer* popBuffer) +{ + return pImpl->PopCommand(popBuffer); +} + +void ThreadCommandQueue::PushExitCommand(bool wait) +{ + // Exit is processed in two stages: + // - First, ExitEnqueued flag is set to block further commands from queuing up. + // - Second, the actual exit call is processed on the consumer thread, flushing + // any prior commands. + // IsExiting() only returns true after exit has flushed. + { + Lock::Locker lock(&pImpl->QueueLock); + if (pImpl->ExitEnqueued) + return; + pImpl->ExitEnqueued = true; + } + + PushCommand(ThreadCommandQueueImpl::ExitCommand(pImpl, wait)); +} + +bool ThreadCommandQueue::IsExiting() const +{ + return pImpl->ExitProcessed; +} + + +} // namespace OVR diff --git a/LibOVR/Src/OVR_ThreadCommandQueue.h b/LibOVR/Src/OVR_ThreadCommandQueue.h new file mode 100644 index 0000000..98d6228 --- /dev/null +++ b/LibOVR/Src/OVR_ThreadCommandQueue.h @@ -0,0 +1,308 @@ +/************************************************************************************ + +PublicHeader: None +Filename : OVR_ThreadCommandQueue.h +Content : Command queue for operations executed on a thread +Created : October 29, 2012 +Author : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_ThreadCommandQueue_h +#define OVR_ThreadCommandQueue_h + +#include "Kernel/OVR_Types.h" +#include "Kernel/OVR_List.h" +#include "Kernel/OVR_Atomic.h" +#include "Kernel/OVR_Threads.h" + +namespace OVR { + +class ThreadCommand; +class ThreadCommandQueue; + + +//------------------------------------------------------------------------------------- +// ***** ThreadCommand + +// ThreadCommand is a base class implementation for commands stored in ThreadCommandQueue. +class ThreadCommand +{ +public: + + // NotifyEvent is used by ThreadCommandQueue::PushCallAndWait to notify the + // calling (producer) thread when command is completed or queue slot is available. + class NotifyEvent : public ListNode, public NewOverrideBase + { + Event E; + public: + NotifyEvent() { } + + void Wait() { E.Wait(); } + void PulseEvent() { E.PulseEvent(); } + }; + + // ThreadCommand::PopBuffer is temporary storage for a command popped off + // by ThreadCommandQueue::PopCommand. + class PopBuffer + { + enum { MaxSize = 256 }; + + UPInt Size; + union { + UByte Buffer[MaxSize]; + UPInt Align; + }; + + ThreadCommand* toCommand() const { return (ThreadCommand*)Buffer; } + + public: + PopBuffer() : Size(0) { } + ~PopBuffer(); + + void InitFromBuffer(void* data); + + bool HasCommand() const { return Size != 0; } + UPInt GetSize() const { return Size; } + bool NeedsWait() const { return toCommand()->NeedsWait(); } + NotifyEvent* GetEvent() const { return toCommand()->pEvent; } + + // Execute the command and also notifies caller to finish waiting, + // if necessary. + void Execute(); + }; + + UInt16 Size; + bool WaitFlag; + bool ExitFlag; // Marks the last exit command. + NotifyEvent* pEvent; + + ThreadCommand(UPInt size, bool waitFlag, bool exitFlag = false) + : Size((UInt16)size), WaitFlag(waitFlag), ExitFlag(exitFlag), pEvent(0) { } + virtual ~ThreadCommand() { } + + bool NeedsWait() const { return WaitFlag; } + UPInt GetSize() const { return Size; } + + virtual void Execute() const = 0; + // Copy constructor used for serializing this to memory buffer. + virtual ThreadCommand* CopyConstruct(void* p) const = 0; +}; + + +//------------------------------------------------------------------------------------- + +// CleanType is a template that strips 'const' and '&' modifiers from the argument type; +// for example, typename CleanType::Type is equivalent to A. +template struct CleanType { typedef T Type; }; +template struct CleanType { typedef T Type; }; +template struct CleanType { typedef T Type; }; +template struct CleanType { typedef T Type; }; + +// SelfType is a template that yields the argument type. This helps avoid conflicts with +// automatic template argument deduction for function calls when identical argument +// is already defined. +template struct SelfType { typedef T Type; }; + + + +//------------------------------------------------------------------------------------- +// ThreadCommand specializations for member functions with different number of +// arguments and argument types. + +// Used to return nothing from a ThreadCommand, to avoid problems with 'void'. +struct Void +{ + Void() {} + Void(int) {} +}; + +// ThreadCommand for member function with 0 arguments. +template +class ThreadCommandMF0 : public ThreadCommand +{ + typedef R (C::*FnPtr)(); + C* pClass; + FnPtr pFn; + R* pRet; + + void executeImpl() const + { + pRet ? (void)(*pRet = (pClass->*pFn)()) : + (void)(pClass->*pFn)(); + } + +public: + ThreadCommandMF0(C* pclass, FnPtr fn, R* ret, bool needsWait) + : ThreadCommand(sizeof(ThreadCommandMF0), needsWait), + pClass(pclass), pFn(fn), pRet(ret) { } + + virtual void Execute() const { executeImpl(); } + virtual ThreadCommand* CopyConstruct(void* p) const + { return Construct(p, *this); } +}; + + +// ThreadCommand for member function with 1 argument. +template +class ThreadCommandMF1 : public ThreadCommand +{ + typedef R (C::*FnPtr)(A0); + C* pClass; + FnPtr pFn; + R* pRet; + typename CleanType::Type AVal0; + + void executeImpl() const + { + pRet ? (void)(*pRet = (pClass->*pFn)(AVal0)) : + (void)(pClass->*pFn)(AVal0); + } + +public: + ThreadCommandMF1(C* pclass, FnPtr fn, R* ret, A0 a0, bool needsWait) + : ThreadCommand(sizeof(ThreadCommandMF1), needsWait), + pClass(pclass), pFn(fn), pRet(ret), AVal0(a0) { } + + virtual void Execute() const { executeImpl(); } + virtual ThreadCommand* CopyConstruct(void* p) const + { return Construct(p, *this); } +}; + +// ThreadCommand for member function with 2 arguments. +template +class ThreadCommandMF2 : public ThreadCommand +{ + typedef R (C::*FnPtr)(A0, A1); + C* pClass; + FnPtr pFn; + R* pRet; + typename CleanType::Type AVal0; + typename CleanType::Type AVal1; + + void executeImpl() const + { + pRet ? (void)(*pRet = (pClass->*pFn)(AVal0, AVal1)) : + (void)(pClass->*pFn)(AVal0, AVal1); + } + +public: + ThreadCommandMF2(C* pclass, FnPtr fn, R* ret, A0 a0, A1 a1, bool needsWait) + : ThreadCommand(sizeof(ThreadCommandMF2), needsWait), + pClass(pclass), pFn(fn), pRet(ret), AVal0(a0), AVal1(a1) { } + + virtual void Execute() const { executeImpl(); } + virtual ThreadCommand* CopyConstruct(void* p) const + { return Construct(p, *this); } +}; + + +//------------------------------------------------------------------------------------- +// ***** ThreadCommandQueue + +// ThreadCommandQueue is a queue of executable function-call commands intended to be +// serviced by a single consumer thread. Commands are added to the queue with PushCall +// and removed with PopCall; they are processed in FIFO order. Multiple producer threads +// are supported and will be blocked if internal data buffer is full. + +class ThreadCommandQueue +{ +public: + + ThreadCommandQueue(); + virtual ~ThreadCommandQueue(); + + + // Pops the next command from the thread queue, if any is available. + // The command should be executed by calling popBuffer->Execute(). + // Returns 'false' if no command is available at the time of the call. + bool PopCommand(ThreadCommand::PopBuffer* popBuffer); + + // Generic implementaion of PushCommand; enqueues a command for execution. + // Returns 'false' if push failed, usually indicating thread shutdown. + bool PushCommand(const ThreadCommand& command); + + // + void PushExitCommand(bool wait); + + // Returns 'true' once ExitCommand has been processed, so the thread can shut down. + bool IsExiting() const; + + + // These two virtual functions serve as notifications for derived + // thread waiting. + virtual void OnPushNonEmpty_Locked() { } + virtual void OnPopEmpty_Locked() { } + + + // *** PushCall with no result + + // Enqueue a member function of 'this' class to be called on consumer thread. + // By default the function returns immediately; set 'wait' argument to 'true' to + // wait for completion. + template + bool PushCall(R (C::*fn)(), bool wait = false) + { return PushCommand(ThreadCommandMF0(static_cast(this), fn, 0, wait)); } + template + bool PushCall(R (C::*fn)(A0), typename SelfType::Type a0, bool wait = false) + { return PushCommand(ThreadCommandMF1(static_cast(this), fn, 0, a0, wait)); } + template + bool PushCall(R (C::*fn)(A0, A1), + typename SelfType::Type a0, typename SelfType::Type a1, bool wait = false) + { return PushCommand(ThreadCommandMF2(static_cast(this), fn, 0, a0, a1, wait)); } + // Enqueue a specified member function call of class C. + // By default the function returns immediately; set 'wait' argument to 'true' to + // wait for completion. + template + bool PushCall(C* p, R (C::*fn)(), bool wait = false) + { return PushCommand(ThreadCommandMF0(p, fn, 0, wait)); } + template + bool PushCall(C* p, R (C::*fn)(A0), typename SelfType::Type a0, bool wait = false) + { return PushCommand(ThreadCommandMF1(p, fn, 0, a0, wait)); } + template + bool PushCall(C* p, R (C::*fn)(A0, A1), + typename SelfType::Type a0, typename SelfType::Type a1, bool wait = false) + { return PushCommand(ThreadCommandMF2(p, fn, 0, a0, a1, wait)); } + + + // *** PushCall with Result + + // Enqueue a member function of 'this' class call and wait for call to complete + // on consumer thread before returning. + template + bool PushCallAndWaitResult(R (C::*fn)(), R* ret) + { return PushCommand(ThreadCommandMF0(static_cast(this), fn, ret, true)); } + template + bool PushCallAndWaitResult(R (C::*fn)(A0), R* ret, typename SelfType::Type a0) + { return PushCommand(ThreadCommandMF1(static_cast(this), fn, ret, a0, true)); } + template + bool PushCallAndWaitResult(R (C::*fn)(A0, A1), R* ret, + typename SelfType::Type a0, typename SelfType::Type a1) + { return PushCommand(ThreadCommandMF2(static_cast(this), fn, ret, a0, a1, true)); } + // Enqueue a member function call for class C and wait for the call to complete + // on consumer thread before returning. + template + bool PushCallAndWaitResult(C* p, R (C::*fn)(), R* ret) + { return PushCommand(ThreadCommandMF0(p, fn, ret, true)); } + template + bool PushCallAndWaitResult(C* p, R (C::*fn)(A0), R* ret, typename SelfType::Type a0) + { return PushCommand(ThreadCommandMF1(p, fn, ret, a0, true)); } + template + bool PushCallAndWaitResult(C* p, R (C::*fn)(A0, A1), R* ret, + typename SelfType::Type a0, typename SelfType::Type a1) + { return PushCommand(ThreadCommandMF2(p, fn, ret, a0, a1, true)); } + +private: + class ThreadCommandQueueImpl* pImpl; +}; + + +} + +#endif // OVR_ThreadCommandQueue_h diff --git a/LibOVR/Src/OVR_Win32_DeviceManager.cpp b/LibOVR/Src/OVR_Win32_DeviceManager.cpp new file mode 100644 index 0000000..f7bb9eb --- /dev/null +++ b/LibOVR/Src/OVR_Win32_DeviceManager.cpp @@ -0,0 +1,423 @@ +/************************************************************************************ + +Filename : OVR_Win32_DeviceManager.cpp +Content : Win32 implementation of DeviceManager. +Created : September 21, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_Win32_DeviceManager.h" + +// Sensor & HMD Factories +#include "OVR_SensorImpl.h" +#include "OVR_LatencyTestImpl.h" +#include "OVR_Win32_HMDDevice.h" +#include "OVR_Win32_DeviceStatus.h" +#include "OVR_Win32_HIDDevice.h" + +#include "Kernel/OVR_Timer.h" +#include "Kernel/OVR_Std.h" +#include "Kernel/OVR_Log.h" + +DWORD Debug_WaitedObjectCount = 0; + +namespace OVR { namespace Win32 { + + +//------------------------------------------------------------------------------------- +// **** Win32::DeviceManager + +DeviceManager::DeviceManager() +{ + HidDeviceManager = *HIDDeviceManager::CreateInternal(this); +} + +DeviceManager::~DeviceManager() +{ + // make sure Shutdown was called. + OVR_ASSERT(!pThread); +} + +bool DeviceManager::Initialize(DeviceBase*) +{ + if (!DeviceManagerImpl::Initialize(0)) + return false; + + pThread = *new DeviceManagerThread(this); + if (!pThread || !pThread->Start()) + return false; + + pCreateDesc->pDevice = this; + LogText("OVR::DeviceManager - initialized.\n"); + return true; +} + +void DeviceManager::Shutdown() +{ + LogText("OVR::DeviceManager - shutting down.\n"); + + // Set Manager shutdown marker variable; this prevents + // any existing DeviceHandle objects from accessing device. + pCreateDesc->pLock->pManager = 0; + + // Push for thread shutdown *WITH NO WAIT*. + // This will have the following effect: + // - Exit command will get enqueued, which will be executed later on the thread itself. + // - Beyond this point, this DeviceManager object may be deleted by our caller. + // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will + // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued + // after pManager is null. + // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last + // reference to the thread object. + pThread->PushExitCommand(false); + pThread->DetachDeviceManager(); + pThread.Clear(); + + DeviceManagerImpl::Shutdown(); +} + +ThreadCommandQueue* DeviceManager::GetThreadQueue() +{ + return pThread; +} + +bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const +{ + if ((info->InfoClassType != Device_Manager) && + (info->InfoClassType != Device_None)) + return false; + + info->Type = Device_Manager; + info->Version = 0; + OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, "DeviceManager"); + OVR_strcpy(info->Manufacturer,DeviceInfo::MaxNameLength, "Oculus VR, Inc."); + return true; +} + +DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args) +{ + // TBD: Can this be avoided in the future, once proper device notification is in place? + if (GetThreadId() != OVR::GetCurrentThreadId()) + { + pThread->PushCall((DeviceManagerImpl*)this, + &DeviceManager::EnumerateAllFactoryDevices, true); + } + else + DeviceManager::EnumerateAllFactoryDevices(); + + return DeviceManagerImpl::EnumerateDevicesEx(args); +} + +ThreadId DeviceManager::GetThreadId() const +{ + return pThread->GetThreadId(); +} + +bool DeviceManager::GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const +{ + if (GetHIDDeviceManager()) + return static_cast(GetHIDDeviceManager())->GetHIDDeviceDesc(path, pdevDesc); + return false; +} + + +//------------------------------------------------------------------------------------- +// ***** DeviceManager Thread + +DeviceManagerThread::DeviceManagerThread(DeviceManager* pdevMgr) + : Thread(ThreadStackSize), hCommandEvent(0), pDeviceMgr(pdevMgr) +{ + // Create a non-signaled manual-reset event. + hCommandEvent = ::CreateEvent(0, TRUE, FALSE, 0); + if (!hCommandEvent) + return; + + // Must add event before starting. + AddOverlappedEvent(0, hCommandEvent); + + // Create device messages object. + pStatusObject = *new DeviceStatus(this); +} + +DeviceManagerThread::~DeviceManagerThread() +{ + // Remove overlapped event [0], after thread service exit. + if (hCommandEvent) + { + RemoveOverlappedEvent(0, hCommandEvent); + ::CloseHandle(hCommandEvent); + hCommandEvent = 0; + } +} + +int DeviceManagerThread::Run() +{ + ThreadCommand::PopBuffer command; + + SetThreadName("OVR::DeviceManagerThread"); + LogText("OVR::DeviceManagerThread - running (ThreadId=0x%X).\n", GetThreadId()); + + if (!pStatusObject->Initialize()) + { + LogText("OVR::DeviceManagerThread - failed to initialize MessageObject.\n"); + } + + while(!IsExiting()) + { + // PopCommand will reset event on empty queue. + if (PopCommand(&command)) + { + command.Execute(); + } + else + { + DWORD eventIndex = 0; + do { + UPInt numberOfWaitHandles = WaitHandles.GetSize(); + Debug_WaitedObjectCount = (DWORD)numberOfWaitHandles; + + DWORD waitMs = INFINITE; + + // If devices have time-dependent logic registered, get the longest wait + // allowed based on current ticks. + if (!TicksNotifiers.IsEmpty()) + { + UInt64 ticksMks = Timer::GetTicks(); + DWORD waitAllowed; + + for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++) + { + waitAllowed = (DWORD)(TicksNotifiers[j]->OnTicks(ticksMks) / Timer::MksPerMs); + if (waitAllowed < waitMs) + waitMs = waitAllowed; + } + } + + // Wait for event signals or window messages. + eventIndex = MsgWaitForMultipleObjects((DWORD)numberOfWaitHandles, &WaitHandles[0], FALSE, waitMs, QS_ALLINPUT); + + if (eventIndex != WAIT_FAILED) + { + if (eventIndex == WAIT_TIMEOUT) + continue; + + // TBD: Does this ever apply? + OVR_ASSERT(eventIndex < WAIT_ABANDONED_0); + + if (eventIndex == WAIT_OBJECT_0) + { + // Handle [0] services commands. + break; + } + else if (eventIndex == WAIT_OBJECT_0 + numberOfWaitHandles) + { + // Handle Windows messages. + pStatusObject->ProcessMessages(); + } + else + { + // Notify waiting device that its event is signaled. + unsigned i = eventIndex - WAIT_OBJECT_0; + OVR_ASSERT(i < numberOfWaitHandles); + if (WaitNotifiers[i]) + WaitNotifiers[i]->OnOverlappedEvent(WaitHandles[i]); + } + } + + } while(eventIndex != WAIT_FAILED); + + } + } + + pStatusObject->ShutDown(); + + LogText("OVR::DeviceManagerThread - exiting (ThreadId=0x%X).\n", GetThreadId()); + return 0; +} + +bool DeviceManagerThread::AddOverlappedEvent(Notifier* notify, HANDLE hevent) +{ + WaitNotifiers.PushBack(notify); + WaitHandles.PushBack(hevent); + + OVR_ASSERT(WaitNotifiers.GetSize() <= MAXIMUM_WAIT_OBJECTS); + return true; +} + +bool DeviceManagerThread::RemoveOverlappedEvent(Notifier* notify, HANDLE hevent) +{ + // [0] is reserved for thread commands with notify of null, but we still + // can use this function to remove it. + for (UPInt i = 0; i < WaitNotifiers.GetSize(); i++) + { + if ((WaitNotifiers[i] == notify) && (WaitHandles[i] == hevent)) + { + WaitNotifiers.RemoveAt(i); + WaitHandles.RemoveAt(i); + return true; + } + } + return false; +} + +bool DeviceManagerThread::AddTicksNotifier(Notifier* notify) +{ + TicksNotifiers.PushBack(notify); + return true; +} + +bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify) +{ + for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++) + { + if (TicksNotifiers[i] == notify) + { + TicksNotifiers.RemoveAt(i); + return true; + } + } + return false; +} + +bool DeviceManagerThread::AddMessageNotifier(Notifier* notify) +{ + MessageNotifiers.PushBack(notify); + return true; +} + +bool DeviceManagerThread::RemoveMessageNotifier(Notifier* notify) +{ + for (UPInt i = 0; i < MessageNotifiers.GetSize(); i++) + { + if (MessageNotifiers[i] == notify) + { + MessageNotifiers.RemoveAt(i); + return true; + } + } + return false; +} + +bool DeviceManagerThread::OnMessage(MessageType type, const String& devicePath) +{ + Notifier::DeviceMessageType notifierMessageType = Notifier::DeviceMessage_DeviceAdded; + if (type == DeviceAdded) + { + } + else if (type == DeviceRemoved) + { + notifierMessageType = Notifier::DeviceMessage_DeviceRemoved; + } + else + { + OVR_ASSERT(false); + } + + bool error = false; + bool deviceFound = false; + for (UPInt i = 0; i < MessageNotifiers.GetSize(); i++) + { + if (MessageNotifiers[i] && + MessageNotifiers[i]->OnDeviceMessage(notifierMessageType, devicePath, &error)) + { + // The notifier belonged to a device with the specified device name so we're done. + deviceFound = true; + break; + } + } + if (type == DeviceAdded && !deviceFound) + { + Lock::Locker devMgrLock(&DevMgrLock); + // a new device was connected. Go through all device factories and + // try to detect the device using HIDDeviceDesc. + HIDDeviceDesc devDesc; + if (pDeviceMgr->GetHIDDeviceDesc(devicePath, &devDesc)) + { + Lock::Locker deviceLock(pDeviceMgr->GetLock()); + DeviceFactory* factory = pDeviceMgr->Factories.GetFirst(); + while(!pDeviceMgr->Factories.IsNull(factory)) + { + if (factory->DetectHIDDevice(pDeviceMgr, devDesc)) + { + deviceFound = true; + break; + } + factory = factory->pNext; + } + } + } + + if (!deviceFound && strstr(devicePath.ToCStr(), "#OVR00")) + { + Ptr pmgr; + { + Lock::Locker devMgrLock(&DevMgrLock); + pmgr = pDeviceMgr; + } + // HMD plugged/unplugged + // This is not a final solution to enumerate HMD devices and get + // a first available handle. This won't work with multiple rifts. + // @TODO (!AB) + pmgr->EnumerateDevices(); + } + + return !error; +} + +void DeviceManagerThread::DetachDeviceManager() +{ + Lock::Locker devMgrLock(&DevMgrLock); + pDeviceMgr = NULL; +} + +} // namespace Win32 + + +//------------------------------------------------------------------------------------- +// ***** Creation + + +// Creates a new DeviceManager and initializes OVR. +DeviceManager* DeviceManager::Create() +{ + + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr manager = *new Win32::DeviceManager; + + if (manager) + { + if (manager->Initialize(0)) + { + manager->AddFactory(&SensorDeviceFactory::Instance); + manager->AddFactory(&LatencyTestDeviceFactory::Instance); + manager->AddFactory(&Win32::HMDDeviceFactory::Instance); + + manager->AddRef(); + } + else + { + manager.Clear(); + } + + } + + return manager.GetPtr(); +} + + +} // namespace OVR + diff --git a/LibOVR/Src/OVR_Win32_DeviceManager.h b/LibOVR/Src/OVR_Win32_DeviceManager.h new file mode 100644 index 0000000..3b86c67 --- /dev/null +++ b/LibOVR/Src/OVR_Win32_DeviceManager.h @@ -0,0 +1,146 @@ +/************************************************************************************ + +Filename : OVR_Win32_DeviceManager.h +Content : Win32-specific DeviceManager header. +Created : September 21, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Win32_DeviceManager_h +#define OVR_Win32_DeviceManager_h + +#include "OVR_DeviceImpl.h" +#include "OVR_Win32_DeviceStatus.h" + +#include "Kernel/OVR_Timer.h" + + +namespace OVR { namespace Win32 { + +class DeviceManagerThread; + +//------------------------------------------------------------------------------------- +// ***** Win32 DeviceManager + +class DeviceManager : public DeviceManagerImpl +{ +public: + DeviceManager(); + ~DeviceManager(); + + // Initialize/Shutdowncreate and shutdown manger thread. + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + virtual ThreadCommandQueue* GetThreadQueue(); + virtual ThreadId GetThreadId() const; + + virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args); + + virtual bool GetDeviceInfo(DeviceInfo* info) const; + + // Fills HIDDeviceDesc by using the path. + // Returns 'true' if successful, 'false' otherwise. + bool GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const; + + Ptr pThread; +}; + +//------------------------------------------------------------------------------------- +// ***** Device Manager Background Thread + +class DeviceManagerThread : public Thread, public ThreadCommandQueue, public DeviceStatus::Notifier +{ + friend class DeviceManager; + enum { ThreadStackSize = 32 * 1024 }; +public: + DeviceManagerThread(DeviceManager* pdevMgr); + ~DeviceManagerThread(); + + virtual int Run(); + + // ThreadCommandQueue notifications for CommandEvent handling. + virtual void OnPushNonEmpty_Locked() { ::SetEvent(hCommandEvent); } + virtual void OnPopEmpty_Locked() { ::ResetEvent(hCommandEvent); } + + + // Notifier used for different updates (EVENT or regular timing or messages). + class Notifier + { + public: + // Called when overlapped I/O handle is signaled. + virtual void OnOverlappedEvent(HANDLE hevent) { OVR_UNUSED1(hevent); } + + // Called when timing ticks are updated. + // Returns the largest number of microseconds this function can + // wait till next call. + virtual UInt64 OnTicks(UInt64 ticksMks) + { OVR_UNUSED1(ticksMks); return Timer::MksPerSecond * 1000; } + + enum DeviceMessageType + { + DeviceMessage_DeviceAdded = 0, + DeviceMessage_DeviceRemoved = 1, + }; + + // Called to notify device object. + virtual bool OnDeviceMessage(DeviceMessageType messageType, + const String& devicePath, + bool* error) + { OVR_UNUSED3(messageType, devicePath, error); return false; } + }; + + + // Adds device's OVERLAPPED structure for I/O. + // After it's added, Overlapped object will be signaled if a message arrives. + bool AddOverlappedEvent(Notifier* notify, HANDLE hevent); + bool RemoveOverlappedEvent(Notifier* notify, HANDLE hevent); + + // Add notifier that will be called at regular intervals. + bool AddTicksNotifier(Notifier* notify); + bool RemoveTicksNotifier(Notifier* notify); + + bool AddMessageNotifier(Notifier* notify); + bool RemoveMessageNotifier(Notifier* notify); + + // DeviceStatus::Notifier interface. + bool OnMessage(MessageType type, const String& devicePath); + + void DetachDeviceManager(); + +private: + bool threadInitialized() { return hCommandEvent != 0; } + + // Event used to wake us up thread commands are enqueued. + HANDLE hCommandEvent; + + // Event notifications for devices whose OVERLAPPED I/O we service. + // This list is modified through AddDeviceOverlappedEvent. + // WaitHandles[0] always == hCommandEvent, with null device. + Array WaitHandles; + Array WaitNotifiers; + + // Ticks notifiers - used for time-dependent events such as keep-alive. + Array TicksNotifiers; + + // Message notifiers. + Array MessageNotifiers; + + // Object that manages notifications originating from Windows messages. + Ptr pStatusObject; + + Lock DevMgrLock; + // pDeviceMgr should be accessed under DevMgrLock + DeviceManager* pDeviceMgr; // back ptr, no addref. +}; + +}} // namespace Win32::OVR + +#endif // OVR_Win32_DeviceManager_h diff --git a/LibOVR/Src/OVR_Win32_DeviceStatus.cpp b/LibOVR/Src/OVR_Win32_DeviceStatus.cpp new file mode 100644 index 0000000..80ec4f9 --- /dev/null +++ b/LibOVR/Src/OVR_Win32_DeviceStatus.cpp @@ -0,0 +1,350 @@ +/************************************************************************************ + +Filename : OVR_Win32_DeviceStatus.cpp +Content : Win32 implementation of DeviceStatus. +Created : January 24, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_Win32_DeviceStatus.h" + +#include "OVR_Win32_HIDDevice.h" + +#include "Kernel/OVR_Log.h" + +#include + +namespace OVR { namespace Win32 { + +static TCHAR windowClassName[] = TEXT("LibOVR_DeviceStatus_WindowClass"); + +//------------------------------------------------------------------------------------- +DeviceStatus::DeviceStatus(Notifier* const pClient) + : pNotificationClient(pClient), LastTimerId(0) +{ +} + +bool DeviceStatus::Initialize() +{ + + WNDCLASS wndClass; + wndClass.style = CS_HREDRAW | CS_VREDRAW; + wndClass.lpfnWndProc = WindowsMessageCallback; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = 0; + wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); + wndClass.lpszMenuName = NULL; + wndClass.lpszClassName = windowClassName; + + if (!RegisterClass(&wndClass)) + { + OVR_ASSERT_LOG(false, ("Failed to register window class.")); + return false; + } + + // We're going to create a 'message-only' window. This will be hidden, can't be enumerated etc. + // To do this we supply 'HWND_MESSAGE' as the hWndParent. + // http://msdn.microsoft.com/en-us/library/ms632599%28VS.85%29.aspx#message_only + hMessageWindow = CreateWindow( windowClassName, + windowClassName, + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + HWND_MESSAGE, + NULL, + 0, + this); // Pass this object via the CREATESTRUCT mechanism + // so that we can attach it to the window user data. + + if (hMessageWindow == NULL) + { + OVR_ASSERT_LOG(false, ("Failed to create window.")); + return false; + } + + // According to MS, topmost windows receive WM_DEVICECHANGE faster. + ::SetWindowPos(hMessageWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE); + UpdateWindow(hMessageWindow); + + + // Register notification for additional HID messages. + HIDDeviceManager* hidDeviceManager = new HIDDeviceManager(NULL); + HidGuid = hidDeviceManager->GetHIDGuid(); + hidDeviceManager->Release(); + + DEV_BROADCAST_DEVICEINTERFACE notificationFilter; + + ZeroMemory(¬ificationFilter, sizeof(notificationFilter)); + notificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); + notificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + //notificationFilter.dbcc_classguid = hidguid; + + // We need DEVICE_NOTIFY_ALL_INTERFACE_CLASSES to detect + // HDMI plug/unplug events. + hDeviceNotify = RegisterDeviceNotification( + hMessageWindow, + ¬ificationFilter, + DEVICE_NOTIFY_ALL_INTERFACE_CLASSES|DEVICE_NOTIFY_WINDOW_HANDLE); + + if (hDeviceNotify == NULL) + { + OVR_ASSERT_LOG(false, ("Failed to register for device notifications.")); + return false; + } + + return true; +} + +void DeviceStatus::ShutDown() +{ + OVR_ASSERT(hMessageWindow); + + if (!UnregisterDeviceNotification(hDeviceNotify)) + { + OVR_ASSERT_LOG(false, ("Failed to unregister device notification.")); + } + + PostMessage(hMessageWindow, WM_CLOSE, 0, 0); + + while (hMessageWindow != NULL) + { + ProcessMessages(); + Sleep(1); + } + + if (!UnregisterClass(windowClassName, NULL)) + { + OVR_ASSERT_LOG(false, ("Failed to unregister window class.")); + } +} + +DeviceStatus::~DeviceStatus() +{ + OVR_ASSERT_LOG(hMessageWindow == NULL, ("Need to call 'ShutDown' from DeviceManagerThread.")); +} + +void DeviceStatus::ProcessMessages() +{ + OVR_ASSERT_LOG(hMessageWindow != NULL, ("Need to call 'Initialize' before first use.")); + + MSG msg; + + // Note WM_DEVICECHANGED messages are dispatched but not retrieved by PeekMessage. + // I think this is because they are pending, non-queued messages. + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +bool DeviceStatus::MessageCallback(WORD messageType, const String& devicePath) +{ + bool rv = true; + if (messageType == DBT_DEVICEARRIVAL) + { + rv = pNotificationClient->OnMessage(Notifier::DeviceAdded, devicePath); + } + else if (messageType == DBT_DEVICEREMOVECOMPLETE) + { + pNotificationClient->OnMessage(Notifier::DeviceRemoved, devicePath); + } + else + { + OVR_ASSERT(0); + } + return rv; +} + +void DeviceStatus::CleanupRecoveryTimer(UPInt index) +{ + ::KillTimer(hMessageWindow, RecoveryTimers[index].TimerId); + RecoveryTimers.RemoveAt(index); +} + +DeviceStatus::RecoveryTimerDesc* +DeviceStatus::FindRecoveryTimer(UINT_PTR timerId, UPInt* pindex) +{ + for (UPInt i = 0, n = RecoveryTimers.GetSize(); i < n; ++i) + { + RecoveryTimerDesc* pdesc = &RecoveryTimers[i]; + if (pdesc->TimerId == timerId) + { + *pindex = i; + return pdesc; + } + } + return NULL; +} + +void DeviceStatus::FindAndCleanupRecoveryTimer(const String& devicePath) +{ + for (UPInt i = 0, n = RecoveryTimers.GetSize(); i < n; ++i) + { + RecoveryTimerDesc* pdesc = &RecoveryTimers[i]; + if (pdesc->DevicePath.CompareNoCase(devicePath)) + { + CleanupRecoveryTimer(i); + break; + } + } +} + +LRESULT CALLBACK DeviceStatus::WindowsMessageCallback( HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + switch (message) + { + case WM_CREATE: + { + // Setup window user data with device status object pointer. + LPCREATESTRUCT create_struct = reinterpret_cast(lParam); + void *lpCreateParam = create_struct->lpCreateParams; + DeviceStatus *pDeviceStatus = reinterpret_cast(lpCreateParam); + + SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(pDeviceStatus)); + } + return 0; // Return 0 for successfully handled WM_CREATE. + + case WM_DEVICECHANGE: + { + WORD loword = LOWORD(wParam); + + if (loword != DBT_DEVICEARRIVAL && + loword != DBT_DEVICEREMOVECOMPLETE) + { + // Ignore messages other than device arrive and remove complete + // (we're not handling intermediate ones). + return TRUE; // Grant WM_DEVICECHANGE request. + } + + DEV_BROADCAST_DEVICEINTERFACE* hdr; + hdr = (DEV_BROADCAST_DEVICEINTERFACE*) lParam; + + if (hdr->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE) + { + // Ignore non interface device messages. + return TRUE; // Grant WM_DEVICECHANGE request. + } + + LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA); + OVR_ASSERT(userData != NULL); + + // Call callback on device messages object with the device path. + DeviceStatus* pDeviceStatus = (DeviceStatus*) userData; + String devicePath(hdr->dbcc_name); + + // check if HID device caused the event... + if (pDeviceStatus->HidGuid == hdr->dbcc_classguid) + { + // check if recovery timer is already running; stop it and + // remove it, if so. + pDeviceStatus->FindAndCleanupRecoveryTimer(devicePath); + + if (!pDeviceStatus->MessageCallback(loword, devicePath)) + { + // hmmm.... unsuccessful + if (loword == DBT_DEVICEARRIVAL) + { + // Windows sometimes may return errors ERROR_SHARING_VIOLATION and + // ERROR_FILE_NOT_FOUND when trying to open an USB device via + // CreateFile. Need to start a recovery timer that will try to + // re-open the device again. + OVR_DEBUG_LOG(("Adding failed, recovering through a timer...")); + UINT_PTR tid = ::SetTimer(hwnd, ++pDeviceStatus->LastTimerId, + USBRecoveryTimeInterval, NULL); + RecoveryTimerDesc rtDesc; + rtDesc.TimerId = tid; + rtDesc.DevicePath = devicePath; + rtDesc.NumAttempts= 0; + pDeviceStatus->RecoveryTimers.PushBack(rtDesc); + // wrap around the timer counter, avoid timerId == 0... + if (pDeviceStatus->LastTimerId + 1 == 0) + pDeviceStatus->LastTimerId = 0; + } + } + } + // Check if Oculus HDMI device was plugged/unplugged, preliminary + // filtering. (is there any way to get GUID? !AB) + //else if (strstr(devicePath.ToCStr(), "DISPLAY#")) + else if (strstr(devicePath.ToCStr(), "#OVR00")) + { + pDeviceStatus->MessageCallback(loword, devicePath); + } + } + return TRUE; // Grant WM_DEVICECHANGE request. + + case WM_TIMER: + { + if (wParam != 0) + { + LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA); + OVR_ASSERT(userData != NULL); + + // Call callback on device messages object with the device path. + DeviceStatus* pDeviceStatus = (DeviceStatus*) userData; + + // Check if we have recovery timer running (actually, we must be!) + UPInt rtIndex; + RecoveryTimerDesc* prtDesc = pDeviceStatus->FindRecoveryTimer(wParam, &rtIndex); + if (prtDesc) + { + if (pDeviceStatus->MessageCallback(DBT_DEVICEARRIVAL, prtDesc->DevicePath)) + { + OVR_DEBUG_LOG(("Recovered, adding is successful, cleaning up the timer...")); + // now it is successful, kill the timer and cleanup + pDeviceStatus->CleanupRecoveryTimer(rtIndex); + } + else + { + if (++prtDesc->NumAttempts >= MaxUSBRecoveryAttempts) + { + OVR_DEBUG_LOG(("Failed to recover USB after %d attempts, path = '%s', aborting...", + prtDesc->NumAttempts, prtDesc->DevicePath.ToCStr())); + pDeviceStatus->CleanupRecoveryTimer(rtIndex); + } + else + { + OVR_DEBUG_LOG(("Failed to recover USB, %d attempts, path = '%s'", + prtDesc->NumAttempts, prtDesc->DevicePath.ToCStr())); + } + } + } + } + } + return 0; + + case WM_CLOSE: + { + LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA); + OVR_ASSERT(userData != NULL); + DeviceStatus* pDeviceStatus = (DeviceStatus*) userData; + pDeviceStatus->hMessageWindow = NULL; + + DestroyWindow(hwnd); + } + return 0; // We processed the WM_CLOSE message. + + case WM_DESTROY: + PostQuitMessage(0); + return 0; // We processed the WM_DESTROY message. + } + + return DefWindowProc(hwnd, message, wParam, lParam); +} + +}} // namespace OVR::Win32 diff --git a/LibOVR/Src/OVR_Win32_DeviceStatus.h b/LibOVR/Src/OVR_Win32_DeviceStatus.h new file mode 100644 index 0000000..820e3a5 --- /dev/null +++ b/LibOVR/Src/OVR_Win32_DeviceStatus.h @@ -0,0 +1,101 @@ +/************************************************************************************ + +Filename : OVR_Win32_DeviceStatus.h +Content : Win32-specific DeviceStatus header. +Created : January 24, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Win32_DeviceStatus_h +#define OVR_Win32_DeviceStatus_h + +#include +#include "Kernel/OVR_String.h" +#include "Kernel/OVR_RefCount.h" +#include "Kernel/OVR_Array.h" + +namespace OVR { namespace Win32 { + +//------------------------------------------------------------------------------------- +// ***** DeviceStatus +// +// DeviceStatus abstracts the handling of windows messages of interest for +// example the WM_DEVICECHANGED message which occurs when a device is plugged/unplugged. +// The device manager thread creates an instance of this class and passes its pointer +// in the constructor. That thread is also responsible for periodically calling 'ProcessMessages' +// to process queued windows messages. The client is notified via the 'OnMessage' method +// declared in the 'DeviceMessages::Notifier' interface. +class DeviceStatus : public RefCountBase +{ +public: + + // Notifier used for device messages. + class Notifier + { + public: + enum MessageType + { + DeviceAdded = 0, + DeviceRemoved = 1, + }; + + virtual bool OnMessage(MessageType type, const String& devicePath) + { OVR_UNUSED2(type, devicePath); return true; } + }; + + DeviceStatus(Notifier* const pClient); + ~DeviceStatus(); + + void operator = (const DeviceStatus&); // No assignment implementation. + + bool Initialize(); + void ShutDown(); + + void ProcessMessages(); + +private: + enum + { + MaxUSBRecoveryAttempts = 20, + USBRecoveryTimeInterval = 500 // ms + }; + struct RecoveryTimerDesc + { + UINT_PTR TimerId; + String DevicePath; + unsigned NumAttempts; + }; + + static LRESULT CALLBACK WindowsMessageCallback( HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam); + + bool MessageCallback(WORD messageType, const String& devicePath); + + void CleanupRecoveryTimer(UPInt index); + RecoveryTimerDesc* FindRecoveryTimer(UINT_PTR timerId, UPInt* pindex); + void FindAndCleanupRecoveryTimer(const String& devicePath); + +private: // data + Notifier* const pNotificationClient; // Don't reference count a back-pointer. + + HWND hMessageWindow; + HDEVNOTIFY hDeviceNotify; + + UINT_PTR LastTimerId; + Array RecoveryTimers; + + GUID HidGuid; +}; + +}} // namespace OVR::Win32 + +#endif // OVR_Win32_DeviceStatus_h diff --git a/LibOVR/Src/OVR_Win32_HIDDevice.cpp b/LibOVR/Src/OVR_Win32_HIDDevice.cpp new file mode 100644 index 0000000..8c04bcf --- /dev/null +++ b/LibOVR/Src/OVR_Win32_HIDDevice.cpp @@ -0,0 +1,637 @@ +/************************************************************************************ + +Filename : OVR_Win32_HIDDevice.cpp +Content : Win32 HID device implementation. +Created : February 22, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_Win32_HIDDevice.h" +#include "OVR_Win32_DeviceManager.h" + +#include "Kernel/OVR_System.h" +#include "Kernel/OVR_Log.h" + +namespace OVR { namespace Win32 { + +//------------------------------------------------------------------------------------- +// HIDDevicePathWrapper is a simple class used to extract HID device file path +// through SetupDiGetDeviceInterfaceDetail. We use a class since this is a bit messy. +class HIDDevicePathWrapper +{ + SP_INTERFACE_DEVICE_DETAIL_DATA_A* pData; +public: + HIDDevicePathWrapper() : pData(0) { } + ~HIDDevicePathWrapper() { if (pData) OVR_FREE(pData); } + + const char* GetPath() const { return pData ? pData->DevicePath : 0; } + + bool InitPathFromInterfaceData(HDEVINFO hdevInfoSet, SP_DEVICE_INTERFACE_DATA* pidata); +}; + +bool HIDDevicePathWrapper::InitPathFromInterfaceData(HDEVINFO hdevInfoSet, SP_DEVICE_INTERFACE_DATA* pidata) +{ + DWORD detailSize = 0; + // SetupDiGetDeviceInterfaceDetailA returns "not enough buffer error code" + // doe size request. Just check valid size. + SetupDiGetDeviceInterfaceDetailA(hdevInfoSet, pidata, NULL, 0, &detailSize, NULL); + if (!detailSize || + ((pData = (SP_INTERFACE_DEVICE_DETAIL_DATA_A*)OVR_ALLOC(detailSize)) == 0)) + return false; + pData->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA_A); + + if (!SetupDiGetDeviceInterfaceDetailA(hdevInfoSet, pidata, pData, detailSize, NULL, NULL)) + return false; + return true; +} + + +//------------------------------------------------------------------------------------- +// **** Win32::DeviceManager + +HIDDeviceManager::HIDDeviceManager(DeviceManager* manager) + : Manager(manager) +{ + hHidLib = ::LoadLibraryA("hid.dll"); + OVR_ASSERT_LOG(hHidLib, ("Couldn't load Win32 'hid.dll'.")); + + OVR_RESOLVE_HIDFUNC(HidD_GetHidGuid); + OVR_RESOLVE_HIDFUNC(HidD_SetNumInputBuffers); + OVR_RESOLVE_HIDFUNC(HidD_GetFeature); + OVR_RESOLVE_HIDFUNC(HidD_SetFeature); + OVR_RESOLVE_HIDFUNC(HidD_GetAttributes); + OVR_RESOLVE_HIDFUNC(HidD_GetManufacturerString); + OVR_RESOLVE_HIDFUNC(HidD_GetProductString); + OVR_RESOLVE_HIDFUNC(HidD_GetSerialNumberString); + OVR_RESOLVE_HIDFUNC(HidD_GetPreparsedData); + OVR_RESOLVE_HIDFUNC(HidD_FreePreparsedData); + OVR_RESOLVE_HIDFUNC(HidP_GetCaps); + + if (HidD_GetHidGuid) + HidD_GetHidGuid(&HidGuid); +} + +HIDDeviceManager::~HIDDeviceManager() +{ + ::FreeLibrary(hHidLib); +} + +bool HIDDeviceManager::Initialize() +{ + return true; +} + +void HIDDeviceManager::Shutdown() +{ + LogText("OVR::Win32::HIDDeviceManager - shutting down.\n"); +} + +bool HIDDeviceManager::Enumerate(HIDEnumerateVisitor* enumVisitor) +{ + HDEVINFO hdevInfoSet; + SP_DEVICE_INTERFACE_DATA interfaceData; + interfaceData.cbSize = sizeof(interfaceData); + + // Get handle to info data set describing all available HIDs. + hdevInfoSet = SetupDiGetClassDevsA(&HidGuid, NULL, NULL, DIGCF_INTERFACEDEVICE | DIGCF_PRESENT); + if (hdevInfoSet == INVALID_HANDLE_VALUE) + return false; + + for(int deviceIndex = 0; + SetupDiEnumDeviceInterfaces(hdevInfoSet, NULL, &HidGuid, deviceIndex, &interfaceData); + deviceIndex++) + { + // For each device, we extract its file path and open it to get attributes, + // such as vendor and product id. If anything goes wrong, we move onto next device. + HIDDevicePathWrapper pathWrapper; + if (!pathWrapper.InitPathFromInterfaceData(hdevInfoSet, &interfaceData)) + continue; + + // Look for the device to check if it is already opened. + Ptr existingDevice = Manager->FindDevice(pathWrapper.GetPath()); + // if device exists and it is opened then most likely the CreateHIDFile + // will fail; therefore, we just set Enumerated to 'true' and continue. + if (existingDevice && existingDevice->pDevice) + { + existingDevice->Enumerated = true; + continue; + } + + // open device in non-exclusive mode for detection... + HANDLE hidDev = CreateHIDFile(pathWrapper.GetPath(), false); + if (hidDev == INVALID_HANDLE_VALUE) + continue; + + HIDDeviceDesc devDesc; + devDesc.Path = pathWrapper.GetPath(); + if (initVendorProductVersion(hidDev, &devDesc) && + enumVisitor->MatchVendorProduct(devDesc.VendorId, devDesc.ProductId) && + initUsage(hidDev, &devDesc)) + { + initStrings(hidDev, &devDesc); + + // Construct minimal device that the visitor callback can get feature reports from. + Win32::HIDDevice device(this, hidDev); + enumVisitor->Visit(device, devDesc); + } + + ::CloseHandle(hidDev); + } + + SetupDiDestroyDeviceInfoList(hdevInfoSet); + return true; +} + +bool HIDDeviceManager::GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const +{ + // open device in non-exclusive mode for detection... + HANDLE hidDev = CreateHIDFile(path, false); + if (hidDev == INVALID_HANDLE_VALUE) + return false; + + pdevDesc->Path = path; + getFullDesc(hidDev, pdevDesc); + + ::CloseHandle(hidDev); + return true; +} + +OVR::HIDDevice* HIDDeviceManager::Open(const String& path) +{ + + Ptr device = *new Win32::HIDDevice(this); + + if (device->HIDInitialize(path)) + { + device->AddRef(); + return device; + } + + return NULL; +} + +bool HIDDeviceManager::getFullDesc(HANDLE hidDev, HIDDeviceDesc* desc) const +{ + + if (!initVendorProductVersion(hidDev, desc)) + { + return false; + } + + if (!initUsage(hidDev, desc)) + { + return false; + } + + initStrings(hidDev, desc); + + return true; +} + +bool HIDDeviceManager::initVendorProductVersion(HANDLE hidDev, HIDDeviceDesc* desc) const +{ + HIDD_ATTRIBUTES attr; + attr.Size = sizeof(attr); + if (!HidD_GetAttributes(hidDev, &attr)) + return false; + desc->VendorId = attr.VendorID; + desc->ProductId = attr.ProductID; + desc->VersionNumber = attr.VersionNumber; + return true; +} + +bool HIDDeviceManager::initUsage(HANDLE hidDev, HIDDeviceDesc* desc) const +{ + bool result = false; + HIDP_CAPS caps; + HIDP_PREPARSED_DATA* preparsedData = 0; + + if (!HidD_GetPreparsedData(hidDev, &preparsedData)) + return false; + + if (HidP_GetCaps(preparsedData, &caps) == HIDP_STATUS_SUCCESS) + { + desc->Usage = caps.Usage; + desc->UsagePage = caps.UsagePage; + result = true; + } + HidD_FreePreparsedData(preparsedData); + return result; +} + +void HIDDeviceManager::initStrings(HANDLE hidDev, HIDDeviceDesc* desc) const +{ + // Documentation mentions 126 as being the max for USB. + wchar_t strBuffer[196]; + + // HidD_Get*String functions return nothing in buffer on failure, + // so it's ok to do this without further error checking. + strBuffer[0] = 0; + HidD_GetManufacturerString(hidDev, strBuffer, sizeof(strBuffer)); + desc->Manufacturer = strBuffer; + + strBuffer[0] = 0; + HidD_GetProductString(hidDev, strBuffer, sizeof(strBuffer)); + desc->Product = strBuffer; + + strBuffer[0] = 0; + HidD_GetSerialNumberString(hidDev, strBuffer, sizeof(strBuffer)); + desc->SerialNumber = strBuffer; +} + +//------------------------------------------------------------------------------------- +// **** Win32::HIDDevice + +HIDDevice::HIDDevice(HIDDeviceManager* manager) + : HIDManager(manager), inMinimalMode(false), Device(0), ReadRequested(false) +{ + memset(&ReadOverlapped, 0, sizeof(OVERLAPPED)); +} + +// This is a minimal constructor used during enumeration for us to pass +// a HIDDevice to the visit function (so that it can query feature reports). +HIDDevice::HIDDevice(HIDDeviceManager* manager, HANDLE device) + : HIDManager(manager), inMinimalMode(true), Device(device), ReadRequested(true) +{ + memset(&ReadOverlapped, 0, sizeof(OVERLAPPED)); +} + +HIDDevice::~HIDDevice() +{ + if (!inMinimalMode) + { + HIDShutdown(); + } +} + +bool HIDDevice::HIDInitialize(const String& path) +{ + + DevDesc.Path = path; + + if (!openDevice()) + { + LogText("OVR::Win32::HIDDevice - Failed to open HIDDevice: ", path); + return false; + } + + + HIDManager->Manager->pThread->AddTicksNotifier(this); + HIDManager->Manager->pThread->AddMessageNotifier(this); + + LogText("OVR::Win32::HIDDevice - Opened '%s'\n" + " Manufacturer:'%s' Product:'%s' Serial#:'%s'\n", + DevDesc.Path.ToCStr(), + DevDesc.Manufacturer.ToCStr(), DevDesc.Product.ToCStr(), + DevDesc.SerialNumber.ToCStr()); + + return true; +} + +bool HIDDevice::initInfo() +{ + // Device must have been successfully opened. + OVR_ASSERT(Device); + + // Get report lengths. + HIDP_PREPARSED_DATA* preparsedData = 0; + if (!HIDManager->HidD_GetPreparsedData(Device, &preparsedData)) + { + return false; + } + + HIDP_CAPS caps; + if (HIDManager->HidP_GetCaps(preparsedData, &caps) != HIDP_STATUS_SUCCESS) + { + HIDManager->HidD_FreePreparsedData(preparsedData); + return false; + } + + InputReportBufferLength = caps.InputReportByteLength; + OutputReportBufferLength = caps.OutputReportByteLength; + FeatureReportBufferLength= caps.FeatureReportByteLength; + HIDManager->HidD_FreePreparsedData(preparsedData); + + if (ReadBufferSize < InputReportBufferLength) + { + OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer.")); + return false; + } + + // Get device desc. + if (!HIDManager->getFullDesc(Device, &DevDesc)) + { + OVR_ASSERT_LOG(false, ("Failed to get device desc while initializing device.")); + return false; + } + + return true; +} + +bool HIDDevice::openDevice() +{ + memset(&ReadOverlapped, 0, sizeof(OVERLAPPED)); + + Device = HIDManager->CreateHIDFile(DevDesc.Path.ToCStr()); + if (Device == INVALID_HANDLE_VALUE) + { + OVR_DEBUG_LOG(("Failed 'CreateHIDFile' while opening device, error = 0x%X.", + ::GetLastError())); + Device = 0; + return false; + } + + if (!HIDManager->HidD_SetNumInputBuffers(Device, 128)) + { + OVR_ASSERT_LOG(false, ("Failed 'HidD_SetNumInputBuffers' while initializing device.")); + ::CloseHandle(Device); + Device = 0; + return false; + } + + + // Create a manual-reset non-signaled event. + ReadOverlapped.hEvent = ::CreateEvent(0, TRUE, FALSE, 0); + + if (!ReadOverlapped.hEvent) + { + OVR_ASSERT_LOG(false, ("Failed to create event.")); + ::CloseHandle(Device); + Device = 0; + return false; + } + + if (!initInfo()) + { + OVR_ASSERT_LOG(false, ("Failed to get HIDDevice info.")); + + ::CloseHandle(ReadOverlapped.hEvent); + memset(&ReadOverlapped, 0, sizeof(OVERLAPPED)); + + ::CloseHandle(Device); + Device = 0; + return false; + } + + if (!initializeRead()) + { + OVR_ASSERT_LOG(false, ("Failed to get intialize read for HIDDevice.")); + + ::CloseHandle(ReadOverlapped.hEvent); + memset(&ReadOverlapped, 0, sizeof(OVERLAPPED)); + + ::CloseHandle(Device); + Device = 0; + return false; + } + + return true; +} + +void HIDDevice::HIDShutdown() +{ + + HIDManager->Manager->pThread->RemoveTicksNotifier(this); + HIDManager->Manager->pThread->RemoveMessageNotifier(this); + + closeDevice(); + LogText("OVR::Win32::HIDDevice - Closed '%s'\n", DevDesc.Path.ToCStr()); +} + +bool HIDDevice::initializeRead() +{ + + if (!ReadRequested) + { + HIDManager->Manager->pThread->AddOverlappedEvent(this, ReadOverlapped.hEvent); + ReadRequested = true; + } + + // Read resets the event... + while(::ReadFile(Device, ReadBuffer, InputReportBufferLength, 0, &ReadOverlapped)) + { + processReadResult(); + } + + if (GetLastError() != ERROR_IO_PENDING) + { + // Some other error (such as unplugged). + closeDeviceOnIOError(); + return false; + } + + return true; +} + +bool HIDDevice::processReadResult() +{ + + OVR_ASSERT(ReadRequested); + + DWORD bytesRead = 0; + + if (GetOverlappedResult(Device, &ReadOverlapped, &bytesRead, FALSE)) + { + // We've got data. + if (Handler) + { + Handler->OnInputReport(ReadBuffer, bytesRead); + } + + // TBD: Not needed? + // Event should be reset by Read call... + ReadOverlapped.Pointer = 0; + ReadOverlapped.Internal = 0; + ReadOverlapped.InternalHigh = 0; + return true; + } + else + { + if (GetLastError() != ERROR_IO_PENDING) + { + closeDeviceOnIOError(); + return false; + } + } + + return false; +} + +void HIDDevice::closeDevice() +{ + if (ReadRequested) + { + HIDManager->Manager->pThread->RemoveOverlappedEvent(this, ReadOverlapped.hEvent); + ReadRequested = false; + // Must call this to avoid Win32 assertion; CloseHandle is not enough. + ::CancelIo(Device); + } + + ::CloseHandle(ReadOverlapped.hEvent); + memset(&ReadOverlapped, 0, sizeof(OVERLAPPED)); + + ::CloseHandle(Device); + Device = 0; +} + +void HIDDevice::closeDeviceOnIOError() +{ + LogText("OVR::Win32::HIDDevice - Lost connection to '%s'\n", DevDesc.Path.ToCStr()); + closeDevice(); +} + +bool HIDDevice::SetFeatureReport(UByte* data, UInt32 length) +{ + if (!ReadRequested) + return false; + + return HIDManager->HidD_SetFeature(Device, data, (ULONG) length) != FALSE; +} + +bool HIDDevice::GetFeatureReport(UByte* data, UInt32 length) +{ + if (!ReadRequested) + return false; + + return HIDManager->HidD_GetFeature(Device, data, (ULONG) length) != FALSE; +} + +void HIDDevice::OnOverlappedEvent(HANDLE hevent) +{ + OVR_UNUSED(hevent); + OVR_ASSERT(hevent == ReadOverlapped.hEvent); + + if (processReadResult()) + { + // Proceed to read again. + initializeRead(); + } +} + +UInt64 HIDDevice::OnTicks(UInt64 ticksMks) +{ + if (Handler) + { + return Handler->OnTicks(ticksMks); + } + + return DeviceManagerThread::Notifier::OnTicks(ticksMks); +} + +bool HIDDevice::OnDeviceMessage(DeviceMessageType messageType, + const String& devicePath, + bool* error) +{ + + // Is this the correct device? + if (DevDesc.Path.CompareNoCase(devicePath) != 0) + { + return false; + } + + if (messageType == DeviceMessage_DeviceAdded && !Device) + { + // A closed device has been re-added. Try to reopen. + if (!openDevice()) + { + LogError("OVR::Win32::HIDDevice - Failed to reopen a device '%s' that was re-added.\n", devicePath.ToCStr()); + *error = true; + return true; + } + + LogText("OVR::Win32::HIDDevice - Reopened device '%s'\n", devicePath.ToCStr()); + } + + HIDHandler::HIDDeviceMessageType handlerMessageType = HIDHandler::HIDDeviceMessage_DeviceAdded; + if (messageType == DeviceMessage_DeviceAdded) + { + } + else if (messageType == DeviceMessage_DeviceRemoved) + { + handlerMessageType = HIDHandler::HIDDeviceMessage_DeviceRemoved; + } + else + { + OVR_ASSERT(0); + } + + if (Handler) + { + Handler->OnDeviceMessage(handlerMessageType); + } + + *error = false; + return true; +} + +HIDDeviceManager* HIDDeviceManager::CreateInternal(Win32::DeviceManager* devManager) +{ + + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr manager = *new Win32::HIDDeviceManager(devManager); + + if (manager) + { + if (manager->Initialize()) + { + manager->AddRef(); + } + else + { + manager.Clear(); + } + } + + return manager.GetPtr(); +} + +} // namespace Win32 + +//------------------------------------------------------------------------------------- +// ***** Creation + +// Creates a new HIDDeviceManager and initializes OVR. +HIDDeviceManager* HIDDeviceManager::Create() +{ + OVR_ASSERT_LOG(false, ("Standalone mode not implemented yet.")); + + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr manager = *new Win32::HIDDeviceManager(NULL); + + if (manager) + { + if (manager->Initialize()) + { + manager->AddRef(); + } + else + { + manager.Clear(); + } + } + + return manager.GetPtr(); +} + +} // namespace OVR diff --git a/LibOVR/Src/OVR_Win32_HIDDevice.h b/LibOVR/Src/OVR_Win32_HIDDevice.h new file mode 100644 index 0000000..efe308d --- /dev/null +++ b/LibOVR/Src/OVR_Win32_HIDDevice.h @@ -0,0 +1,194 @@ +/************************************************************************************ + +Filename : OVR_Win32_HIDDevice.h +Content : Win32 HID device implementation. +Created : February 22, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Win32_HIDDevice_h +#define OVR_Win32_HIDDevice_h + +#include "OVR_HIDDevice.h" +#include "OVR_Win32_DeviceManager.h" + +#include +#include + +//------------------------------------------------------------------------------------- +// Define needed "hidsdi.h" functionality to avoid requiring DDK installation. +// #include "hidsdi.h" + +#ifndef _HIDSDI_H +#define _HIDSDI_H +#include + +#define HIDP_STATUS_SUCCESS (0x11 << 16) +struct HIDP_PREPARSED_DATA; + +struct HIDD_ATTRIBUTES +{ + ULONG Size; // = sizeof (struct _HIDD_ATTRIBUTES) + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; +}; + +struct HIDP_CAPS +{ + USHORT Usage; + USHORT UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + + USHORT NumberLinkCollectionNodes; + USHORT NumberInputButtonCaps; + USHORT NumberInputValueCaps; + USHORT NumberInputDataIndices; + USHORT NumberOutputButtonCaps; + USHORT NumberOutputValueCaps; + USHORT NumberOutputDataIndices; + USHORT NumberFeatureButtonCaps; + USHORT NumberFeatureValueCaps; + USHORT NumberFeatureDataIndices; +}; + +#include +#endif + + +namespace OVR { namespace Win32 { + +class HIDDeviceManager; +class DeviceManager; + +//------------------------------------------------------------------------------------- +// ***** Win32 HIDDevice + +class HIDDevice : public OVR::HIDDevice, public DeviceManagerThread::Notifier +{ +public: + + HIDDevice(HIDDeviceManager* manager); + + // This is a minimal constructor used during enumeration for us to pass + // a HIDDevice to the visit function (so that it can query feature reports). + HIDDevice(HIDDeviceManager* manager, HANDLE device); + + ~HIDDevice(); + + bool HIDInitialize(const String& path); + void HIDShutdown(); + + // OVR::HIDDevice + bool SetFeatureReport(UByte* data, UInt32 length); + bool GetFeatureReport(UByte* data, UInt32 length); + + + // DeviceManagerThread::Notifier + void OnOverlappedEvent(HANDLE hevent); + UInt64 OnTicks(UInt64 ticksMks); + bool OnDeviceMessage(DeviceMessageType messageType, const String& devicePath, bool* error); + +private: + bool openDevice(); + bool initInfo(); + bool initializeRead(); + bool processReadResult(); + void closeDevice(); + void closeDeviceOnIOError(); + + bool inMinimalMode; + HIDDeviceManager* HIDManager; + HANDLE Device; + HIDDeviceDesc DevDesc; + + OVERLAPPED ReadOverlapped; + bool ReadRequested; + + enum { ReadBufferSize = 96 }; + UByte ReadBuffer[ReadBufferSize]; + + UInt16 InputReportBufferLength; + UInt16 OutputReportBufferLength; + UInt16 FeatureReportBufferLength; +}; + +//------------------------------------------------------------------------------------- +// ***** Win32 HIDDeviceManager + +class HIDDeviceManager : public OVR::HIDDeviceManager +{ + friend class HIDDevice; +public: + + HIDDeviceManager(DeviceManager* manager); + virtual ~HIDDeviceManager(); + + virtual bool Initialize(); + virtual void Shutdown(); + + virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor); + virtual OVR::HIDDevice* Open(const String& path); + + // Fills HIDDeviceDesc by using the path. + // Returns 'true' if successful, 'false' otherwise. + bool GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const; + + GUID GetHIDGuid() { return HidGuid; } + + static HIDDeviceManager* CreateInternal(DeviceManager* manager); + +private: + + DeviceManager* Manager; // Back pointer can just be a raw pointer. + + HMODULE hHidLib; + GUID HidGuid; + + // Macros to declare and resolve needed functions from library. +#define OVR_DECLARE_HIDFUNC(func, rettype, args) \ +typedef rettype (__stdcall *PFn_##func) args; \ +PFn_##func func; +#define OVR_RESOLVE_HIDFUNC(func) \ +func = (PFn_##func)::GetProcAddress(hHidLib, #func) + + OVR_DECLARE_HIDFUNC(HidD_GetHidGuid, void, (GUID *hidGuid)); + OVR_DECLARE_HIDFUNC(HidD_SetNumInputBuffers, BOOLEAN, (HANDLE hidDev, ULONG numberBuffers)); + OVR_DECLARE_HIDFUNC(HidD_GetFeature, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength)); + OVR_DECLARE_HIDFUNC(HidD_SetFeature, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength)); + OVR_DECLARE_HIDFUNC(HidD_GetAttributes, BOOLEAN, (HANDLE hidDev, HIDD_ATTRIBUTES *attributes)); + OVR_DECLARE_HIDFUNC(HidD_GetManufacturerString, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength)); + OVR_DECLARE_HIDFUNC(HidD_GetProductString, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength)); + OVR_DECLARE_HIDFUNC(HidD_GetSerialNumberString, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength)); + OVR_DECLARE_HIDFUNC(HidD_GetPreparsedData, BOOLEAN, (HANDLE hidDev, HIDP_PREPARSED_DATA **preparsedData)); + OVR_DECLARE_HIDFUNC(HidD_FreePreparsedData, BOOLEAN, (HIDP_PREPARSED_DATA *preparsedData)); + OVR_DECLARE_HIDFUNC(HidP_GetCaps, NTSTATUS,(HIDP_PREPARSED_DATA *preparsedData, HIDP_CAPS* caps)); + + HANDLE CreateHIDFile(const char* path, bool exclusiveAccess = true) const + { + return ::CreateFileA(path, GENERIC_WRITE|GENERIC_READ, + (!exclusiveAccess) ? (FILE_SHARE_READ|FILE_SHARE_WRITE) : 0x0, + NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); + } + + // Helper functions to fill in HIDDeviceDesc from open device handle. + bool initVendorProductVersion(HANDLE hidDev, HIDDeviceDesc* desc) const; + bool initUsage(HANDLE hidDev, HIDDeviceDesc* desc) const; + void initStrings(HANDLE hidDev, HIDDeviceDesc* desc) const; + + bool getFullDesc(HANDLE hidDev, HIDDeviceDesc* desc) const; +}; + +}} // namespace OVR::Win32 + +#endif // OVR_Win32_HIDDevice_h diff --git a/LibOVR/Src/OVR_Win32_HMDDevice.cpp b/LibOVR/Src/OVR_Win32_HMDDevice.cpp new file mode 100644 index 0000000..095d3a8 --- /dev/null +++ b/LibOVR/Src/OVR_Win32_HMDDevice.cpp @@ -0,0 +1,418 @@ +/************************************************************************************ + +Filename : OVR_Win32_HMDDevice.cpp +Content : Win32 Interface to HMD - detects HMD display +Created : September 21, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_Win32_HMDDevice.h" + +#include "OVR_Win32_DeviceManager.h" + +#include + +namespace OVR { namespace Win32 { + +//------------------------------------------------------------------------------------- + +HMDDeviceCreateDesc::HMDDeviceCreateDesc(DeviceFactory* factory, + const String& deviceId, const String& displayDeviceName) + : DeviceCreateDesc(factory, Device_HMD), + DeviceId(deviceId), DisplayDeviceName(displayDeviceName), + DesktopX(0), DesktopY(0), Contents(0), + HResolution(0), VResolution(0), HScreenSize(0), VScreenSize(0) +{ +} +HMDDeviceCreateDesc::HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other) + : DeviceCreateDesc(other.pFactory, Device_HMD), + DeviceId(other.DeviceId), DisplayDeviceName(other.DisplayDeviceName), + DesktopX(other.DesktopX), DesktopY(other.DesktopY), Contents(other.Contents), + HResolution(other.HResolution), VResolution(other.VResolution), + HScreenSize(other.HScreenSize), VScreenSize(other.VScreenSize) +{ +} + +HMDDeviceCreateDesc::MatchResult HMDDeviceCreateDesc::MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc** pcandidate) const +{ + if ((other.Type != Device_HMD) || (other.pFactory != pFactory)) + return Match_None; + + // There are several reasons we can come in here: + // a) Matching this HMD Monitor created desc to OTHER HMD Monitor desc + // - Require exact device DeviceId/DeviceName match + // b) Matching SensorDisplayInfo created desc to OTHER HMD Monitor desc + // - This DeviceId is empty; becomes candidate + // c) Matching this HMD Monitor created desc to SensorDisplayInfo desc + // - This other.DeviceId is empty; becomes candidate + + const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; + + if ((DeviceId == s2.DeviceId) && + (DisplayDeviceName == s2.DisplayDeviceName)) + { + // Non-null DeviceId may match while size is different if screen size was overwritten + // by SensorDisplayInfo in prior iteration. + if (!DeviceId.IsEmpty() || + ((HScreenSize == s2.HScreenSize) && + (VScreenSize == s2.VScreenSize)) ) + { + *pcandidate = 0; + return Match_Found; + } + } + + + // DisplayInfo takes precedence, although we try to match it first. + if ((HResolution == s2.HResolution) && + (VResolution == s2.VResolution) && + (HScreenSize == s2.HScreenSize) && + (VScreenSize == s2.VScreenSize)) + { + if (DeviceId.IsEmpty() && !s2.DeviceId.IsEmpty()) + { + *pcandidate = const_cast((const DeviceCreateDesc*)this); + return Match_Candidate; + } + + *pcandidate = 0; + return Match_Found; + } + + // SensorDisplayInfo may override resolution settings, so store as candidate. + if (s2.DeviceId.IsEmpty()) + { + *pcandidate = const_cast((const DeviceCreateDesc*)this); + return Match_Candidate; + } + // OTHER HMD Monitor desc may initialize DeviceName/Id + else if (DeviceId.IsEmpty()) + { + *pcandidate = const_cast((const DeviceCreateDesc*)this); + return Match_Candidate; + } + + return Match_None; +} + + +bool HMDDeviceCreateDesc::UpdateMatchedCandidate(const DeviceCreateDesc& other, + bool* newDeviceFlag) +{ + // This candidate was the the "best fit" to apply sensor DisplayInfo to. + OVR_ASSERT(other.Type == Device_HMD); + + const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; + + // Force screen size on resolution from SensorDisplayInfo. + // We do this because USB detection is more reliable as compared to HDMI EDID, + // which may be corrupted by splitter reporting wrong monitor + if (s2.DeviceId.IsEmpty()) + { + HScreenSize = s2.HScreenSize; + VScreenSize = s2.VScreenSize; + Contents |= Contents_Screen; + + if (s2.Contents & HMDDeviceCreateDesc::Contents_Distortion) + { + memcpy(DistortionK, s2.DistortionK, sizeof(float)*4); + Contents |= Contents_Distortion; + } + DeviceId = s2.DeviceId; + DisplayDeviceName = s2.DisplayDeviceName; + if (newDeviceFlag) *newDeviceFlag = true; + } + else if (DeviceId.IsEmpty()) + { + DeviceId = s2.DeviceId; + DisplayDeviceName = s2.DisplayDeviceName; + if (newDeviceFlag) *newDeviceFlag = true; + } + else + { + if (newDeviceFlag) *newDeviceFlag = false; + } + + return true; +} + +bool HMDDeviceCreateDesc::MatchDevice(const String& path) +{ + return DeviceId.CompareNoCase(path) == 0; +} + +//------------------------------------------------------------------------------------- + + +const wchar_t* FormatDisplayStateFlags(wchar_t* buff, int length, DWORD flags) +{ + buff[0] = 0; + if (flags & DISPLAY_DEVICE_ACTIVE) + wcscat_s(buff, length, L"Active "); + if (flags & DISPLAY_DEVICE_MIRRORING_DRIVER) + wcscat_s(buff, length, L"Mirroring_Driver "); + if (flags & DISPLAY_DEVICE_MODESPRUNED) + wcscat_s(buff, length, L"ModesPruned "); + if (flags & DISPLAY_DEVICE_PRIMARY_DEVICE) + wcscat_s(buff, length, L"Primary "); + if (flags & DISPLAY_DEVICE_REMOVABLE) + wcscat_s(buff, length, L"Removable "); + if (flags & DISPLAY_DEVICE_VGA_COMPATIBLE) + wcscat_s(buff, length, L"VGA_Compatible "); + return buff; +} + + +//------------------------------------------------------------------------------------- +// Callback for monitor enumeration to store all the monitor handles + +// Used to capture all the active monitor handles +struct MonitorSet +{ + enum { MaxMonitors = 8 }; + HMONITOR Monitors[MaxMonitors]; + int MonitorCount; +}; + +BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData) +{ + MonitorSet* monitorSet = (MonitorSet*)dwData; + if (monitorSet->MonitorCount > MonitorSet::MaxMonitors) + return FALSE; + + monitorSet->Monitors[monitorSet->MonitorCount] = hMonitor; + monitorSet->MonitorCount++; + return TRUE; +}; + +//------------------------------------------------------------------------------------- +// ***** HMDDeviceFactory + +HMDDeviceFactory HMDDeviceFactory::Instance; + +void HMDDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor) +{ + MonitorSet monitors; + monitors.MonitorCount = 0; + // Get all the monitor handles + EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&monitors); + + bool foundHMD = false; + + // DeviceManager* manager = getManager(); + DISPLAY_DEVICE dd, ddm; + UINT i, j; + + for (i = 0; + (ZeroMemory(&dd, sizeof(dd)), dd.cb = sizeof(dd), + EnumDisplayDevices(0, i, &dd, 0)) != 0; i++) + { + + /* + wchar_t buff[500], flagsBuff[200]; + + swprintf_s(buff, 500, L"\nDEV: \"%s\" \"%s\" 0x%08x=%s\n \"%s\" \"%s\"\n", + dd.DeviceName, dd.DeviceString, + dd.StateFlags, FormatDisplayStateFlags(flagsBuff, 200, dd.StateFlags), + dd.DeviceID, dd.DeviceKey); + ::OutputDebugString(buff); + */ + + for (j = 0; + (ZeroMemory(&ddm, sizeof(ddm)), ddm.cb = sizeof(ddm), + EnumDisplayDevices(dd.DeviceName, j, &ddm, 0)) != 0; j++) + { + /* + wchar_t mbuff[500]; + swprintf_s(mbuff, 500, L"MON: \"%s\" \"%s\" 0x%08x=%s\n \"%s\" \"%s\"\n", + ddm.DeviceName, ddm.DeviceString, + ddm.StateFlags, FormatDisplayStateFlags(flagsBuff, 200, ddm.StateFlags), + ddm.DeviceID, ddm.DeviceKey); + ::OutputDebugString(mbuff); + */ + + // Our monitor hardware has string "RTD2205" in it + // Nate's device "CVT0003" + if (wcsstr(ddm.DeviceID, L"RTD2205") || + wcsstr(ddm.DeviceID, L"CVT0003") || + wcsstr(ddm.DeviceID, L"MST0030") || + wcsstr(ddm.DeviceID, L"OVR00") ) // Part of Oculus EDID. + { + String deviceId(ddm.DeviceID); + String displayDeviceName(ddm.DeviceName); + + // The default monitor coordinates + int mx = 0; + int my = 0; + int mwidth = 1280; + int mheight = 800; + + // Find the matching MONITORINFOEX for this device so we can get the + // screen coordinates + MONITORINFOEX info; + for (int m=0; m < monitors.MonitorCount; m++) + { + info.cbSize = sizeof(MONITORINFOEX); + GetMonitorInfo(monitors.Monitors[m], &info); + if (_tcsstr(ddm.DeviceName, info.szDevice) == ddm.DeviceName) + { // If the device name starts with the monitor name + // then we found the matching DISPLAY_DEVICE and MONITORINFO + // so we can gather the monitor coordinates + mx = info.rcMonitor.left; + my = info.rcMonitor.top; + //mwidth = info.rcMonitor.right - info.rcMonitor.left; + //mheight = info.rcMonitor.bottom - info.rcMonitor.top; + break; + } + } + + HMDDeviceCreateDesc hmdCreateDesc(this, deviceId, displayDeviceName); + + if (hmdCreateDesc.Is7Inch()) + { + // Physical dimension of SLA screen. + hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.14976f, 0.0936f); + } + else + { + hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.12096f, 0.0756f); + } + + OVR_DEBUG_LOG_TEXT(("DeviceManager - HMD Found %s - %s\n", + deviceId.ToCStr(), displayDeviceName.ToCStr())); + + // Notify caller about detected device. This will call EnumerateAddDevice + // if the this is the first time device was detected. + visitor.Visit(hmdCreateDesc); + foundHMD = true; + break; + } + } + } + + // Real HMD device is not found; however, we still may have a 'fake' HMD + // device created via SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo. + // Need to find it and set 'Enumerated' to true to avoid Removal notification. + if (!foundHMD) + { + Ptr hmdDevDesc = getManager()->FindDevice("", Device_HMD); + if (hmdDevDesc) + hmdDevDesc->Enumerated = true; + } +} + +DeviceBase* HMDDeviceCreateDesc::NewDeviceInstance() +{ + return new HMDDevice(this); +} + +bool HMDDeviceCreateDesc::Is7Inch() const +{ + return (strstr(DeviceId.ToCStr(), "OVR0001") != 0) || (Contents & Contents_7Inch); +} + +bool HMDDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const +{ + if ((info->InfoClassType != Device_HMD) && + (info->InfoClassType != Device_None)) + return false; + + bool is7Inch = Is7Inch(); + + OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, + is7Inch ? "Oculus Rift DK1" : "Oculus Rift DK1-Prototype"); + OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, "Oculus VR"); + info->Type = Device_HMD; + info->Version = 0; + + // Display detection. + if (info->InfoClassType == Device_HMD) + { + HMDInfo* hmdInfo = static_cast(info); + + hmdInfo->DesktopX = DesktopX; + hmdInfo->DesktopY = DesktopY; + hmdInfo->HResolution = HResolution; + hmdInfo->VResolution = VResolution; + hmdInfo->HScreenSize = HScreenSize; + hmdInfo->VScreenSize = VScreenSize; + hmdInfo->VScreenCenter = VScreenSize * 0.5f; + hmdInfo->InterpupillaryDistance = 0.064f; // Default IPD; should be configurable. + hmdInfo->LensSeparationDistance = 0.0635f; + + if (Contents & Contents_Distortion) + { + memcpy(hmdInfo->DistortionK, DistortionK, sizeof(float)*4); + } + else + { + if (is7Inch) + { + // 7" screen. + hmdInfo->DistortionK[0] = 1.0f; + hmdInfo->DistortionK[1] = 0.22f; + hmdInfo->DistortionK[2] = 0.24f; + hmdInfo->EyeToScreenDistance = 0.041f; + + hmdInfo->ChromaAbCorrection[0] = 0.996f; + hmdInfo->ChromaAbCorrection[1] = -0.004f; + hmdInfo->ChromaAbCorrection[2] = 1.014f; + hmdInfo->ChromaAbCorrection[3] = 0.0f; + } + else + { + hmdInfo->DistortionK[0] = 1.0f; + hmdInfo->DistortionK[1] = 0.18f; + hmdInfo->DistortionK[2] = 0.115f; + hmdInfo->EyeToScreenDistance = 0.0387f; + } + } + + OVR_strcpy(hmdInfo->DisplayDeviceName, sizeof(hmdInfo->DisplayDeviceName), + DisplayDeviceName.ToCStr()); + } + + return true; +} + +//------------------------------------------------------------------------------------- +// ***** HMDDevice + +HMDDevice::HMDDevice(HMDDeviceCreateDesc* createDesc) + : OVR::DeviceImpl(createDesc, 0) +{ +} +HMDDevice::~HMDDevice() +{ +} + +bool HMDDevice::Initialize(DeviceBase* parent) +{ + pParent = parent; + return true; +} +void HMDDevice::Shutdown() +{ + pParent.Clear(); +} + +OVR::SensorDevice* HMDDevice::GetSensor() +{ + // Just return first sensor found since we have no way to match it yet. + OVR::SensorDevice* sensor = GetManager()->EnumerateDevices().CreateDevice(); + if (sensor) + sensor->SetCoordinateFrame(SensorDevice::Coord_HMD); + return sensor; +} + +}} // namespace OVR::Win32 + + diff --git a/LibOVR/Src/OVR_Win32_HMDDevice.h b/LibOVR/Src/OVR_Win32_HMDDevice.h new file mode 100644 index 0000000..f653de9 --- /dev/null +++ b/LibOVR/Src/OVR_Win32_HMDDevice.h @@ -0,0 +1,133 @@ +/************************************************************************************ + +Filename : OVR_Win32_HMDDevice.h +Content : Win32 HMDDevice implementation +Created : September 21, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Win32_HMDDevice_h +#define OVR_Win32_HMDDevice_h + +#include "OVR_Win32_DeviceManager.h" + +namespace OVR { namespace Win32 { + +class HMDDevice; + + +//------------------------------------------------------------------------------------- + +// HMDDeviceFactory enumerates attached Oculus HMD devices. +// +// This is currently done by matching monitor device strings. + +class HMDDeviceFactory : public DeviceFactory +{ +public: + static HMDDeviceFactory Instance; + + // Enumerates devices, creating and destroying relevant objects in manager. + virtual void EnumerateDevices(EnumerateVisitor& visitor); + +protected: + DeviceManager* getManager() const { return (DeviceManager*) pManager; } +}; + + +class HMDDeviceCreateDesc : public DeviceCreateDesc +{ + friend class HMDDevice; + +protected: + enum + { + Contents_Screen = 1, + Contents_Distortion = 2, + Contents_7Inch = 4, + }; + String DeviceId; + String DisplayDeviceName; + int DesktopX, DesktopY; + unsigned Contents; + unsigned HResolution, VResolution; + float HScreenSize, VScreenSize; + float DistortionK[4]; + +public: + HMDDeviceCreateDesc(DeviceFactory* factory, + const String& deviceId, const String& displayDeviceName); + HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other); + + virtual DeviceCreateDesc* Clone() const + { + return new HMDDeviceCreateDesc(*this); + } + + virtual DeviceBase* NewDeviceInstance(); + + virtual MatchResult MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc**) const; + + // Matches device by path. + virtual bool MatchDevice(const String& path); + + virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&, bool* newDeviceFlag = NULL); + + virtual bool GetDeviceInfo(DeviceInfo* info) const; + + void SetScreenParameters(int x, int y, unsigned hres, unsigned vres, float hsize, float vsize) + { + DesktopX = x; + DesktopY = y; + HResolution = hres; + VResolution = vres; + HScreenSize = hsize; + VScreenSize = vsize; + Contents |= Contents_Screen; + } + void SetDistortion(const float* dks) + { + for (int i = 0; i < 4; i++) + DistortionK[i] = dks[i]; + Contents |= Contents_Distortion; + } + + void Set7Inch() { Contents |= Contents_7Inch; } + + bool Is7Inch() const; +}; + + +//------------------------------------------------------------------------------------- + +// HMDDevice represents an Oculus HMD device unit. An instance of this class +// is typically created from the DeviceManager. +// After HMD device is created, we its sensor data can be obtained by +// first creating a Sensor object and then wrappig it in SensorFusion. + +class HMDDevice : public DeviceImpl +{ +public: + HMDDevice(HMDDeviceCreateDesc* createDesc); + ~HMDDevice(); + + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + // Query associated sensor. + virtual OVR::SensorDevice* GetSensor(); +}; + + +}} // namespace OVR::Win32 + +#endif // OVR_Win32_HMDDevice_h + diff --git a/LibOVR/Src/OVR_Win32_SensorDevice.cpp b/LibOVR/Src/OVR_Win32_SensorDevice.cpp new file mode 100644 index 0000000..ce02b9e --- /dev/null +++ b/LibOVR/Src/OVR_Win32_SensorDevice.cpp @@ -0,0 +1,47 @@ +/************************************************************************************ + +Filename : OVR_Win32_SensorDevice.cpp +Content : Win32 SensorDevice implementation +Created : March 12, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_Win32_SensorDevice.h" + +#include "OVR_Win32_HMDDevice.h" +#include "OVR_SensorImpl.h" +#include "OVR_DeviceImpl.h" + +namespace OVR { namespace Win32 { + +} // namespace Win32 + +//------------------------------------------------------------------------------------- +void SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo + (const SensorDisplayInfoImpl& displayInfo, + DeviceFactory::EnumerateVisitor& visitor) +{ + + Win32::HMDDeviceCreateDesc hmdCreateDesc(&Win32::HMDDeviceFactory::Instance, String(), String()); + hmdCreateDesc.SetScreenParameters( 0, 0, + displayInfo.HResolution, displayInfo.VResolution, + displayInfo.HScreenSize, displayInfo.VScreenSize); + + if ((displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) == SensorDisplayInfoImpl::Base_Distortion) + hmdCreateDesc.SetDistortion(displayInfo.DistortionK); + if (displayInfo.HScreenSize > 0.14f) + hmdCreateDesc.Set7Inch(); + + visitor.Visit(hmdCreateDesc); +} + +} // namespace OVR + + diff --git a/LibOVR/Src/OVR_Win32_SensorDevice.h b/LibOVR/Src/OVR_Win32_SensorDevice.h new file mode 100644 index 0000000..730620e --- /dev/null +++ b/LibOVR/Src/OVR_Win32_SensorDevice.h @@ -0,0 +1,24 @@ +/************************************************************************************ + +Filename : OVR_Win32_SensorDevice.h +Content : Win32 SensorDevice implementation +Created : March 12, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Win32_SensorDevice_h +#define OVR_Win32_SensorDevice_h + +namespace OVR { namespace Win32 { + +}} // namespace OVR::Win32 + +#endif // OVR_Win32_SensorDevice_h + diff --git a/LibOVR/Src/Util/Util_LatencyTest.cpp b/LibOVR/Src/Util/Util_LatencyTest.cpp new file mode 100644 index 0000000..1a1c303 --- /dev/null +++ b/LibOVR/Src/Util/Util_LatencyTest.cpp @@ -0,0 +1,560 @@ +/************************************************************************************ + +Filename : Util_LatencyTest.cpp +Content : Wraps the lower level LatencyTester interface and adds functionality. +Created : February 14, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "Util_LatencyTest.h" + +#include "../Kernel/OVR_Log.h" +#include "../Kernel/OVR_Timer.h" + +namespace OVR { namespace Util { + +static const UInt32 TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION = 16*10; +static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION = 16*10; +static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT = 16*5; +static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS = 16*5; +static const UInt32 DEFAULT_NUMBER_OF_SAMPLES = 10; // For both color 1->2 and color 2->1 transitions. +static const UInt32 INITIAL_SAMPLES_TO_IGNORE = 4; +static const UInt32 TIMEOUT_WAITING_FOR_TEST_STARTED = 1000; +static const UInt32 TIMEOUT_WAITING_FOR_COLOR_DETECTED = 4000; +static const Color CALIBRATE_BLACK(0, 0, 0); +static const Color CALIBRATE_WHITE(255, 255, 255); +static const Color COLOR1(0, 0, 0); +static const Color COLOR2(255, 255, 255); +static const Color SENSOR_DETECT_THRESHOLD(128, 255, 255); +static const float BIG_FLOAT = 1000000.0f; +static const float SMALL_FLOAT = -1000000.0f; + +//------------------------------------------------------------------------------------- +// ***** LatencyTest + +LatencyTest::LatencyTest(LatencyTestDevice* device) + : Handler(getThis()) +{ + if (device != NULL) + { + SetDevice(device); + } + + reset(); + + srand(Timer::GetTicksMs()); +} + +LatencyTest::~LatencyTest() +{ + clearMeasurementResults(); +} + +bool LatencyTest::SetDevice(LatencyTestDevice* device) +{ + + if (device != Device) + { + if (device != NULL) + { + if (device->GetMessageHandler() != NULL) + { + OVR_DEBUG_LOG( + ("LatencyTest::AttachToDevice failed - device %p already has handler", device)); + return false; + } + } + + if (Device != NULL) + { + Device->SetMessageHandler(0); + } + Device = device; + + if (Device != NULL) + { + Device->SetMessageHandler(&Handler); + + // Set trigger threshold. + LatencyTestConfiguration configuration(SENSOR_DETECT_THRESHOLD, false); // No samples streaming. + Device->SetConfiguration(configuration, true); + } + } + + return true; +} + +UInt32 LatencyTest::getRandomComponent(UInt32 range) +{ + UInt32 val = rand() % range; + return val; +} + +void LatencyTest::BeginTest() +{ + if (State == State_WaitingForButton) + { + // Set color to black and wait a while. + RenderColor = CALIBRATE_BLACK; + + State = State_WaitingForSettlePreCalibrationColorBlack; + OVR_DEBUG_LOG(("State_WaitingForButton -> State_WaitingForSettlePreCalibrationColorBlack.")); + + setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION); + } +} + +void LatencyTest::handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage) +{ + // For debugging. +/* if (msg.Type == Message_LatencyTestSamples) + { + MessageLatencyTestSamples* pSamples = (MessageLatencyTestSamples*) &msg; + + if (pSamples->Samples.GetSize() > 0) + { + // Just show the first one for now. + Color c = pSamples->Samples[0]; + OVR_DEBUG_LOG(("%d %d %d", c.R, c.G, c.B)); + } + return; + } +*/ + + if (latencyTestMessage == LatencyTest_Timer) + { + if (!Device) + { + reset(); + return; + } + + if (State == State_WaitingForSettlePreCalibrationColorBlack) + { + // Send calibrate message to device and wait a while. + LatencyTestCalibrate calibrate(CALIBRATE_BLACK); + Device->SetCalibrate(calibrate); + + State = State_WaitingForSettlePostCalibrationColorBlack; + OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorBlack -> State_WaitingForSettlePostCalibrationColorBlack.")); + + setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION); + } + else if (State == State_WaitingForSettlePostCalibrationColorBlack) + { + // Change color to white and wait a while. + RenderColor = CALIBRATE_WHITE; + + State = State_WaitingForSettlePreCalibrationColorWhite; + OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorBlack -> State_WaitingForSettlePreCalibrationColorWhite.")); + + setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION); + } + else if (State == State_WaitingForSettlePreCalibrationColorWhite) + { + // Send calibrate message to device and wait a while. + LatencyTestCalibrate calibrate(CALIBRATE_WHITE); + Device->SetCalibrate(calibrate); + + State = State_WaitingForSettlePostCalibrationColorWhite; + OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorWhite -> State_WaitingForSettlePostCalibrationColorWhite.")); + + setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION); + } + else if (State == State_WaitingForSettlePostCalibrationColorWhite) + { + // Calibration is done. Switch to color 1 and wait for it to settle. + RenderColor = COLOR1; + + State = State_WaitingForSettlePostMeasurement; + OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorWhite -> State_WaitingForSettlePostMeasurement.")); + + UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); + setTimer(waitTime); + } + else if (State == State_WaitingForSettlePostMeasurement) + { + // Prepare for next measurement. + + // Create a new result object. + MeasurementResult* pResult = new MeasurementResult(); + Results.PushBack(pResult); + + State = State_WaitingToTakeMeasurement; + OVR_DEBUG_LOG(("State_WaitingForSettlePostMeasurement -> State_WaitingToTakeMeasurement.")); + } + else if (State == State_WaitingForTestStarted) + { + // We timed out waiting for 'TestStarted'. Abandon this measurement and setup for the next. + getActiveResult()->TimedOutWaitingForTestStarted = true; + + State = State_WaitingForSettlePostMeasurement; + OVR_DEBUG_LOG(("** Timed out waiting for 'TestStarted'.")); + OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForSettlePostMeasurement.")); + + UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); + setTimer(waitTime); + } + else if (State == State_WaitingForColorDetected) + { + // We timed out waiting for 'ColorDetected'. Abandon this measurement and setup for the next. + getActiveResult()->TimedOutWaitingForColorDetected = true; + + State = State_WaitingForSettlePostMeasurement; + OVR_DEBUG_LOG(("** Timed out waiting for 'ColorDetected'.")); + OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement.")); + + UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); + setTimer(waitTime); + } + } + else if (latencyTestMessage == LatencyTest_ProcessInputs) + { + if (State == State_WaitingToTakeMeasurement) + { + if (!Device) + { + reset(); + return; + } + + // Send 'StartTest' feature report with opposite target color. + if (RenderColor == COLOR1) + { + RenderColor = COLOR2; + } + else + { + RenderColor = COLOR1; + } + + getActiveResult()->TargetColor = RenderColor; + + // Record time so we can determine usb roundtrip time. + getActiveResult()->StartTestTicksMicroS = Timer::GetTicks(); + + LatencyTestStartTest startTest(RenderColor); + Device->SetStartTest(startTest); + + State = State_WaitingForTestStarted; + OVR_DEBUG_LOG(("State_WaitingToTakeMeasurement -> State_WaitingForTestStarted.")); + + setTimer(TIMEOUT_WAITING_FOR_TEST_STARTED); + } + } + else if (msg.Type == Message_LatencyTestButton) + { + BeginTest(); + } + else if (msg.Type == Message_LatencyTestStarted) + { + if (State == State_WaitingForTestStarted) + { + clearTimer(); + + // Record time so we can determine usb roundtrip time. + getActiveResult()->TestStartedTicksMicroS = Timer::GetTicks(); + + State = State_WaitingForColorDetected; + OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForColorDetected.")); + + setTimer(TIMEOUT_WAITING_FOR_COLOR_DETECTED); + } + } + else if (msg.Type == Message_LatencyTestColorDetected) + { + if (State == State_WaitingForColorDetected) + { + // Record time to detect color. + MessageLatencyTestColorDetected* pDetected = (MessageLatencyTestColorDetected*) &msg; + UInt16 elapsedTime = pDetected->Elapsed; + OVR_DEBUG_LOG(("Time to 'ColorDetected' = %d", elapsedTime)); + + getActiveResult()->DeviceMeasuredElapsedMilliS = elapsedTime; + + if (areResultsComplete()) + { + // We're done. + processResults(); + reset(); + } + else + { + // Run another measurement. + State = State_WaitingForSettlePostMeasurement; + OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement.")); + + UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); + setTimer(waitTime); + } + } + } + else if (msg.Type == Message_DeviceRemoved) + { + reset(); + } +} + +LatencyTest::MeasurementResult* LatencyTest::getActiveResult() +{ + OVR_ASSERT(!Results.IsEmpty()); + return Results.GetLast(); +} + +void LatencyTest::setTimer(UInt32 timeMilliS) +{ + ActiveTimerMilliS = timeMilliS; +} + +void LatencyTest::clearTimer() +{ + ActiveTimerMilliS = 0; +} + +void LatencyTest::reset() +{ + clearMeasurementResults(); + State = State_WaitingForButton; + + HaveOldTime = false; + ActiveTimerMilliS = 0; +} + +void LatencyTest::clearMeasurementResults() +{ + while(!Results.IsEmpty()) + { + MeasurementResult* pElem = Results.GetFirst(); + pElem->RemoveNode(); + delete pElem; + } +} + +LatencyTest::LatencyTestHandler::~LatencyTestHandler() +{ + RemoveHandlerFromDevices(); +} + +void LatencyTest::LatencyTestHandler::OnMessage(const Message& msg) +{ + pLatencyTestUtil->handleMessage(msg); +} + +void LatencyTest::ProcessInputs() +{ + updateForTimeouts(); + handleMessage(Message(), LatencyTest_ProcessInputs); +} + +bool LatencyTest::DisplayScreenColor(Color& colorToDisplay) +{ + updateForTimeouts(); + + if (State == State_WaitingForButton) + { + return false; + } + + colorToDisplay = RenderColor; + return true; +} + +const char* LatencyTest::GetResultsString() +{ + if (!ResultsString.IsEmpty() && ReturnedResultString != ResultsString.ToCStr()) + { + ReturnedResultString = ResultsString; + return ReturnedResultString.ToCStr(); + } + + return NULL; +} + +bool LatencyTest::areResultsComplete() +{ + UInt32 initialMeasurements = 0; + + UInt32 measurements1to2 = 0; + UInt32 measurements2to1 = 0; + + MeasurementResult* pCurr = Results.GetFirst(); + while(true) + { + // Process. + if (!pCurr->TimedOutWaitingForTestStarted && + !pCurr->TimedOutWaitingForColorDetected) + { + initialMeasurements++; + + if (initialMeasurements > INITIAL_SAMPLES_TO_IGNORE) + { + if (pCurr->TargetColor == COLOR2) + { + measurements1to2++; + } + else + { + measurements2to1++; + } + } + } + + if (Results.IsLast(pCurr)) + { + break; + } + pCurr = Results.GetNext(pCurr); + } + + if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES && + measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES) + { + return true; + } + + return false; +} + +void LatencyTest::processResults() +{ + + UInt32 minTime1To2 = UINT_MAX; + UInt32 maxTime1To2 = 0; + float averageTime1To2 = 0.0f; + UInt32 minTime2To1 = UINT_MAX; + UInt32 maxTime2To1 = 0; + float averageTime2To1 = 0.0f; + + float minUSBTripMilliS = BIG_FLOAT; + float maxUSBTripMilliS = SMALL_FLOAT; + float averageUSBTripMilliS = 0.0f; + UInt32 countUSBTripTime = 0; + + UInt32 measurementsCount = 0; + UInt32 measurements1to2 = 0; + UInt32 measurements2to1 = 0; + + MeasurementResult* pCurr = Results.GetFirst(); + UInt32 count = 0; + while(true) + { + count++; + + if (!pCurr->TimedOutWaitingForTestStarted && + !pCurr->TimedOutWaitingForColorDetected) + { + measurementsCount++; + + if (measurementsCount > INITIAL_SAMPLES_TO_IGNORE) + { + if (pCurr->TargetColor == COLOR2) + { + measurements1to2++; + + if (measurements1to2 <= DEFAULT_NUMBER_OF_SAMPLES) + { + UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS; + + minTime1To2 = Alg::Min(elapsed, minTime1To2); + maxTime1To2 = Alg::Max(elapsed, maxTime1To2); + + averageTime1To2 += (float) elapsed; + } + } + else + { + measurements2to1++; + + if (measurements2to1 <= DEFAULT_NUMBER_OF_SAMPLES) + { + UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS; + + minTime2To1 = Alg::Min(elapsed, minTime2To1); + maxTime2To1 = Alg::Max(elapsed, maxTime2To1); + + averageTime2To1 += (float) elapsed; + } + } + + float usbRountripElapsedMilliS = 0.001f * (float) (pCurr->TestStartedTicksMicroS - pCurr->StartTestTicksMicroS); + minUSBTripMilliS = Alg::Min(usbRountripElapsedMilliS, minUSBTripMilliS); + maxUSBTripMilliS = Alg::Max(usbRountripElapsedMilliS, maxUSBTripMilliS); + averageUSBTripMilliS += usbRountripElapsedMilliS; + countUSBTripTime++; + } + } + + if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES && + measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES) + { + break; + } + + if (Results.IsLast(pCurr)) + { + break; + } + pCurr = Results.GetNext(pCurr); + } + + averageTime1To2 /= (float) DEFAULT_NUMBER_OF_SAMPLES; + averageTime2To1 /= (float) DEFAULT_NUMBER_OF_SAMPLES; + + averageUSBTripMilliS /= countUSBTripTime; + + float finalResult = 0.5f * (averageTime1To2 + averageTime2To1); + finalResult += averageUSBTripMilliS; + + ResultsString.Clear(); + ResultsString.AppendFormat("RESULT=%.1f (add half Tracker period) [b->w %d|%.1f|%d] [w->b %d|%.1f|%d] [usb rndtrp %.1f|%.1f|%.1f] [cnt %d] [tmouts %d]", + finalResult, + minTime1To2, averageTime1To2, maxTime1To2, + minTime2To1, averageTime2To1, maxTime2To1, + minUSBTripMilliS, averageUSBTripMilliS, maxUSBTripMilliS, + DEFAULT_NUMBER_OF_SAMPLES*2, count - measurementsCount); +} + +void LatencyTest::updateForTimeouts() +{ + if (!HaveOldTime) + { + HaveOldTime = true; + OldTime = Timer::GetTicksMs(); + return; + } + + UInt32 newTime = Timer::GetTicksMs(); + UInt32 elapsedMilliS = newTime - OldTime; + if (newTime < OldTime) + { + elapsedMilliS = OldTime - newTime; + elapsedMilliS = UINT_MAX - elapsedMilliS; + } + OldTime = newTime; + + elapsedMilliS = Alg::Min(elapsedMilliS, (UInt32) 100); // Clamp at 100mS in case we're not being called very often. + + + if (ActiveTimerMilliS == 0) + { + return; + } + + if (elapsedMilliS >= ActiveTimerMilliS) + { + ActiveTimerMilliS = 0; + handleMessage(Message(), LatencyTest_Timer); + return; + } + + ActiveTimerMilliS -= elapsedMilliS; +} + +}} // namespace OVR::Util diff --git a/LibOVR/Src/Util/Util_LatencyTest.h b/LibOVR/Src/Util/Util_LatencyTest.h new file mode 100644 index 0000000..47f98f7 --- /dev/null +++ b/LibOVR/Src/Util/Util_LatencyTest.h @@ -0,0 +1,160 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : Util_LatencyTest.h +Content : Wraps the lower level LatencyTesterDevice and adds functionality. +Created : February 14, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Util_LatencyTest_h +#define OVR_Util_LatencyTest_h + +#include "../OVR_Device.h" + +#include "../Kernel/OVR_String.h" +#include "../Kernel/OVR_List.h" + +namespace OVR { namespace Util { + + +//------------------------------------------------------------------------------------- +// ***** LatencyTest +// +// LatencyTest utility class wraps the low level LatencyTestDevice and manages the scheduling +// of a latency test. A single test is composed of a series of individual latency measurements +// which are used to derive min, max, and an average latency value. +// +// Developers are required to call the following methods: +// SetDevice - Sets the LatencyTestDevice to be used for the tests. +// ProcessInputs - This should be called at the same place in the code where the game engine +// reads the headset orientation from LibOVR (typically done by calling +// 'GetOrientation' on the SensorFusion object). Calling this at the right time +// enables us to measure the same latency that occurs for headset orientation +// changes. +// DisplayScreenColor - The latency tester works by sensing the color of the pixels directly +// beneath it. The color of these pixels can be set by drawing a small +// quad at the end of the rendering stage. The quad should be small +// such that it doesn't significantly impact the rendering of the scene, +// but large enough to be 'seen' by the sensor. See the SDK +// documentation for more information. +// GetResultsString - Call this to get a string containing the most recent results. +// If the string has already been gotten then NULL will be returned. +// The string pointer will remain valid until the next time this +// method is called. +// + +class LatencyTest : public NewOverrideBase +{ +public: + LatencyTest(LatencyTestDevice* device = NULL); + ~LatencyTest(); + + // Set the Latency Tester device that we'll use to send commands to and receive + // notification messages from. + bool SetDevice(LatencyTestDevice* device); + + // Returns true if this LatencyTestUtil has a Latency Tester device. + bool HasDevice() const + { return Handler.IsHandlerInstalled(); } + + void ProcessInputs(); + bool DisplayScreenColor(Color& colorToDisplay); + const char* GetResultsString(); + + // Begin test. Equivalent to pressing the button on the latency tester. + void BeginTest(); + +private: + LatencyTest* getThis() { return this; } + + enum LatencyTestMessageType + { + LatencyTest_None, + LatencyTest_Timer, + LatencyTest_ProcessInputs, + }; + + UInt32 getRandomComponent(UInt32 range); + void handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage = LatencyTest_None); + void reset(); + void setTimer(UInt32 timeMilliS); + void clearTimer(); + + class LatencyTestHandler : public MessageHandler + { + LatencyTest* pLatencyTestUtil; + public: + LatencyTestHandler(LatencyTest* latencyTester) : pLatencyTestUtil(latencyTester) { } + ~LatencyTestHandler(); + + virtual void OnMessage(const Message& msg); + }; + + bool areResultsComplete(); + void processResults(); + void updateForTimeouts(); + + Ptr Device; + LatencyTestHandler Handler; + + enum TesterState + { + State_WaitingForButton, + State_WaitingForSettlePreCalibrationColorBlack, + State_WaitingForSettlePostCalibrationColorBlack, + State_WaitingForSettlePreCalibrationColorWhite, + State_WaitingForSettlePostCalibrationColorWhite, + State_WaitingToTakeMeasurement, + State_WaitingForTestStarted, + State_WaitingForColorDetected, + State_WaitingForSettlePostMeasurement + }; + TesterState State; + + bool HaveOldTime; + UInt32 OldTime; + UInt32 ActiveTimerMilliS; + + Color RenderColor; + + struct MeasurementResult : public ListNode, public NewOverrideBase + { + MeasurementResult() + : DeviceMeasuredElapsedMilliS(0), + TimedOutWaitingForTestStarted(false), + TimedOutWaitingForColorDetected(false), + StartTestTicksMicroS(0), + TestStartedTicksMicroS(0) + {} + + Color TargetColor; + + UInt32 DeviceMeasuredElapsedMilliS; + + bool TimedOutWaitingForTestStarted; + bool TimedOutWaitingForColorDetected; + + UInt64 StartTestTicksMicroS; + UInt64 TestStartedTicksMicroS; + }; + + List Results; + void clearMeasurementResults(); + + MeasurementResult* getActiveResult(); + + StringBuffer ResultsString; + String ReturnedResultString; +}; + +}} // namespace OVR::Util + +#endif // OVR_Util_LatencyTest_h diff --git a/LibOVR/Src/Util/Util_MagCalibration.cpp b/LibOVR/Src/Util/Util_MagCalibration.cpp new file mode 100644 index 0000000..f3e72f5 --- /dev/null +++ b/LibOVR/Src/Util/Util_MagCalibration.cpp @@ -0,0 +1,180 @@ +/************************************************************************************ + +Filename : Util_MagCalibration.cpp +Content : Procedures for calibrating the magnetometer +Created : April 16, 2013 +Authors : Steve LaValle, Andrew Reisse + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "Util_MagCalibration.h" + +namespace OVR { namespace Util { + +void MagCalibration::BeginAutoCalibration(SensorFusion& sf) +{ + Status = Mag_AutoCalibrating; + // This is a "hard" reset of the mag, so need to clear stored values + sf.ClearMagCalibration(); + SampleCount = 0; +} + +unsigned MagCalibration::UpdateAutoCalibration(SensorFusion& sf) +{ + if (Status != Mag_AutoCalibrating) + return Status; + + Quatf q = sf.GetOrientation(); + Vector3f m = sf.GetMagnetometer(); + + InsertIfAcceptable(q, m); + + if ((SampleCount == 4) && (Status == Mag_AutoCalibrating)) + SetCalibration(sf); + + return Status; + +} + +void MagCalibration::BeginManualCalibration(SensorFusion& sf) +{ + Status = Mag_ManuallyCalibrating; + sf.ClearMagCalibration(); + SampleCount = 0; +} + +bool MagCalibration::IsAcceptableSample(const Quatf& q, const Vector3f& m) +{ + switch (SampleCount) + { + // Initial sample is always acceptable + case 0: + return true; + break; + case 1: + return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&& + ((m - MagSamples[0]).LengthSq() > MinMagDistanceSq); + break; + case 2: + return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&& + (q.DistanceSq(QuatSamples[1]) > MinQuatDistanceSq)&& + ((m - MagSamples[0]).LengthSq() > MinMagDistanceSq)&& + ((m - MagSamples[1]).LengthSq() > MinMagDistanceSq); + break; + case 3: + return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&& + (q.DistanceSq(QuatSamples[1]) > MinQuatDistanceSq)&& + (q.DistanceSq(QuatSamples[2]) > MinQuatDistanceSq)&& + ((PointToPlaneDistance(MagSamples[0],MagSamples[1],MagSamples[2],m) > MinMagDistance)|| + (PointToPlaneDistance(MagSamples[1],MagSamples[2],m,MagSamples[0]) > MinMagDistance)|| + (PointToPlaneDistance(MagSamples[2],m,MagSamples[0],MagSamples[1]) > MinMagDistance)|| + (PointToPlaneDistance(m,MagSamples[0],MagSamples[1],MagSamples[2]) > MinMagDistance)); + } + + return false; +} + + +bool MagCalibration::InsertIfAcceptable(const Quatf& q, const Vector3f& m) +{ + if (IsAcceptableSample(q, m)) + { + MagSamples[SampleCount] = m; + QuatSamples[SampleCount] = q; + SampleCount++; + return true; + } + + return false; +} + + +bool MagCalibration::SetCalibration(SensorFusion& sf) +{ + if (SampleCount < 4) + return false; + + MagCenter = CalculateSphereCenter(MagSamples[0],MagSamples[1],MagSamples[2],MagSamples[3]); + Matrix4f calMat = Matrix4f(); + calMat.M[0][3] = -MagCenter.x; + calMat.M[1][3] = -MagCenter.y; + calMat.M[2][3] = -MagCenter.z; + sf.SetMagCalibration(calMat); + Status = Mag_Calibrated; + //LogText("MagCenter: %f %f %f\n",MagCenter.x,MagCenter.y,MagCenter.z); + + return true; +} + + +// Calculate the center of a sphere that passes through p1, p2, p3, p4 +Vector3f MagCalibration::CalculateSphereCenter(const Vector3f& p1, const Vector3f& p2, + const Vector3f& p3, const Vector3f& p4) +{ + Matrix4f A; + int i; + Vector3f p[4]; + p[0] = p1; + p[1] = p2; + p[2] = p3; + p[3] = p4; + + for (i = 0; i < 4; i++) + { + A.M[i][0] = p[i].x; + A.M[i][1] = p[i].y; + A.M[i][2] = p[i].z; + A.M[i][3] = 1.0f; + } + float m11 = A.Determinant(); + OVR_ASSERT(m11 != 0.0f); + + for (i = 0; i < 4; i++) + { + A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z; + A.M[i][1] = p[i].y; + A.M[i][2] = p[i].z; + A.M[i][3] = 1.0f; + } + float m12 = A.Determinant(); + + for (i = 0; i < 4; i++) + { + A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z; + A.M[i][1] = p[i].x; + A.M[i][2] = p[i].z; + A.M[i][3] = 1.0f; + } + float m13 = A.Determinant(); + + for (i = 0; i < 4; i++) + { + A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z; + A.M[i][1] = p[i].x; + A.M[i][2] = p[i].y; + A.M[i][3] = 1.0f; + } + float m14 = A.Determinant(); + + float c = 0.5f / m11; + return Vector3f(c*m12, -c*m13, c*m14); +} + +// Distance from p4 to the nearest point on a plane through p1, p2, p3 +float MagCalibration::PointToPlaneDistance(const Vector3f& p1, const Vector3f& p2, + const Vector3f& p3, const Vector3f& p4) +{ + Vector3f v1 = p1 - p2; + Vector3f v2 = p1 - p3; + Vector3f planeNormal = v1.Cross(v2); + planeNormal.Normalize(); + return (fabs((planeNormal * p4) - planeNormal * p1)); +} + +}} diff --git a/LibOVR/Src/Util/Util_MagCalibration.h b/LibOVR/Src/Util/Util_MagCalibration.h new file mode 100644 index 0000000..9371125 --- /dev/null +++ b/LibOVR/Src/Util/Util_MagCalibration.h @@ -0,0 +1,115 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : Util_MagCalibration.h +Content : Procedures for calibrating the magnetometer +Created : April 16, 2013 +Authors : Steve LaValle, Andrew Reisse + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Util_MagCalibration_h +#define OVR_Util_MagCalibration_h + +#include "../OVR_SensorFusion.h" +#include "../Kernel/OVR_String.h" +#include "../Kernel/OVR_Log.h" + +namespace OVR { namespace Util { + +class MagCalibration +{ +public: + enum MagStatus + { + Mag_Uninitialized = 0, + Mag_AutoCalibrating = 1, + Mag_ManuallyCalibrating = 2, + Mag_Calibrated = 3, + }; + + MagCalibration() : + Status(Mag_Uninitialized), + MinMagDistance(0.3f), MinQuatDistance(0.5f), + SampleCount(0) + { + MinMagDistanceSq = MinMagDistance * MinMagDistance; + MinQuatDistanceSq = MinQuatDistance * MinQuatDistance; + } + + // Methods that are useful for either auto or manual calibration + bool IsUnitialized() const { return Status == Mag_Uninitialized; } + bool IsCalibrated() const { return Status == Mag_Calibrated; } + int NumberOfSamples() const { return SampleCount; } + int RequiredSampleCount() const { return 4; } + void ClearCalibration(SensorFusion& sf) + { + Status = Mag_Uninitialized; + SampleCount = 0; + sf.ClearMagCalibration(); + }; + + // Methods for automatic magnetometer calibration + void BeginAutoCalibration(SensorFusion& sf); + unsigned UpdateAutoCalibration(SensorFusion& sf); + bool IsAutoCalibrating() const { return Status == Mag_AutoCalibrating; } + + // Methods for building a manual (user-guided) calibraton procedure + void BeginManualCalibration(SensorFusion& sf); + bool IsAcceptableSample(const Quatf& q, const Vector3f& m); + bool InsertIfAcceptable(const Quatf& q, const Vector3f& m); + // Returns true if successful, requiring that SampleCount = 4 + bool SetCalibration(SensorFusion& sf); + bool IsManuallyCalibrating() const { return Status == Mag_ManuallyCalibrating; } + + // This is the minimum acceptable distance (Euclidean) between raw + // magnetometer values to be acceptable for usage in calibration. + void SetMinMagDistance(float dist) + { + MinMagDistance = dist; + MinMagDistanceSq = MinMagDistance * MinMagDistance; + } + + // The minimum acceptable distance (4D Euclidean) between orientations + // to be acceptable for calibration usage. + void SetMinQuatDistance(float dist) + { + MinQuatDistance = dist; + MinQuatDistanceSq = MinQuatDistance * MinQuatDistance; + } + + // A result of the calibration, which is the center of a sphere that + // roughly approximates the magnetometer data. + Vector3f GetMagCenter() const { return MagCenter; } + +private: + // Determine the unique sphere through 4 non-coplanar points + Vector3f CalculateSphereCenter(const Vector3f& p1, const Vector3f& p2, + const Vector3f& p3, const Vector3f& p4); + + // Distance from p4 to the nearest point on a plane through p1, p2, p3 + float PointToPlaneDistance(const Vector3f& p1, const Vector3f& p2, + const Vector3f& p3, const Vector3f& p4); + + Vector3f MagCenter; + unsigned Status; + float MinMagDistance; + float MinQuatDistance; + float MinMagDistanceSq; + float MinQuatDistanceSq; + + unsigned SampleCount; + Vector3f MagSamples[4]; + Quatf QuatSamples[4]; + +}; + +}} + +#endif diff --git a/LibOVR/Src/Util/Util_Render_Stereo.cpp b/LibOVR/Src/Util/Util_Render_Stereo.cpp new file mode 100644 index 0000000..8986e30 --- /dev/null +++ b/LibOVR/Src/Util/Util_Render_Stereo.cpp @@ -0,0 +1,311 @@ +/************************************************************************************ + +Filename : Util_Render_Stereo.cpp +Content : Stereo rendering configuration implementation +Created : October 22, 2012 +Authors : Michael Antonov, Andrew Reisse + +Copyright : Copyright 2012 Oculus, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus Inc license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "Util_Render_Stereo.h" + +namespace OVR { namespace Util { namespace Render { + + +//----------------------------------------------------------------------------------- + +// DistortionFnInverse computes the inverse of the distortion function on an argument. +float DistortionConfig::DistortionFnInverse(float r) +{ + OVR_ASSERT((r <= 10.0f)); + + float s, d; + float delta = r * 0.25f; + + s = r * 0.5f; + d = fabs(r - DistortionFn(s)); + + for (int i = 0; i < 20; i++) + { + float sUp = s + delta; + float sDown = s - delta; + float dUp = fabs(r - DistortionFn(sUp)); + float dDown = fabs(r - DistortionFn(sDown)); + + if (dUp < d) + { + s = sUp; + d = dUp; + } + else if (dDown < d) + { + s = sDown; + d = dDown; + } + else + { + delta *= 0.5f; + } + } + + return s; +} + + +//----------------------------------------------------------------------------------- +// **** StereoConfig Implementation + +StereoConfig::StereoConfig(StereoMode mode, const Viewport& vp) + : Mode(mode), + InterpupillaryDistance(0.064f), AspectMultiplier(1.0f), + FullView(vp), DirtyFlag(true), + YFov(0), Aspect(vp.w / float(vp.h)), ProjectionCenterOffset(0), + OrthoPixelOffset(0) +{ + // And default distortion for it. + Distortion.SetCoefficients(1.0f, 0.22f, 0.24f); + Distortion.Scale = 1.0f; // Will be computed later. + + // Fit left of the image. + DistortionFitX = -1.0f; + DistortionFitY = 0.0f; + + // Initialize "fake" default HMD values for testing without HMD plugged in. + // These default values match those returned by the HMD. + HMD.HResolution = 1280; + HMD.VResolution = 800; + HMD.HScreenSize = 0.14976f; + HMD.VScreenSize = HMD.HScreenSize / (1280.0f / 800.0f); + HMD.InterpupillaryDistance = InterpupillaryDistance; + HMD.LensSeparationDistance = 0.0635f; + HMD.EyeToScreenDistance = 0.041f; + HMD.DistortionK[0] = Distortion.K[0]; + HMD.DistortionK[1] = Distortion.K[1]; + HMD.DistortionK[2] = Distortion.K[2]; + HMD.DistortionK[3] = 0; + + Set2DAreaFov(DegreeToRad(85.0f)); +} + +void StereoConfig::SetFullViewport(const Viewport& vp) +{ + if (vp != FullView) + { + FullView = vp; + DirtyFlag = true; + } +} + +void StereoConfig::SetHMDInfo(const HMDInfo& hmd) +{ + HMD = hmd; + Distortion.K[0] = hmd.DistortionK[0]; + Distortion.K[1] = hmd.DistortionK[1]; + Distortion.K[2] = hmd.DistortionK[2]; + Distortion.K[3] = hmd.DistortionK[3]; + + Distortion.SetChromaticAberration(hmd.ChromaAbCorrection[0], hmd.ChromaAbCorrection[1], + hmd.ChromaAbCorrection[2], hmd.ChromaAbCorrection[3]); + + DirtyFlag = true; +} + +void StereoConfig::SetDistortionFitPointVP(float x, float y) +{ + DistortionFitX = x; + DistortionFitY = y; + DirtyFlag = true; +} + +void StereoConfig::SetDistortionFitPointPixels(float x, float y) +{ + DistortionFitX = (4 * x / float(FullView.w)) - 1.0f; + DistortionFitY = (2 * y / float(FullView.h)) - 1.0f; + DirtyFlag = true; +} + +void StereoConfig::Set2DAreaFov(float fovRadians) +{ + Area2DFov = fovRadians; + DirtyFlag = true; +} + + +const StereoEyeParams& StereoConfig::GetEyeRenderParams(StereoEye eye) +{ + static const UByte eyeParamIndices[3] = { 0, 0, 1 }; + + updateIfDirty(); + OVR_ASSERT(eye < sizeof(eyeParamIndices)); + return EyeRenderParams[eyeParamIndices[eye]]; +} + + +void StereoConfig::updateComputedState() +{ + // Need to compute all of the following: + // - Aspect Ratio + // - FOV + // - Projection offsets for 3D + // - Distortion XCenterOffset + // - Update 2D + // - Initialize EyeRenderParams + + // Compute aspect ratio. Stereo mode cuts width in half. + Aspect = float(FullView.w) / float(FullView.h); + Aspect *= (Mode == Stereo_None) ? 1.0f : 0.5f; + Aspect *= AspectMultiplier; + + updateDistortionOffsetAndScale(); + + // Compute Vertical FOV based on distance, distortion, etc. + // Distance from vertical center to render vertical edge perceived through the lens. + // This will be larger then normal screen size due to magnification & distortion. + // + // This percievedHalfRTDistance equation should hold as long as the render target + // and display have the same aspect ratios. What we'd like to know is where the edge + // of the render target will on the perceived screen surface. With NO LENS, + // the answer would be: + // + // halfRTDistance = (VScreenSize / 2) * aspect * + // DistortionFn_Inverse( DistortionScale / aspect ) + // + // To model the optical lens we eliminates DistortionFn_Inverse. Aspect ratios + // cancel out, so we get: + // + // halfRTDistance = (VScreenSize / 2) * DistortionScale + // + if (Mode == Stereo_None) + { + YFov = DegreeToRad(80.0f); + } + else + { + float percievedHalfRTDistance = (HMD.VScreenSize / 2) * Distortion.Scale; + YFov = 2.0f * atan(percievedHalfRTDistance/HMD.EyeToScreenDistance); + } + + updateProjectionOffset(); + update2D(); + updateEyeParams(); + + DirtyFlag = false; +} + +void StereoConfig::updateDistortionOffsetAndScale() +{ + // Distortion center shift is stored separately, since it isn't affected + // by the eye distance. + float lensOffset = HMD.LensSeparationDistance * 0.5f; + float lensShift = HMD.HScreenSize * 0.25f - lensOffset; + float lensViewportShift = 4.0f * lensShift / HMD.HScreenSize; + Distortion.XCenterOffset= lensViewportShift; + + // Compute distortion scale from DistortionFitX & DistortionFitY. + // Fit value of 0.0 means "no fit". + if ((fabs(DistortionFitX) < 0.0001f) && (fabs(DistortionFitY) < 0.0001f)) + { + Distortion.Scale = 1.0f; + } + else + { + // Convert fit value to distortion-centered coordinates before fit radius + // calculation. + float stereoAspect = 0.5f * float(FullView.w) / float(FullView.h); + float dx = DistortionFitX - Distortion.XCenterOffset; + float dy = DistortionFitY / stereoAspect; + float fitRadius = sqrt(dx * dx + dy * dy); + Distortion.Scale = Distortion.DistortionFn(fitRadius)/fitRadius; + } +} + +void StereoConfig::updateProjectionOffset() +{ + // Post-projection viewport coordinates range from (-1.0, 1.0), with the + // center of the left viewport falling at (1/4) of horizontal screen size. + // We need to shift this projection center to match with the lens center; + // note that we don't use the IPD here due to collimated light property of the lens. + // We compute this shift in physical units (meters) to + // correct for different screen sizes and then rescale to viewport coordinates. + float viewCenter = HMD.HScreenSize * 0.25f; + float eyeProjectionShift = viewCenter - HMD.LensSeparationDistance*0.5f; + ProjectionCenterOffset = 4.0f * eyeProjectionShift / HMD.HScreenSize; +} + +void StereoConfig::update2D() +{ + // Orthographic projection fakes a screen at a distance of 0.8m from the + // eye, where hmd screen projection surface is at 0.05m distance. + // This introduces an extra off-center pixel projection shift based on eye distance. + // This offCenterShift is the pixel offset of the other camera's center + // in your reference camera based on surface distance. + float metersToPixels = (HMD.HResolution / HMD.HScreenSize); + float lensDistanceScreenPixels= metersToPixels * HMD.LensSeparationDistance; + float eyeDistanceScreenPixels = metersToPixels * InterpupillaryDistance; + float offCenterShiftPixels = (HMD.EyeToScreenDistance / 0.8f) * eyeDistanceScreenPixels; + float leftPixelCenter = (HMD.HResolution / 2) - lensDistanceScreenPixels * 0.5f; + float rightPixelCenter = lensDistanceScreenPixels * 0.5f; + float pixelDifference = leftPixelCenter - rightPixelCenter; + + // This computes the number of pixels that fit within specified 2D FOV (assuming + // distortion scaling will be done). + float percievedHalfScreenDistance = tan(Area2DFov * 0.5f) * HMD.EyeToScreenDistance; + float vfovSize = 2.0f * percievedHalfScreenDistance / Distortion.Scale; + FovPixels = HMD.VResolution * vfovSize / HMD.VScreenSize; + + // Create orthographic matrix. + Matrix4f& m = OrthoCenter; + m.SetIdentity(); + m.M[0][0] = FovPixels / (FullView.w * 0.5f); + m.M[1][1] = -FovPixels / FullView.h; + m.M[0][3] = 0; + m.M[1][3] = 0; + m.M[2][2] = 0; + + float orthoPixelOffset = (pixelDifference + offCenterShiftPixels/Distortion.Scale) * 0.5f; + OrthoPixelOffset = orthoPixelOffset * 2.0f / FovPixels; +} + +void StereoConfig::updateEyeParams() +{ + // Projection matrix for the center eye, which the left/right matrices are based on. + Matrix4f projCenter = Matrix4f::PerspectiveRH(YFov, Aspect, 0.01f, 1000.0f); + + switch(Mode) + { + case Stereo_None: + { + EyeRenderParams[0].Init(StereoEye_Center, FullView, 0, projCenter, OrthoCenter); + } + break; + + case Stereo_LeftRight_Multipass: + { + Matrix4f projLeft = Matrix4f::Translation(ProjectionCenterOffset, 0, 0) * projCenter, + projRight = Matrix4f::Translation(-ProjectionCenterOffset, 0, 0) * projCenter; + + EyeRenderParams[0].Init(StereoEye_Left, + Viewport(FullView.x, FullView.y, FullView.w/2, FullView.h), + +InterpupillaryDistance * 0.5f, // World view shift. + projLeft, OrthoCenter * Matrix4f::Translation(OrthoPixelOffset, 0, 0), + &Distortion); + EyeRenderParams[1].Init(StereoEye_Right, + Viewport(FullView.x + FullView.w/2, FullView.y, FullView.w/2, FullView.h), + -InterpupillaryDistance * 0.5f, + projRight, OrthoCenter * Matrix4f::Translation(-OrthoPixelOffset, 0, 0), + &Distortion); + } + break; + } + +} + + +}}} // OVR::Util::Render + diff --git a/LibOVR/Src/Util/Util_Render_Stereo.h b/LibOVR/Src/Util/Util_Render_Stereo.h new file mode 100644 index 0000000..9dc110c --- /dev/null +++ b/LibOVR/Src/Util/Util_Render_Stereo.h @@ -0,0 +1,299 @@ +/************************************************************************************ + +PublicHeader: OVR.h +Filename : Util_Render_Stereo.h +Content : Sample stereo rendering configuration classes. +Created : October 22, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus Inc license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Util_Render_Stereo_h +#define OVR_Util_Render_Stereo_h + +#include "../OVR_Device.h" + +namespace OVR { namespace Util { namespace Render { + + +//----------------------------------------------------------------------------------- +// ***** Stereo Enumerations + +// StereoMode describes rendering modes that can be used by StereoConfig. +// These modes control whether stereo rendering is used or not (Stereo_None), +// and how it is implemented. +enum StereoMode +{ + Stereo_None = 0, + Stereo_LeftRight_Multipass = 1 +}; + + +// StereoEye specifies which eye we are rendering for; it is used to +// retrieve StereoEyeParams. +enum StereoEye +{ + StereoEye_Center, + StereoEye_Left, + StereoEye_Right +}; + + +//----------------------------------------------------------------------------------- +// ***** Viewport + +// Viewport describes a rectangular area used for rendering, in pixels. +struct Viewport +{ + int x, y; + int w, h; + + Viewport() {} + Viewport(int x1, int y1, int w1, int h1) : x(x1), y(y1), w(w1), h(h1) { } + + bool operator == (const Viewport& vp) const + { return (x == vp.x) && (y == vp.y) && (w == vp.w) && (h == vp.h); } + bool operator != (const Viewport& vp) const + { return !operator == (vp); } +}; + + +//----------------------------------------------------------------------------------- +// ***** DistortionConfig + +// DistortionConfig Provides controls for the distortion shader. +// - K[0] - K[3] are coefficients for the distortion function. +// - XCenterOffset is the offset of lens distortion center from the +// center of one-eye screen half. [-1, 1] Range. +// - Scale is a factor of how much larger will the input image be, +// with a factor of 1.0f being no scaling. An inverse of this +// value is applied to sampled UV coordinates (1/Scale). +// - ChromaticAberration is an array of parameters for controlling +// additional Red and Blue scaling in order to reduce chromatic aberration +// caused by the Rift lenses. +class DistortionConfig +{ +public: + DistortionConfig(float k0 = 1.0f, float k1 = 0.0f, float k2 = 0.0f, float k3 = 0.0f) + : XCenterOffset(0), YCenterOffset(0), Scale(1.0f) + { + SetCoefficients(k0, k1, k2, k3); + SetChromaticAberration(); + } + + void SetCoefficients(float k0, float k1 = 0.0f, float k2 = 0.0f, float k3 = 0.0f) + { K[0] = k0; K[1] = k1; K[2] = k2; K[3] = k3; } + + void SetChromaticAberration(float red1 = 1.0f, float red2 = 0.0f, float blue1 = 1.0f, float blue2 = 0.0f) + { ChromaticAberration[0] = red1; ChromaticAberration[1] = red2; ChromaticAberration[2] = blue1; ChromaticAberration[3] = blue2; } + + + // DistortionFn applies distortion equation to the argument. The returned + // value should match distortion equation used in shader. + float DistortionFn(float r) const + { + float rsq = r * r; + float scale = r * (K[0] + K[1] * rsq + K[2] * rsq * rsq + K[3] * rsq * rsq * rsq); + return scale; + } + + // DistortionFnInverse computes the inverse of the distortion function on an argument. + float DistortionFnInverse(float r); + + float K[4]; + float XCenterOffset, YCenterOffset; + float Scale; + + float ChromaticAberration[4]; // Additional per-channel scaling is applied after distortion: + // Index [0] - Red channel constant coefficient. + // Index [1] - Red channel r^2 coefficient. + // Index [2] - Blue channel constant coefficient. + // Index [3] - Blue channel r^2 coefficient. +}; + + +//----------------------------------------------------------------------------------- +// ***** StereoEyeParams + +// StereoEyeParams describes RenderDevice configuration needed to render +// the scene for one eye. +class StereoEyeParams +{ +public: + StereoEye Eye; + Viewport VP; // Viewport that we are rendering to + const DistortionConfig* pDistortion; + + Matrix4f ViewAdjust; // Translation to be applied to view matrix. + Matrix4f Projection; // Projection matrix used with this eye. + Matrix4f OrthoProjection; // Orthographic projection used with this eye. + + void Init(StereoEye eye, const Viewport &vp, float vofs, + const Matrix4f& proj, const Matrix4f& orthoProj, + const DistortionConfig* distortion = 0) + { + Eye = eye; + VP = vp; + ViewAdjust = Matrix4f::Translation(Vector3f(vofs,0,0)); + Projection = proj; + OrthoProjection = orthoProj; + pDistortion = distortion; + } +}; + + +//----------------------------------------------------------------------------------- +// ***** StereoConfig + +// StereoConfig maintains a scene stereo state and allow switching between different +// stereo rendering modes. To support rendering, StereoConfig keeps track of HMD +// variables such as screen size, eye-to-screen distance and distortion, and computes +// extra data such as FOV and distortion center offsets based on it. Rendering +// parameters are returned though StereoEyeParams for each eye. +// +// Beyond regular 3D projection, this class supports rendering a 2D orthographic +// surface for UI and text. The 2D surface will be defined as fitting within a 2D +// field of view (85 degrees by default) and used [-1,1] coordinate system with +// square pixels. The (0,0) coordinate corresponds to eye center location +// that is properly adjusted during rendering through SterepRenderParams::Adjust2D. +// Genreally speaking, text outside [-1,1] coordinate range will not be readable. + +class StereoConfig +{ +public: + + StereoConfig(StereoMode mode = Stereo_LeftRight_Multipass, + const Viewport& fullViewport = Viewport(0,0, 1280,800)); + + + // *** Modifiable State Access + + // Sets a stereo rendering mode and updates internal cached + // state (matrices, per-eye view) based on it. + void SetStereoMode(StereoMode mode) { Mode = mode; DirtyFlag = true; } + StereoMode GetStereoMode() const { return Mode; } + + // Sets HMD parameters; also initializes distortion coefficients. + void SetHMDInfo(const HMDInfo& hmd); + const HMDInfo& GetHMDInfo() const { return HMD; } + + // Query physical eye-to-screen distance in meters, which combines screen-to-lens and + // and lens-to-eye pupil distances. Modifying this value adjusts FOV. + float GetEyeToScreenDistance() const { return HMD.EyeToScreenDistance; } + void SetEyeToScreenDistance(float esd) { HMD.EyeToScreenDistance = esd; DirtyFlag = true; } + + // Interpupillary distance used for stereo, in meters. Default is 0.064m (64 mm). + void SetIPD(float ipd) { InterpupillaryDistance = ipd; DirtyFlag = true; } + float GetIPD() const { return InterpupillaryDistance; } + + // Set full render target viewport; for HMD this includes both eyes. + void SetFullViewport(const Viewport& vp); + const Viewport& GetFullViewport() const { return FullView; } + + // Aspect ratio defaults to ((w/h)*multiplier) computed per eye. + // Aspect multiplier allows adjusting aspect ratio consistently for Stereo/NoStereo. + void SetAspectMultiplier(float m) { AspectMultiplier = m; DirtyFlag = true; } + float GetAspectMultiplier() const { return AspectMultiplier; } + + + // For the distorted image to fill rendered viewport, input texture render target needs to be + // scaled by DistortionScale before sampling. The scale factor is computed by fitting a point + // on of specified radius from a distortion center, more easily specified as a coordinate. + // SetDistortionFitPointVP sets the (x,y) coordinate of the point that scale will be "fit" to, + // assuming [-1,1] coordinate range for full left-eye viewport. A fit point is a location + // where source (pre-distortion) and target (post-distortion) image match each other. + // For the right eye, the interpretation of 'u' will be inverted. + void SetDistortionFitPointVP(float x, float y); + // SetDistortionFitPointPixels sets the (x,y) coordinate of the point that scale will be "fit" to, + // specified in pixeld for full left-eye texture. + void SetDistortionFitPointPixels(float x, float y); + + // Changes all distortion settings. + // Note that setting HMDInfo also changes Distortion coefficients. + void SetDistortionConfig(const DistortionConfig& d) { Distortion = d; DirtyFlag = true; } + + // Modify distortion coefficients; useful for adjustment tweaking. + void SetDistortionK(int i, float k) { Distortion.K[i] = k; DirtyFlag = true; } + float GetDistortionK(int i) const { return Distortion.K[i]; } + + // Sets the fieldOfView that the 2D coordinate area stretches to. + void Set2DAreaFov(float fovRadians); + + + // *** Computed State + + // Return current aspect ratio. + float GetAspect() { updateIfDirty(); return Aspect; } + + // Return computed vertical FOV in radians/degrees. + float GetYFOVRadians() { updateIfDirty(); return YFov; } + float GetYFOVDegrees() { return RadToDegree(GetYFOVRadians()); } + + // Query horizontal projection center offset as a distance away from the + // one-eye [-1,1] unit viewport. + // Positive return value should be used for left eye, negative for right eye. + float GetProjectionCenterOffset() { updateIfDirty(); return ProjectionCenterOffset; } + + // GetDistortionConfig isn't const because XCenterOffset bay need to be recomputed. + const DistortionConfig& GetDistortionConfig() { updateIfDirty(); return Distortion; } + + // Returns DistortionScale factor by which input texture size is increased to make + // post-distortion result distortion fit the viewport. + float GetDistortionScale() { updateIfDirty(); return Distortion.Scale; } + + // Returns the size of a pixel within 2D coordinate system. + float Get2DUnitPixel() { updateIfDirty(); return (2.0f / (FovPixels * Distortion.Scale)); } + + // Returns full set of Stereo rendering parameters for the specified eye. + const StereoEyeParams& GetEyeRenderParams(StereoEye eye); + +private: + + void updateIfDirty() { if (DirtyFlag) updateComputedState(); } + void updateComputedState(); + + void updateDistortionOffsetAndScale(); + void updateProjectionOffset(); + void update2D(); + void updateEyeParams(); + + + // *** Modifiable State + + StereoMode Mode; + float InterpupillaryDistance; + float AspectMultiplier; // Multiplied into aspect ratio to change it. + HMDInfo HMD; + DistortionConfig Distortion; + float DistortionFitX, DistortionFitY; // In [-1,1] half-screen viewport units. + Viewport FullView; // Entire window viewport. + + float Area2DFov; // FOV range mapping to [-1, 1] 2D area. + + // *** Computed State + + bool DirtyFlag; // Set when any if the modifiable state changed. + float YFov; // Vertical FOV. + float Aspect; // Aspect ratio: (w/h)*AspectMultiplier. + float ProjectionCenterOffset; + StereoEyeParams EyeRenderParams[2]; + + + // ** 2D Rendering + + // Number of 2D pixels in the FOV. This defines [-1,1] coordinate range for 2D. + float FovPixels; + Matrix4f OrthoCenter; + float OrthoPixelOffset; +}; + + +}}} // OVR::Util::Render + +#endif diff --git a/Mac_OculusWorldDemo.app/Contents/Info.plist b/Mac_OculusWorldDemo.app/Contents/Info.plist new file mode 100644 index 0000000..6ae01b6 --- /dev/null +++ b/Mac_OculusWorldDemo.app/Contents/Info.plist @@ -0,0 +1,50 @@ + + + + + BuildMachineOSBuild + 12C3103 + CFBundleDevelopmentRegion + en + CFBundleExecutable + OculusWorldDemo + CFBundleIconFile + Oculus + CFBundleIdentifier + com.oculusvr.OculusWorldDemo + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + OculusWorldDemo + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + DTCompiler + + DTPlatformBuild + 4H127 + DTPlatformVersion + GM + DTSDKBuild + 12C37 + DTSDKName + macosx10.8 + DTXcode + 0460 + DTXcodeBuild + 4H127 + LSMinimumSystemVersion + 10.6 + NSHumanReadableCopyright + Copyright © 2013 Oculus VR. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/Mac_OculusWorldDemo.app/Contents/MacOS/OculusWorldDemo b/Mac_OculusWorldDemo.app/Contents/MacOS/OculusWorldDemo new file mode 100644 index 0000000..b4c9208 Binary files /dev/null and b/Mac_OculusWorldDemo.app/Contents/MacOS/OculusWorldDemo differ diff --git a/Mac_OculusWorldDemo.app/Contents/PkgInfo b/Mac_OculusWorldDemo.app/Contents/PkgInfo new file mode 100644 index 0000000..bd04210 --- /dev/null +++ b/Mac_OculusWorldDemo.app/Contents/PkgInfo @@ -0,0 +1 @@ +APPL???? \ No newline at end of file diff --git a/Samples/CommonSrc/Makefile b/Samples/CommonSrc/Makefile new file mode 100644 index 0000000..60b84ed --- /dev/null +++ b/Samples/CommonSrc/Makefile @@ -0,0 +1,7 @@ + +PLATFORM_SRCS := Samples/CommonSrc/Platform/Platform.cpp Samples/CommonSrc/Platform/X11_Platform.cpp + +RENDER_SRCS := Samples/CommonSrc/Render/Render_Device.cpp Samples/CommonSrc/Render/Render_Stereo.cpp \ + Samples/CommonSrc/Render/Render_GL_Device.cpp \ + Samples/CommonSrc/Render/Render_LoadTextureTGA.cpp + diff --git a/Samples/CommonSrc/Oculus.ico b/Samples/CommonSrc/Oculus.ico new file mode 100644 index 0000000..bb61593 Binary files /dev/null and b/Samples/CommonSrc/Oculus.ico differ diff --git a/Samples/CommonSrc/Platform/Gamepad.h b/Samples/CommonSrc/Platform/Gamepad.h new file mode 100644 index 0000000..01eaab4 --- /dev/null +++ b/Samples/CommonSrc/Platform/Gamepad.h @@ -0,0 +1,102 @@ +/************************************************************************************ + +Filename : Gamepad.h +Content : Cross platform Gamepad interface. +Created : May 6, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef OVR_Gamepad_h +#define OVR_Gamepad_h + +#include "OVR.h" + +namespace OVR { namespace Platform { + +// Buttons on a typical gamepad controller. +enum GamepadButtons +{ + Gamepad_A = 0x1000, + Gamepad_CROSS = 0x1000, + Gamepad_B = 0x2000, + Gamepad_CIRCLE = 0x2000, + Gamepad_X = 0x4000, + Gamepad_SQUARE = 0x4000, + Gamepad_Y = 0x8000, + Gamepad_TRIANGLE = 0x8000, + Gamepad_Up = 0x0001, + Gamepad_Down = 0x0002, + Gamepad_Left = 0x0004, + Gamepad_Right = 0x0008, + Gamepad_Start = 0x0010, + Gamepad_Back = 0x0020, + Gamepad_LStick = 0x0040, + Gamepad_RStick = 0x0080, + Gamepad_L1 = 0x0100, + Gamepad_R1 = 0x0200, +}; + +//------------------------------------------------------------------------------------- +// ***** GamepadState + +// Describes the state of the controller buttons and analog inputs. +struct GamepadState +{ + UInt32 Buttons; // Bitfield representing button state. + float LX; // Left stick X axis [-1,1] + float LY; // Left stick Y axis [-1,1] + float RX; // Right stick X axis [-1,1] + float RY; // Right stick Y axis [-1,1] + float LT; // Left trigger [0,1] + float RT; // Right trigger [0,1] + + GamepadState() : Buttons(0), LX(0), LY(0), RX(0), RY(0), LT(0), RT(0) {} + + bool operator==(const GamepadState& b) const + { + return Buttons == b.Buttons && LX == b.LX && LY == b.LY && RX == b.RX && RY == b.RY && LT == b.LT && RT == b.RT; + } + bool operator!=(const GamepadState& b) const + { + return !(*this == b); + } + void Debug() + { + OVR_DEBUG_LOG(("Buttons:0x%4x LX:%.2f LY:%.2f RX:%.2f RY:%.2f LT:%.2f RT:%.2f", Buttons, LX, LY, RX, RY, LT, RT)); + } +}; + +//------------------------------------------------------------------------------------- +// ***** GamepadManager + +// GamepadManager provides a cross platform interface for accessing gamepad controller +// state. +class GamepadManager : public RefCountBase +{ +public: + + // Get the number of connected gamepads. + virtual UInt32 GetGamepadCount() = 0; + + // Get the state of the gamepad with a given index. + virtual bool GetGamepadState(UInt32 index, GamepadState* pState) = 0; +}; + +}} // OVR::Platform + +#endif // OVR_Gamepad_h diff --git a/Samples/CommonSrc/Platform/OSX_Gamepad.cpp b/Samples/CommonSrc/Platform/OSX_Gamepad.cpp new file mode 100644 index 0000000..3ac53ab --- /dev/null +++ b/Samples/CommonSrc/Platform/OSX_Gamepad.cpp @@ -0,0 +1,424 @@ +/************************************************************************************ + +Filename : OSX_Gamepad.cpp +Content : OSX implementation of Gamepad functionality. +Created : May 6, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#include "OSX_Gamepad.h" + + +static const UInt32 Logitech_F710_VendorID = 0x046D; +static const UInt32 Logitech_F710_ProductID = 0xC219; + +static const UInt32 Sony_DualShock3_VendorID = 0x054C; +static const UInt32 Sony_DualShock3_ProductID = 0x0268; + + +namespace OVR { namespace Platform { namespace OSX { + + +GamepadManager::GamepadManager() + : bStateChanged(false) +{ + + HidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + IOHIDManagerOpen(HidManager, kIOHIDOptionsTypeNone); + IOHIDManagerScheduleWithRunLoop(HidManager, + CFRunLoopGetCurrent(), + kCFRunLoopDefaultMode); + + + // Setup device matching. + CFStringRef keys[] = { CFSTR(kIOHIDDeviceUsagePageKey), + CFSTR(kIOHIDDeviceUsageKey)}; + + int value; + CFNumberRef values[2]; + CFDictionaryRef dictionaries[2]; + + // Match joysticks. + value = kHIDPage_GenericDesktop; + values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); + + value = kHIDUsage_GD_Joystick; + values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); + + dictionaries[0] = CFDictionaryCreate(kCFAllocatorDefault, + (const void **) keys, + (const void **) values, + 2, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFRelease(values[0]); + CFRelease(values[1]); + + // Match gamepads. + value = kHIDPage_GenericDesktop; + values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); + + value = kHIDUsage_GD_GamePad; + values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); + + dictionaries[1] = CFDictionaryCreate(kCFAllocatorDefault, + (const void **) keys, + (const void **) values, + 2, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease(values[0]); + CFRelease(values[1]); + + CFArrayRef array = CFArrayCreate( kCFAllocatorDefault, + (const void **) dictionaries, + 2, + &kCFTypeArrayCallBacks); + CFRelease(dictionaries[0]); + CFRelease(dictionaries[1]); + + IOHIDManagerSetDeviceMatchingMultiple(HidManager, array); + + CFRelease(array); + + + IOHIDManagerRegisterDeviceMatchingCallback(HidManager, staticOnDeviceMatched, this); + IOHIDManagerRegisterDeviceRemovalCallback(HidManager, staticOnDeviceRemoved, this); + +} + +GamepadManager::~GamepadManager() +{ + CFRelease(HidManager); +} + +UInt32 GamepadManager::GetGamepadCount() +{ + return 1; +} + +bool GamepadManager::GetGamepadState(UInt32 index, GamepadState* pState) +{ + // For now we just support one gamepad. + OVR_UNUSED(index); + + if (!bStateChanged) + { + return false; + } + + bStateChanged = false; +// State.Debug(); + + *pState = State; + return true; +} + +int GamepadManager::getIntDeviceProperty(IOHIDDeviceRef device, CFStringRef key) +{ + CFTypeRef type = IOHIDDeviceGetProperty(device, key); + OVR_ASSERT(type != NULL && CFGetTypeID(type) == CFNumberGetTypeID()); + + int value; + CFNumberGetValue((CFNumberRef) type, kCFNumberSInt32Type, &value); + + return value; +} + +void GamepadManager::staticOnDeviceMatched(void* context, IOReturn result, void* sender, IOHIDDeviceRef device) +{ + GamepadManager* pManager = (GamepadManager*) context; + pManager->onDeviceMatched(device); +} + +void GamepadManager::onDeviceMatched(IOHIDDeviceRef device) +{ + IOHIDDeviceRegisterInputValueCallback(device, staticOnDeviceValueChanged, this); +} + +void GamepadManager::staticOnDeviceRemoved(void* context, IOReturn result, void* sender, IOHIDDeviceRef device) +{ + GamepadManager* pManager = (GamepadManager*) context; + pManager->onDeviceRemoved(device); +} + +void GamepadManager::onDeviceRemoved(IOHIDDeviceRef device) +{ + IOHIDDeviceRegisterInputValueCallback(device, NULL, NULL); +} + +void GamepadManager::staticOnDeviceValueChanged(void* context, IOReturn result, void* sender, IOHIDValueRef value) +{ + GamepadManager* pManager = (GamepadManager*) context; + pManager->onDeviceValueChanged(value); +} + +float GamepadManager::mapAnalogAxis(IOHIDValueRef value, IOHIDElementRef element) +{ + + CFIndex val = IOHIDValueGetIntegerValue(value); + CFIndex min = IOHIDElementGetLogicalMin(element); + CFIndex max = IOHIDElementGetLogicalMax(element); + + float v = (float) (val - min) / (float) (max - min); + v = v * 2.0f - 1.0f; + + // Dead zone. + if (v < 0.1f && v > -0.1f) + { + v = 0.0f; + } + + return v; +} + +bool GamepadManager::setStateIfDifferent(float& state, float newState) +{ + if (state == newState) + return false; + + state = newState; + + return true; +} + +void GamepadManager::onDeviceValueChanged(IOHIDValueRef value) +{ + + IOHIDElementRef element = IOHIDValueGetElement(value); + IOHIDDeviceRef device = IOHIDElementGetDevice(element); + + int vendorID = getIntDeviceProperty(device, CFSTR(kIOHIDVendorIDKey)); + int productID = getIntDeviceProperty(device, CFSTR(kIOHIDProductIDKey)); + OVR_UNUSED(productID); + + uint32_t usagePage = IOHIDElementGetUsagePage(element); + uint32_t usage = IOHIDElementGetUsage(element); + + // The following controller mapping is based on the Logitech F710, however we use it for + // all Logitech devices on the assumption that they're likely to share the same mapping. + if (vendorID == Logitech_F710_VendorID) + { + // Logitech F710 mapping. + if (usagePage == kHIDPage_Button) + { + bool buttonState = IOHIDValueGetIntegerValue(value); + + switch(usage) + { + case kHIDUsage_Button_1: + manipulateBitField(State.Buttons, Gamepad_X, buttonState); + break; + case kHIDUsage_Button_2: + manipulateBitField(State.Buttons, Gamepad_A, buttonState); + break; + case kHIDUsage_Button_3: + manipulateBitField(State.Buttons, Gamepad_B, buttonState); + break; + case kHIDUsage_Button_4: + manipulateBitField(State.Buttons, Gamepad_Y, buttonState); + break; + case 0x05: + manipulateBitField(State.Buttons, Gamepad_L1, buttonState); + break; + case 0x06: + manipulateBitField(State.Buttons, Gamepad_R1, buttonState); + break; + case 0x07: + State.LT = buttonState ? 1.0f:0.0f; + break; + case 0x08: + State.RT = buttonState ? 1.0f:0.0f; + break; + case 0x09: + manipulateBitField(State.Buttons, Gamepad_Back, buttonState); + break; + case 0x0A: + manipulateBitField(State.Buttons, Gamepad_Start, buttonState); + break; + case 0x0B: + manipulateBitField(State.Buttons, Gamepad_LStick, buttonState); + break; + case 0x0C: + manipulateBitField(State.Buttons, Gamepad_RStick, buttonState); + break; + default: + return; + } + } + else if (usagePage == kHIDPage_GenericDesktop) + { + float v; + switch(usage) + { + case kHIDUsage_GD_X: + v = mapAnalogAxis(value, element); + if (!setStateIfDifferent(State.LX, v)) + return; + break; + case kHIDUsage_GD_Y: + v = mapAnalogAxis(value, element); + if (!setStateIfDifferent(State.LY, -v)) + return; + break; + case kHIDUsage_GD_Z: + v = mapAnalogAxis(value, element); + if (!setStateIfDifferent(State.RX, v)) + return; + break; + case kHIDUsage_GD_Rz: + v = mapAnalogAxis(value, element); + if (!setStateIfDifferent(State.RY, -v)) + return; + break; + case kHIDUsage_GD_Hatswitch: + { + CFIndex integerValue = IOHIDValueGetIntegerValue(value); + + manipulateBitField(State.Buttons, + Gamepad_Up, + integerValue == 7 || integerValue == 0 || integerValue == 1); + manipulateBitField(State.Buttons, + Gamepad_Down, + integerValue == 3 || integerValue == 4 || integerValue == 5); + manipulateBitField(State.Buttons, + Gamepad_Left, + integerValue == 5 || integerValue == 6 || integerValue == 7); + manipulateBitField(State.Buttons, + Gamepad_Right, + integerValue == 1 || integerValue == 2 || integerValue == 3); + } + break; + default: + return; + } + } + } + // The following controller mapping is based on the Sony DualShock3, however we use it for + // all Sony devices on the assumption that they're likely to share the same mapping. + else if (vendorID == Sony_DualShock3_VendorID) + { + // PS3 Controller. + if (usagePage == kHIDPage_Button) + { + bool buttonState = IOHIDValueGetIntegerValue(value); + + switch(usage) + { + case kHIDUsage_Button_1: + manipulateBitField(State.Buttons, Gamepad_Back, buttonState); + break; + case kHIDUsage_Button_2: + manipulateBitField(State.Buttons, Gamepad_LStick, buttonState); + break; + case kHIDUsage_Button_3: + manipulateBitField(State.Buttons, Gamepad_RStick, buttonState); + break; + case kHIDUsage_Button_4: + manipulateBitField(State.Buttons, Gamepad_Start, buttonState); + break; + case 0x05: + manipulateBitField(State.Buttons, Gamepad_Up, buttonState); + break; + case 0x06: + manipulateBitField(State.Buttons, Gamepad_Right, buttonState); + break; + case 0x07: + manipulateBitField(State.Buttons, Gamepad_Down, buttonState); + break; + case 0x08: + manipulateBitField(State.Buttons, Gamepad_Left, buttonState); + break; + case 0x09: + State.LT = buttonState ? 1.0f:0.0f; + break; + case 0x0A: + State.RT = buttonState ? 1.0f:0.0f; + break; + case 0x0B: + manipulateBitField(State.Buttons, Gamepad_L1, buttonState); + break; + case 0x0C: + manipulateBitField(State.Buttons, Gamepad_R1, buttonState); + break; + case 0x0D: + // PS3 Triangle. + manipulateBitField(State.Buttons, Gamepad_TRIANGLE, buttonState); + break; + case 0x0E: + // PS3 Circle + manipulateBitField(State.Buttons, Gamepad_CIRCLE, buttonState); + break; + case 0x0F: + // PS3 Cross + manipulateBitField(State.Buttons, Gamepad_CROSS, buttonState); + break; + case 0x10: + // PS3 Square + manipulateBitField(State.Buttons, Gamepad_SQUARE, buttonState); + break; + default: + return; + } + } + else if (usagePage == kHIDPage_GenericDesktop) + { + float v; + switch(usage) + { + case kHIDUsage_GD_X: + v = mapAnalogAxis(value, element); + if (!setStateIfDifferent(State.LX, v)) + return; + break; + case kHIDUsage_GD_Y: + v = mapAnalogAxis(value, element); + if (!setStateIfDifferent(State.LY, -v)) + return; + break; + case kHIDUsage_GD_Z: + v = mapAnalogAxis(value, element); + if (!setStateIfDifferent(State.RX, v)) + return; + break; + case kHIDUsage_GD_Rz: + v = mapAnalogAxis(value, element); + if (!setStateIfDifferent(State.RY, -v)) + return; + break; + default: + return; + } + } + } + + bStateChanged = true; +} + +void GamepadManager::manipulateBitField(unsigned int& bitfield, unsigned int mask, bool val) +{ + if (val) + { + bitfield |= mask; + } + else + { + bitfield &= ~mask; + } +} + +}}} // OVR::Platform::OSX diff --git a/Samples/CommonSrc/Platform/OSX_Gamepad.h b/Samples/CommonSrc/Platform/OSX_Gamepad.h new file mode 100644 index 0000000..6d3344c --- /dev/null +++ b/Samples/CommonSrc/Platform/OSX_Gamepad.h @@ -0,0 +1,67 @@ +/************************************************************************************ + +Filename : OSX_Gamepad.h +Content : OSX implementation of Gamepad functionality. +Created : May 6, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef OVR_OSX_Gamepad_h +#define OVR_OSX_Gamepad_h + +#include "Gamepad.h" + +#include +#include + + +namespace OVR { namespace Platform { namespace OSX { + + +class GamepadManager : public Platform::GamepadManager +{ +public: + GamepadManager(); + ~GamepadManager(); + + virtual UInt32 GetGamepadCount(); + virtual bool GetGamepadState(UInt32 index, GamepadState* pState); + +private: + static void staticOnDeviceMatched(void* context, IOReturn result, void* sender, IOHIDDeviceRef device); + void onDeviceMatched(IOHIDDeviceRef device); + + static void staticOnDeviceRemoved(void* context, IOReturn result, void* sender, IOHIDDeviceRef device); + void onDeviceRemoved(IOHIDDeviceRef device); + + static void staticOnDeviceValueChanged(void* context, IOReturn result, void* sender, IOHIDValueRef value); + void onDeviceValueChanged(IOHIDValueRef value); + + int getIntDeviceProperty(IOHIDDeviceRef device, CFStringRef key); + float mapAnalogAxis(IOHIDValueRef value, IOHIDElementRef element); + void manipulateBitField(unsigned int& bitfield, unsigned int mask, bool val); + bool setStateIfDifferent(float& state, float newState); + + IOHIDManagerRef HidManager; + GamepadState State; + bool bStateChanged; +}; + +}}} + +#endif // OVR_OSX_Gamepad_h diff --git a/Samples/CommonSrc/Platform/OSX_Platform.h b/Samples/CommonSrc/Platform/OSX_Platform.h new file mode 100644 index 0000000..748c5fa --- /dev/null +++ b/Samples/CommonSrc/Platform/OSX_Platform.h @@ -0,0 +1,80 @@ + +#include "../Platform/Platform.h" +#include "../Render/Render_GL_Device.h" + +namespace OVR { namespace Platform { namespace OSX { + +class PlatformCore : public Platform::PlatformCore +{ +public: + void* Win; + void* View; + void* NsApp; + bool Quit; + int ExitCode; + int Width, Height; + MouseMode MMode; + + void RunIdle(); + +public: + PlatformCore(Application* app, void* nsapp); + ~PlatformCore(); + + bool SetupWindow(int w, int h); + void Exit(int exitcode); + + RenderDevice* SetupGraphics(const SetupGraphicsDeviceSet& setupGraphicsDesc, + const char* gtype, const Render::RendererParams& rp); + + void SetMouseMode(MouseMode mm); + void GetWindowSize(int* w, int* h) const; + + void SetWindowTitle(const char*title); + + void ShowWindow(bool show); + void DestroyWindow(); + bool SetFullscreen(const Render::RendererParams& rp, int fullscreen); + int GetDisplayCount(); + Render::DisplayId GetDisplay(int screen); + + String GetContentDirectory() const; +}; + +}} +namespace Render { namespace GL { namespace OSX { + +class RenderDevice : public Render::GL::RenderDevice +{ +public: + void* Context; + + RenderDevice(const Render::RendererParams& p, void* context) + : GL::RenderDevice(p), Context(context) {} + + virtual void Shutdown(); + virtual void Present(); + + virtual bool SetFullscreen(DisplayMode fullscreen); + + // oswnd = X11::PlatformCore* + static Render::RenderDevice* CreateDevice(const RendererParams& rp, void* oswnd); +}; + +}}}} + + +// OVR_PLATFORM_APP_ARGS specifies the Application class to use for startup, +// providing it with startup arguments. +#define OVR_PLATFORM_APP_ARGS(AppClass, args) \ +OVR::Platform::Application* OVR::Platform::Application::CreateApplication() \ +{ OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); \ +return new AppClass args; } \ +void OVR::Platform::Application::DestroyApplication(OVR::Platform::Application* app) \ +{ OVR::Platform::PlatformCore* platform = app->pPlatform; \ +delete app; delete platform; OVR::System::Destroy(); }; + +// OVR_PLATFORM_APP_ARGS specifies the Application startup class with no args. +#define OVR_PLATFORM_APP(AppClass) OVR_PLATFORM_APP_ARGS(AppClass, ()) + + diff --git a/Samples/CommonSrc/Platform/OSX_Platform.mm b/Samples/CommonSrc/Platform/OSX_Platform.mm new file mode 100644 index 0000000..4c7010f --- /dev/null +++ b/Samples/CommonSrc/Platform/OSX_Platform.mm @@ -0,0 +1,514 @@ + +#import "../Platform/OSX_PlatformObjc.h" + +using namespace OVR; +using namespace OVR::Platform; + +@implementation OVRApp + +- (void)dealloc +{ + [super dealloc]; +} + +- (void)run +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + _running = YES; + OVR::Platform::Application* app; + { + using namespace OVR; + using namespace OVR::Platform; + + // CreateApplication must be the first call since it does OVR::System::Initialize. + app = Application::CreateApplication(); + OSX::PlatformCore* platform = new OSX::PlatformCore(app, self); + // The platform attached to an app will be deleted by DestroyApplication. + app->SetPlatformCore(platform); + + [self setApp:app]; + [self setPlatform:platform]; + + const char* argv[] = {"OVRApp"}; + int exitCode = app->OnStartup(1, argv); + if (exitCode) + { + Application::DestroyApplication(app); + exit(exitCode); + } + } + [self finishLaunching]; + [pool drain]; + + while ([self isRunning]) + { + pool = [[NSAutoreleasePool alloc] init]; + NSEvent* event = [self nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES]; + if (event) + { + [self sendEvent:event]; + } + _App->OnIdle(); + [pool drain]; + } + OVR::Platform::Application::DestroyApplication(app); +} + +@end + +static int KeyMap[][2] = +{ + { NSDeleteFunctionKey, OVR::Key_Delete }, + { '\t', OVR::Key_Tab }, + { '\n', OVR::Key_Return }, + { NSPauseFunctionKey, OVR::Key_Pause }, + { 27, OVR::Key_Escape }, + { 127, OVR::Key_Backspace }, + { ' ', OVR::Key_Space }, + { NSPageUpFunctionKey, OVR::Key_PageUp }, + { NSPageDownFunctionKey, OVR::Key_PageDown }, + { NSNextFunctionKey, OVR::Key_PageDown }, + { NSEndFunctionKey, OVR::Key_End }, + { NSHomeFunctionKey, OVR::Key_Home }, + { NSLeftArrowFunctionKey, OVR::Key_Left }, + { NSUpArrowFunctionKey, OVR::Key_Up }, + { NSRightArrowFunctionKey, OVR::Key_Right }, + { NSDownArrowFunctionKey, OVR::Key_Down }, + { NSInsertFunctionKey, OVR::Key_Insert }, + { NSDeleteFunctionKey, OVR::Key_Delete }, + { NSHelpFunctionKey, OVR::Key_Insert }, +}; + + +static KeyCode MapToKeyCode(wchar_t vk) +{ + unsigned key = Key_None; + + if ((vk >= 'a') && (vk <= 'z')) + { + key = vk - 'a' + Key_A; + } + else if ((vk >= ' ') && (vk <= '~')) + { + key = vk; + } + else if ((vk >= '0') && (vk <= '9')) + { + key = vk - '0' + Key_Num0; + } + else if ((vk >= NSF1FunctionKey) && (vk <= NSF15FunctionKey)) + { + key = vk - NSF1FunctionKey + Key_F1; + } + else + { + for (unsigned i = 0; i< (sizeof(KeyMap) / sizeof(KeyMap[1])); i++) + { + if (vk == KeyMap[i][0]) + { + key = KeyMap[i][1]; + break; + } + } + } + + return (KeyCode)key; +} + +static int MapModifiers(unsigned long xmod) +{ + int mod = 0; + if (xmod & NSShiftKeyMask) + mod |= OVR::Platform::Mod_Shift; + if (xmod & NSCommandKeyMask) + mod |= OVR::Platform::Mod_Control; + if (xmod & NSAlternateKeyMask) + mod |= OVR::Platform::Mod_Alt; + if (xmod & NSControlKeyMask) + mod |= OVR::Platform::Mod_Meta; + return mod; +} + +@implementation OVRView + +-(BOOL) acceptsFirstResponder +{ + return YES; +} +-(BOOL) acceptsFirstMouse:(NSEvent *)ev +{ + return YES; +} + ++(CGDirectDisplayID) displayFromScreen:(NSScreen *)s +{ + NSNumber* didref = (NSNumber*)[[s deviceDescription] objectForKey:@"NSScreenNumber"]; + CGDirectDisplayID disp = (CGDirectDisplayID)[didref longValue]; + return disp; +} + +-(void) warpMouseToCenter +{ + NSPoint w; + w.x = _Platform->Width/2.0f; + w.y = _Platform->Height/2.0f; + w = [[self window] convertBaseToScreen:w]; + CGDirectDisplayID disp = [OVRView displayFromScreen:[[self window] screen]]; + CGPoint p = {w.x, CGDisplayPixelsHigh(disp)-w.y}; + CGDisplayMoveCursorToPoint(disp, p); +} + +static bool LookupKey(NSEvent* ev, wchar_t& ch, OVR::KeyCode& key, unsigned& mods) +{ + NSString* chars = [ev charactersIgnoringModifiers]; + if ([chars length] == 0) + return false; + ch = [chars characterAtIndex:0]; + mods = MapModifiers([ev modifierFlags]); + + // check for Cmd+Latin Letter + NSString* modchars = [ev characters]; + if ([modchars length]) + { + wchar_t modch = [modchars characterAtIndex:0]; + if (modch >= 'a' && modch <= 'z') + ch = modch; + } + key = MapToKeyCode(ch); + return true; +} + +-(void) keyDown:(NSEvent*)ev +{ + OVR::KeyCode key; + unsigned mods; + wchar_t ch; + if (!LookupKey(ev, ch, key, mods)) + return; + if (key == Key_Escape && _Platform->MMode == Mouse_Relative) + { + [self warpMouseToCenter]; + CGAssociateMouseAndMouseCursorPosition(true); + [NSCursor unhide]; + _Platform->MMode = Mouse_RelativeEscaped; + } + _App->OnKey(key, ch, true, mods); +} +-(void) keyUp:(NSEvent*)ev +{ + OVR::KeyCode key; + unsigned mods; + wchar_t ch; + if (LookupKey(ev, ch, key, mods)) + _App->OnKey(key, ch, false, mods); +} + +static const OVR::KeyCode ModifierKeys[] = {OVR::Key_None, OVR::Key_Shift, OVR::Key_Control, OVR::Key_Alt, OVR::Key_Meta}; + +-(void)flagsChanged:(NSEvent *)ev +{ + unsigned long cmods = [ev modifierFlags]; + if ((cmods & 0xffff0000) != _Modifiers) + { + uint32_t mods = MapModifiers(cmods); + for (int i = 1; i <= 4; i++) + { + unsigned long m = (1 << (16+i)); + if ((cmods & m) != (_Modifiers & m)) + { + if (cmods & m) + _App->OnKey(ModifierKeys[i], 0, true, mods); + else + _App->OnKey(ModifierKeys[i], 0, false, mods); + } + } + _Modifiers = cmods & 0xffff0000; + } +} + +-(void)ProcessMouse:(NSEvent*)ev +{ + switch ([ev type]) + { + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDragged: + case NSMouseMoved: + { + if (_Platform->MMode == OVR::Platform::Mouse_Relative) + { + int dx = [ev deltaX]; + int dy = [ev deltaY]; + + if (dx != 0 || dy != 0) + { + _App->OnMouseMove(dx, dy, Mod_MouseRelative|MapModifiers([ev modifierFlags])); + [self warpMouseToCenter]; + } + } + else + { + NSPoint p = [ev locationInWindow]; + _App->OnMouseMove(p.x, p.y, MapModifiers([ev modifierFlags])); + } + } + break; + case NSLeftMouseDown: + case NSRightMouseDown: + case NSOtherMouseDown: + break; + } +} + +-(void) mouseMoved:(NSEvent*)ev +{ + [self ProcessMouse:ev]; +} +-(void) mouseDragged:(NSEvent*)ev +{ + [self ProcessMouse:ev]; +} +-(void) mouseDown:(NSEvent*)ev +{ + if (_Platform->MMode == Mouse_RelativeEscaped) + { + [self warpMouseToCenter]; + CGAssociateMouseAndMouseCursorPosition(false); + [NSCursor hide]; + _Platform->MMode = Mouse_Relative; + } +} + +//-(void) + +-(id) initWithFrame:(NSRect)frameRect +{ + NSOpenGLPixelFormatAttribute attr[] = + {NSOpenGLPFAWindow, NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, 24, nil}; + + NSOpenGLPixelFormat *pf = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attr] autorelease]; + + self = [super initWithFrame:frameRect pixelFormat:pf]; + GLint swap = 0; + [[self openGLContext] setValues:&swap forParameter:NSOpenGLCPSwapInterval]; + //[self setWantsBestResolutionOpenGLSurface:YES]; + return self; +} + +-(void) reshape +{ + NSRect bounds = [self bounds]; + _App->OnResize(bounds.size.width, bounds.size.height); + + _Platform->Width = bounds.size.width; + _Platform->Height = bounds.size.height; + + if (_Platform->GetRenderer()) + _Platform->GetRenderer()->SetWindowSize(bounds.size.width, bounds.size.height); +} + +-(BOOL)windowShouldClose:(id)sender +{ + if (_Platform) + _Platform->Exit(0); + else + exit(0); + return 1; +} + +@end + +namespace OVR { namespace Platform { namespace OSX { + +PlatformCore::PlatformCore(Application* app, void* nsapp) + : Platform::PlatformCore(app), NsApp(nsapp), Win(NULL), View(NULL), Quit(0), MMode(Mouse_Normal) +{ + pGamepadManager = *new OSX::GamepadManager(); +} +PlatformCore::~PlatformCore() +{ +} + +void PlatformCore::Exit(int exitcode) +{ + OVRApp* nsApp = (OVRApp*)NsApp; + [nsApp stop:nil]; +} + +String PlatformCore::GetContentDirectory() const +{ + NSBundle* bundle = [NSBundle mainBundle]; + if (bundle) + return String([[bundle bundlePath] UTF8String]) + "/Contents/Resources"; + else + return "."; +} + + +void PlatformCore::SetMouseMode(MouseMode mm) +{ + if (mm == MMode) + return; + + if (Win) + { + if (mm == Mouse_Relative) + { + [NSCursor hide]; + [(OVRView*)View warpMouseToCenter]; + CGAssociateMouseAndMouseCursorPosition(false); + } + else + { + if (MMode == Mouse_Relative) + { + CGAssociateMouseAndMouseCursorPosition(true); + [NSCursor unhide]; + [(OVRView*)View warpMouseToCenter]; + } + } + } + MMode = mm; +} + + +void PlatformCore::GetWindowSize(int* w, int* h) const +{ + *w = Width; + *h = Height; +} + +bool PlatformCore::SetupWindow(int w, int h) +{ + NSRect winrect; + winrect.origin.x = 0; + winrect.origin.y = 1000; + winrect.size.width = w; + winrect.size.height = h; + NSWindow* win = [[NSWindow alloc] initWithContentRect:winrect styleMask:NSTitledWindowMask|NSClosableWindowMask backing:NSBackingStoreBuffered defer:NO]; + + OVRView* view = [[OVRView alloc] initWithFrame:winrect]; + [view setPlatform:this]; + [win setContentView:view]; + [win setAcceptsMouseMovedEvents:YES]; + [win setDelegate:view]; + [view setApp:pApp]; + Win = win; + View = view; + return 1; +} + +void PlatformCore::SetWindowTitle(const char* title) +{ + [((NSWindow*)Win) setTitle:[[NSString alloc] initWithBytes:title length:strlen(title) encoding:NSUTF8StringEncoding]]; +} + +void PlatformCore::ShowWindow(bool show) +{ + if (show) + [((NSWindow*)Win) makeKeyAndOrderFront:nil]; + else + [((NSWindow*)Win) orderOut:nil]; +} + +void PlatformCore::DestroyWindow() +{ + [((NSWindow*)Win) close]; + Win = NULL; +} + +RenderDevice* PlatformCore::SetupGraphics(const SetupGraphicsDeviceSet& setupGraphicsDesc, + const char* type, const Render::RendererParams& rp) +{ + const SetupGraphicsDeviceSet* setupDesc = setupGraphicsDesc.PickSetupDevice(type); + OVR_ASSERT(setupDesc); + + pRender = *setupDesc->pCreateDevice(rp, this); + if (pRender) + pRender->SetWindowSize(Width, Height); + + return pRender.GetPtr(); +} + +int PlatformCore::GetDisplayCount() +{ + return (int)[[NSScreen screens] count]; +} + +Render::DisplayId PlatformCore::GetDisplay(int i) +{ + NSScreen* s = (NSScreen*)[[NSScreen screens] objectAtIndex:i]; + return Render::DisplayId([OVRView displayFromScreen:s]); +} + +bool PlatformCore::SetFullscreen(const Render::RendererParams& rp, int fullscreen) +{ + if (fullscreen == Render::Display_Window) + [(OVRView*)View exitFullScreenModeWithOptions:nil]; + else + { + NSScreen* usescreen = [NSScreen mainScreen]; + NSArray* screens = [NSScreen screens]; + for (int i = 0; i < [screens count]; i++) + { + NSScreen* s = (NSScreen*)[screens objectAtIndex:i]; + CGDirectDisplayID disp = [OVRView displayFromScreen:s]; + + if (disp == rp.Display.CgDisplayId) + usescreen = s; + } + + [(OVRView*)View enterFullScreenMode:usescreen withOptions:nil]; + } + + if (pRender) + pRender->SetFullscreen((Render::DisplayMode)fullscreen); + return 1; +} + +}} +// GL +namespace Render { namespace GL { namespace OSX { + +Render::RenderDevice* RenderDevice::CreateDevice(const RendererParams& rp, void* oswnd) +{ + Platform::OSX::PlatformCore* PC = (Platform::OSX::PlatformCore*)oswnd; + + OVRView* view = (OVRView*)PC->View; + NSOpenGLContext *context = [view openGLContext]; + if (!context) + return NULL; + + [context makeCurrentContext]; + [((NSWindow*)PC->Win) makeKeyAndOrderFront:nil]; + + return new Render::GL::OSX::RenderDevice(rp, context); +} + +void RenderDevice::Present() +{ + NSOpenGLContext *context = (NSOpenGLContext*)Context; + [context flushBuffer]; +} + +void RenderDevice::Shutdown() +{ + Context = NULL; +} + +bool RenderDevice::SetFullscreen(DisplayMode fullscreen) +{ + Params.Fullscreen = fullscreen; + return 1; +} + +}}}} + + +int main(int argc, char *argv[]) +{ + NSApplication* nsapp = [OVRApp sharedApplication]; + [nsapp run]; + return 0; +} + diff --git a/Samples/CommonSrc/Platform/OSX_PlatformObjc.h b/Samples/CommonSrc/Platform/OSX_PlatformObjc.h new file mode 100644 index 0000000..7e69a4d --- /dev/null +++ b/Samples/CommonSrc/Platform/OSX_PlatformObjc.h @@ -0,0 +1,31 @@ + +#import +#import "OSX_Platform.h" +#import "OSX_Gamepad.h" + +#import +#import + +@interface OVRApp : NSApplication + +@property (assign) IBOutlet NSWindow* win; +@property (assign) OVR::Platform::OSX::PlatformCore* Platform; +@property (assign) OVR::Platform::Application* App; + +-(void) run; + +@end + +@interface OVRView : NSOpenGLView + +@property (assign) OVR::Platform::OSX::PlatformCore* Platform; +@property (assign) OVR::Platform::Application* App; +@property unsigned long Modifiers; + +-(void)ProcessMouse:(NSEvent*)event; +-(void)warpMouseToCenter; + ++(CGDirectDisplayID) displayFromScreen:(NSScreen*)s; + +@end + diff --git a/Samples/CommonSrc/Platform/OSX_WavPlayer.cpp b/Samples/CommonSrc/Platform/OSX_WavPlayer.cpp new file mode 100644 index 0000000..6119888 --- /dev/null +++ b/Samples/CommonSrc/Platform/OSX_WavPlayer.cpp @@ -0,0 +1,242 @@ +/************************************************************************************ + +Filename : WavPlayer_OSX.cpp +Content : An Apple OSX audio handler. +Created : March 5, 2013 +Authors : Robotic Arm Software - Peter Hoff, Dan Goodman, Bryan Croteau + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus LLC license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OSX_WavPlayer.h" + +namespace OVR { namespace Platform { namespace OSX { + +WavPlayer::WavPlayer(const char* fileName) +{ + FileName = fileName; +} + +bool WavPlayer::isDataChunk(unsigned char* buffer, int index) +{ + unsigned char a = buffer[index]; + unsigned char b = buffer[index + 1]; + unsigned char c = buffer[index + 2]; + unsigned char d = buffer[index + 3]; + return (a == 'D' || a == 'd') && (b == 'A' || b == 'a') && + (c == 'T' || c == 't') && (d == 'A' || d == 'a'); +} + +int WavPlayer::getWord(unsigned char* buffer, int index) +{ + unsigned char a = buffer[index]; + unsigned char b = buffer[index + 1]; + unsigned char c = buffer[index + 2]; + unsigned char d = buffer[index + 3]; + int result = 0; + result |= a; + result |= b << 8; + result |= c << 16; + result |= d << 24; + return result; +} + +short WavPlayer::getHalf(unsigned char* buffer, int index) +{ + unsigned char a = buffer[index]; + unsigned char b = buffer[index + 1]; + short result = 0; + result |= a; + result |= b << 8; + return result; +} + +void *WavPlayer::LoadPCM(const char *filename, unsigned long *len) +{ + FILE *file; + struct stat s; + void *pcm; + + if(stat(filename, &s)) + { + return NULL; + } + *len = s.st_size; + pcm = (void *) malloc(s.st_size); + if(!pcm) + { + return NULL; + } + file = fopen(filename, "rb"); + if(!file) + { + free(pcm); + return NULL; + } + fread(pcm, s.st_size, 1, file); + fclose(file); + return pcm; +} + +int WavPlayer::PlayBuffer(void *pcmbuffer, unsigned long len) +{ + AQCallbackStruct aqc; + UInt32 err, bufferSize; + int i; + + aqc.DataFormat.mSampleRate = SampleRate; + aqc.DataFormat.mFormatID = kAudioFormatLinearPCM; + if(BitsPerSample == 16) + { + aqc.DataFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger + | kAudioFormatFlagIsPacked; + } + aqc.DataFormat.mBytesPerPacket = NumChannels * (BitsPerSample / 8); + aqc.DataFormat.mFramesPerPacket = 1; + aqc.DataFormat.mBytesPerFrame = NumChannels * (BitsPerSample / 8); + aqc.DataFormat.mChannelsPerFrame = NumChannels; + aqc.DataFormat.mBitsPerChannel = BitsPerSample; + aqc.FrameCount = SampleRate / 60; + aqc.SampleLen = (UInt32)(len); + aqc.PlayPtr = 0; + aqc.PCMBuffer = static_cast(pcmbuffer); + + err = AudioQueueNewOutput(&aqc.DataFormat, + aqBufferCallback, + &aqc, + NULL, + kCFRunLoopCommonModes, + 0, + &aqc.Queue); + if(err) + { + return err; + } + + aqc.FrameCount = SampleRate / 60; + bufferSize = aqc.FrameCount * aqc.DataFormat.mBytesPerPacket; + + for(i = 0; i < AUDIO_BUFFERS; i++) + { + err = AudioQueueAllocateBuffer(aqc.Queue, bufferSize, + &aqc.Buffers[i]); + if(err) + { + return err; + } + aqBufferCallback(&aqc, aqc.Queue, aqc.Buffers[i]); + } + + err = AudioQueueStart(aqc.Queue, NULL); + if(err) + { + return err; + } + + while(true) + { + } + sleep(1); + return 0; +} + +void WavPlayer::aqBufferCallback(void *in, AudioQueueRef inQ, AudioQueueBufferRef outQB) +{ + AQCallbackStruct *aqc; + unsigned char *coreAudioBuffer; + + aqc = (AQCallbackStruct *) in; + coreAudioBuffer = (unsigned char*) outQB->mAudioData; + + printf("Sync: %u / %u\n", aqc->PlayPtr, aqc->SampleLen); + + if(aqc->FrameCount > 0) + { + outQB->mAudioDataByteSize = aqc->DataFormat.mBytesPerFrame * aqc->FrameCount; + for(int i = 0; i < aqc->FrameCount * aqc->DataFormat.mBytesPerFrame; i++) + { + if(aqc->PlayPtr > aqc->SampleLen) + { + aqc->PlayPtr = 0; + i = 0; + } + coreAudioBuffer[i] = aqc->PCMBuffer[aqc->PlayPtr]; + aqc->PlayPtr++; + } + AudioQueueEnqueueBuffer(inQ, outQB, 0, NULL); + } +} + +int WavPlayer::PlayAudio() +{ + unsigned long len; + void *pcmbuffer; + int ret; + + pcmbuffer = LoadPCM(FileName, &len); + if(!pcmbuffer) + { + fprintf(stderr, "%s: %s\n", FileName, strerror(errno)); + exit(EXIT_FAILURE); + } + + unsigned char* bytes = (unsigned char*)pcmbuffer; + int index = 0; + + // 'RIFF' + getWord(bytes, index); + index += 4; + // int Length + getWord(bytes, index); + index += 4; + // 'WAVE' + getWord(bytes, index); + index += 4; + // 'fmt ' + getWord(bytes, index); + index += 4; + + // int Format Length + int fmtLen = getWord(bytes, index); + index += 4; + AudioFormat = getHalf(bytes, index); + index += 2; + NumChannels = getHalf(bytes, index); + index += 2; + SampleRate = getWord(bytes, index); + index += 4; + ByteRate = getWord(bytes, index); + index += 4; + BlockAlign = getHalf(bytes, index); + index += 2; + BitsPerSample = getHalf(bytes, index); + index += 2; + index += fmtLen - 16; + while(!isDataChunk(bytes, index)) + { + // Any Chunk + getWord(bytes, index); + index += 4; + // Any Chunk Length + int anyChunkLen = getWord(bytes, index); + index += 4 + anyChunkLen; + } + // 'data' + getWord(bytes, index); + index += 4; + // int Data Length + unsigned long dataLen = getWord(bytes, index); + index += 4; + unsigned char* target = &bytes[index]; + + ret = PlayBuffer((void *)target, dataLen); + free(pcmbuffer); + return ret; +} + +}}} diff --git a/Samples/CommonSrc/Platform/OSX_WavPlayer.h b/Samples/CommonSrc/Platform/OSX_WavPlayer.h new file mode 100644 index 0000000..1b88b8c --- /dev/null +++ b/Samples/CommonSrc/Platform/OSX_WavPlayer.h @@ -0,0 +1,64 @@ +/************************************************************************************ + +Filename : WavPlayer_OSX.h +Content : An Apple OSX audio handler. +Created : March 5, 2013 +Authors : Robotic Arm Software - Peter Hoff, Dan Goodman, Bryan Croteau + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus LLC license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_WavPlayer_h +#define OVR_WavPlayer_h + +#include +#include +#include +#include +#include + +#define AUDIO_BUFFERS 4 + +namespace OVR { namespace Platform { namespace OSX { + +typedef struct AQCallbackStruct +{ + AudioQueueRef Queue; + UInt32 FrameCount; + AudioQueueBufferRef Buffers[AUDIO_BUFFERS]; + AudioStreamBasicDescription DataFormat; + UInt32 PlayPtr; + UInt32 SampleLen; + unsigned char* PCMBuffer; +} AQCallbackStruct; + +class WavPlayer +{ +public: + WavPlayer(const char* fileName); + int PlayAudio(); +private: + bool isDataChunk(unsigned char* buffer, int index); + int getWord(unsigned char* buffer, int index); + short getHalf(unsigned char* buffer, int index); + void *LoadPCM(const char *filename, unsigned long *len); + int PlayBuffer(void *pcm, unsigned long len); + static void aqBufferCallback(void *in, AudioQueueRef inQ, AudioQueueBufferRef outQB); + + short AudioFormat; + short NumChannels; + int SampleRate; + int ByteRate; + short BlockAlign; + short BitsPerSample; + const char* FileName; +}; + +}}} + +#endif diff --git a/Samples/CommonSrc/Platform/Platform.cpp b/Samples/CommonSrc/Platform/Platform.cpp new file mode 100644 index 0000000..bf5b8a5 --- /dev/null +++ b/Samples/CommonSrc/Platform/Platform.cpp @@ -0,0 +1,75 @@ +/************************************************************************************ + +Filename : Platform.cpp +Content : Platform-independent app framework for Oculus samples +Created : September 6, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + + + +#include "Platform.h" +#include +#include +#include "../Render/Render_Device.h" +#include "Gamepad.h" + +namespace OVR { namespace Platform { + + +const SetupGraphicsDeviceSet* SetupGraphicsDeviceSet::PickSetupDevice(const char* typeArg) const +{ + // Search for graphics creation object that matches type arg. + if (typeArg) + { + for (const SetupGraphicsDeviceSet* p = this; p != 0; p = p->pNext) + { + if (!OVR_stricmp(p->pTypeArg, typeArg)) + return p; + } + } + return this; +} + +//------------------------------------------------------------------------------------- + +PlatformCore::PlatformCore(Application *app) +{ + pApp = app; + pApp->SetPlatformCore(this); + StartupTicks = OVR::Timer::GetTicks(); +} + +double PlatformCore::GetAppTime() const +{ + return (OVR::Timer::GetTicks() - StartupTicks) * (1.0 / (double)OVR::Timer::MksPerSecond); +} + +bool PlatformCore::SetFullscreen(const Render::RendererParams&, int fullscreen) +{ + if (pRender) + return pRender->SetFullscreen((Render::DisplayMode)fullscreen); + return 0; +} + +Render::DisplayId PlatformCore::GetDisplay(int screen) +{ + OVR_UNUSED(screen); return Render::DisplayId(); +} + +}} diff --git a/Samples/CommonSrc/Platform/Platform.h b/Samples/CommonSrc/Platform/Platform.h new file mode 100644 index 0000000..8ef4593 --- /dev/null +++ b/Samples/CommonSrc/Platform/Platform.h @@ -0,0 +1,190 @@ +/************************************************************************************ + +Filename : Platform.h +Content : Platform-independent app and rendering framework for Oculus samples +Created : September 6, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef OVR_Platform_h +#define OVR_Platform_h + +#include "OVR.h" + +#include "Kernel/OVR_KeyCodes.h" + +namespace OVR { namespace Render { + class RenderDevice; + struct DisplayId; + struct RendererParams; +}} + +namespace OVR { namespace Platform { + +using Render::RenderDevice; + +class PlatformCore; +class Application; +class GamepadManager; + +// MouseMode configures mouse input behavior of the app. Three states are +// currently supported: +// Normal - Reports absolute coordinates with cursor shown. +// Relative - Reports relative delta coordinates with cursor hidden +// until 'Esc' key is pressed or window loses focus. +// RelativeEscaped - Relative input is desired, but has been escaped until +// mouse is clicked in the window, which will return the state +// to relative. Absolute coordinates are reported. + +enum MouseMode +{ + Mouse_Normal, + Mouse_Relative, // Cursor hidden, mouse grab, OnMouseMove reports relative deltas. + Mouse_RelativeEscaped, // Clicking in window will return to Relative state. +}; + + +enum Modifiers +{ + Mod_Shift = 0x001, + Mod_Control = 0x002, + Mod_Meta = 0x004, + Mod_Alt = 0x008, + + // Set for input Mouse_Relative mode, indicating that x,y are relative deltas. + Mod_MouseRelative = 0x100, +}; + +//------------------------------------------------------------------------------------- +// ***** SetupGraphicsDeviceSet + +typedef RenderDevice* (*RenderDeviceCreateFunc)(const Render::RendererParams&, void*); + +// SetupGraphicsDeviceSet is a PlatformCore::SetupGraphics initialization helper class, +// used to build up a list of RenderDevices that can be used for rendering. +// Specifying a smaller set allows application to avoid linking unused graphics devices. +struct SetupGraphicsDeviceSet +{ + SetupGraphicsDeviceSet(const char* typeArg, RenderDeviceCreateFunc createFunc) + : pTypeArg(typeArg), pCreateDevice(createFunc), pNext(0) { } + SetupGraphicsDeviceSet(const char* typeArg, RenderDeviceCreateFunc createFunc, + const SetupGraphicsDeviceSet& next) + : pTypeArg(typeArg), pCreateDevice(createFunc), pNext(&next) { } + + // Selects graphics object based on type string; returns 'this' if not found. + const SetupGraphicsDeviceSet* PickSetupDevice(const char* typeArg) const; + + const char* pTypeArg; + RenderDeviceCreateFunc pCreateDevice; + +private: + const SetupGraphicsDeviceSet* pNext; +}; + +//------------------------------------------------------------------------------------- +// ***** PlatformCore + +// PlatformCore defines abstract system window/viewport setup functionality and +// maintains a renderer. This class is separated from Application because it can have +// derived platform-specific implementations. +// Specific implementation classes are hidden within platform-specific versions +// such as Win32::PlatformCore. + +class PlatformCore : public NewOverrideBase +{ +protected: + Application* pApp; + Ptr pRender; + Ptr pGamepadManager; + UInt64 StartupTicks; + +public: + PlatformCore(Application *app); + virtual ~PlatformCore() { } + Application* GetApp() { return pApp; } + RenderDevice* GetRenderer() const { return pRender; } + GamepadManager* GetGamepadManager() const { return pGamepadManager; } + + virtual bool SetupWindow(int w, int h) = 0; + // Destroys window and also releases renderer. + virtual void DestroyWindow() = 0; + virtual void Exit(int exitcode) = 0; + + virtual void ShowWindow(bool visible) = 0; + + virtual bool SetFullscreen(const Render::RendererParams& rp, int fullscreen); + + // Search for a matching graphics renderer based on type argument and initializes it. + virtual RenderDevice* SetupGraphics(const SetupGraphicsDeviceSet& setupGraphicsDesc, + const char* gtype, + const Render::RendererParams& rp) = 0; + + virtual void SetMouseMode(MouseMode mm) { OVR_UNUSED(mm); } + + virtual void GetWindowSize(int* w, int* h) const = 0; + + virtual void SetWindowTitle(const char*title) = 0; + virtual void PlayMusicFile(const char *fileName) { OVR_UNUSED(fileName); } + virtual int GetDisplayCount() { return 0; } + virtual Render::DisplayId GetDisplay(int screen); + + // Get time since start of application in seconds. + double GetAppTime() const; + + virtual String GetContentDirectory() const { return "."; } +}; + +//------------------------------------------------------------------------------------- +// PlatformApp is a base application class from which end-user application +// classes derive. + +class Application : public NewOverrideBase +{ +protected: + class PlatformCore* pPlatform; + +public: + virtual ~Application() { } + + virtual int OnStartup(int argc, const char** argv) = 0; + virtual void OnQuitRequest() { pPlatform->Exit(0); } + + virtual void OnIdle() {} + + virtual void OnKey(KeyCode key, int chr, bool down, int modifiers) + { OVR_UNUSED4(key, chr, down, modifiers); } + virtual void OnMouseMove(int x, int y, int modifiers) + { OVR_UNUSED3(x, y, modifiers); } + + virtual void OnResize(int width, int height) + { OVR_UNUSED2(width, height); } + + void SetPlatformCore(PlatformCore* p) { pPlatform = p; } + PlatformCore* GetPlatformCore() const { return pPlatform; } + + + // Static functions defined by OVR_PLATFORM_APP and used to initialize and + // shut down the application class. + static Application* CreateApplication(); + static void DestroyApplication(Application* app); +}; + + +}} + +#endif diff --git a/Samples/CommonSrc/Platform/Platform_Default.h b/Samples/CommonSrc/Platform/Platform_Default.h new file mode 100644 index 0000000..d82be10 --- /dev/null +++ b/Samples/CommonSrc/Platform/Platform_Default.h @@ -0,0 +1,59 @@ +/************************************************************************************ + +Filename : Platform_Default.h +Content : Default Platform class and RenderDevice selection file +Created : October 4, 2012 +Authors : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#ifndef OVR_Platform_Default_h +#define OVR_Platform_Default_h + +// This should select proper header file for the platform/compiler. +#include + +#if defined(OVR_OS_WIN32) + #include "Win32_Platform.h" + + #include "../Render/Render_D3D11_Device.h" + #undef OVR_D3D_VERSION + #include "../Render/Render_D3D10_Device.h" +// #include "../Render/Render_GL_Win32_Device.h" + +// Modify this list or pass a smaller set to select a specific render device, +// while avoiding linking extra classes. + #define OVR_DEFAULT_RENDER_DEVICE_SET \ + SetupGraphicsDeviceSet("D3D11", &OVR::Render::D3D11::RenderDevice::CreateDevice, \ + SetupGraphicsDeviceSet("D3D10", &OVR::Render::D3D10::RenderDevice::CreateDevice) ) + +#elif defined(OVR_OS_MAC) && !defined(OVR_MAC_X11) + #include "OSX_Platform.h" + + #define OVR_DEFAULT_RENDER_DEVICE_SET \ + SetupGraphicsDeviceSet("GL", &OVR::Render::GL::OSX::RenderDevice::CreateDevice) + +#else + + #include "X11_Platform.h" + + #define OVR_DEFAULT_RENDER_DEVICE_SET \ + SetupGraphicsDeviceSet("GL", &OVR::Render::GL::X11::RenderDevice::CreateDevice) + +#endif + +#endif // OVR_Platform_Default_h diff --git a/Samples/CommonSrc/Platform/Win32_Gamepad.cpp b/Samples/CommonSrc/Platform/Win32_Gamepad.cpp new file mode 100644 index 0000000..db6524a --- /dev/null +++ b/Samples/CommonSrc/Platform/Win32_Gamepad.cpp @@ -0,0 +1,101 @@ +/************************************************************************************ + +Filename : Win32_Gamepad.cpp +Content : Win32 implementation of Platform app infrastructure +Created : May 6, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#include "Win32_Gamepad.h" + +namespace OVR { namespace Platform { namespace Win32 { + +GamepadManager::GamepadManager() +{ + hXInputModule = ::LoadLibraryA("Xinput9_1_0.dll"); + if (hXInputModule) + { + pXInputGetState = (PFn_XInputGetState) + ::GetProcAddress(hXInputModule, "XInputGetState"); + } +} + +GamepadManager::~GamepadManager() +{ + if (hXInputModule) + ::FreeLibrary(hXInputModule); +} + +static inline float GamepadStick(short in) +{ + float v; + if (abs(in) < 9000) + return 0; + else if (in > 9000) + v = (float) in - 9000; + else + v = (float) in + 9000; + return v / (32767 - 9000); +} + +static inline float GamepadTrigger(BYTE in) +{ + if (in < 30) + return 0; + else + return float(in-30) / 225; +} + +UInt32 GamepadManager::GetGamepadCount() +{ + return 1; +} + +bool GamepadManager::GetGamepadState(UInt32 index, GamepadState* pState) +{ + // For now we just support one gamepad. + OVR_UNUSED(index); + + if (pXInputGetState) + { + XINPUT_STATE xis; + + if (pXInputGetState(0, &xis)) + return false; + + if (xis.dwPacketNumber == LastPadPacketNo) + return false; + + // State changed. + pState->Buttons = xis.Gamepad.wButtons; // Currently matches Xinput + pState->LT = GamepadTrigger(xis.Gamepad.bLeftTrigger); + pState->RT = GamepadTrigger(xis.Gamepad.bRightTrigger); + pState->LX = GamepadStick(xis.Gamepad.sThumbLX); + pState->LY = GamepadStick(xis.Gamepad.sThumbLY); + pState->RX = GamepadStick(xis.Gamepad.sThumbRX); + pState->RY = GamepadStick(xis.Gamepad.sThumbRY); + + LastPadPacketNo = xis.dwPacketNumber; + + return true; + } + + return false; +} + +}}} // OVR::Platform::Win32 diff --git a/Samples/CommonSrc/Platform/Win32_Gamepad.h b/Samples/CommonSrc/Platform/Win32_Gamepad.h new file mode 100644 index 0000000..9066798 --- /dev/null +++ b/Samples/CommonSrc/Platform/Win32_Gamepad.h @@ -0,0 +1,54 @@ +/************************************************************************************ + +Filename : Win32_Gamepad.h +Content : Win32 implementation of Gamepad functionality. +Created : May 6, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef OVR_Win32_Gamepad_h +#define OVR_Win32_Gamepad_h + +#include "Gamepad.h" + +#include +#include + +namespace OVR { namespace Platform { namespace Win32 { + +class GamepadManager : public Platform::GamepadManager +{ +public: + GamepadManager(); + ~GamepadManager(); + + virtual UInt32 GetGamepadCount(); + virtual bool GetGamepadState(UInt32 index, GamepadState* pState); + +private: + // Dynamically ink to XInput to simplify projects. + HMODULE hXInputModule; + typedef DWORD (WINAPI *PFn_XInputGetState)(DWORD dwUserIndex, XINPUT_STATE* pState); + PFn_XInputGetState pXInputGetState; + + UInt32 LastPadPacketNo; +}; + +}}} + +#endif // OVR_Win32_Gamepad_h diff --git a/Samples/CommonSrc/Platform/Win32_Platform.cpp b/Samples/CommonSrc/Platform/Win32_Platform.cpp new file mode 100644 index 0000000..cdd1e1f --- /dev/null +++ b/Samples/CommonSrc/Platform/Win32_Platform.cpp @@ -0,0 +1,600 @@ +/************************************************************************************ + +Filename : Win32_Platform.cpp +Content : Win32 implementation of Platform app infrastructure +Created : September 6, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#include "Kernel/OVR_System.h" +#include "Kernel/OVR_Array.h" +#include "Kernel/OVR_String.h" + +#include "Win32_Platform.h" +#include "Win32_Gamepad.h" +#include "../Render/Render_Device.h" + +namespace OVR { namespace Platform { namespace Win32 { + + +PlatformCore::PlatformCore(Application* app, HINSTANCE hinst) + : Platform::PlatformCore(app), hWnd(NULL), hInstance(hinst), Quit(0), MMode(Mouse_Normal), + Cursor(0), Modifiers(0), WindowTitle("App") +{ + pGamepadManager = *new Win32::GamepadManager(); +} + +PlatformCore::~PlatformCore() +{ +} + +bool PlatformCore::SetupWindow(int w, int h) +{ + WNDCLASS wc; + memset(&wc, 0, sizeof(wc)); + wc.lpszClassName = L"OVRAppWindow"; + wc.style = CS_OWNDC; + wc.lpfnWndProc = systemWindowProc; + wc.cbWndExtra = sizeof(PlatformCore*); + + RegisterClass(&wc); + + Width = w; + Height = h; + RECT winSize; + winSize.left = winSize.top = 0; + winSize.right = Width; + winSize.bottom = Height; + AdjustWindowRect(&winSize, WS_OVERLAPPEDWINDOW, false); + hWnd = CreateWindowA("OVRAppWindow", WindowTitle.ToCStr(), WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + // 1950, 10, + winSize.right-winSize.left, winSize.bottom-winSize.top, + NULL, NULL, hInstance, (LPVOID)this); + Modifiers = 0; + + Cursor = LoadCursor(NULL, IDC_CROSS); + + // Initialize Window center in screen coordinates + POINT center = { Width / 2, Height / 2 }; + ::ClientToScreen(hWnd, ¢er); + WindowCenter = center; + + if (MMode == Mouse_Relative) + { + ::SetCursorPos(WindowCenter.x, WindowCenter.y); + ShowCursor(FALSE); + } + ::SetFocus(hWnd); + + return (hWnd != NULL); +} + +void PlatformCore::DestroyWindow() +{ + // Release renderer. + pRender.Clear(); + + // Release gamepad. + pGamepadManager.Clear(); + + // Release window resources. + ::DestroyWindow(hWnd); + UnregisterClass(L"OVRAppWindow", hInstance); + hWnd = 0; + Width = Height = 0; + + //DestroyCursor(Cursor); + Cursor = 0; +} + +void PlatformCore::ShowWindow(bool visible) +{ + ::ShowWindow(hWnd, visible ? SW_SHOW : SW_HIDE); +} + +void PlatformCore::SetMouseMode(MouseMode mm) +{ + if (mm == MMode) + return; + + if (hWnd) + { + if (mm == Mouse_Relative) + { + ShowCursor(FALSE); + ::SetCursorPos(WindowCenter.x, WindowCenter.y); + } + else + { + if (MMode == Mouse_Relative) + ShowCursor(TRUE); + } + } + MMode = mm; +} + +void PlatformCore::GetWindowSize(int* w, int* h) const +{ + *w = Width; + *h = Height; +} + + +void PlatformCore::SetWindowTitle(const char* title) +{ + WindowTitle = title; + if (hWnd) + ::SetWindowTextA(hWnd, title); +} + +static UByte KeyMap[][2] = +{ + { VK_BACK, Key_Backspace }, + { VK_TAB, Key_Tab }, + { VK_CLEAR, Key_Clear }, + { VK_RETURN, Key_Return }, + { VK_SHIFT, Key_Shift }, + { VK_CONTROL, Key_Control }, + { VK_MENU, Key_Alt }, + { VK_PAUSE, Key_Pause }, + { VK_CAPITAL, Key_CapsLock }, + { VK_ESCAPE, Key_Escape }, + { VK_SPACE, Key_Space }, + { VK_PRIOR, Key_PageUp }, + { VK_NEXT, Key_PageDown }, + { VK_END, Key_End }, + { VK_HOME, Key_Home }, + { VK_LEFT, Key_Left }, + { VK_UP, Key_Up }, + { VK_RIGHT, Key_Right }, + { VK_DOWN, Key_Down }, + { VK_INSERT, Key_Insert }, + { VK_DELETE, Key_Delete }, + { VK_HELP, Key_Help }, + + { VK_NUMLOCK, Key_NumLock }, + { VK_SCROLL, Key_ScrollLock }, + + { VK_OEM_1, Key_Semicolon }, + { VK_OEM_PLUS, Key_Equal }, + { VK_OEM_COMMA, Key_Comma }, + { VK_OEM_MINUS, Key_Minus }, + { VK_OEM_PERIOD,Key_Period }, + { VK_OEM_2, Key_Slash }, + { VK_OEM_3, Key_Bar }, + { VK_OEM_4, Key_BracketLeft }, + { VK_OEM_5, Key_Backslash }, + { VK_OEM_6, Key_BracketRight }, + { VK_OEM_7, Key_Quote }, + + { VK_OEM_AX, Key_OEM_AX }, // 'AX' key on Japanese AX keyboard. + { VK_OEM_102, Key_OEM_102 }, // "<>" or "\|" on RT 102-key keyboard. + { VK_ICO_HELP, Key_ICO_HELP }, + { VK_ICO_00, Key_ICO_00 } +}; + + +KeyCode MapVKToKeyCode(unsigned vk) +{ + unsigned key = Key_None; + + if ((vk >= '0') && (vk <= '9')) + { + key = vk - '0' + Key_Num0; + } + else if ((vk >= 'A') && (vk <= 'Z')) + { + key = vk - 'A' + Key_A; + } + else if ((vk >= VK_NUMPAD0) && (vk <= VK_DIVIDE)) + { + key = vk - VK_NUMPAD0 + Key_KP_0; + } + else if ((vk >= VK_F1) && (vk <= VK_F15)) + { + key = vk - VK_F1 + Key_F1; + } + else + { + for (unsigned i = 0; i< (sizeof(KeyMap) / sizeof(KeyMap[1])); i++) + { + if (vk == KeyMap[i][0]) + { + key = KeyMap[i][1]; + break; + } + } + } + + return (KeyCode)key; +} + + + +LRESULT CALLBACK PlatformCore::systemWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) +{ + PlatformCore* self; + + // WM_NCCREATE should be the first message to come it; use it to set class pointer. + if (msg == WM_NCCREATE) + { + self = static_cast(((LPCREATESTRUCT)lp)->lpCreateParams); + + if (self) + { + SetWindowLongPtr(hwnd, 0, (LONG_PTR)self); + self->hWnd = hwnd; + } + } + else + { + self = (PlatformCore*)(UPInt)GetWindowLongPtr(hwnd, 0); + } + + return self ? self->WindowProc(msg, wp, lp) : + DefWindowProc(hwnd, msg, wp, lp); +} + + +LRESULT PlatformCore::WindowProc(UINT msg, WPARAM wp, LPARAM lp) +{ + KeyCode keyCode; + + switch (msg) + { + case WM_PAINT: + { + PAINTSTRUCT ps; + BeginPaint(hWnd, &ps); + EndPaint(hWnd, &ps); + } + return 0; + + case WM_SETCURSOR: + ::SetCursor(Cursor); + return 0; + + case WM_MOUSEMOVE: + if (MMode == Mouse_Relative) + { + POINT newPos = { LOWORD(lp), HIWORD(lp) }; + ::ClientToScreen(hWnd, &newPos); + if ((newPos.x == WindowCenter.x) && (newPos.y == WindowCenter.y)) + break; + ::SetCursorPos(WindowCenter.x, WindowCenter.y); + + LONG dx = newPos.x - WindowCenter.x; + LONG dy = newPos.y - WindowCenter.y; + + pApp->OnMouseMove(dx, dy, Mod_MouseRelative); + } + else + { + pApp->OnMouseMove(LOWORD(lp), HIWORD(lp), 0); + } + break; + + case WM_MOVE: + { + RECT r; + GetClientRect(hWnd, &r); + WindowCenter.x = r.right/2; + WindowCenter.y = r.bottom/2; + ::ClientToScreen(hWnd, &WindowCenter); + } + break; + + case WM_KEYDOWN: + switch (wp) + { + case VK_CONTROL: Modifiers |= Mod_Control; break; + case VK_MENU: Modifiers |= Mod_Alt; break; + case VK_SHIFT: Modifiers |= Mod_Shift; break; + case VK_LWIN: + case VK_RWIN: Modifiers |= Mod_Meta; break; + } + if ((keyCode = MapVKToKeyCode((unsigned)wp)) != Key_None) + pApp->OnKey(keyCode, 0, true, Modifiers); + + if (keyCode == Key_Escape && MMode == Mouse_Relative) + { + MMode = Mouse_RelativeEscaped; + ShowCursor(TRUE); + } + break; + + case WM_KEYUP: + if ((keyCode = MapVKToKeyCode((unsigned)wp)) != Key_None) + pApp->OnKey(keyCode, 0, false, Modifiers); + switch (wp) + { + case VK_CONTROL: Modifiers &= ~Mod_Control; break; + case VK_MENU: Modifiers &= ~Mod_Alt; break; + case VK_SHIFT: Modifiers &= ~Mod_Shift; break; + case VK_LWIN: + case VK_RWIN: Modifiers &= ~Mod_Meta; break; + } + break; + + case WM_LBUTTONDOWN: + //App->OnMouseButton(0, + + ::SetCapture(hWnd); + + if (MMode == Mouse_RelativeEscaped) + { + ::SetCursorPos(WindowCenter.x, WindowCenter.y); + ::ShowCursor(FALSE); + MMode = Mouse_Relative; + } + break; + + case WM_LBUTTONUP: + ReleaseCapture(); + break; + + case WM_SETFOCUS: + // Do NOT restore the Relative mode here, since calling SetCursorPos + // would screw up titlebar window dragging. + // Let users click in the center instead to resume. + break; + + case WM_KILLFOCUS: + if (MMode == Mouse_Relative) + { + MMode = Mouse_RelativeEscaped; + ShowCursor(TRUE); + } + break; + + case WM_SIZE: + // Change window size as long as we're not being minimized. + if (wp != SIZE_MINIMIZED) + { + Width = LOWORD(lp); + Height = HIWORD(lp); + if (pRender) + pRender->SetWindowSize(Width, Height); + pApp->OnResize(Width,Height); + } + break; + + case WM_STYLECHANGING: + // Resize the window. This is needed because the size includes any present system controls, and + // windows does not adjust it when changing to fullscreen. + { + STYLESTRUCT* pss = (STYLESTRUCT*)lp; + RECT winSize; + winSize.left = winSize.top = 0; + winSize.right = Width; + winSize.bottom = Height; + int w = winSize.right-winSize.left; + int h = winSize.bottom-winSize.top; + AdjustWindowRect(&winSize, pss->styleNew, false); + ::SetWindowPos(hWnd, NULL, 0, 0, w, h, SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER); + } + break; + + case WM_QUIT: + case WM_CLOSE: + pApp->OnQuitRequest(); + return false; + } + + return DefWindowProc(hWnd, msg, wp, lp); +} + +int PlatformCore::Run() +{ + while (!Quit) + { + MSG msg; + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + else + { + pApp->OnIdle(); + + // Keep sleeping when we're minimized. + if (IsIconic(hWnd)) + { + Sleep(10); + } + } + } + + return ExitCode; +} + + +RenderDevice* PlatformCore::SetupGraphics(const SetupGraphicsDeviceSet& setupGraphicsDesc, + const char* type, const Render::RendererParams& rp) +{ + const SetupGraphicsDeviceSet* setupDesc = setupGraphicsDesc.PickSetupDevice(type); + OVR_ASSERT(setupDesc); + + pRender = *setupDesc->pCreateDevice(rp, (void*)hWnd); + if (pRender) + pRender->SetWindowSize(Width, Height); + + ::ShowWindow(hWnd, SW_RESTORE); + return pRender.GetPtr(); +} + + +void PlatformCore::PlayMusicFile(const char *fileName) +{ + PlaySoundA(fileName, NULL, SND_FILENAME | SND_LOOP | SND_ASYNC); +} + + +//----------------------------------------------------------------------------- + +// Used to capture all the active monitor handles +struct MonitorSet +{ + enum { MaxMonitors = 8 }; + HMONITOR Monitors[MaxMonitors]; + int MonitorCount; + int PrimaryCount; +}; + + +BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData) +{ + MonitorSet* monitorSet = (MonitorSet*)dwData; + if (monitorSet->MonitorCount > MonitorSet::MaxMonitors) + return FALSE; + + monitorSet->Monitors[monitorSet->MonitorCount] = hMonitor; + monitorSet->MonitorCount++; + return TRUE; +}; + + +// Returns the number of active screens for extended displays and 1 for mirrored display +int PlatformCore::GetDisplayCount() +{ + // Get all the monitor handles + MonitorSet monitors; + monitors.MonitorCount = 0; + EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&monitors); + + // Count the primary monitors + int primary = 0; + MONITORINFOEX info; + for (int m=0; m < monitors.MonitorCount; m++) + { + info.cbSize = sizeof(MONITORINFOEX); + GetMonitorInfo(monitors.Monitors[m], &info); + + if (info.dwFlags & MONITORINFOF_PRIMARY) + primary++; + } + + if (primary > 1) + return 1; // Regard mirrored displays as a single screen + else + return monitors.MonitorCount; // Return all extended displays +} + +//----------------------------------------------------------------------------- +// Returns the device name for the given screen index or empty string for invalid index +// The zero index will always return the primary screen name +Render::DisplayId PlatformCore::GetDisplay(int screen) +{ + // Get all the monitor handles + MonitorSet monitors; + monitors.MonitorCount = 0; + EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&monitors); + + String screen_name; + + // Get the name of the suppled screen index with the requirement + // that screen 0 is the primary monitor + int non_primary_count = 0; + MONITORINFOEX info; + for (int m=0; m < monitors.MonitorCount; m++) + { + info.cbSize = sizeof(MONITORINFOEX); + GetMonitorInfo(monitors.Monitors[m], &info); + + if (info.dwFlags & MONITORINFOF_PRIMARY) + { + if (screen == 0) + { + screen_name = info.szDevice; + break; + } + } + else + { + non_primary_count++; + if (screen == non_primary_count) + { + screen_name = info.szDevice; + break; + } + } + } + + return screen_name; +} + + +}}} + + +int WINAPI WinMain(HINSTANCE hinst, HINSTANCE prevInst, LPSTR inArgs, int show) +{ + using namespace OVR; + using namespace OVR::Platform; + + OVR_UNUSED2(prevInst, show); + + // CreateApplication must be the first call since it does OVR::System::Initialize. + Application* app = Application::CreateApplication(); + Win32::PlatformCore* platform = new Win32::PlatformCore(app, hinst); + // The platform attached to an app will be deleted by DestroyApplication. + app->SetPlatformCore(platform); + + int exitCode = 0; + + // Nested scope for container destructors to shutdown before DestroyApplication. + { + Array args; + Array argv; + argv.PushBack("app"); + + const char* p = inArgs; + const char* pstart = inArgs; + while (*p) + { + if (*p == ' ') + { + args.PushBack(String(pstart, p - pstart)); + while (*p == ' ') + p++; + pstart = p; + } + else + { + p++; + } + } + if (p != pstart) + args.PushBack(String(pstart, p - pstart)); + for (UPInt i = 0; i < args.GetSize(); i++) + argv.PushBack(args[i].ToCStr()); + + exitCode = app->OnStartup((int)argv.GetSize(), &argv[0]); + if (!exitCode) + exitCode = platform->Run(); + } + + // No OVR functions involving memory are allowed after this. + Application::DestroyApplication(app); + app = 0; + + OVR_DEBUG_STATEMENT(_CrtDumpMemoryLeaks()); + return exitCode; +} diff --git a/Samples/CommonSrc/Platform/Win32_Platform.h b/Samples/CommonSrc/Platform/Win32_Platform.h new file mode 100644 index 0000000..5d40861 --- /dev/null +++ b/Samples/CommonSrc/Platform/Win32_Platform.h @@ -0,0 +1,101 @@ +/************************************************************************************ + +Filename : Win32_Platform.h +Content : Win32 implementation of Platform app infrastructure +Created : September 6, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef OVR_Win32_Platform_h +#define OVR_Win32_Platform_h + +#include "Platform.h" +#include + + +namespace OVR { namespace Render { + class RenderDevice; + struct DisplayId; +}} + +namespace OVR { namespace Platform { namespace Win32 { + +class PlatformCore : public Platform::PlatformCore +{ + HWND hWnd; + HINSTANCE hInstance; + bool Quit; + int ExitCode; + int Width, Height; + + MouseMode MMode; + POINT WindowCenter; // In desktop coordinates + HCURSOR Cursor; + int Modifiers; + String WindowTitle; + + // Win32 static function that delegates to WindowProc member function. + static LRESULT CALLBACK systemWindowProc(HWND window, UINT msg, WPARAM wp, LPARAM lp); + + LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp); + +public: + PlatformCore(Application* app, HINSTANCE hinst); + ~PlatformCore(); + + bool SetupWindow(int w, int h); + void DestroyWindow(); + void ShowWindow(bool visible); + void Exit(int exitcode) { Quit = 1; ExitCode = exitcode; } + + RenderDevice* SetupGraphics(const SetupGraphicsDeviceSet& setupGraphicsDesc, + const char* type, + const Render::RendererParams& rp); + + void SetMouseMode(MouseMode mm); + void GetWindowSize(int* w, int* h) const; + + void SetWindowTitle(const char*title); + void PlayMusicFile(const char *fileName); + int GetDisplayCount(); + Render::DisplayId GetDisplay(int screen); + + int Run(); +}; + + +// Win32 key conversion helper. +KeyCode MapVKToKeyCode(unsigned vk); + +}}} + + +// OVR_PLATFORM_APP_ARGS specifies the Application class to use for startup, +// providing it with startup arguments. +#define OVR_PLATFORM_APP_ARGS(AppClass, args) \ + OVR::Platform::Application* OVR::Platform::Application::CreateApplication() \ + { OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); \ + return new AppClass args; } \ + void OVR::Platform::Application::DestroyApplication(OVR::Platform::Application* app) \ + { OVR::Platform::PlatformCore* platform = app->pPlatform; \ + delete app; delete platform; OVR::System::Destroy(); }; + +// OVR_PLATFORM_APP_ARGS specifies the Application startup class with no args. +#define OVR_PLATFORM_APP(AppClass) OVR_PLATFORM_APP_ARGS(AppClass, ()) + +#endif // OVR_Win32_Platform_h diff --git a/Samples/CommonSrc/Render/Render_D3D10_Device.cpp b/Samples/CommonSrc/Render/Render_D3D10_Device.cpp new file mode 100644 index 0000000..b2fe9be --- /dev/null +++ b/Samples/CommonSrc/Render/Render_D3D10_Device.cpp @@ -0,0 +1,26 @@ +/************************************************************************************ + +Filename : Renderer_D3D10_Device.cpp +Content : Builds D3D10 renderer versions (to avoid duplication). +Created : September 10, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#define OVR_D3D_VERSION 10 +#include "Render_D3D1X_Device.cpp" +#undef OVR_D3D_VERSION \ No newline at end of file diff --git a/Samples/CommonSrc/Render/Render_D3D10_Device.h b/Samples/CommonSrc/Render/Render_D3D10_Device.h new file mode 100644 index 0000000..504e6ae --- /dev/null +++ b/Samples/CommonSrc/Render/Render_D3D10_Device.h @@ -0,0 +1,26 @@ +/************************************************************************************ + +Filename : Renderer_D3D10_Device.h +Content : Builds D3D10 renderer versions (to avoid duplication). +Created : September 10, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#define OVR_D3D_VERSION 10 +#include "Render_D3D1X_Device.h" +#undef OVR_D3D_VERSION \ No newline at end of file diff --git a/Samples/CommonSrc/Render/Render_D3D11_Device.cpp b/Samples/CommonSrc/Render/Render_D3D11_Device.cpp new file mode 100644 index 0000000..33821da --- /dev/null +++ b/Samples/CommonSrc/Render/Render_D3D11_Device.cpp @@ -0,0 +1,26 @@ +/************************************************************************************ + +Filename : Renderer_D3D11_Device.cpp +Content : Builds D3D11 renderer versions (to avoid duplication). +Created : September 10, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#define OVR_D3D_VERSION 11 +#include "Render_D3D1X_Device.cpp" +#undef OVR_D3D_VERSION \ No newline at end of file diff --git a/Samples/CommonSrc/Render/Render_D3D11_Device.h b/Samples/CommonSrc/Render/Render_D3D11_Device.h new file mode 100644 index 0000000..4a41e99 --- /dev/null +++ b/Samples/CommonSrc/Render/Render_D3D11_Device.h @@ -0,0 +1,26 @@ +/************************************************************************************ + +Filename : Renderer_D3D11_Device.h +Content : Builds D3D11 renderer versions (to avoid duplication). +Created : September 10, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#define OVR_D3D_VERSION 11 +#include "Render_D3D1X_Device.h" +#undef OVR_D3D_VERSION \ No newline at end of file diff --git a/Samples/CommonSrc/Render/Render_D3D1X_Device.cpp b/Samples/CommonSrc/Render/Render_D3D1X_Device.cpp new file mode 100644 index 0000000..aac5816 --- /dev/null +++ b/Samples/CommonSrc/Render/Render_D3D1X_Device.cpp @@ -0,0 +1,1831 @@ +/************************************************************************************ + +Filename : Renderer_D3D1x.cpp +Content : RenderDevice implementation for D3DX10/11. +Created : September 10, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#include "Kernel/OVR_Log.h" +#include "Kernel/OVR_Std.h" + +#include "Render_D3D1X_Device.h" + +#include + +#if (OVR_D3D_VERSION == 10) +namespace OVR { namespace Render { namespace D3D10 { +#else +namespace OVR { namespace Render { namespace D3D11 { +#endif + +static D3D1x_(INPUT_ELEMENT_DESC) ModelVertexDesc[] = +{ + {"Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, Pos), D3D1x_(INPUT_PER_VERTEX_DATA), 0}, + {"Color", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(Vertex, C), D3D1x_(INPUT_PER_VERTEX_DATA), 0}, + {"TexCoord", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(Vertex, U), D3D1x_(INPUT_PER_VERTEX_DATA), 0}, + {"TexCoord", 1, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(Vertex, U2), D3D1x_(INPUT_PER_VERTEX_DATA), 0}, + {"Normal", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, Norm), D3D1x_(INPUT_PER_VERTEX_DATA), 0}, +}; + + +static const char* StdVertexShaderSrc = + "float4x4 Proj;\n" + "float4x4 View;\n" + "struct Varyings\n" + "{\n" + " float4 Position : SV_Position;\n" + " float4 Color : COLOR0;\n" + " float2 TexCoord : TEXCOORD0;\n" + " float2 TexCoord1 : TEXCOORD1;\n" + " float3 Normal : NORMAL;\n" + " float3 VPos : TEXCOORD4;\n" + "};\n" + "void main(in float4 Position : POSITION, in float4 Color : COLOR0, in float2 TexCoord : TEXCOORD0, in float2 TexCoord1 : TEXCOORD1, in float3 Normal : NORMAL,\n" + " out Varyings ov)\n" + "{\n" + " ov.Position = mul(Proj, mul(View, Position));\n" + " ov.Normal = mul(View, Normal);\n" + " ov.VPos = mul(View, Position);\n" + " ov.TexCoord = TexCoord;\n" + " ov.TexCoord1 = TexCoord1;\n" + " ov.Color = Color;\n" + "}\n"; + +static const char* DirectVertexShaderSrc = + "float4x4 View : register(c4);\n" + "void main(in float4 Position : POSITION, in float4 Color : COLOR0, in float2 TexCoord : TEXCOORD0, in float2 TexCoord1 : TEXCOORD1, in float3 Normal : NORMAL,\n" + " out float4 oPosition : SV_Position, out float4 oColor : COLOR, out float2 oTexCoord : TEXCOORD0, out float2 oTexCoord1 : TEXCOORD1, out float3 oNormal : NORMAL)\n" + "{\n" + " oPosition = mul(View, Position);\n" + " oTexCoord = TexCoord;\n" + " oTexCoord1 = TexCoord1;\n" + " oColor = Color;\n" + " oNormal = mul(View, Normal);\n" + "}\n"; + +static const char* SolidPixelShaderSrc = + "float4 Color;\n" + "struct Varyings\n" + "{\n" + " float4 Position : SV_Position;\n" + " float4 Color : COLOR0;\n" + " float2 TexCoord : TEXCOORD0;\n" + "};\n" + "float4 main(in Varyings ov) : SV_Target\n" + "{\n" + " return Color;\n" + "}\n"; + +static const char* GouraudPixelShaderSrc = + "struct Varyings\n" + "{\n" + " float4 Position : SV_Position;\n" + " float4 Color : COLOR0;\n" + " float2 TexCoord : TEXCOORD0;\n" + "};\n" + "float4 main(in Varyings ov) : SV_Target\n" + "{\n" + " return ov.Color;\n" + "}\n"; + +static const char* TexturePixelShaderSrc = + "Texture2D Texture : register(t0);\n" + "SamplerState Linear : register(s0);\n" + "struct Varyings\n" + "{\n" + " float4 Position : SV_Position;\n" + " float4 Color : COLOR0;\n" + " float2 TexCoord : TEXCOORD0;\n" + "};\n" + "float4 main(in Varyings ov) : SV_Target\n" + "{\n" + " float4 color2 = ov.Color * Texture.Sample(Linear, ov.TexCoord);\n" + " if (color2.a <= 0.4)\n" + " discard;\n" + " return color2;\n" + "}\n"; + +static const char* MultiTexturePixelShaderSrc = + "Texture2D Texture[2] : register(t0);\n" + "SamplerState Linear : register(s0);\n" + "struct Varyings\n" + "{\n" + " float4 Position : SV_Position;\n" + " float4 Color : COLOR0;\n" + " float2 TexCoord : TEXCOORD0;\n" + " float2 TexCoord1 : TEXCOORD1;\n" + "};\n" + "float4 main(in Varyings ov) : SV_Target\n" + "{\n" + "float4 color1;\n" + "float4 color2;\n" + " color1 = Texture[0].Sample(Linear, ov.TexCoord);\n" + " color2 = Texture[1].Sample(Linear, ov.TexCoord1);\n" + " color2.rgb = color2.rgb * lerp(1.2, 1.9, saturate(length(color2.rgb)));\n" + " color2 = color1 * color2;\n" + " if (color2.a <= 0.4)\n" + " discard;\n" + " return color2;\n" + "}\n"; + +#define LIGHTING_COMMON \ + "cbuffer Lighting : register(b1)\n" \ + "{\n" \ + " float3 Ambient;\n" \ + " float3 LightPos[8];\n" \ + " float4 LightColor[8];\n" \ + " float LightCount;\n" \ + "};\n" \ + "struct Varyings\n" \ + "{\n" \ + " float4 Position : SV_Position;\n" \ + " float4 Color : COLOR0;\n" \ + " float2 TexCoord : TEXCOORD0;\n" \ + " float3 Normal : NORMAL;\n" \ + " float3 VPos : TEXCOORD4;\n" \ + "};\n" \ + "float4 DoLight(Varyings v)\n" \ + "{\n" \ + " float3 norm = normalize(v.Normal);\n" \ + " float3 light = Ambient;\n" \ + " for (uint i = 0; i < LightCount; i++)\n"\ + " {\n" \ + " float3 ltp = (LightPos[i] - v.VPos);\n" \ + " float ldist = dot(ltp,ltp);\n" \ + " ltp = normalize(ltp);\n" \ + " light += saturate(LightColor[i] * v.Color.rgb * dot(norm, ltp) / sqrt(ldist));\n"\ + " }\n" \ + " return float4(light, v.Color.a);\n" \ + "}\n" + +static const char* LitSolidPixelShaderSrc = + LIGHTING_COMMON + "float4 main(in Varyings ov) : SV_Target\n" + "{\n" + " return DoLight(ov) * ov.Color;\n" + "}\n"; + +static const char* LitTexturePixelShaderSrc = + "Texture2D Texture : register(t0);\n" + "SamplerState Linear : register(s0);\n" + LIGHTING_COMMON + "float4 main(in Varyings ov) : SV_Target\n" + "{\n" + " return DoLight(ov) * Texture.Sample(Linear, ov.TexCoord);\n" + "}\n"; + +static const char* AlphaTexturePixelShaderSrc = + "Texture2D Texture : register(t0);\n" + "SamplerState Linear : register(s0);\n" + "struct Varyings\n" + "{\n" + " float4 Position : SV_Position;\n" + " float4 Color : COLOR0;\n" + " float2 TexCoord : TEXCOORD0;\n" + "};\n" + "float4 main(in Varyings ov) : SV_Target\n" + "{\n" + " return ov.Color * float4(1,1,1,Texture.Sample(Linear, ov.TexCoord).r);\n" + "}\n"; + + +// ***** PostProcess Shader + +static const char* PostProcessVertexShaderSrc = + "float4x4 View : register(c4);\n" + "float4x4 Texm : register(c8);\n" + "void main(in float4 Position : POSITION, in float4 Color : COLOR0, in float2 TexCoord : TEXCOORD0, in float2 TexCoord1 : TEXCOORD1,\n" + " out float4 oPosition : SV_Position, out float4 oColor : COLOR, out float2 oTexCoord : TEXCOORD0)\n" + "{\n" + " oPosition = mul(View, Position);\n" + " oTexCoord = mul(Texm, float4(TexCoord,0,1));\n" + " oColor = Color;\n" + "}\n"; + +// Shader with just lens distortion correction. +static const char* PostProcessPixelShaderSrc = + "Texture2D Texture : register(t0);\n" + "SamplerState Linear : register(s0);\n" + "float2 LensCenter;\n" + "float2 ScreenCenter;\n" + "float2 Scale;\n" + "float2 ScaleIn;\n" + "float4 HmdWarpParam;\n" + "\n" + + // Scales input texture coordinates for distortion. + // ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be + // larger due to aspect ratio. + "float2 HmdWarp(float2 in01)\n" + "{\n" + " float2 theta = (in01 - LensCenter) * ScaleIn;\n" // Scales to [-1, 1] + " float rSq = theta.x * theta.x + theta.y * theta.y;\n" + " float2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + " + " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n" + " return LensCenter + Scale * theta1;\n" + "}\n" + + "float4 main(in float4 oPosition : SV_Position, in float4 oColor : COLOR,\n" + " in float2 oTexCoord : TEXCOORD0) : SV_Target\n" + "{\n" + " float2 tc = HmdWarp(oTexCoord);\n" + " if (any(clamp(tc, ScreenCenter-float2(0.25,0.5), ScreenCenter+float2(0.25, 0.5)) - tc))\n" + " return 0;\n" + " return Texture.Sample(Linear, tc);\n" + "}\n"; + +// Shader with lens distortion and chromatic aberration correction. +static const char* PostProcessPixelShaderWithChromAbSrc = + "Texture2D Texture : register(t0);\n" + "SamplerState Linear : register(s0);\n" + "float2 LensCenter;\n" + "float2 ScreenCenter;\n" + "float2 Scale;\n" + "float2 ScaleIn;\n" + "float4 HmdWarpParam;\n" + "float4 ChromAbParam;\n" + "\n" + + // Scales input texture coordinates for distortion. + // ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be + // larger due to aspect ratio. + "float4 main(in float4 oPosition : SV_Position, in float4 oColor : COLOR,\n" + " in float2 oTexCoord : TEXCOORD0) : SV_Target\n" + "{\n" + " float2 theta = (oTexCoord - LensCenter) * ScaleIn;\n" // Scales to [-1, 1] + " float rSq = theta.x * theta.x + theta.y * theta.y;\n" + " float2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + " + " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n" + " \n" + " // Detect whether blue texture coordinates are out of range since these will scaled out the furthest.\n" + " float2 thetaBlue = theta1 * (ChromAbParam.z + ChromAbParam.w * rSq);\n" + " float2 tcBlue = LensCenter + Scale * thetaBlue;\n" + " if (any(clamp(tcBlue, ScreenCenter-float2(0.25,0.5), ScreenCenter+float2(0.25, 0.5)) - tcBlue))\n" + " return 0;\n" + " \n" + " // Now do blue texture lookup.\n" + " float blue = Texture.Sample(Linear, tcBlue).b;\n" + " \n" + " // Do green lookup (no scaling).\n" + " float2 tcGreen = LensCenter + Scale * theta1;\n" + " float4 greenColor = Texture.Sample(Linear, tcGreen);\n" + " float green = greenColor.g;\n" + " float alpha = greenColor.a;\n" + " \n" + " // Do red scale and lookup.\n" + " float2 thetaRed = theta1 * (ChromAbParam.x + ChromAbParam.y * rSq);\n" + " float2 tcRed = LensCenter + Scale * thetaRed;\n" + " float red = Texture.Sample(Linear, tcRed).r;\n" + " \n" + " return float4(red, green, blue, alpha);\n" + "}\n"; + + +static const char* VShaderSrcs[VShader_Count] = +{ + DirectVertexShaderSrc, + StdVertexShaderSrc, + PostProcessVertexShaderSrc +}; +static const char* FShaderSrcs[FShader_Count] = +{ + SolidPixelShaderSrc, + GouraudPixelShaderSrc, + TexturePixelShaderSrc, + AlphaTexturePixelShaderSrc, + PostProcessPixelShaderSrc, + PostProcessPixelShaderWithChromAbSrc, + LitSolidPixelShaderSrc, + LitTexturePixelShaderSrc, + MultiTexturePixelShaderSrc +}; + +RenderDevice::RenderDevice(const RendererParams& p, HWND window) +{ + RECT rc; + GetClientRect(window, &rc); + UINT width = rc.right - rc.left; + UINT height = rc.bottom - rc.top; + WindowWidth = width; + WindowHeight = height; + Window = window; + + Params = p; + HRESULT hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)(&DXGIFactory.GetRawRef())); + if (FAILED(hr)) + return; + + // Find the adapter & output (monitor) to use for fullscreen, based on the reported name of the HMD's monitor. + if (Params.Display.MonitorName.GetLength() > 0) + { + for(UINT AdapterIndex = 0; ; AdapterIndex++) + { + HRESULT hr = DXGIFactory->EnumAdapters(AdapterIndex, &Adapter.GetRawRef()); + if (hr == DXGI_ERROR_NOT_FOUND) + break; + + DXGI_ADAPTER_DESC Desc; + Adapter->GetDesc(&Desc); + + UpdateMonitorOutputs(); + + if (FullscreenOutput) + break; + } + + if (!FullscreenOutput) + Adapter = NULL; + } + + if (!Adapter) + { + DXGIFactory->EnumAdapters(0, &Adapter.GetRawRef()); + UpdateMonitorOutputs(); + } + + int flags = 0; + +#if (OVR_D3D_VERSION == 10) + hr = D3D10CreateDevice(Adapter, D3D10_DRIVER_TYPE_HARDWARE, NULL, flags, D3D1x_(SDK_VERSION), + &Device.GetRawRef()); + Context = Device; + Context->AddRef(); +#else //11 + hr = D3D11CreateDevice(Adapter, Adapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE, + NULL, flags, NULL, 0, D3D1x_(SDK_VERSION), + &Device.GetRawRef(), NULL, &Context.GetRawRef()); +#endif + if (FAILED(hr)) + return; + + if (!RecreateSwapChain()) + return; + + if (Params.Fullscreen) + SwapChain->SetFullscreenState(1, FullscreenOutput); + + CurRenderTarget = NULL; + for(int i = 0; i < Shader_Count; i++) + { + UniformBuffers[i] = *CreateBuffer(); + MaxTextureSet[i] = 0; + } + + ID3D10Blob* vsData = CompileShader("vs_4_0", DirectVertexShaderSrc); + VertexShaders[VShader_MV] = *new VertexShader(this, vsData); + for(int i = 1; i < VShader_Count; i++) + { + VertexShaders[i] = *new VertexShader(this, CompileShader("vs_4_0", VShaderSrcs[i])); + } + + for(int i = 0; i < FShader_Count; i++) + { + PixelShaders[i] = *new PixelShader(this, CompileShader("ps_4_0", FShaderSrcs[i])); + } + + SPInt bufferSize = vsData->GetBufferSize(); + const void* buffer = vsData->GetBufferPointer(); + ID3D1xInputLayout** objRef = &ModelVertexIL.GetRawRef(); + + HRESULT validate = Device->CreateInputLayout(ModelVertexDesc, 5, buffer, bufferSize, objRef); + OVR_UNUSED(validate); + + Ptr gouraudShaders = *new ShaderSet(); + gouraudShaders->SetShader(VertexShaders[VShader_MVP]); + gouraudShaders->SetShader(PixelShaders[FShader_Gouraud]); + DefaultFill = *new ShaderFill(gouraudShaders); + +#if (OVR_D3D_VERSION == 10) + D3D1x_(BLEND_DESC) bm; + memset(&bm, 0, sizeof(bm)); + bm.BlendEnable[0] = true; + bm.BlendOp = bm.BlendOpAlpha = D3D1x_(BLEND_OP_ADD); + bm.SrcBlend = bm.SrcBlendAlpha = D3D1x_(BLEND_SRC_ALPHA); + bm.DestBlend = bm.DestBlendAlpha = D3D1x_(BLEND_INV_SRC_ALPHA); + bm.RenderTargetWriteMask[0] = D3D1x_(COLOR_WRITE_ENABLE_ALL); + Device->CreateBlendState(&bm, &BlendState.GetRawRef()); +#else + D3D1x_(BLEND_DESC) bm; + memset(&bm, 0, sizeof(bm)); + bm.RenderTarget[0].BlendEnable = true; + bm.RenderTarget[0].BlendOp = bm.RenderTarget[0].BlendOpAlpha = D3D1x_(BLEND_OP_ADD); + bm.RenderTarget[0].SrcBlend = bm.RenderTarget[0].SrcBlendAlpha = D3D1x_(BLEND_SRC_ALPHA); + bm.RenderTarget[0].DestBlend = bm.RenderTarget[0].DestBlendAlpha = D3D1x_(BLEND_INV_SRC_ALPHA); + bm.RenderTarget[0].RenderTargetWriteMask = D3D1x_(COLOR_WRITE_ENABLE_ALL); + Device->CreateBlendState(&bm, &BlendState.GetRawRef()); +#endif + + D3D1x_(RASTERIZER_DESC) rs; + memset(&rs, 0, sizeof(rs)); + rs.AntialiasedLineEnable = true; + rs.CullMode = D3D1x_(CULL_BACK); + // rs.CullMode = D3D1x_(CULL_NONE); + rs.DepthClipEnable = true; + rs.FillMode = D3D1x_(FILL_SOLID); + Device->CreateRasterizerState(&rs, &Rasterizer.GetRawRef()); + + QuadVertexBuffer = *CreateBuffer(); + const Render::Vertex QuadVertices[] = + { Vertex(Vector3f(0, 1, 0)), Vertex(Vector3f(1, 1, 0)), + Vertex(Vector3f(0, 0, 0)), Vertex(Vector3f(1, 0, 0)) }; + QuadVertexBuffer->Data(Buffer_Vertex, QuadVertices, sizeof(QuadVertices)); + + SetDepthMode(0, 0); +} + +RenderDevice::~RenderDevice() +{ + if (SwapChain && Params.Fullscreen) + { + SwapChain->SetFullscreenState(false, NULL); + } +} + + +// Implement static initializer function to create this class. +Render::RenderDevice* RenderDevice::CreateDevice(const RendererParams& rp, void* oswnd) +{ + return new RenderDevice(rp, (HWND)oswnd); +} + + +// Fallback monitor enumeration in case newly plugged in monitor wasn't detected. +// Added originally for the FactoryTest app. +// New Outputs don't seem to be detected unless adapter is re-created, but that would also +// require us to re-initialize D3D10 (recreating objects, etc). This bypasses that for "fake" +// fullscreen modes. +BOOL CALLBACK MonitorEnumFunc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData) +{ + RenderDevice* renderer = (RenderDevice*)dwData; + + MONITORINFOEX monitor; + monitor.cbSize = sizeof(monitor); + + if (::GetMonitorInfo(hMonitor, &monitor) && monitor.szDevice[0]) + { + DISPLAY_DEVICE dispDev; + memset(&dispDev, 0, sizeof(dispDev)); + dispDev.cb = sizeof(dispDev); + + if (::EnumDisplayDevices(monitor.szDevice, 0, &dispDev, 0)) + { + if (strstr(String(dispDev.DeviceName).ToCStr(), renderer->GetParams().Display.MonitorName.ToCStr())) + { + renderer->FSDesktopX = monitor.rcMonitor.left; + renderer->FSDesktopY = monitor.rcMonitor.top; + return FALSE; + } + } + } + + return TRUE; +} + + +void RenderDevice::UpdateMonitorOutputs(bool needRecreate) +{ + HRESULT hr; + + if (needRecreate) + { + // need to recreate DXGIFactory and Adapter in order + // to get latest info about monitors. + if (SwapChain) + { + SwapChain->SetFullscreenState(FALSE, NULL); + SwapChain->Release(); + SwapChain = NULL; + } + + DXGIFactory = NULL; + Adapter = NULL; + hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)(&DXGIFactory.GetRawRef())); + if (FAILED(hr)) + return; + DXGIFactory->EnumAdapters(0, &Adapter.GetRawRef()); + } + + bool deviceNameFound = false; + + for(UINT OutputIndex = 0; ; OutputIndex++) + { + Ptr Output; + hr = Adapter->EnumOutputs(OutputIndex, &Output.GetRawRef()); + if (hr == DXGI_ERROR_NOT_FOUND) + { + break; + } + + DXGI_OUTPUT_DESC OutDesc; + Output->GetDesc(&OutDesc); + + MONITORINFOEX monitor; + monitor.cbSize = sizeof(monitor); + if (::GetMonitorInfo(OutDesc.Monitor, &monitor) && monitor.szDevice[0]) + { + DISPLAY_DEVICE dispDev; + memset(&dispDev, 0, sizeof(dispDev)); + dispDev.cb = sizeof(dispDev); + + if (::EnumDisplayDevices(monitor.szDevice, 0, &dispDev, 0)) + { + if (strstr(String(dispDev.DeviceName).ToCStr(), Params.Display.MonitorName.ToCStr())) + { + deviceNameFound = true; + FullscreenOutput = Output; + FSDesktopX = monitor.rcMonitor.left; + FSDesktopY = monitor.rcMonitor.top; + break; + } + } + } + } + + if (!deviceNameFound && !Params.Display.MonitorName.IsEmpty()) + { + EnumDisplayMonitors(0, 0, MonitorEnumFunc, (LPARAM)this); + } +} + +bool RenderDevice::RecreateSwapChain() +{ + DXGI_SWAP_CHAIN_DESC scDesc; + memset(&scDesc, 0, sizeof(scDesc)); + scDesc.BufferCount = 1; + scDesc.BufferDesc.Width = WindowWidth; + scDesc.BufferDesc.Height = WindowHeight; + scDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + scDesc.BufferDesc.RefreshRate.Numerator = 60; + scDesc.BufferDesc.RefreshRate.Denominator = 1; + scDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + scDesc.OutputWindow = Window; + scDesc.SampleDesc.Count = Params.Multisample; + scDesc.SampleDesc.Quality = 0; + scDesc.Windowed = Params.Fullscreen != Display_Fullscreen; + scDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + + if (SwapChain) + { + SwapChain->SetFullscreenState(FALSE, NULL); + SwapChain->Release(); + SwapChain = NULL; + } + + Ptr newSC; + if (FAILED(DXGIFactory->CreateSwapChain(Device, &scDesc, &newSC.GetRawRef()))) + return false; + SwapChain = newSC; + + BackBuffer = NULL; + BackBufferRT = NULL; + HRESULT hr = SwapChain->GetBuffer(0, __uuidof(ID3D1xTexture2D), (void**)&BackBuffer.GetRawRef()); + if (FAILED(hr)) + return false; + + hr = Device->CreateRenderTargetView(BackBuffer, NULL, &BackBufferRT.GetRawRef()); + if (FAILED(hr)) + return false; + + Texture* depthBuffer = GetDepthBuffer(WindowWidth, WindowHeight, Params.Multisample); + CurDepthBuffer = depthBuffer; + if (CurRenderTarget == NULL) + { + Context->OMSetRenderTargets(1, &BackBufferRT.GetRawRef(), depthBuffer->TexDsv); + } + return true; +} + +bool RenderDevice::SetParams(const RendererParams& newParams) +{ + String oldMonitor = Params.Display.MonitorName; + + Params = newParams; + if (newParams.Display.MonitorName != oldMonitor) + { + UpdateMonitorOutputs(true); + } + + // Cause this to be recreated with the new multisample mode. + pSceneColorTex = NULL; + return RecreateSwapChain(); +} + +void RenderDevice::SetWindowSize(int w, int h) +{ + if (w == WindowWidth && h == WindowHeight) + return; + + WindowWidth = w; + WindowHeight = h; + Context->OMSetRenderTargets(0, NULL, NULL); + BackBuffer = NULL; + BackBufferRT = NULL; + if (SwapChain) + { + SwapChain->ResizeBuffers(2, WindowWidth, WindowHeight, DXGI_FORMAT_R8G8B8A8_UNORM, 0); + SwapChain->GetBuffer(0, __uuidof(ID3D1xTexture2D), (void**)&BackBuffer.GetRawRef()); + } + Device->CreateRenderTargetView(BackBuffer, NULL, &BackBufferRT.GetRawRef()); +} + +bool RenderDevice::SetFullscreen(DisplayMode fullscreen) +{ + if (fullscreen == Params.Fullscreen) + { + return true; + } + + if (Params.Fullscreen == Display_FakeFullscreen) + { + SetWindowLong(Window, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPSIBLINGS); + SetWindowPos(Window, NULL, PreFullscreenX, PreFullscreenY, + PreFullscreenW, PreFullscreenH, SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); + } + + if (fullscreen == Display_FakeFullscreen) + { + // Get WINDOWPLACEMENT before changing style to get OVERLAPPED coordinates, + // which we will restore. + WINDOWPLACEMENT wp; + wp.length = sizeof(wp); + GetWindowPlacement(Window, &wp); + PreFullscreenW = wp.rcNormalPosition.right - wp.rcNormalPosition.left; + PreFullscreenH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; + PreFullscreenX = wp.rcNormalPosition.left; + PreFullscreenY = wp.rcNormalPosition.top; + // Warning: SetWindowLong sends message computed based on old size (incorrect). + // A proper work-around would be to mask that message out during window frame change in Platform. + SetWindowLong(Window, GWL_STYLE, WS_OVERLAPPED | WS_VISIBLE | WS_CLIPSIBLINGS); + SetWindowPos(Window, NULL, FSDesktopX, FSDesktopY, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_FRAMECHANGED); + + // Relocate cursor into the window to avoid losing focus on first click. + POINT oldCursor; + if (GetCursorPos(&oldCursor) && + ((oldCursor.x < FSDesktopX) || (oldCursor.x > (FSDesktopX + PreFullscreenW)) || + (oldCursor.y < FSDesktopY) || (oldCursor.x > (FSDesktopY + PreFullscreenH)))) + { + // TBD: FullScreen window logic should really be in platform; it causes world rotation + // in relative mouse mode. + ::SetCursorPos(FSDesktopX, FSDesktopY); + } + } + else + { + HRESULT hr = SwapChain->SetFullscreenState(fullscreen, fullscreen ? FullscreenOutput : NULL); + if (FAILED(hr)) + { + return false; + } + } + + Params.Fullscreen = fullscreen; + return true; +} + +void RenderDevice::SetMultipleViewports(int n, const Viewport* vps) +{ + if (n > 2) + { + n = 2; + } + for(int i = 0; i < n; i++) + { +#if (OVR_D3D_VERSION == 10) + Viewports[i].Width = vps[i].w; + Viewports[i].Height = vps[i].h; + Viewports[i].MinDepth = 0; + Viewports[i].MaxDepth = 1; + Viewports[i].TopLeftX = vps[i].x; + Viewports[i].TopLeftY = vps[i].y; +#else + Viewports[i].Width = (float)vps[i].w; + Viewports[i].Height = (float)vps[i].h; + Viewports[i].MinDepth = 0; + Viewports[i].MaxDepth = 1; + Viewports[i].TopLeftX = (float)vps[i].x; + Viewports[i].TopLeftY = (float)vps[i].y; +#endif + } + NumViewports = n; + Context->RSSetViewports(n, Viewports); +} + +static int GetDepthStateIndex(bool enable, bool write, RenderDevice::CompareFunc func) +{ + if (!enable) + { + return 0; + } + return 1 + int(func) * 2 + write; +} + +void RenderDevice::SetDepthMode(bool enable, bool write, CompareFunc func) +{ + int index = GetDepthStateIndex(enable, write, func); + if (DepthStates[index]) + { + CurDepthState = DepthStates[index]; + Context->OMSetDepthStencilState(DepthStates[index], 0); + return; + } + + D3D1x_(DEPTH_STENCIL_DESC) dss; + memset(&dss, 0, sizeof(dss)); + dss.DepthEnable = enable; + switch(func) + { + case Compare_Always: dss.DepthFunc = D3D1x_(COMPARISON_ALWAYS); break; + case Compare_Less: dss.DepthFunc = D3D1x_(COMPARISON_LESS); break; + case Compare_Greater: dss.DepthFunc = D3D1x_(COMPARISON_GREATER); break; + default: + assert(0); + } + dss.DepthWriteMask = write ? D3D1x_(DEPTH_WRITE_MASK_ALL) : D3D1x_(DEPTH_WRITE_MASK_ZERO); + Device->CreateDepthStencilState(&dss, &DepthStates[index].GetRawRef()); + Context->OMSetDepthStencilState(DepthStates[index], 0); + CurDepthState = DepthStates[index]; +} + +Texture* RenderDevice::GetDepthBuffer(int w, int h, int ms) +{ + for(unsigned i = 0; i < DepthBuffers.GetSize(); i++) + { + if (w == DepthBuffers[i]->Width && h == DepthBuffers[i]->Height && + ms == DepthBuffers[i]->Samples) + return DepthBuffers[i]; + } + + Ptr newDepth = *CreateTexture(Texture_Depth | Texture_RenderTarget | ms, w, h, NULL); + if (newDepth == NULL) + { + OVR_DEBUG_LOG(("Failed to get depth buffer.")); + return NULL; + } + + DepthBuffers.PushBack(newDepth); + return newDepth.GetPtr(); +} + +void RenderDevice::Clear(float r, float g, float b, float a, float depth) +{ + const float color[] = {r, g, b, a}; + + // save state that is affected by clearing this way + ID3D1xDepthStencilState* oldDepthState = CurDepthState; + StandardUniformData clearUniforms; + + SetDepthMode(true, true, Compare_Always); + + Context->IASetInputLayout(ModelVertexIL); +#if (OVR_D3D_VERSION == 10) + Context->GSSetShader(NULL); +#else + Context->GSSetShader(NULL, NULL, 0); +#endif + //Shader NullGS(this,(ID3D1xGeometryShader*)NULL); + //NullGS.Set(Prim_TriangleStrip); + + ID3D1xShaderResourceView* sv[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + if (MaxTextureSet[Shader_Fragment]) + { + Context->PSSetShaderResources(0, MaxTextureSet[Shader_Fragment], sv); + } + + ID3D1xBuffer* vertexBuffer = QuadVertexBuffer->GetBuffer(); + UINT vertexStride = sizeof(Vertex); + UINT vertexOffset = 0; + Context->IASetVertexBuffers(0, 1, &vertexBuffer, &vertexStride, &vertexOffset); + + clearUniforms.View = Matrix4f(2, 0, 0, 0, + 0, 2, 0, 0, + 0, 0, 0, 0, + -1, -1, depth, 1); + UniformBuffers[Shader_Vertex]->Data(Buffer_Uniform, &clearUniforms, sizeof(clearUniforms)); + + ID3D1xBuffer* vertexConstants = UniformBuffers[Shader_Vertex]->GetBuffer(); + Context->VSSetConstantBuffers(0, 1, &vertexConstants); + Context->IASetPrimitiveTopology(D3D1x_(PRIMITIVE_TOPOLOGY_TRIANGLESTRIP)); + VertexShaders[VShader_MV]->Set(Prim_TriangleStrip); + PixelShaders[FShader_Solid]->Set(Prim_TriangleStrip); + + UniformBuffers[Shader_Pixel]->Data(Buffer_Uniform, color, sizeof(color)); + PixelShaders[FShader_Solid]->SetUniformBuffer(UniformBuffers[Shader_Pixel]); + + if (NumViewports > 1) + { + for(int i = 0; i < NumViewports; i++) + { + Context->RSSetViewports(1, &Viewports[i]); + Context->OMSetBlendState(NULL, NULL, 0xffffffff); + Context->Draw(4, 0); + } + Context->RSSetViewports(NumViewports, Viewports); + } + else + { + Context->OMSetBlendState(NULL, NULL, 0xffffffff); + Context->Draw(4, 0); + } + + // reset + CurDepthState = oldDepthState; + Context->OMSetDepthStencilState(CurDepthState, 0); +} + +// Buffers + +Buffer* RenderDevice::CreateBuffer() +{ + return new Buffer(this); +} + +Buffer::~Buffer() +{ +} + +bool Buffer::Data(int use, const void *buffer, size_t size) +{ + if (D3DBuffer && Size >= size) + { + if (Dynamic) + { + if (!buffer) + return true; + + void* v = Map(0, size, Map_Discard); + if (v) + { + memcpy(v, buffer, size); + Unmap(v); + return true; + } + } + else + { + Ren->Context->UpdateSubresource(D3DBuffer, 0, NULL, buffer, 0, 0); + return true; + } + } + if (D3DBuffer) + { + D3DBuffer = NULL; + Size = 0; + Use = 0; + Dynamic = 0; + } + + D3D1x_(BUFFER_DESC) desc; + memset(&desc, 0, sizeof(desc)); + if (use & Buffer_ReadOnly) + { + desc.Usage = D3D1x_(USAGE_IMMUTABLE); + desc.CPUAccessFlags = 0; + } + else + { + desc.Usage = D3D1x_(USAGE_DYNAMIC); + desc.CPUAccessFlags = D3D1x_(CPU_ACCESS_WRITE); + Dynamic = 1; + } + + switch(use & Buffer_TypeMask) + { + case Buffer_Vertex: desc.BindFlags = D3D1x_(BIND_VERTEX_BUFFER); break; + case Buffer_Index: desc.BindFlags = D3D1x_(BIND_INDEX_BUFFER); break; + case Buffer_Uniform: + desc.BindFlags = D3D1x_(BIND_CONSTANT_BUFFER); + size += ((size + 15) & ~15) - size; + break; + case Buffer_Feedback: + desc.BindFlags = D3D1x_(BIND_STREAM_OUTPUT); + desc.Usage = D3D1x_(USAGE_DEFAULT); + desc.CPUAccessFlags = 0; + size += ((size + 15) & ~15) - size; + break; + } + + desc.ByteWidth = (unsigned)size; + + D3D1x_(SUBRESOURCE_DATA) sr; + sr.pSysMem = buffer; + sr.SysMemPitch = 0; + sr.SysMemSlicePitch = 0; + + HRESULT hr = Ren->Device->CreateBuffer(&desc, buffer ? &sr : NULL, &D3DBuffer.GetRawRef()); + if (SUCCEEDED(hr)) + { + Use = use; + Size = desc.ByteWidth; + return 1; + } + return 0; +} + +void* Buffer::Map(size_t start, size_t size, int flags) +{ + OVR_UNUSED(size); + + D3D1x_(MAP) mapFlags = D3D1x_(MAP_WRITE); + if (flags & Map_Discard) + mapFlags = D3D1x_(MAP_WRITE_DISCARD); + if (flags & Map_Unsynchronized) + mapFlags = D3D1x_(MAP_WRITE_NO_OVERWRITE); + +#if (OVR_D3D_VERSION == 10) + void* map; + if (SUCCEEDED(D3DBuffer->Map(mapFlags, 0, &map))) + return ((char*)map) + start; + else + return NULL; +#else + D3D1x_(MAPPED_SUBRESOURCE) map; + if (SUCCEEDED(Ren->Context->Map(D3DBuffer, 0, mapFlags, 0, &map))) + return ((char*)map.pData) + start; + else + return NULL; +#endif +} + +bool Buffer::Unmap(void *m) +{ + OVR_UNUSED(m); + +#if (OVR_D3D_VERSION == 10) + D3DBuffer->Unmap(); +#else + Ren->Context->Unmap(D3DBuffer, 0); +#endif + return true; +} + + +// Shaders + +#if (OVR_D3D_VERSION == 10) +template<> bool Shader::Load(void* shader, size_t size) +{ + return SUCCEEDED(Ren->Device->CreateVertexShader(shader, size, &D3DShader)); +} +template<> bool Shader::Load(void* shader, size_t size) +{ + return SUCCEEDED(Ren->Device->CreatePixelShader(shader, size, &D3DShader)); +} +template<> bool Shader::Load(void* shader, size_t size) +{ + return SUCCEEDED(Ren->Device->CreateGeometryShader(shader, size, &D3DShader)); +} + +template<> void Shader::Set(PrimitiveType) const +{ + Ren->Context->VSSetShader(D3DShader); +} +template<> void Shader::Set(PrimitiveType) const +{ + Ren->Context->PSSetShader(D3DShader); +} +template<> void Shader::Set(PrimitiveType) const +{ + Ren->Context->GSSetShader(D3DShader); +} + +#else // 11 +template<> bool Shader::Load(void* shader, size_t size) +{ + return SUCCEEDED(Ren->Device->CreateVertexShader(shader, size, NULL, &D3DShader)); +} +template<> bool Shader::Load(void* shader, size_t size) +{ + return SUCCEEDED(Ren->Device->CreatePixelShader(shader, size, NULL, &D3DShader)); +} +template<> bool Shader::Load(void* shader, size_t size) +{ + return SUCCEEDED(Ren->Device->CreateGeometryShader(shader, size, NULL, &D3DShader)); +} + +template<> void Shader::Set(PrimitiveType) const +{ + Ren->Context->VSSetShader(D3DShader, NULL, 0); +} +template<> void Shader::Set(PrimitiveType) const +{ + Ren->Context->PSSetShader(D3DShader, NULL, 0); +} +template<> void Shader::Set(PrimitiveType) const +{ + Ren->Context->GSSetShader(D3DShader, NULL, 0); +} +#endif + +template<> void Shader::SetUniformBuffer(Render::Buffer* buffer, int i) +{ + Ren->Context->VSSetConstantBuffers(i, 1, &((Buffer*)buffer)->D3DBuffer.GetRawRef()); +} +template<> void Shader::SetUniformBuffer(Render::Buffer* buffer, int i) +{ + Ren->Context->PSSetConstantBuffers(i, 1, &((Buffer*)buffer)->D3DBuffer.GetRawRef()); +} +template<> void Shader::SetUniformBuffer(Render::Buffer* buffer, int i) +{ + Ren->Context->GSSetConstantBuffers(i, 1, &((Buffer*)buffer)->D3DBuffer.GetRawRef()); +} + +ID3D10Blob* RenderDevice::CompileShader(const char* profile, const char* src, const char* mainName) +{ + ID3D10Blob* shader; + ID3D10Blob* errors; + HRESULT hr = D3DCompile(src, strlen(src), NULL, NULL, NULL, mainName, profile, + 0, 0, &shader, &errors); + if (FAILED(hr)) + { + OVR_DEBUG_LOG(("Compiling D3D shader for %s failed\n%s\n\n%s", + profile, src, errors->GetBufferPointer())); + OutputDebugStringA((char*)errors->GetBufferPointer()); + return NULL; + } + if (errors) + { + errors->Release(); + } + return shader; +} + + +ShaderBase::ShaderBase(RenderDevice* r, ShaderStage stage) + : Render::Shader(stage), Ren(r), UniformData(0) +{ +} +ShaderBase::~ShaderBase() +{ + if (UniformData) + { + OVR_FREE(UniformData); + } +} + +bool ShaderBase::SetUniform(const char* name, int n, const float* v) +{ + for(unsigned i = 0; i < UniformInfo.GetSize(); i++) + if (!strcmp(UniformInfo[i].Name.ToCStr(), name)) + { + memcpy(UniformData + UniformInfo[i].Offset, v, n * sizeof(float)); + return 1; + } + return 0; +} + +void ShaderBase::InitUniforms(ID3D10Blob* s) +{ + ID3D10ShaderReflection* ref = NULL; + D3D10ReflectShader(s->GetBufferPointer(), s->GetBufferSize(), &ref); + ID3D10ShaderReflectionConstantBuffer* buf = ref->GetConstantBufferByIndex(0); + D3D10_SHADER_BUFFER_DESC bufd; + if (FAILED(buf->GetDesc(&bufd))) + { + UniformsSize = 0; + if (UniformData) + { + OVR_FREE(UniformData); + UniformData = 0; + } + return; + } + + for(unsigned i = 0; i < bufd.Variables; i++) + { + ID3D10ShaderReflectionVariable* var = buf->GetVariableByIndex(i); + if (var) + { + D3D10_SHADER_VARIABLE_DESC vd; + if (SUCCEEDED(var->GetDesc(&vd))) + { + Uniform u; + u.Name = vd.Name; + u.Offset = vd.StartOffset; + u.Size = vd.Size; + UniformInfo.PushBack(u); + } + } + } + + UniformsSize = bufd.Size; + UniformData = (unsigned char*)OVR_ALLOC(bufd.Size); +} + +void ShaderBase::UpdateBuffer(Buffer* buf) +{ + if (UniformsSize) + { + buf->Data(Buffer_Uniform, UniformData, UniformsSize); + } +} + +void RenderDevice::SetCommonUniformBuffer(int i, Render::Buffer* buffer) +{ + CommonUniforms[i] = (Buffer*)buffer; + + Context->PSSetConstantBuffers(1, 1, &CommonUniforms[1]->D3DBuffer.GetRawRef()); + Context->VSSetConstantBuffers(1, 1, &CommonUniforms[1]->D3DBuffer.GetRawRef()); +} + +Render::Shader *RenderDevice::LoadBuiltinShader(ShaderStage stage, int shader) +{ + switch(stage) + { + case Shader_Vertex: + return VertexShaders[shader]; + case Shader_Pixel: + return PixelShaders[shader]; + default: + return NULL; + } +} + +ShaderBase* RenderDevice::CreateStereoShader(PrimitiveType prim, Render::Shader* vs) +{ + if (pStereoShaders[prim]) + { + return pStereoShaders[prim]; + } + + OVR_UNUSED(vs); + const char* varyings = + " float4 Position : SV_Position;\n" + " float4 Color : COLOR0;\n" + " float2 TexCoord : TEXCOORD0;\n" + " float3 Normal : NORMAL;\n"; + const char* copyVaryings = + " o.Color = iv[i].Color;\n" + " o.Normal = iv[i].Normal;\n" + " o.TexCoord = iv[i].TexCoord;\n"; + + StringBuffer src = + "float4x4 Proj[2] : register(c0);\n" + "float4 ViewOffset : register(c8);\n" + "struct Varyings\n" + "{\n"; + src += varyings; + src += "};\n" + "struct OutVaryings\n" + "{\n"; + src += varyings; + src += + " float3 VPos : TEXCOORD4;\n" + " uint Viewport : SV_ViewportArrayIndex;\n" + "};\n"; + + if (prim == Prim_Lines) + src += + "[maxvertexcount(4)]\n" + "void main(line Varyings iv[2], inout LineStream v)\n"; + else + src += + "[maxvertexcount(6)]\n" + "void main(triangle Varyings iv[3], inout TriangleStream v)\n"; + + char ivsize[6]; + OVR_sprintf(ivsize, 6, "%d", (prim == Prim_Lines) ? 2 : 3); + + src += + "{\n" + " OutVaryings o;\n" + " for (uint i = 0; i < "; + src += ivsize; + src += "; i++)\n" + " {\n" + " o.Position = mul(Proj[0], iv[i].Position - ViewOffset);\n" + " o.VPos = iv[i].Position;\n" + " o.Viewport = 0;\n"; + src += copyVaryings; + src += + " v.Append(o);\n" + " }\n" + " v.RestartStrip();\n" + " for (uint i = 0; i < "; + src += ivsize; + src += "; i++)\n" + " {\n" + " o.Position = mul(Proj[1], iv[i].Position + ViewOffset);\n" + " o.VPos = iv[i].Position;\n" + " o.Viewport = 1;\n"; + src += copyVaryings; + src += + " v.Append(o);\n" + " }\n" + " v.RestartStrip();\n" + "}\n"; + + pStereoShaders[prim] = *new GeomShader(this, CompileShader("gs_4_0", src.ToCStr())); + return pStereoShaders[prim]; +} + +Fill* RenderDevice::CreateSimpleFill(int flags) +{ + OVR_UNUSED(flags); + return DefaultFill; +} + +// Textures + +ID3D1xSamplerState* RenderDevice::GetSamplerState(int sm) +{ + if (SamplerStates[sm]) + return SamplerStates[sm]; + + D3D1x_(SAMPLER_DESC) ss; + memset(&ss, 0, sizeof(ss)); + if (sm & Sample_Clamp) + ss.AddressU = ss.AddressV = ss.AddressW = D3D1x_(TEXTURE_ADDRESS_CLAMP); + else if (sm & Sample_ClampBorder) + ss.AddressU = ss.AddressV = ss.AddressW = D3D1x_(TEXTURE_ADDRESS_BORDER); + else + ss.AddressU = ss.AddressV = ss.AddressW = D3D1x_(TEXTURE_ADDRESS_WRAP); + + if (sm & Sample_Nearest) + { + ss.Filter = D3D1x_(FILTER_MIN_MAG_MIP_POINT); + } + else if (sm & Sample_Anisotropic) + { + ss.Filter = D3D1x_(FILTER_ANISOTROPIC); + ss.MaxAnisotropy = 8; + } + else + { + ss.Filter = D3D1x_(FILTER_MIN_MAG_MIP_LINEAR); + } + ss.MaxLOD = 15; + Device->CreateSamplerState(&ss, &SamplerStates[sm].GetRawRef()); + return SamplerStates[sm]; +} + +Texture::Texture(RenderDevice* ren, int fmt, int w, int h) : Ren(ren), Tex(NULL), TexSv(NULL), TexRtv(NULL), TexDsv(NULL), Width(w), Height(h) +{ + OVR_UNUSED(fmt); + Sampler = Ren->GetSamplerState(0); +} + +Texture::~Texture() +{ +} + +void Texture::Set(int slot, Render::ShaderStage stage) const +{ + Ren->SetTexture(stage, slot, this); +} + +void Texture::SetSampleMode(int sm) +{ + Sampler = Ren->GetSamplerState(sm); +} + +void RenderDevice::SetTexture(Render::ShaderStage stage, int slot, const Texture* t) +{ + if (MaxTextureSet[stage] <= slot) + MaxTextureSet[stage] = slot + 1; + + ID3D1xShaderResourceView* sv = t ? t->TexSv : NULL; + switch(stage) + { + case Shader_Fragment: + Context->PSSetShaderResources(slot, 1, &sv); + if (t) + { + Context->PSSetSamplers(slot, 1, &t->Sampler.GetRawRef()); + } + break; + + case Shader_Vertex: + Context->VSSetShaderResources(slot, 1, &sv); + break; + } +} + +void RenderDevice::GenerateSubresourceData( + unsigned imageWidth, unsigned imageHeight, int format, unsigned imageDimUpperLimit, + const void* rawBytes, D3D1x_(SUBRESOURCE_DATA)* subresData, + unsigned& largestMipWidth, unsigned& largestMipHeight, unsigned& byteSize, unsigned& effectiveMipCount) +{ + largestMipWidth = 0; + largestMipHeight = 0; + + unsigned sliceLen = 0; + unsigned rowLen = 0; + unsigned numRows = 0; + const byte* mipBytes = static_cast(rawBytes); + + unsigned index = 0; + unsigned subresWidth = imageWidth; + unsigned subresHeight = imageHeight; + unsigned numMips = effectiveMipCount; + + for(unsigned i = 0; i < numMips; i++) + { + unsigned bytesPerBlock = 0; + if (format == DXGI_FORMAT_BC1_UNORM) + { + bytesPerBlock = 8; + } + else if (format == DXGI_FORMAT_BC3_UNORM) + { + bytesPerBlock = 16; + } + + unsigned blockWidth = 0; + blockWidth = (subresWidth + 3) / 4; + if (blockWidth < 1) + { + blockWidth = 1; + } + + unsigned blockHeight = 0; + blockHeight = (subresHeight + 3) / 4; + if (blockHeight < 1) + { + blockHeight = 1; + } + + rowLen = blockWidth * bytesPerBlock; + numRows = blockHeight; + sliceLen = rowLen * numRows; + + if (imageDimUpperLimit == 0 || (effectiveMipCount == 1) || + (subresWidth <= imageDimUpperLimit && subresHeight <= imageDimUpperLimit)) + { + if(!largestMipWidth) + { + largestMipWidth = subresWidth; + largestMipHeight = subresHeight; + } + + subresData[index].pSysMem = (const void*)mipBytes; + subresData[index].SysMemPitch = static_cast(rowLen); + subresData[index].SysMemSlicePitch = static_cast(sliceLen); + byteSize += sliceLen; + ++index; + } + else + { + effectiveMipCount--; + } + + mipBytes += sliceLen; + + subresWidth = subresWidth >> 1; + subresHeight = subresHeight >> 1; + if (subresWidth <= 0) + { + subresWidth = 1; + } + if (subresHeight <= 0) + { + subresHeight = 1; + } + } +} + +#define _256Megabytes 268435456 +#define _512Megabytes 536870912 + +Texture* RenderDevice::CreateTexture(int format, int width, int height, const void* data, int mipcount) +{ + UPInt gpuMemorySize = 0; + { + IDXGIDevice* pDXGIDevice; + Device->QueryInterface(__uuidof(IDXGIDevice), (void **)&pDXGIDevice); + IDXGIAdapter * pDXGIAdapter; + pDXGIDevice->GetAdapter(&pDXGIAdapter); + DXGI_ADAPTER_DESC adapterDesc; + pDXGIAdapter->GetDesc(&adapterDesc); + gpuMemorySize = adapterDesc.DedicatedVideoMemory; + pDXGIAdapter->Release(); + pDXGIDevice->Release(); + } + + unsigned imageDimUpperLimit = 0; + if (gpuMemorySize <= _256Megabytes) + { + imageDimUpperLimit = 512; + } + else if (gpuMemorySize <= _512Megabytes) + { + imageDimUpperLimit = 1024; + } + + if (format == Texture_DXT1 || format == Texture_DXT5) + { + int convertedFormat = (format == Texture_DXT1) ? DXGI_FORMAT_BC1_UNORM : DXGI_FORMAT_BC3_UNORM; + unsigned largestMipWidth = 0; + unsigned largestMipHeight = 0; + unsigned effectiveMipCount = mipcount; + unsigned textureSize = 0; + +#ifdef OVR_DEFINE_NEW +#undef new +#endif + + D3D1x_(SUBRESOURCE_DATA)* subresData = (D3D1x_(SUBRESOURCE_DATA)*) + OVR_ALLOC(sizeof(D3D1x_(SUBRESOURCE_DATA)) * mipcount); + GenerateSubresourceData(width, height, convertedFormat, imageDimUpperLimit, data, subresData, largestMipWidth, + largestMipHeight, textureSize, effectiveMipCount); + TotalTextureMemoryUsage += textureSize; + +#ifdef OVR_DEFINE_NEW +#define new OVR_DEFINE_NEW +#endif + + if (!Device || !subresData) + { + return NULL; + } + int samples = (Texture_RGBA & Texture_SamplesMask); + if (samples < 1) + { + samples = 1; + } + + Texture* NewTex = new Texture(this, format, largestMipWidth, largestMipHeight); + NewTex->Samples = samples; + + D3D1x_(TEXTURE2D_DESC) desc; + desc.Width = largestMipWidth; + desc.Height = largestMipHeight; + desc.MipLevels = effectiveMipCount; + desc.ArraySize = 1; + desc.Format = static_cast(convertedFormat); + desc.SampleDesc.Count = samples; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D1x_(USAGE_DEFAULT); + desc.BindFlags = D3D1x_(BIND_SHADER_RESOURCE); + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + + HRESULT hr = Device->CreateTexture2D(&desc, static_cast(subresData), + &NewTex->Tex.GetRawRef()); + OVR_FREE(subresData); + + if (SUCCEEDED(hr) && NewTex != 0) + { + D3D1x_(SHADER_RESOURCE_VIEW_DESC) SRVDesc; + memset(&SRVDesc, 0, sizeof(SRVDesc)); + SRVDesc.Format = static_cast(format); + SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE2D; + SRVDesc.Texture2D.MipLevels = desc.MipLevels; + + hr = Device->CreateShaderResourceView(NewTex->Tex, NULL, &NewTex->TexSv.GetRawRef()); + + if (FAILED(hr)) + { + NewTex->Release(); + return NULL; + } + return NewTex; + } + + return NULL; + } + else + { + DXGI_FORMAT d3dformat; + int bpp; + switch(format & Texture_TypeMask) + { + case Texture_RGBA: + bpp = 4; + d3dformat = DXGI_FORMAT_R8G8B8A8_UNORM; + break; + case Texture_R: + bpp = 1; + d3dformat = DXGI_FORMAT_R8_UNORM; + break; + case Texture_Depth: + bpp = 0; + d3dformat = DXGI_FORMAT_D32_FLOAT; + break; + default: + return NULL; + } + + int samples = (format & Texture_SamplesMask); + if (samples < 1) + { + samples = 1; + } + + Texture* NewTex = new Texture(this, format, width, height); + NewTex->Samples = samples; + + D3D1x_(TEXTURE2D_DESC) dsDesc; + dsDesc.Width = width; + dsDesc.Height = height; + dsDesc.MipLevels = (format == (Texture_RGBA | Texture_GenMipmaps) && data) ? GetNumMipLevels(width, height) : 1; + dsDesc.ArraySize = 1; + dsDesc.Format = d3dformat; + dsDesc.SampleDesc.Count = samples; + dsDesc.SampleDesc.Quality = 0; + dsDesc.Usage = D3D1x_(USAGE_DEFAULT); + dsDesc.BindFlags = D3D1x_(BIND_SHADER_RESOURCE); + dsDesc.CPUAccessFlags = 0; + dsDesc.MiscFlags = 0; + + if (format & Texture_RenderTarget) + { + if ((format & Texture_TypeMask) == Texture_Depth) + // We don't use depth textures, and creating them in d3d10 requires different options. + { + dsDesc.BindFlags = D3D1x_(BIND_DEPTH_STENCIL); + } + else + { + dsDesc.BindFlags |= D3D1x_(BIND_RENDER_TARGET); + } + } + + HRESULT hr = Device->CreateTexture2D(&dsDesc, NULL, &NewTex->Tex.GetRawRef()); + if (FAILED(hr)) + { + OVR_DEBUG_LOG_TEXT(("Failed to create 2D D3D texture.")); + NewTex->Release(); + return NULL; + } + if (dsDesc.BindFlags & D3D1x_(BIND_SHADER_RESOURCE)) + { + Device->CreateShaderResourceView(NewTex->Tex, NULL, &NewTex->TexSv.GetRawRef()); + } + + if (data) + { + Context->UpdateSubresource(NewTex->Tex, 0, NULL, data, width * bpp, width * height * bpp); + if (format == (Texture_RGBA | Texture_GenMipmaps)) + { + int srcw = width, srch = height; + int level = 0; + UByte* mipmaps = NULL; + do + { + level++; + int mipw = srcw >> 1; + if (mipw < 1) + { + mipw = 1; + } + int miph = srch >> 1; + if (miph < 1) + { + miph = 1; + } + if (mipmaps == NULL) + { + mipmaps = (UByte*)OVR_ALLOC(mipw * miph * 4); + } + FilterRgba2x2(level == 1 ? (const UByte*)data : mipmaps, srcw, srch, mipmaps); + Context->UpdateSubresource(NewTex->Tex, level, NULL, mipmaps, mipw * bpp, miph * bpp); + srcw = mipw; + srch = miph; + } + while(srcw > 1 || srch > 1); + + if (mipmaps != NULL) + { + OVR_FREE(mipmaps); + } + } + } + + if (format & Texture_RenderTarget) + { + if ((format & Texture_TypeMask) == Texture_Depth) + { + Device->CreateDepthStencilView(NewTex->Tex, NULL, &NewTex->TexDsv.GetRawRef()); + } + else + { + Device->CreateRenderTargetView(NewTex->Tex, NULL, &NewTex->TexRtv.GetRawRef()); + } + } + + return NewTex; + } +} + +// Rendering + +void RenderDevice::BeginRendering() +{ + Context->RSSetState(Rasterizer); +} + +void RenderDevice::SetRenderTarget(Render::Texture* color, Render::Texture* depth, Render::Texture* stencil) +{ + OVR_UNUSED(stencil); + + CurRenderTarget = (Texture*)color; + if (color == NULL) + { + Texture* newDepthBuffer = GetDepthBuffer(WindowWidth, WindowHeight, Params.Multisample); + if (newDepthBuffer == NULL) + { + OVR_DEBUG_LOG(("New depth buffer creation failed.")); + } + if (newDepthBuffer != NULL) + { + CurDepthBuffer = GetDepthBuffer(WindowWidth, WindowHeight, Params.Multisample); + Context->OMSetRenderTargets(1, &BackBufferRT.GetRawRef(), CurDepthBuffer->TexDsv); + } + return; + } + if (depth == NULL) + { + depth = GetDepthBuffer(color->GetWidth(), color->GetHeight(), CurRenderTarget->Samples); + } + + ID3D1xShaderResourceView* sv[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + if (MaxTextureSet[Shader_Fragment]) + { + Context->PSSetShaderResources(0, MaxTextureSet[Shader_Fragment], sv); + } + memset(MaxTextureSet, 0, sizeof(MaxTextureSet)); + + CurDepthBuffer = (Texture*)depth; + Context->OMSetRenderTargets(1, &((Texture*)color)->TexRtv.GetRawRef(), ((Texture*)depth)->TexDsv); +} + + +void RenderDevice::SetWorldUniforms(const Matrix4f& proj) +{ + StdUniforms.Proj = proj.Transposed(); + // Shader constant buffers cannot be partially updated. +} + + +void RenderDevice::Render(const Matrix4f& matrix, Model* model) +{ + // Store data in buffers if not already + if (!model->VertexBuffer) + { + Ptr vb = *CreateBuffer(); + vb->Data(Buffer_Vertex, &model->Vertices[0], model->Vertices.GetSize() * sizeof(Vertex)); + model->VertexBuffer = vb; + } + if (!model->IndexBuffer) + { + Ptr ib = *CreateBuffer(); + ib->Data(Buffer_Index, &model->Indices[0], model->Indices.GetSize() * 2); + model->IndexBuffer = ib; + } + + Render(model->Fill ? model->Fill : DefaultFill, + model->VertexBuffer, model->IndexBuffer, + matrix, 0, (unsigned)model->Indices.GetSize(), model->GetPrimType()); +} + +void RenderDevice::RenderWithAlpha( const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices, + const Matrix4f& matrix, int offset, int count, PrimitiveType rprim) +{ + Context->OMSetBlendState(BlendState, NULL, 0xffffffff); + Render(fill, vertices, indices, matrix, offset, count, rprim); + Context->OMSetBlendState(NULL, NULL, 0xffffffff); +} + +void RenderDevice::Render(const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices, + const Matrix4f& matrix, int offset, int count, PrimitiveType rprim) +{ + Context->IASetInputLayout(ModelVertexIL); + if (indices) + { + Context->IASetIndexBuffer(((Buffer*)indices)->GetBuffer(), DXGI_FORMAT_R16_UINT, 0); + } + + ID3D1xBuffer* vertexBuffer = ((Buffer*)vertices)->GetBuffer(); + UINT vertexStride = sizeof(Vertex); + UINT vertexOffset = offset; + Context->IASetVertexBuffers(0, 1, &vertexBuffer, &vertexStride, &vertexOffset); + + ShaderSet* shaders = ((ShaderFill*)fill)->GetShaders(); + + ShaderBase* vshader = ((ShaderBase*)shaders->GetShader(Shader_Vertex)); + unsigned char* vertexData = vshader->UniformData; + if (vertexData) + { + StandardUniformData* stdUniforms = (StandardUniformData*) vertexData; + stdUniforms->View = matrix.Transposed(); + stdUniforms->Proj = StdUniforms.Proj; + UniformBuffers[Shader_Vertex]->Data(Buffer_Uniform, vertexData, vshader->UniformsSize); + vshader->SetUniformBuffer(UniformBuffers[Shader_Vertex]); + } + + for(int i = Shader_Vertex + 1; i < Shader_Count; i++) + if (shaders->GetShader(i)) + { + ((ShaderBase*)shaders->GetShader(i))->UpdateBuffer(UniformBuffers[i]); + ((ShaderBase*)shaders->GetShader(i))->SetUniformBuffer(UniformBuffers[i]); + } + + D3D1x_(PRIMITIVE_TOPOLOGY) prim; + switch(rprim) + { + case Prim_Triangles: + prim = D3D1x_(PRIMITIVE_TOPOLOGY_TRIANGLELIST); + break; + case Prim_Lines: + prim = D3D1x_(PRIMITIVE_TOPOLOGY_LINELIST); + break; + case Prim_TriangleStrip: + prim = D3D1x_(PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + break; + default: + assert(0); + return; + } + Context->IASetPrimitiveTopology(prim); + + fill->Set(rprim); + if (ExtraShaders) + { + ExtraShaders->Set(rprim); + } + + if (indices) + { + Context->DrawIndexed(count, 0, 0); + } + else + { + Context->Draw(count, 0); + } +} + +UPInt RenderDevice::QueryGPUMemorySize() +{ + IDXGIDevice* pDXGIDevice; + Device->QueryInterface(__uuidof(IDXGIDevice), (void **)&pDXGIDevice); + IDXGIAdapter * pDXGIAdapter; + pDXGIDevice->GetAdapter(&pDXGIAdapter); + DXGI_ADAPTER_DESC adapterDesc; + pDXGIAdapter->GetDesc(&adapterDesc); + return adapterDesc.DedicatedVideoMemory; +} + + +void RenderDevice::Present() +{ + SwapChain->Present(0, 0); +} + +void RenderDevice::ForceFlushGPU() +{ + D3D1x_QUERY_DESC queryDesc = { D3D1x_(QUERY_EVENT), 0 }; + Ptr query; + BOOL done = FALSE; + + if (Device->CreateQuery(&queryDesc, &query.GetRawRef()) == S_OK) + { + +#if (OVR_D3D_VERSION == 10) + // Begin() not used for EVENT query. + query->End(); + // GetData will returns S_OK for both done == TRUE or FALSE. + // Exit on failure to avoid infinite loop. + do { } + while(!done && !FAILED(query->GetData(&done, sizeof(BOOL), 0))); +#else + Context->End(query); + do { } + while(!done && !FAILED(Context->GetData(query, &done, sizeof(BOOL), 0))); +#endif + + } +} + + +void RenderDevice::FillRect(float left, float top, float right, float bottom, Color c) +{ + Context->OMSetBlendState(BlendState, NULL, 0xffffffff); + OVR::Render::RenderDevice::FillRect(left, top, right, bottom, c); + Context->OMSetBlendState(NULL, NULL, 0xffffffff); +} + +void RenderDevice::RenderText(const struct Font* font, const char* str, float x, float y, float size, Color c) +{ + Context->OMSetBlendState(BlendState, NULL, 0xffffffff); + OVR::Render::RenderDevice::RenderText(font, str, x, y, size, c); + Context->OMSetBlendState(NULL, NULL, 0xffffffff); +} + +}}} + diff --git a/Samples/CommonSrc/Render/Render_D3D1X_Device.h b/Samples/CommonSrc/Render/Render_D3D1X_Device.h new file mode 100644 index 0000000..c0edc28 --- /dev/null +++ b/Samples/CommonSrc/Render/Render_D3D1X_Device.h @@ -0,0 +1,360 @@ +/************************************************************************************ + +Filename : Render_D3D1X_Device.h +Content : RenderDevice implementation header for D3DX10/11. +Created : September 10, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +// ***** IMPORTANT: +// This file can be included twice, once with OVR_D3D_VERSION=10 and +// once with OVR_D3D_VERSION=11. + + +#ifndef OVR_D3D_VERSION +#error define OVR_D3D_VERSION to 10 or 11 +#endif + +#include "Kernel/OVR_String.h" +#include "Kernel/OVR_Array.h" + +#if (OVR_D3D_VERSION == 10 && !defined(_OVR_RENDERER_D3D10)) || \ + (OVR_D3D_VERSION == 11 && !defined(_OVR_RENDERER_D3D11)) + +#include "../Render/Render_Device.h" + +#include + +#if (OVR_D3D_VERSION == 10) +#define _OVR_RENDERER_D3D10 +#include + +namespace OVR { namespace Render { namespace D3D10 { + +#else // 11 + +#define _OVR_RENDERER_D3D11 +#include + +namespace OVR { namespace Render { namespace D3D11 { +#endif + +class RenderDevice; + +#ifdef D3D1x_ +#undef D3D1x_ +#endif +#ifdef ID3D1x +#undef ID3D1x +#endif + +#if (OVR_D3D_VERSION == 10) +typedef ID3D10Device ID3D1xDevice; +typedef ID3D10Device ID3D1xDeviceContext; +typedef ID3D10RenderTargetView ID3D1xRenderTargetView; +typedef ID3D10Texture2D ID3D1xTexture2D; +typedef ID3D10ShaderResourceView ID3D1xShaderResourceView; +typedef ID3D10DepthStencilView ID3D1xDepthStencilView; +typedef ID3D10DepthStencilState ID3D1xDepthStencilState; +typedef ID3D10InputLayout ID3D1xInputLayout; +typedef ID3D10Buffer ID3D1xBuffer; +typedef ID3D10VertexShader ID3D1xVertexShader; +typedef ID3D10PixelShader ID3D1xPixelShader; +typedef ID3D10GeometryShader ID3D1xGeometryShader; +typedef ID3D10BlendState ID3D1xBlendState; +typedef ID3D10RasterizerState ID3D1xRasterizerState; +typedef ID3D10SamplerState ID3D1xSamplerState; +typedef ID3D10Query ID3D1xQuery; +typedef ID3D10Blob ID3D1xBlob; +typedef D3D10_VIEWPORT D3D1x_VIEWPORT; +typedef D3D10_QUERY_DESC D3D1x_QUERY_DESC; +#define D3D1x_(x) D3D10_##x +#define ID3D1x(x) ID3D10##x + +#else // D3D 11 +typedef ID3D11Device ID3D1xDevice; +typedef ID3D11DeviceContext ID3D1xDeviceContext; +typedef ID3D11RenderTargetView ID3D1xRenderTargetView; +typedef ID3D11Texture2D ID3D1xTexture2D; +typedef ID3D11ShaderResourceView ID3D1xShaderResourceView; +typedef ID3D11DepthStencilView ID3D1xDepthStencilView; +typedef ID3D11DepthStencilState ID3D1xDepthStencilState; +typedef ID3D11InputLayout ID3D1xInputLayout; +typedef ID3D11Buffer ID3D1xBuffer; +typedef ID3D10Blob ID3D1xBlob; +typedef ID3D11VertexShader ID3D1xVertexShader; +typedef ID3D11PixelShader ID3D1xPixelShader; +typedef ID3D11GeometryShader ID3D1xGeometryShader; +typedef ID3D11BlendState ID3D1xBlendState; +typedef ID3D11RasterizerState ID3D1xRasterizerState; +typedef ID3D11SamplerState ID3D1xSamplerState; +typedef ID3D11Query ID3D1xQuery; +typedef D3D11_VIEWPORT D3D1x_VIEWPORT; +typedef D3D11_QUERY_DESC D3D1x_QUERY_DESC; +#define D3D1x_(x) D3D11_##x +#define ID3D1x(x) ID3D11##x +#endif + +class Buffer; + +class ShaderBase : public Render::Shader +{ +public: + RenderDevice* Ren; + unsigned char* UniformData; + int UniformsSize; + + struct Uniform + { + String Name; + int Offset, Size; + }; + Array UniformInfo; + + ShaderBase(RenderDevice* r, ShaderStage stage); + ~ShaderBase(); + + void InitUniforms(ID3D10Blob* s); + bool SetUniform(const char* name, int n, const float* v); + //virtual bool UseTransposeMatrix() const { return 1; } + + void UpdateBuffer(Buffer* b); +}; + +template +class Shader : public ShaderBase +{ +public: + D3DShaderType* D3DShader; + + Shader(RenderDevice* r, D3DShaderType* s) : ShaderBase(r, SStage), D3DShader(s) {} + Shader(RenderDevice* r, ID3D1xBlob* s) : ShaderBase(r, SStage) + { + Load(s); + InitUniforms(s); + } + ~Shader() + { + if(D3DShader) + { + D3DShader->Release(); + } + } + bool Load(ID3D1xBlob* shader) + { + return Load(shader->GetBufferPointer(), shader->GetBufferSize()); + } + + // These functions have specializations. + bool Load(void* shader, size_t size); + void Set(PrimitiveType prim) const; + void SetUniformBuffer(Render::Buffer* buffers, int i = 0); +}; + +typedef Shader VertexShader; +typedef Shader GeomShader; +typedef Shader PixelShader; + + +class Buffer : public Render::Buffer +{ +public: + RenderDevice* Ren; + Ptr D3DBuffer; + size_t Size; + int Use; + bool Dynamic; + +public: + Buffer(RenderDevice* r) : Ren(r), Size(0), Use(0) {} + ~Buffer(); + + ID3D1xBuffer* GetBuffer() + { + return D3DBuffer; + } + + virtual size_t GetSize() + { + return Size; + } + virtual void* Map(size_t start, size_t size, int flags = 0); + virtual bool Unmap(void *m); + virtual bool Data(int use, const void* buffer, size_t size); +}; + +class Texture : public Render::Texture +{ +public: + RenderDevice* Ren; + Ptr Tex; + Ptr TexSv; + Ptr TexRtv; + Ptr TexDsv; + mutable Ptr Sampler; + int Width, Height; + int Samples; + + Texture(RenderDevice* r, int fmt, int w, int h); + ~Texture(); + + virtual int GetWidth() const + { + return Width; + } + virtual int GetHeight() const + { + return Height; + } + virtual int GetSamples() const + { + return Samples; + } + + virtual void SetSampleMode(int sm); + + virtual void Set(int slot, Render::ShaderStage stage = Render::Shader_Fragment) const; +}; + +class RenderDevice : public Render::RenderDevice +{ +public: + Ptr DXGIFactory; + HWND Window; + + Ptr Device; + Ptr Context; + Ptr SwapChain; + Ptr Adapter; + Ptr FullscreenOutput; + int FSDesktopX, FSDesktopY; + int PreFullscreenX, PreFullscreenY, PreFullscreenW, PreFullscreenH; + + Ptr BackBuffer; + Ptr BackBufferRT; + Ptr CurRenderTarget; + Ptr CurDepthBuffer; + Ptr Rasterizer; + Ptr BlendState; + int NumViewports; + D3D1x_VIEWPORT Viewports[2]; + + Ptr DepthStates[1 + 2 * Compare_Count]; + Ptr CurDepthState; + Ptr ModelVertexIL; + + Ptr SamplerStates[Sample_Count]; + + struct StandardUniformData + { + Matrix4f Proj; + Matrix4f View; + } StdUniforms; + Ptr UniformBuffers[Shader_Count]; + int MaxTextureSet[Shader_Count]; + + Ptr VertexShaders[VShader_Count]; + Ptr PixelShaders[FShader_Count]; + Ptr pStereoShaders[Prim_Count]; + Ptr CommonUniforms[8]; + Ptr ExtraShaders; + Ptr DefaultFill; + + Ptr QuadVertexBuffer; + + Array > DepthBuffers; + +public: + RenderDevice(const RendererParams& p, HWND window); + ~RenderDevice(); + + // Implement static initializer function to create this class. + static Render::RenderDevice* CreateDevice(const RendererParams& rp, void* oswnd); + + // if needRecreate == true it will recreate DXGIFactory and Adapter + // to get the latest info about monitors (including just connected/ + // disconnected ones). Note, SwapChain will be released in this case + // and it should be recreated. + void UpdateMonitorOutputs(bool needRecreate = false); + + virtual void SetMultipleViewports(int n, const Viewport* vps); + virtual void SetWindowSize(int w, int h); + virtual bool SetParams(const RendererParams& newParams); + //virtual void SetScissor(int x, int y, int w, int h); + + virtual void Present(); + virtual void ForceFlushGPU(); + + virtual bool SetFullscreen(DisplayMode fullscreen); + virtual UPInt QueryGPUMemorySize(); + + virtual void Clear(float r = 0, float g = 0, float b = 0, float a = 1, float depth = 1); + virtual void Rect(float left, float top, float right, float bottom) + { + OVR_UNUSED4(left, top, right, bottom); + } + + virtual Buffer* CreateBuffer(); + virtual Texture* CreateTexture(int format, int width, int height, const void* data, int mipcount=1); + + static void GenerateSubresourceData( + unsigned imageWidth, unsigned imageHeight, int format, unsigned imageDimUpperLimit, + const void* rawBytes, + D3D1x_(SUBRESOURCE_DATA)* subresData, + unsigned& largestMipWidth, unsigned& largestMipHeight, unsigned& byteSize, unsigned& effectiveMipCount); + + Texture* GetDepthBuffer(int w, int h, int ms); + + virtual void BeginRendering(); + virtual void SetRenderTarget(Render::Texture* color, + Render::Texture* depth = NULL, Render::Texture* stencil = NULL); + virtual void SetDepthMode(bool enable, bool write, CompareFunc func = Compare_Less); + virtual void SetWorldUniforms(const Matrix4f& proj); + virtual void SetCommonUniformBuffer(int i, Render::Buffer* buffer); + virtual void SetExtraShaders(ShaderSet* s) + { + ExtraShaders = s; + } + + + // Overrident to apply proper blend state. + virtual void FillRect(float left, float top, float right, float bottom, Color c); + virtual void RenderText(const struct Font* font, const char* str, float x, float y, float size, Color c); + + virtual void Render(const Matrix4f& matrix, Model* model); + virtual void Render(const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices, + const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles); + virtual void RenderWithAlpha( const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices, + const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles); + + virtual Fill *CreateSimpleFill(int flags = Fill::F_Solid); + + virtual Render::Shader *LoadBuiltinShader(ShaderStage stage, int shader); + + bool RecreateSwapChain(); + virtual ID3D10Blob* CompileShader(const char* profile, const char* src, const char* mainName = "main"); + virtual ShaderBase* CreateStereoShader(PrimitiveType prim, Render::Shader* vs); + + ID3D1xSamplerState* GetSamplerState(int sm); + + void SetTexture(Render::ShaderStage stage, int slot, const Texture* t); +}; + +}}} + +#endif diff --git a/Samples/CommonSrc/Render/Render_Device.cpp b/Samples/CommonSrc/Render/Render_Device.cpp new file mode 100644 index 0000000..d1687d5 --- /dev/null +++ b/Samples/CommonSrc/Render/Render_Device.cpp @@ -0,0 +1,1033 @@ +/************************************************************************************ + +Filename : Render_Device.cpp +Content : Platform renderer for simple scene graph - implementation +Created : September 6, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#include "../Render/Render_Device.h" +#include "../Render/Render_Font.h" + +#include "Kernel/OVR_Log.h" + +namespace OVR { namespace Render { + +void Model::Render(const Matrix4f& ltw, RenderDevice* ren) +{ + if(Visible) + { + Matrix4f m = ltw * GetMatrix(); + ren->Render(m, this); + } +} + +void Container::Render(const Matrix4f& ltw, RenderDevice* ren) +{ + Matrix4f m = ltw * GetMatrix(); + for(unsigned i = 0; i < Nodes.GetSize(); i++) + { + Nodes[i]->Render(m, ren); + } +} + +Matrix4f SceneView::GetViewMatrix() const +{ + Matrix4f view = Matrix4f(GetOrientation().Conj()) * Matrix4f::Translation(GetPosition()); + return view; +} + +void LightingParams::Update(const Matrix4f& view, const Vector4f* SceneLightPos) +{ + Version++; + for (int i = 0; i < LightCount; i++) + { + LightPos[i] = view.Transform(SceneLightPos[i]); + } +} + +void Scene::Render(RenderDevice* ren, const Matrix4f& view) +{ + Lighting.Update(view, LightPos); + + ren->SetLighting(&Lighting); + + World.Render(view, ren); +} + + + +UInt16 CubeIndices[] = +{ + 0, 1, 3, + 3, 1, 2, + + 5, 4, 6, + 6, 4, 7, + + 8, 9, 11, + 11, 9, 10, + + 13, 12, 14, + 14, 12, 15, + + 16, 17, 19, + 19, 17, 18, + + 21, 20, 22, + 22, 20, 23 +}; + +// Colors are specified for planes perpendicular to the axis +// For example, "xColor" is the color of the y-z plane +Model* Model::CreateAxisFaceColorBox(float x1, float x2, Color xcolor, + float y1, float y2, Color ycolor, + float z1, float z2, Color zcolor) +{ + float t; + + if(x1 > x2) + { + t = x1; + x1 = x2; + x2 = t; + } + if(y1 > y2) + { + t = y1; + y1 = y2; + y2 = t; + } + if(z1 > z2) + { + t = z1; + z1 = z2; + z2 = t; + } + + Model* box = new Model(); + + UInt16 startIndex = 0; + // Cube + startIndex = + box->AddVertex(Vector3f(x1, y2, z1), ycolor); + box->AddVertex(Vector3f(x2, y2, z1), ycolor); + box->AddVertex(Vector3f(x2, y2, z2), ycolor); + box->AddVertex(Vector3f(x1, y2, z2), ycolor); + + box->AddVertex(Vector3f(x1, y1, z1), ycolor); + box->AddVertex(Vector3f(x2, y1, z1), ycolor); + box->AddVertex(Vector3f(x2, y1, z2), ycolor); + box->AddVertex(Vector3f(x1, y1, z2), ycolor); + + box->AddVertex(Vector3f(x1, y1, z2), xcolor); + box->AddVertex(Vector3f(x1, y1, z1), xcolor); + box->AddVertex(Vector3f(x1, y2, z1), xcolor); + box->AddVertex(Vector3f(x1, y2, z2), xcolor); + + box->AddVertex(Vector3f(x2, y1, z2), xcolor); + box->AddVertex(Vector3f(x2, y1, z1), xcolor); + box->AddVertex(Vector3f(x2, y2, z1), xcolor); + box->AddVertex(Vector3f(x2, y2, z2), xcolor); + + box->AddVertex(Vector3f(x1, y1, z1), zcolor); + box->AddVertex(Vector3f(x2, y1, z1), zcolor); + box->AddVertex(Vector3f(x2, y2, z1), zcolor); + box->AddVertex(Vector3f(x1, y2, z1), zcolor); + + box->AddVertex(Vector3f(x1, y1, z2), zcolor); + box->AddVertex(Vector3f(x2, y1, z2), zcolor); + box->AddVertex(Vector3f(x2, y2, z2), zcolor); + box->AddVertex(Vector3f(x1, y2, z2), zcolor); + + + enum + { + // CubeVertexCount = sizeof(CubeVertices)/sizeof(CubeVertices[0]), + CubeIndexCount = sizeof(CubeIndices) / sizeof(CubeIndices[0]) + }; + + // Renumber indices + for(int i = 0; i < CubeIndexCount / 3; i++) + { + box->AddTriangle(CubeIndices[i * 3] + startIndex, + CubeIndices[i * 3 + 1] + startIndex, + CubeIndices[i * 3 + 2] + startIndex); + } + + return box; +} + +void Model::AddSolidColorBox(float x1, float y1, float z1, + float x2, float y2, float z2, + Color c) +{ + float t; + + if(x1 > x2) + { + t = x1; + x1 = x2; + x2 = t; + } + if(y1 > y2) + { + t = y1; + y1 = y2; + y2 = t; + } + if(z1 > z2) + { + t = z1; + z1 = z2; + z2 = t; + } + + // Cube vertices and their normals. + Vector3f CubeVertices[][3] = + { + Vector3f(x1, y2, z1), Vector3f(z1, x1), Vector3f(0.0f, 1.0f, 0.0f), + Vector3f(x2, y2, z1), Vector3f(z1, x2), Vector3f(0.0f, 1.0f, 0.0f), + Vector3f(x2, y2, z2), Vector3f(z2, x2), Vector3f(0.0f, 1.0f, 0.0f), + Vector3f(x1, y2, z2), Vector3f(z2, x1), Vector3f(0.0f, 1.0f, 0.0f), + + Vector3f(x1, y1, z1), Vector3f(z1, x1), Vector3f(0.0f, -1.0f, 0.0f), + Vector3f(x2, y1, z1), Vector3f(z1, x2), Vector3f(0.0f, -1.0f, 0.0f), + Vector3f(x2, y1, z2), Vector3f(z2, x2), Vector3f(0.0f, -1.0f, 0.0f), + Vector3f(x1, y1, z2), Vector3f(z2, x1), Vector3f(0.0f, -1.0f, 0.0f), + + Vector3f(x1, y1, z2), Vector3f(z2, y1), Vector3f(-1.0f, 0.0f, 0.0f), + Vector3f(x1, y1, z1), Vector3f(z1, y1), Vector3f(-1.0f, 0.0f, 0.0f), + Vector3f(x1, y2, z1), Vector3f(z1, y2), Vector3f(-1.0f, 0.0f, 0.0f), + Vector3f(x1, y2, z2), Vector3f(z2, y2), Vector3f(-1.0f, 0.0f, 0.0f), + + Vector3f(x2, y1, z2), Vector3f(z2, y1), Vector3f(1.0f, 0.0f, 0.0f), + Vector3f(x2, y1, z1), Vector3f(z1, y1), Vector3f(1.0f, 0.0f, 0.0f), + Vector3f(x2, y2, z1), Vector3f(z1, y2), Vector3f(1.0f, 0.0f, 0.0f), + Vector3f(x2, y2, z2), Vector3f(z2, y2), Vector3f(1.0f, 0.0f, 0.0f), + + Vector3f(x1, y1, z1), Vector3f(x1, y1), Vector3f(0.0f, 0.0f, -1.0f), + Vector3f(x2, y1, z1), Vector3f(x2, y1), Vector3f(0.0f, 0.0f, -1.0f), + Vector3f(x2, y2, z1), Vector3f(x2, y2), Vector3f(0.0f, 0.0f, -1.0f), + Vector3f(x1, y2, z1), Vector3f(x1, y2), Vector3f(0.0f, 0.0f, -1.0f), + + Vector3f(x1, y1, z2), Vector3f(x1, y1), Vector3f(0.0f, 0.0f, 1.0f), + Vector3f(x2, y1, z2), Vector3f(x2, y1), Vector3f(0.0f, 0.0f, 1.0f), + Vector3f(x2, y2, z2), Vector3f(x2, y2), Vector3f(0.0f, 0.0f, 1.0f), + Vector3f(x1, y2, z2), Vector3f(x1, y2), Vector3f(0.0f, 0.0f, 1.0f) + }; + + + UInt16 startIndex = GetNextVertexIndex(); + + enum + { + CubeVertexCount = sizeof(CubeVertices) / sizeof(CubeVertices[0]), + CubeIndexCount = sizeof(CubeIndices) / sizeof(CubeIndices[0]) + }; + + for(int v = 0; v < CubeVertexCount; v++) + { + AddVertex(Vertex(CubeVertices[v][0], c, CubeVertices[v][1].x, CubeVertices[v][1].y, CubeVertices[v][2])); + } + + // Renumber indices + for(int i = 0; i < CubeIndexCount / 3; i++) + { + AddTriangle(CubeIndices[i * 3] + startIndex, + CubeIndices[i * 3 + 1] + startIndex, + CubeIndices[i * 3 + 2] + startIndex); + } +} + + + +Model* Model::CreateBox(Color c, Vector3f origin, Vector3f size) +{ + Model *box = new Model(); + Vector3f s = size * 0.5f; + + box->AddVertex(-s.x, s.y, -s.z, c, 0, 1, 0, 0, -1); + box->AddVertex(s.x, s.y, -s.z, c, 1, 1, 0, 0, -1); + box->AddVertex(s.x, -s.y, -s.z, c, 1, 0, 0, 0, -1); + box->AddVertex(-s.x, -s.y, -s.z, c, 0, 0, 0, 0, -1); + box->AddTriangle(2, 1, 0); + box->AddTriangle(0, 3, 2); + + box->AddVertex(s.x, s.y, s.z, c, 1, 1, 0, 0, 1); + box->AddVertex(-s.x, s.y, s.z, c, 0, 1, 0, 0, 1); + box->AddVertex(-s.x, -s.y, s.z, c, 0, 0, 0, 0, 1); + box->AddVertex(s.x, -s.y, s.z, c, 1, 0, 0, 0, 1); + box->AddTriangle(6, 5, 4); + box->AddTriangle(4, 7, 6); + + box->AddVertex(-s.x, s.y, -s.z, c, 1, 0, -1, 0, 0); + box->AddVertex(-s.x, s.y, s.z, c, 1, 1, -1, 0, 0); + box->AddVertex(-s.x, -s.y, s.z, c, 0, 1, -1, 0, 0); + box->AddVertex(-s.x, -s.y, -s.z, c, 0, 0, -1, 0, 0); + box->AddTriangle(10, 11, 8); + box->AddTriangle(8, 9, 10); + + box->AddVertex(s.x, s.y, -s.z, c, 1, 0, 1, 0, 0); + box->AddVertex(s.x, -s.y, -s.z, c, 0, 0, 1, 0, 0); + box->AddVertex(s.x, -s.y, s.z, c, 0, 1, 1, 0, 0); + box->AddVertex(s.x, s.y, s.z, c, 1, 1, 1, 0, 0); + box->AddTriangle(14, 15, 12); + box->AddTriangle(12, 13, 14); + + box->AddVertex(-s.x, -s.y, s.z, c, 0, 1, 0, -1, 0); + box->AddVertex(s.x, -s.y, s.z, c, 1, 1, 0, -1, 0); + box->AddVertex(s.x, -s.y, -s.z, c, 1, 0, 0, -1, 0); + box->AddVertex(-s.x, -s.y, -s.z, c, 0, 0, 0, -1, 0); + box->AddTriangle(18, 19, 16); + box->AddTriangle(16, 17, 18); + + box->AddVertex(-s.x, s.y, -s.z, c, 0, 0, 0, 1, 0); + box->AddVertex(s.x, s.y, -s.z, c, 1, 0, 0, 1, 0); + box->AddVertex(s.x, s.y, s.z, c, 1, 1, 0, 1, 0); + box->AddVertex(-s.x, s.y, s.z, c, 0, 1, 0, 1, 0); + box->AddTriangle(20, 21, 22); + box->AddTriangle(22, 23, 20); + + box->SetPosition(origin); + return box; +} + +// Triangulation of a cylinder centered at the origin +Model* Model::CreateCylinder(Color color, Vector3f origin, float height, float radius, int sides) +{ + Model *cyl = new Model(); + float halfht = height * 0.5f; + for(UInt16 i = 0; i < sides; i++) + { + float x = cosf(Math::TwoPi * i / float(sides)); + float y = sinf(Math::TwoPi * i / float(sides)); + + cyl->AddVertex(radius * x, radius * y, halfht, color, x + 1, y, 0, 0, 1); + cyl->AddVertex(radius * x, radius * y, -1.0f*halfht, color, x, y, 0, 0, -1); + + UInt16 j = 0; + if(i < sides - 1) + { + j = i + 1; + cyl->AddTriangle(0, i * 4 + 4, i * 4); + cyl->AddTriangle(1, i * 4 + 1, i * 4 + 5); + } + + float nx = cosf(Math::Pi * (0.5f + 2.0f * i / float(sides))); + float ny = sinf(Math::Pi * (0.5f + 2.0f * i / float(sides))); + cyl->AddVertex(radius * x, radius * y, halfht, color, x + 1, y, nx, ny, 0); + cyl->AddVertex(radius * x, radius * y, -1.0f*halfht, color, x, y, nx, ny, 0); + + cyl->AddTriangle(i * 4 + 2, j * 4 + 2, i * 4 + 3); + cyl->AddTriangle(i * 4 + 3, j * 4 + 2, j * 4 + 3); + } + cyl->SetPosition(origin); + return cyl; +}; + +//Triangulation of a cone centered at the origin +Model* Model::CreateCone(Color color, Vector3f origin, float height, float radius, int sides) +{ + Model *cone = new Model(); + float halfht = height * 0.5f; + cone->AddVertex(0.0f, 0.0f, -1.0f*halfht, color, 0, 0, 0, 0, -1); + + for(UInt16 i = 0; i < sides; i++) + { + float x = cosf(Math::TwoPi * i / float(sides)); + float y = sinf(Math::TwoPi * i / float(sides)); + + cone->AddVertex(radius * x, radius * y, -1.0f*halfht, color, 0, 0, 0, 0, -1); + + UInt16 j = 1; + if(i < sides - 1) + { + j = i + 1; + } + + float next_x = cosf(Math::TwoPi * j / float(sides)); + float next_y = sinf(Math::TwoPi * j / float(sides)); + + Vector3f normal = Vector3f(x, y, -halfht).Cross(Vector3f(next_x, next_y, -halfht)); + + cone->AddVertex(0.0f, 0.0f, halfht, color, 1, 0, normal.x, normal.y, normal.z); + cone->AddVertex(radius * x, radius * y, -1.0f*halfht, color, 0, 0, normal.x, normal.y, normal.z); + + cone->AddTriangle(0, 3*i + 1, 3*j + 1); + cone->AddTriangle(3*i + 2, 3*j + 3, 3*i + 3); + } + cone->SetPosition(origin); + return cone; +}; + +//Triangulation of a sphere centered at the origin +Model* Model::CreateSphere(Color color, Vector3f origin, float radius, int sides) +{ + Model *sphere = new Model(); + UInt16 usides = (UInt16) sides; + UInt16 halfsides = usides/2; + + for(UInt16 k = 0; k < halfsides; k++) { + + float z = cosf(Math::Pi * k / float(halfsides)); + float z_r = sinf(Math::Pi * k / float(halfsides)); // the radius of the cross circle with coordinate z + + if (k == 0) + { // add north and south poles + sphere->AddVertex(0.0f, 0.0f, radius, color, 0, 0, 0, 0, 1); + sphere->AddVertex(0.0f, 0.0f, -radius, color, 1, 1, 0, 0, -1); + } + else + { + for(UInt16 i = 0; i < sides; i++) + { + float x = cosf(Math::TwoPi * i / float(sides)) * z_r; + float y = sinf(Math::TwoPi * i / float(sides)) * z_r; + + UInt16 j = 0; + if(i < sides - 1) + { + j = i + 1; + } + + sphere->AddVertex(radius * x, radius * y, radius * z, color, 0, 1, x, y, z); + + UInt16 indi = 2 + (k -1)*usides + i; + UInt16 indj = 2 + (k -1)*usides + j; + if (k == 1) // NorthPole + sphere->AddTriangle(0, j + 2, i + 2); + else if (k == halfsides - 1) //SouthPole + { + sphere->AddTriangle(1, indi, indj); + sphere->AddTriangle(indi, indi - usides, indj); + sphere->AddTriangle(indi - usides, indj - usides, indj); + } + else + { + sphere->AddTriangle(indi, indi - usides, indj); + sphere->AddTriangle(indi - usides, indj - usides, indj); + } + } + } // end else + } + sphere->SetPosition(origin); + return sphere; +}; + +Model* Model::CreateGrid(Vector3f origin, Vector3f stepx, Vector3f stepy, + int halfx, int halfy, int nmajor, Color minor, Color major) +{ + Model* grid = new Model(Prim_Lines); + float halfxf = (float)halfx; + float halfyf = (float)halfy; + + for(int jn = 0; jn <= halfy; jn++) + { + float j = (float)jn; + + grid->AddLine(grid->AddVertex((stepx * -halfxf) + (stepy * j), (jn % nmajor) ? minor : major, 0, 0.5f), + grid->AddVertex((stepx * halfxf) + (stepy * j), (jn % nmajor) ? minor : major, 1, 0.5f)); + + if(j) + grid->AddLine(grid->AddVertex((stepx * -halfxf) + (stepy * -j), (jn % nmajor) ? minor : major, 0, 0.5f), + grid->AddVertex((stepx * halfxf) + (stepy * -j), (jn % nmajor) ? minor : major, 1, 0.5f)); + } + + for(int in = 0; in <= halfx; in++) + { + float i = (float)in; + + grid->AddLine(grid->AddVertex((stepx * i) + (stepy * -halfyf), (in % nmajor) ? minor : major, 0, 0.5f), + grid->AddVertex((stepx * i) + (stepy * halfyf), (in % nmajor) ? minor : major, 1, 0.5f)); + + if(i) + grid->AddLine(grid->AddVertex((stepx * -i) + (stepy * -halfyf), (in % nmajor) ? minor : major, 0, 0.5f), + grid->AddVertex((stepx * -i) + (stepy * halfyf), (in % nmajor) ? minor : major, 1, 0.5f)); + } + + grid->SetPosition(origin); + return grid; +} + + +//------------------------------------------------------------------------------------- + + +void ShaderFill::Set(PrimitiveType prim) const +{ + Shaders->Set(prim); + for(int i = 0; i < 8; i++) + if(Textures[i]) + { + Textures[i]->Set(i); + } +} + + + +//------------------------------------------------------------------------------------- +// ***** Rendering + + +RenderDevice::RenderDevice() + : CurPostProcess(PostProcess_None), + SceneColorTexW(0), SceneColorTexH(0), + SceneRenderScale(1), + + Distortion(1.0f, 0.18f, 0.115f), + DistortionClearColor(0, 0, 0), + PostProcessShaderActive(PostProcessShader_DistortionAndChromAb), + TotalTextureMemoryUsage(0) +{ + PostProcessShaderRequested = PostProcessShaderActive; +} + +Fill* RenderDevice::CreateTextureFill(Render::Texture* t, bool useAlpha) +{ + ShaderSet* shaders = CreateShaderSet(); + shaders->SetShader(LoadBuiltinShader(Shader_Vertex, VShader_MVP)); + shaders->SetShader(LoadBuiltinShader(Shader_Fragment, useAlpha ? FShader_AlphaTexture : FShader_Texture)); + Fill* f = new ShaderFill(*shaders); + f->SetTexture(0, t); + return f; +} + +void LightingParams::Set(ShaderSet* s) const +{ + s->SetUniform4fv("Ambient", 1, &Ambient); + s->SetUniform1f("LightCount", LightCount); + s->SetUniform4fv("LightPos", (int)LightCount, LightPos); + s->SetUniform4fv("LightColor", (int)LightCount, LightColor); +} + +void RenderDevice::SetLighting(const LightingParams* lt) +{ + if (!LightingBuffer) + LightingBuffer = *CreateBuffer(); + + LightingBuffer->Data(Buffer_Uniform, lt, sizeof(LightingParams)); + SetCommonUniformBuffer(1, LightingBuffer); +} + +float RenderDevice::MeasureText(const Font* font, const char* str, float size, float* strsize) +{ + UPInt length = strlen(str); + float w = 0; + float xp = 0; + float yp = 0; + + for (UPInt i = 0; i < length; i++) + { + if(str[i] == '\n') + { + yp += font->lineheight; + if(xp > w) + { + w = xp; + } + xp = 0; + continue; + } + + // Tab followed by a numbers sets position to specified offset. + if(str[i] == '\t') + { + char *p = 0; + float tabPixels = (float)OVR_strtoq(str + i + 1, &p, 10); + i += p - (str + i + 1); + xp = tabPixels; + } + else + { + const Font::Char* ch = &font->chars[str[i]]; + xp += ch->advance; + } + } + + if(xp > w) + { + w = xp; + } + + if(strsize) + { + strsize[0] = (size / font->lineheight) * w; + strsize[1] = (size / font->lineheight) * (yp + font->lineheight); + } + return (size / font->lineheight) * w; +} + +void RenderDevice::RenderText(const Font* font, const char* str, + float x, float y, float size, Color c) +{ + if(!pTextVertexBuffer) + { + pTextVertexBuffer = *CreateBuffer(); + if(!pTextVertexBuffer) + { + return; + } + } + + if(!font->fill) + { + font->fill = CreateTextureFill(Ptr( + *CreateTexture(Texture_R, font->twidth, font->theight, font->tex)), true); + } + + UPInt length = strlen(str); + + pTextVertexBuffer->Data(Buffer_Vertex, NULL, length * 6 * sizeof(Vertex)); + Vertex* vertices = (Vertex*)pTextVertexBuffer->Map(0, length * 6 * sizeof(Vertex), Map_Discard); + if(!vertices) + { + return; + } + + Matrix4f m = Matrix4f(size / font->lineheight, 0, 0, 0, + 0, size / font->lineheight, 0, 0, + 0, 0, 0, 0, + x, y, 0, 1).Transposed(); + + float xp = 0, yp = (float)font->ascent; + int ivertex = 0; + + for (UPInt i = 0; i < length; i++) + { + if(str[i] == '\n') + { + yp += font->lineheight; + xp = 0; + continue; + } + // Tab followed by a numbers sets position to specified offset. + if(str[i] == '\t') + { + char *p = 0; + float tabPixels = (float)OVR_strtoq(str + i + 1, &p, 10); + i += p - (str + i + 1); + xp = tabPixels; + continue; + } + + const Font::Char* ch = &font->chars[str[i]]; + Vertex* chv = &vertices[ivertex]; + for(int j = 0; j < 6; j++) + { + chv[j].C = c; + } + float x = xp + ch->x; + float y = yp - ch->y; + float cx = font->twidth * (ch->u2 - ch->u1); + float cy = font->theight * (ch->v2 - ch->v1); + chv[0] = Vertex(Vector3f(x, y, 0), c, ch->u1, ch->v1); + chv[1] = Vertex(Vector3f(x + cx, y, 0), c, ch->u2, ch->v1); + chv[2] = Vertex(Vector3f(x + cx, cy + y, 0), c, ch->u2, ch->v2); + chv[3] = Vertex(Vector3f(x, y, 0), c, ch->u1, ch->v1); + chv[4] = Vertex(Vector3f(x + cx, cy + y, 0), c, ch->u2, ch->v2); + chv[5] = Vertex(Vector3f(x, y + cy, 0), c, ch->u1, ch->v2); + ivertex += 6; + + xp += ch->advance; + } + + pTextVertexBuffer->Unmap(vertices); + + Render(font->fill, pTextVertexBuffer, NULL, m, 0, ivertex, Prim_Triangles); +} + +void RenderDevice::FillRect(float left, float top, float right, float bottom, Color c) +{ + if(!pTextVertexBuffer) + { + pTextVertexBuffer = *CreateBuffer(); + if(!pTextVertexBuffer) + { + return; + } + } + + // Get!! + Fill* fill = CreateSimpleFill(); + + pTextVertexBuffer->Data(Buffer_Vertex, NULL, 6 * sizeof(Vertex)); + Vertex* vertices = (Vertex*)pTextVertexBuffer->Map(0, 6 * sizeof(Vertex), Map_Discard); + if(!vertices) + { + return; + } + + vertices[0] = Vertex(Vector3f(left, top, 0.0f), c); + vertices[1] = Vertex(Vector3f(right, top, 0), c); + vertices[2] = Vertex(Vector3f(left, bottom, 0), c); + vertices[3] = Vertex(Vector3f(left, bottom, 0), c); + vertices[4] = Vertex(Vector3f(right, top, 0), c); + vertices[5] = Vertex(Vector3f(right, bottom, 0), c); + + pTextVertexBuffer->Unmap(vertices); + + Render(fill, pTextVertexBuffer, NULL, Matrix4f(), 0, 6, Prim_Triangles); +} + +/* +void RenderDevice::GetStereoViewports(const Viewport& in, Viewport* out) const +{ + out[0] = out[1] = in; + if (StereoBufferMode == Stereo_SplitH) + { + out[0].w = out[1].w = in.w / 2; + out[1].x += WindowWidth / 2; + } +} +*/ + +void RenderDevice::SetSceneRenderScale(float ss) +{ + SceneRenderScale = ss; + pSceneColorTex = NULL; +} + +void RenderDevice::SetViewport(const Viewport& vp) +{ + VP = vp; + + if(CurPostProcess == PostProcess_Distortion) + { + Viewport svp = vp; + svp.w = (int)ceil(SceneRenderScale * vp.w); + svp.h = (int)ceil(SceneRenderScale * vp.h); + svp.x = (int)ceil(SceneRenderScale * vp.x); + svp.y = (int)ceil(SceneRenderScale * vp.y); + SetRealViewport(svp); + } + else + { + SetRealViewport(vp); + } +} + + +bool RenderDevice::initPostProcessSupport(PostProcessType pptype) +{ + if(pptype != PostProcess_Distortion) + { + return true; + } + + + if (PostProcessShaderRequested != PostProcessShaderActive) + { + pPostProcessShader.Clear(); + PostProcessShaderActive = PostProcessShaderRequested; + } + + if (!pPostProcessShader) + { + Shader *vs = LoadBuiltinShader(Shader_Vertex, VShader_PostProcess); + + Shader *ppfs = NULL; + + if (PostProcessShaderActive == PostProcessShader_Distortion) + { + ppfs = LoadBuiltinShader(Shader_Fragment, FShader_PostProcess); + } + else if (PostProcessShaderActive == PostProcessShader_DistortionAndChromAb) + { + ppfs = LoadBuiltinShader(Shader_Fragment, FShader_PostProcessWithChromAb); + } + else + OVR_ASSERT(false); + + pPostProcessShader = *CreateShaderSet(); + pPostProcessShader->SetShader(vs); + pPostProcessShader->SetShader(ppfs); + } + + + int texw = (int)ceil(SceneRenderScale * WindowWidth), + texh = (int)ceil(SceneRenderScale * WindowHeight); + + // If pSceneColorTex is already created and is of correct size, we are done. + // It's important to check width/height in case window size changed. + if (pSceneColorTex && (texw == SceneColorTexW) && (texh == SceneColorTexH)) + { + return true; + } + + pSceneColorTex = *CreateTexture(Texture_RGBA | Texture_RenderTarget | Params.Multisample, + texw, texh, NULL); + if(!pSceneColorTex) + { + return false; + } + SceneColorTexW = texw; + SceneColorTexH = texh; + pSceneColorTex->SetSampleMode(Sample_ClampBorder | Sample_Linear); + + + if(!pFullScreenVertexBuffer) + { + pFullScreenVertexBuffer = *CreateBuffer(); + const Render::Vertex QuadVertices[] = + { + Vertex(Vector3f(0, 1, 0), Color(1, 1, 1, 1), 0, 0), + Vertex(Vector3f(1, 1, 0), Color(1, 1, 1, 1), 1, 0), + Vertex(Vector3f(0, 0, 0), Color(1, 1, 1, 1), 0, 1), + Vertex(Vector3f(1, 0, 0), Color(1, 1, 1, 1), 1, 1) + }; + pFullScreenVertexBuffer->Data(Buffer_Vertex, QuadVertices, sizeof(QuadVertices)); + } + return true; +} + +void RenderDevice::SetProjection(const Matrix4f& proj) +{ + Proj = proj; + SetWorldUniforms(proj); +} + +void RenderDevice::BeginScene(PostProcessType pptype) +{ + BeginRendering(); + + if((pptype != PostProcess_None) && initPostProcessSupport(pptype)) + { + CurPostProcess = pptype; + } + else + { + CurPostProcess = PostProcess_None; + } + + if(CurPostProcess == PostProcess_Distortion) + { + SetRenderTarget(pSceneColorTex); + SetViewport(VP); + } + else + { + SetRenderTarget(0); + } + + SetWorldUniforms(Proj); + SetExtraShaders(NULL); +} + +void RenderDevice::FinishScene() +{ + SetExtraShaders(0); + if(CurPostProcess == PostProcess_None) + { + return; + } + + SetRenderTarget(0); + SetRealViewport(VP); + FinishScene1(); + + CurPostProcess = PostProcess_None; +} + + + +void RenderDevice::FinishScene1() +{ + float r, g, b, a; + DistortionClearColor.GetRGBA(&r, &g, &b, &a); + Clear(r, g, b, a); + + float w = float(VP.w) / float(WindowWidth), + h = float(VP.h) / float(WindowHeight), + x = float(VP.x) / float(WindowWidth), + y = float(VP.y) / float(WindowHeight); + + float as = float(VP.w) / float(VP.h); + + // We are using 1/4 of DistortionCenter offset value here, since it is + // relative to [-1,1] range that gets mapped to [0, 0.5]. + pPostProcessShader->SetUniform2f("LensCenter", + x + (w + Distortion.XCenterOffset * 0.5f)*0.5f, y + h*0.5f); + pPostProcessShader->SetUniform2f("ScreenCenter", x + w*0.5f, y + h*0.5f); + + // MA: This is more correct but we would need higher-res texture vertically; we should adopt this + // once we have asymmetric input texture scale. + float scaleFactor = 1.0f / Distortion.Scale; + + pPostProcessShader->SetUniform2f("Scale", (w/2) * scaleFactor, (h/2) * scaleFactor * as); + pPostProcessShader->SetUniform2f("ScaleIn", (2/w), (2/h) / as); + + pPostProcessShader->SetUniform4f("HmdWarpParam", + Distortion.K[0], Distortion.K[1], Distortion.K[2], Distortion.K[3]); + + if (PostProcessShaderRequested == PostProcessShader_DistortionAndChromAb) + { + pPostProcessShader->SetUniform4f("ChromAbParam", + Distortion.ChromaticAberration[0], + Distortion.ChromaticAberration[1], + Distortion.ChromaticAberration[2], + Distortion.ChromaticAberration[3]); + } + + Matrix4f texm(w, 0, 0, x, + 0, h, 0, y, + 0, 0, 0, 0, + 0, 0, 0, 1); + pPostProcessShader->SetUniform4x4f("Texm", texm); + + Matrix4f view(2, 0, 0, -1, + 0, 2, 0, -1, + 0, 0, 0, 0, + 0, 0, 0, 1); + + ShaderFill fill(pPostProcessShader); + fill.SetTexture(0, pSceneColorTex); + RenderWithAlpha(&fill, pFullScreenVertexBuffer, NULL, view, 0, 4, Prim_TriangleStrip); +} + +bool CollisionModel::TestPoint(const Vector3f& p) const +{ + for(unsigned i = 0; i < Planes.GetSize(); i++) + if(Planes[i].TestSide(p) > 0) + { + return 0; + } + + return 1; +} + +bool CollisionModel::TestRay(const Vector3f& origin, const Vector3f& norm, float& len, Planef* ph) const +{ + if(TestPoint(origin)) + { + len = 0; + *ph = Planes[0]; + return true; + } + Vector3f fullMove = origin + norm * len; + + int crossing = -1; + float cdot1 = 0, cdot2 = 0; + + for(unsigned i = 0; i < Planes.GetSize(); ++i) + { + float dot2 = Planes[i].TestSide(fullMove); + if(dot2 > 0) + { + return false; + } + float dot1 = Planes[i].TestSide(origin); + if(dot1 > 0) + { + if(dot2 <= 0) + { + //assert(crossing==-1); + if(crossing == -1) + { + crossing = i; + cdot2 = dot2; + cdot1 = dot1; + } + else + { + if(dot2 > cdot2) + { + crossing = i; + cdot2 = dot2; + cdot1 = dot1; + } + } + } + } + } + + if(crossing < 0) + { + return false; + } + + assert(TestPoint(origin + norm * len)); + + len = len * cdot1 / (cdot1 - cdot2) - 0.05f; + if(len < 0) + { + len = 0; + } + float tp = Planes[crossing].TestSide(origin + norm * len); + OVR_ASSERT(fabsf(tp) < 0.05f + Mathf::Tolerance); + OVR_UNUSED(tp); + + if(ph) + { + *ph = Planes[crossing]; + } + return true; +} + +int GetNumMipLevels(int w, int h) +{ + int n = 1; + while(w > 1 || h > 1) + { + w >>= 1; + h >>= 1; + n++; + } + return n; +} + +void FilterRgba2x2(const UByte* src, int w, int h, UByte* dest) +{ + for(int j = 0; j < (h & ~1); j += 2) + { + const UByte* psrc = src + (w * j * 4); + UByte* pdest = dest + ((w >> 1) * (j >> 1) * 4); + + for(int i = 0; i < w >> 1; i++, psrc += 8, pdest += 4) + { + pdest[0] = (((int)psrc[0]) + psrc[4] + psrc[w * 4 + 0] + psrc[w * 4 + 4]) >> 2; + pdest[1] = (((int)psrc[1]) + psrc[5] + psrc[w * 4 + 1] + psrc[w * 4 + 5]) >> 2; + pdest[2] = (((int)psrc[2]) + psrc[6] + psrc[w * 4 + 2] + psrc[w * 4 + 6]) >> 2; + pdest[3] = (((int)psrc[3]) + psrc[7] + psrc[w * 4 + 3] + psrc[w * 4 + 7]) >> 2; + } + } +} + +int GetTextureSize(int format, int w, int h) +{ + switch (format & Texture_TypeMask) + { + case Texture_R: return w*h; + case Texture_RGBA: return w*h*4; + case Texture_DXT1: { + int bw = (w+3)/4, bh = (h+3)/4; + return bw * bh * 8; + } + case Texture_DXT3: + case Texture_DXT5: { + int bw = (w+3)/4, bh = (h+3)/4; + return bw * bh * 16; + } + + default: + OVR_ASSERT(0); + } + return 0; +} + +}} diff --git a/Samples/CommonSrc/Render/Render_Device.h b/Samples/CommonSrc/Render/Render_Device.h new file mode 100644 index 0000000..448d0e6 --- /dev/null +++ b/Samples/CommonSrc/Render/Render_Device.h @@ -0,0 +1,897 @@ +/************************************************************************************ + +Filename : Render_Device.h +Content : Platform renderer for simple scene graph +Created : September 6, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef OVR_Render_Device_h +#define OVR_Render_Device_h + +#include "Kernel/OVR_Math.h" +#include "Kernel/OVR_Array.h" +#include "Kernel/OVR_RefCount.h" +#include "Kernel/OVR_String.h" +#include "Kernel/OVR_File.h" + +#include "Util/Util_Render_Stereo.h" + +namespace OVR { namespace Render { + +using namespace OVR::Util::Render; + +class RenderDevice; + +//----------------------------------------------------------------------------------- + +enum PrimitiveType +{ + Prim_Triangles, + Prim_Lines, + Prim_TriangleStrip, + Prim_Points, + Prim_Unknown, + Prim_Count +}; + +class Fill : public RefCountBase +{ +public: + enum Flags + { + F_Solid = 1, + F_Wireframe = 2, + }; + + virtual ~Fill() {} + + virtual void Set(PrimitiveType prim = Prim_Unknown) const = 0; + virtual void Unset() const {} + + virtual void SetTexture(int i, class Texture* tex) { OVR_UNUSED2(i,tex); } +}; + +enum ShaderStage +{ + Shader_Vertex = 0, + Shader_Geometry = 1, + Shader_Fragment = 2, + Shader_Pixel = 2, + Shader_Count = 3, +}; + +enum BuiltinShaders +{ + VShader_MV = 0, + VShader_MVP = 1, + VShader_PostProcess = 2, + VShader_Count = 3, + + FShader_Solid = 0, + FShader_Gouraud = 1, + FShader_Texture = 2, + FShader_AlphaTexture = 3, + FShader_PostProcess = 4, + FShader_PostProcessWithChromAb = 5, + FShader_LitGouraud = 6, + FShader_LitTexture = 7, + FShader_MultiTexture = 8, + FShader_Count = 9, +}; + + +enum MapFlags +{ + Map_Discard = 1, + Map_Read = 2, // do not use + Map_Unsynchronized = 4, // like D3D11_MAP_NO_OVERWRITE +}; + +enum BufferUsage +{ + Buffer_Unknown = 0, + Buffer_Vertex = 1, + Buffer_Index = 2, + Buffer_Uniform = 4, + Buffer_Feedback = 8, + Buffer_TypeMask = 0xff, + Buffer_ReadOnly = 0x100, // Buffer must be created with Data(). +}; + +enum TextureFormat +{ + Texture_RGBA = 0x100, + Texture_R = 0x200, + Texture_DXT1 = 0x1100, + Texture_DXT3 = 0x1200, + Texture_DXT5 = 0x1300, + Texture_Depth = 0x8000, + Texture_TypeMask = 0xff00, + Texture_Compressed = 0x1000, + Texture_SamplesMask = 0x00ff, + Texture_RenderTarget = 0x10000, + Texture_GenMipmaps = 0x20000, +}; + +enum SampleMode +{ + Sample_Linear = 0, + Sample_Nearest = 1, + Sample_Anisotropic = 2, + Sample_FilterMask = 3, + + Sample_Repeat = 0, + Sample_Clamp = 4, + Sample_ClampBorder = 8, // If unsupported Clamp is used instead. + Sample_AddressMask =12, + + Sample_Count =13, +}; + +// A vector with a dummy w component for alignment in uniform buffers (and for float colors). +// The w component is not used in any calculations. + +struct Vector4f : public Vector3f +{ + float w; + + Vector4f() : w(1) {} + Vector4f(const Vector3f& v) : Vector3f(v), w(1) {} + Vector4f(float r, float g, float b, float a) : Vector3f(r,g,b), w(a) {} +}; + + +class Shader : public RefCountBase +{ + friend class ShaderSet; + +protected: + ShaderStage Stage; + +public: + Shader(ShaderStage s) : Stage(s) {} + virtual ~Shader() {} + + ShaderStage GetStage() const { return Stage; } + + virtual void Set(PrimitiveType) const { } + virtual void SetUniformBuffer(class Buffer* buffers, int i = 0) { OVR_UNUSED2(buffers, i); } + virtual bool UseTransposeMatrix() const { return 0; } + +protected: + virtual bool SetUniform(const char* name, int n, const float* v) { OVR_UNUSED3(name, n, v); return false; } +}; + + +// A group of shaders, one per stage. +// Some renderers subclass this, so CreateShaderSet must be used. + +class ShaderSet : public RefCountBase +{ + protected: + Ptr Shaders[Shader_Count]; + +public: + ShaderSet() { } + ~ShaderSet() { } + + virtual void SetShader(Shader *s) + { + Shaders[s->GetStage()] = s; + } + virtual void UnsetShader(int stage) + { + Shaders[stage] = NULL; + } + Shader* GetShader(int stage) { return Shaders[stage]; } + + virtual void Set(PrimitiveType prim) const + { + for (int i = 0; i < Shader_Count; i++) + if (Shaders[i]) + Shaders[i]->Set(prim); + } + + // Set a uniform (other than the standard matrices). It is undefined whether the + // uniforms from one shader occupy the same space as those in other shaders + // (unless a buffer is used, then each buffer is independent). + virtual bool SetUniform(const char* name, int n, const float* v) + { + bool result = 0; + for (int i = 0; i < Shader_Count; i++) + if (Shaders[i]) + result |= Shaders[i]->SetUniform(name, n, v); + + return result; + } + bool SetUniform1f(const char* name, float x) + { + const float v[] = {x}; + return SetUniform(name, 1, v); + } + bool SetUniform2f(const char* name, float x, float y) + { + const float v[] = {x,y}; + return SetUniform(name, 2, v); + } + bool SetUniform4f(const char* name, float x, float y, float z, float w = 1) + { + const float v[] = {x,y,z,w}; + return SetUniform(name, 4, v); + } + bool SetUniformv(const char* name, const Vector3f& v) + { + const float a[] = {v.x,v.y,v.z,1}; + return SetUniform(name, 4, a); + } + bool SetUniform4fv(const char* name, int n, const Vector4f* v) + { + return SetUniform(name, 4*n, &v[0].x); + } + virtual bool SetUniform4x4f(const char* name, const Matrix4f& m) + { + return SetUniform(name, 16, &m.M[0][0]); + } +}; + +class ShaderSetMatrixTranspose : public ShaderSet +{ +public: + virtual bool SetUniform4x4f(const char* name, const Matrix4f& m) + { + Matrix4f mt = m.Transposed(); + return SetUniform(name, 16, &mt.M[0][0]); + } +}; + +class ShaderFill : public Fill +{ + Ptr Shaders; + Ptr Textures[8]; + +public: + ShaderFill(ShaderSet* sh) : Shaders(sh) { } + ShaderFill(ShaderSet& sh) : Shaders(sh) { } + void Set(PrimitiveType prim) const; + ShaderSet* GetShaders() { return Shaders; } + + virtual void SetTexture(int i, class Texture* tex) { if (i < 8) Textures[i] = tex; } +}; + +/* Buffer for vertex or index data. Some renderers require separate buffers, so that + is recommended. Some renderers cannot have high-performance buffers which are readable, + so reading in Map should not be relied on. + + Constraints on buffers, such as ReadOnly, are not enforced by the api but may result in + rendering-system dependent undesirable behavior, such as terrible performance or unreported failure. + + Use of a buffer inconsistent with usage is also not checked by the api, but it may result in bad + performance or even failure. + + Use the Data() function to set buffer data the first time, if possible (it may be faster). +*/ + +class Buffer : public RefCountBase +{ +public: + virtual ~Buffer() {} + + virtual size_t GetSize() = 0; + virtual void* Map(size_t start, size_t size, int flags = 0) = 0; + virtual bool Unmap(void *m) = 0; + + // Allocates a buffer, optionally filling it with data. + virtual bool Data(int use, const void* buffer, size_t size) = 0; +}; + +class Texture : public RefCountBase +{ +public: + virtual ~Texture() {} + + virtual int GetWidth() const = 0; + virtual int GetHeight() const = 0; + virtual int GetSamples() const { return 1; } + + virtual void SetSampleMode(int sm) = 0; + virtual void Set(int slot, ShaderStage stage = Shader_Fragment) const = 0; +}; + + + +//----------------------------------------------------------------------------------- + +class CollisionModel : public RefCountBase +{ +public: + Array Planes; + + void Add(const Planef& p) + { + Planes.PushBack(p); + } + + // Return whether p is inside this + bool TestPoint(const Vector3f& p) const; + + // Assumes that the origin of the ray is outside this. + bool TestRay(const Vector3f& origin, const Vector3f& norm, float& len, Planef* ph = NULL) const; +}; + +class Node : public RefCountBase +{ + Vector3f Pos; + Quatf Rot; + + mutable Matrix4f Mat; + mutable bool MatCurrent; + +public: + Node() : Pos(Vector3f(0)), MatCurrent(1) { } + virtual ~Node() { } + + enum NodeType + { + Node_NonDisplay, + Node_Container, + Node_Model + }; + virtual NodeType GetType() const { return Node_NonDisplay; } + + virtual void ClearRenderer() { } + + const Vector3f& GetPosition() const { return Pos; } + const Quatf& GetOrientation() const { return Rot; } + void SetPosition(Vector3f p) { Pos = p; MatCurrent = 0; } + void SetOrientation(Quatf q) { Rot = q; MatCurrent = 0; } + + void Move(Vector3f p) { Pos += p; MatCurrent = 0; } + void Rotate(Quatf q) { Rot = q * Rot; MatCurrent = 0; } + + + // For testing only; causes Position an Orientation + void SetMatrix(const Matrix4f& m) + { + MatCurrent = true; + Mat = m; + } + + + const Matrix4f& GetMatrix() const + { + if (!MatCurrent) + { + Mat = Rot; + Mat = Matrix4f::Translation(Pos) * Mat; + MatCurrent = 1; + } + return Mat; + } + + virtual void Render(const Matrix4f& ltw, RenderDevice* ren) { OVR_UNUSED2(ltw, ren); } +}; + +struct Vertex +{ + Vector3f Pos; + Color C; + float U, V; + float U2, V2; + Vector3f Norm; + + Vertex (const Vector3f& p, const Color& c = Color(64,0,0,255), + float u = 0, float v = 0, Vector3f n = Vector3f(1,0,0)) + : Pos(p), C(c), U(u), V(v), Norm(n), U2(u), V2(v) {} + Vertex(float x, float y, float z, const Color& c = Color(64,0,0,255), + float u = 0, float v = 0) : Pos(x,y,z), C(c), U(u), V(v), U2(u), V2(v) { } + + // for multiple UV coords + Vertex(const Vector3f& p, const Color& c, + float u, float v, float u2, float v2, Vector3f n) : Pos(p), C(c), U(u), V(v), U2(u2), V2(v2), Norm(n) { } + + bool operator==(const Vertex& b) const + { + return Pos == b.Pos && C == b.C && U == b.U && V == b.V; + } +}; + +// this is stored in a uniform buffer, don't change it without fixing all renderers +struct LightingParams +{ + Vector4f Ambient; + Vector4f LightPos[8]; + Vector4f LightColor[8]; + float LightCount; + int Version; + + LightingParams() : LightCount(0), Version(0) {} + + void Update(const Matrix4f& view, const Vector4f* SceneLightPos); + + void Set(ShaderSet* s) const; +}; + +//----------------------------------------------------------------------------------- + +class Model : public Node +{ +public: + Array Vertices; + Array Indices; + PrimitiveType Type; + Ptr Fill; + bool Visible; + bool IsCollisionModel; + + // Some renderers will create these if they didn't exist before rendering. + // Currently they are not updated, so vertex data should not be changed after rendering. + Ptr VertexBuffer; + Ptr IndexBuffer; + + Model(PrimitiveType t = Prim_Triangles) : Type(t), Fill(NULL), Visible(true) { } + ~Model() { } + + virtual NodeType GetType() const { return Node_Model; } + + virtual void Render(const Matrix4f& ltw, RenderDevice* ren); + + PrimitiveType GetPrimType() const { return Type; } + + void SetVisible(bool visible) { Visible = visible; } + bool IsVisible() const { return Visible; } + + void ClearRenderer() + { + VertexBuffer.Clear(); + IndexBuffer.Clear(); + } + + // Returns the index next added vertex will have. + UInt16 GetNextVertexIndex() const + { + return (UInt16)Vertices.GetSize(); + } + + UInt16 AddVertex(const Vertex& v) + { + assert(!VertexBuffer && !IndexBuffer); + UInt16 index = (UInt16)Vertices.GetSize(); + Vertices.PushBack(v); + return index; + } + UInt16 AddVertex(const Vector3f& v, const Color& c, float u_ = 0, float v_ = 0) + { + return AddVertex(Vertex(v,c,u_,v_)); + } + UInt16 AddVertex(float x, float y, float z, const Color& c, float u, float v) + { + return AddVertex(Vertex(Vector3f(x,y,z),c, u,v)); + } + + void AddLine(UInt16 a, UInt16 b) + { + Indices.PushBack(a); + Indices.PushBack(b); + } + + UInt16 AddVertex(float x, float y, float z, const Color& c, + float u, float v, float nx, float ny, float nz) + { + return AddVertex(Vertex(Vector3f(x,y,z),c, u,v, Vector3f(nx,ny,nz))); + } + + UInt16 AddVertex(float x, float y, float z, const Color& c, + float u1, float v1, float u2, float v2, float nx, float ny, float nz) + { + return AddVertex(Vertex(Vector3f(x,y,z), c, u1, v1, u2, v2, Vector3f(nx,ny,nz))); + } + + void AddLine(const Vertex& a, const Vertex& b) + { + AddLine(AddVertex(a), AddVertex(b)); + } + + void AddTriangle(UInt16 a, UInt16 b, UInt16 c) + { + Indices.PushBack(a); + Indices.PushBack(b); + Indices.PushBack(c); + } + + + // Uses texture coordinates for uniform world scaling (must use a repeat sampler). + void AddSolidColorBox(float x1, float y1, float z1, + float x2, float y2, float z2, + Color c); + + + static Model* CreateAxisFaceColorBox(float x1, float x2, Color xcolor, + float y1, float y2, Color ycolor, + float z1, float z2, Color zcolor); + + + + // Uses texture coordinates for exactly covering each surface once. + static Model* CreateBox(Color c, Vector3f origin, Vector3f size); + static Model* CreateCylinder(Color c, Vector3f origin, float height, float radius, int sides = 20); + static Model* CreateCone(Color c, Vector3f origin, float height, float radius, int sides = 20); + static Model* CreateSphere(Color c, Vector3f origin, float radius, int sides = 20); + + // Grid having halfx,halfy lines in each direction from the origin + static Model* CreateGrid(Vector3f origin, Vector3f stepx, Vector3f stepy, + int halfx, int halfy, int nmajor = 5, + Color minor = Color(64,64,64,192), Color major = Color(128,128,128,192)); +}; + +class Container : public Node +{ +public: + Array > Nodes; + + ~Container() + { + + } + + void ClearRenderer() + { + for (UPInt i=0; i< Nodes.GetSize(); i++) + Nodes[i]->ClearRenderer(); + } + + virtual NodeType GetType() const { return Node_Container; } + + virtual void Render(const Matrix4f& ltw, RenderDevice* ren); + + void Add(Node *n) { Nodes.PushBack(n); } + void Add(Model *n, class Fill *f) { n->Fill = f; Nodes.PushBack(n); } + void Clear() { Nodes.Clear(); } + + bool CollideChildren; + + Container() : CollideChildren(1) {} +}; + +class Scene +{ +public: + Container World; + Vector4f LightPos[8]; + LightingParams Lighting; + Array > Models; + +public: + void Render(RenderDevice* ren, const Matrix4f& view); + + void SetAmbient(Vector4f color) + { + Lighting.Ambient = color; + } + void AddLight(Vector3f pos, Vector4f color) + { + int n = (int)Lighting.LightCount; + OVR_ASSERT(n < 8); + LightPos[n] = pos; + Lighting.LightColor[n] = color; + Lighting.LightCount++; + } + + void Clear() + { + World.Clear(); + Models.Clear(); + Lighting.Ambient = Vector4f(0.0f, 0.0f, 0.0f, 0.0f); + Lighting.LightCount = 0; + } + + void ClearRenderer() + { + World.ClearRenderer(); + } +}; + +class SceneView : public Node +{ +public: + Matrix4f GetViewMatrix() const; +}; + + +//----------------------------------------------------------------------------------- + +enum RenderCaps +{ + Cap_VertexBuffer = 1, +}; + +// Post-processing type to apply to scene after rendering. PostProcess_Distortion +// applied distortion as described by DistortionConfig. +enum PostProcessType +{ + PostProcess_None, + PostProcess_Distortion +}; + +enum DisplayMode +{ + Display_Window = 0, + Display_Fullscreen = 1, + Display_FakeFullscreen +}; + +struct DisplayId +{ + // Windows + String MonitorName; // Monitor name for fullscreen mode + + // MacOS + long CgDisplayId; // CGDirectDisplayID + + DisplayId() : CgDisplayId(0) {} + DisplayId(long id) : CgDisplayId(id) {} + DisplayId(String m, long id=0) : MonitorName(m), CgDisplayId(id) {} + + operator bool () const + { + return MonitorName.GetLength() || CgDisplayId; + } + + bool operator== (const DisplayId& b) const + { + return CgDisplayId == b.CgDisplayId && + (strstr(MonitorName.ToCStr(), b.MonitorName.ToCStr()) || + strstr(b.MonitorName.ToCStr(), MonitorName.ToCStr())); + } +}; + +struct RendererParams +{ + int Multisample; + int Fullscreen; + DisplayId Display; + + RendererParams(int ms = 1) : Multisample(ms), Fullscreen(0) {} + + bool IsDisplaySet() const + { + return Display; + } +}; + + + +//----------------------------------------------------------------------------------- +// ***** RenderDevice + +class RenderDevice : public RefCountBase +{ + friend class StereoGeomShaders; +protected: + int WindowWidth, WindowHeight; + RendererParams Params; + Viewport VP; + + Matrix4f Proj; + Ptr pTextVertexBuffer; + + + // For rendering with lens warping + PostProcessType CurPostProcess; + Ptr pSceneColorTex; + int SceneColorTexW; + int SceneColorTexH; + Ptr pPostProcessShader; + Ptr pFullScreenVertexBuffer; + float SceneRenderScale; + DistortionConfig Distortion; + Color DistortionClearColor; + UPInt TotalTextureMemoryUsage; + + // For lighting on platforms with uniform buffers + Ptr LightingBuffer; + + void FinishScene1(); + +public: + enum CompareFunc + { + Compare_Always = 0, + Compare_Less = 1, + Compare_Greater = 2, + Compare_Count + }; + RenderDevice(); + virtual ~RenderDevice() { Shutdown(); } + + // This static function is implemented in each derived class + // to support a specific renderer type. + //static RenderDevice* CreateDevice(const RendererParams& rp, void* oswnd); + + + virtual void Init() {} + virtual void Shutdown() {} + virtual bool SetParams(const RendererParams&) { return 0; } + + const RendererParams& GetParams() const { return Params; } + + + // StereoParams apply Viewport, Projection and Distortion simultaneously, + // doing full configuration for one eye. + void ApplyStereoParams(const StereoEyeParams& params) + { + SetViewport(params.VP); + SetProjection(params.Projection); + if (params.pDistortion) + SetDistortionConfig(*params.pDistortion, params.Eye); + } + + // Apply "orthographic" stereo parameters used for rendering 2D HUD overlays. + void ApplyStereoParams2D(const StereoEyeParams& params) + { + SetViewport(params.VP); + SetProjection(params.OrthoProjection); + if (params.pDistortion) + SetDistortionConfig(*params.pDistortion, params.Eye); + } + + + virtual void SetViewport(const Viewport& vp); + void SetViewport(int x, int y, int w, int h) { SetViewport(Viewport(x,y,w,h)); } + //virtual void SetScissor(int x, int y, int w, int h) = 0; + + // Set viewport ignoring any adjustments used for the stereo mode. + virtual void SetRealViewport(const Viewport& vp) { SetMultipleViewports(1, &vp); } + virtual void SetMultipleViewports(int n, const Viewport* vps) { OVR_UNUSED2(n, vps); } + + virtual void Clear(float r = 0, float g = 0, float b = 0, float a = 1, float depth = 1) = 0; + virtual void Rect(float left, float top, float right, float bottom) = 0; + + inline void Clear(const Color &c, float depth = 1) + { + float r, g, b, a; + c.GetRGBA(&r, &g, &b, &a); + Clear(r, g, b, a, depth); + } + + virtual bool IsFullscreen() const { return Params.Fullscreen != Display_Window; } + virtual void Present() = 0; + // Waits for rendering to complete; important for reducing latency. + virtual void ForceFlushGPU() { } + + // Resources + virtual Buffer* CreateBuffer() { return NULL; } + virtual Texture* CreateTexture(int format, int width, int height, const void* data, int mipcount=1) + { OVR_UNUSED5(format,width,height,data, mipcount); return NULL; } + + virtual bool GetSamplePositions(Render::Texture*, Vector3f* pos) { pos[0] = Vector3f(0); return 1; } + + virtual ShaderSet* CreateShaderSet() { return new ShaderSetMatrixTranspose; } + virtual Shader* LoadBuiltinShader(ShaderStage stage, int shader) = 0; + + // Rendering + + // Begin drawing directly to the currently selected render target, no post-processing. + virtual void BeginRendering() {} + // Begin drawing the primary scene. This will have post-processing applied (if enabled) + // during FinishScene. + virtual void BeginScene(PostProcessType pp = PostProcess_None); //StereoDisplay disp = Stereo_Center); + // Postprocess the scene and return to the screen render target. + virtual void FinishScene(); + + // Texture must have been created with Texture_RenderTarget. Use NULL for the default render target. + // NULL depth buffer means use an internal, temporary one. + virtual void SetRenderTarget(Texture* color, Texture* depth = NULL, Texture* stencil = NULL) + { OVR_UNUSED3(color, depth, stencil); } + virtual void SetDepthMode(bool enable, bool write, CompareFunc func = Compare_Less) = 0; + virtual void SetProjection(const Matrix4f& proj); + virtual void SetWorldUniforms(const Matrix4f& proj) = 0; + + // The data is not copied, it must remain valid until the end of the frame + virtual void SetLighting(const LightingParams* light); + + // The index 0 is reserved for non-buffer uniforms, and so cannot be used with this function. + virtual void SetCommonUniformBuffer(int i, Buffer* buffer) { OVR_UNUSED2(i, buffer); } + + virtual void SetExtraShaders(ShaderSet* s) { OVR_UNUSED(s); } + virtual Matrix4f GetProjection() const { return Proj; } + + // This is a View matrix only, it will be combined with the projection matrix from SetProjection + virtual void Render(const Matrix4f& matrix, Model* model) = 0; + // offset is in bytes; indices can be null. + virtual void Render(const Fill* fill, Buffer* vertices, Buffer* indices, + const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles) = 0; + virtual void RenderWithAlpha(const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices, + const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles) = 0; + + // Returns width of text in same units as drawing. If strsize is not null, stores width and height. + float MeasureText(const struct Font* font, const char* str, float size, float* strsize = NULL); + virtual void RenderText(const struct Font* font, const char* str, float x, float y, float size, Color c); + + virtual void FillRect(float left, float top, float right, float bottom, Color c); + + virtual Fill *CreateSimpleFill(int flags = Fill::F_Solid) = 0; + Fill * CreateTextureFill(Texture* tex, bool useAlpha = false); + + // PostProcess distortion + void SetSceneRenderScale(float ss); + + void SetDistortionConfig(const DistortionConfig& config, StereoEye eye = StereoEye_Left) + { + Distortion = config; + if (eye == StereoEye_Right) + Distortion.XCenterOffset = -Distortion.XCenterOffset; + } + + // Sets the color that is applied around distortion. + void SetDistortionClearColor(Color clearColor) + { + DistortionClearColor = clearColor; + } + + // Don't call these directly, use App/Platform instead + virtual bool SetFullscreen(DisplayMode fullscreen) { OVR_UNUSED(fullscreen); return false; } + virtual void SetWindowSize(int w, int h) { WindowWidth = w; WindowHeight = h; } + + UPInt GetTotalTextureMemoryUsage() const + { + return TotalTextureMemoryUsage; + } + + enum PostProcessShader + { + PostProcessShader_Distortion = 0, + PostProcessShader_DistortionAndChromAb = 1, + PostProcessShader_Count + }; + + PostProcessShader GetPostProcessShader() + { + return PostProcessShaderActive; + } + + void SetPostProcessShader(PostProcessShader newShader) + { + PostProcessShaderRequested = newShader; + } + +protected: + // Stereo & post-processing + virtual bool initPostProcessSupport(PostProcessType pptype); + + virtual Shader* CreateStereoShader(PrimitiveType prim, Shader* vs) + { OVR_UNUSED2(prim, vs); return NULL; } + +private: + PostProcessShader PostProcessShaderRequested; + PostProcessShader PostProcessShaderActive; +}; + +int GetNumMipLevels(int w, int h); +int GetTextureSize(int format, int w, int h); + +// Filter an rgba image with a 2x2 box filter, for mipmaps. +// Image size must be a power of 2. +void FilterRgba2x2(const UByte* src, int w, int h, UByte* dest); + +Texture* LoadTextureTga(RenderDevice* ren, File* f); +Texture* LoadTextureDDS(RenderDevice* ren, File* f); + +}} + +#endif diff --git a/Samples/CommonSrc/Render/Render_Font.h b/Samples/CommonSrc/Render/Render_Font.h new file mode 100644 index 0000000..0a7acb5 --- /dev/null +++ b/Samples/CommonSrc/Render/Render_Font.h @@ -0,0 +1,51 @@ +/************************************************************************************ + +Filename : Render_Font_h +Content : Font data structure used by renderer +Created : September, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef OVR_Render_Font_h +#define OVR_Render_Font_h + +namespace OVR { namespace Render { + +class Fill; + +struct Font +{ + struct Char + { + short x, y; // offset + short advance; + float u1, v1, u2, v2; + }; + + int lineheight, ascent, descent; + const Char* chars; + const short** kerning; + int twidth, theight; + const + unsigned char* tex; + mutable Fill* fill; +}; + +}} + +#endif diff --git a/Samples/CommonSrc/Render/Render_FontEmbed_DejaVu48.h b/Samples/CommonSrc/Render/Render_FontEmbed_DejaVu48.h new file mode 100644 index 0000000..0558866 --- /dev/null +++ b/Samples/CommonSrc/Render/Render_FontEmbed_DejaVu48.h @@ -0,0 +1,9463 @@ +#include "Render_Font.h" + +namespace OVR { namespace Render { + +const Font::Char DejaVu_chars[] = { + /* 0 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 1 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 2 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 3 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 4 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 5 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 6 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 7 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 8 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 9 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 10 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 11 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 12 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 13 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 14 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 15 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 16 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 17 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 18 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 19 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 20 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 21 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 22 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 23 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 24 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 25 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 26 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 27 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 28 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 29 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 30 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 31 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 32 */ { 0, 0, 15, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}, + /* 33 ! */ { 7,35, 19, 0.0009842520f, 0.0000000000f, 0.0059055118f, 0.2482269555f}, + /* 34 " */ { 5,35, 22, 0.0068897638f, 0.0000000000f, 0.0196850393f, 0.0921985805f}, + /* 35 # */ { 4,35, 40, 0.0206692908f, 0.0000000000f, 0.0531496070f, 0.2482269555f}, + /* 36 $ */ { 4,37, 31, 0.0541338585f, 0.0000000000f, 0.0757874027f, 0.3120567501f}, + /* 37 % */ { 3,36, 46, 0.0767716542f, 0.0000000000f, 0.1161417291f, 0.2624113560f}, + /* 38 & */ { 3,36, 37, 0.1171259806f, 0.0000000000f, 0.1496063024f, 0.2624113560f}, + /* 39 ' */ { 5,35, 13, 0.1505905539f, 0.0000000000f, 0.1545275599f, 0.0921985805f}, + /* 40 ( */ { 4,36, 19, 0.1555118114f, 0.0000000000f, 0.1663385779f, 0.3049645424f}, + /* 41 ) */ { 4,36, 19, 0.1673228294f, 0.0000000000f, 0.1781496108f, 0.3049645424f}, + /* 42 * */ { 2,36, 24, 0.1791338623f, 0.0000000000f, 0.1998031437f, 0.1560283750f}, + /* 43 + */ { 5,30, 40, 0.2007873952f, 0.0000000000f, 0.2303149551f, 0.2127659619f}, + /* 44 , */ { 4, 6, 15, 0.2312992066f, 0.0000000000f, 0.2372047305f, 0.0851063803f}, + /* 45 - */ { 2,15, 17, 0.2381889820f, 0.0000000000f, 0.2509842515f, 0.0283687934f}, + /* 46 . */ { 6, 6, 15, 0.2519685030f, 0.0000000000f, 0.2559055090f, 0.0425531901f}, + /* 47 / */ { 0,35, 16, 0.2568897605f, 0.0000000000f, 0.2726377845f, 0.2765957415f}, + /* 48 0 */ { 3,36, 31, 0.2736220360f, 0.0000000000f, 0.2972441018f, 0.2624113560f}, + /* 49 1 */ { 6,35, 31, 0.2982283533f, 0.0000000000f, 0.3179133832f, 0.2482269555f}, + /* 50 2 */ { 4,36, 31, 0.3188976347f, 0.0000000000f, 0.3415354192f, 0.2553191483f}, + /* 51 3 */ { 4,36, 31, 0.3425196707f, 0.0000000000f, 0.3651574850f, 0.2624113560f}, + /* 52 4 */ { 2,35, 31, 0.3661417365f, 0.0000000000f, 0.3917322755f, 0.2482269555f}, + /* 53 5 */ { 4,35, 31, 0.3927165270f, 0.0000000000f, 0.4153543413f, 0.2553191483f}, + /* 54 6 */ { 3,36, 31, 0.4163385928f, 0.0000000000f, 0.4399606287f, 0.2624113560f}, + /* 55 7 */ { 4,35, 31, 0.4409448802f, 0.0000000000f, 0.4635826647f, 0.2482269555f}, + /* 56 8 */ { 3,36, 31, 0.4645669162f, 0.0000000000f, 0.4881889820f, 0.2624113560f}, + /* 57 9 */ { 3,36, 31, 0.4891732335f, 0.0000000000f, 0.5127952695f, 0.2624113560f}, + /* 58 : */ { 6,25, 16, 0.5137795210f, 0.0000000000f, 0.5187007785f, 0.1773049682f}, + /* 59 ; */ { 4,25, 16, 0.5196850300f, 0.0000000000f, 0.5255905390f, 0.2198581547f}, + /* 60 < */ { 5,28, 40, 0.5265747905f, 0.0000000000f, 0.5561023355f, 0.1843971610f}, + /* 61 = */ { 5,22, 40, 0.5570865870f, 0.0000000000f, 0.5866141915f, 0.0992907807f}, + /* 62 > */ { 5,28, 40, 0.5875984430f, 0.0000000000f, 0.6171259880f, 0.1843971610f}, + /* 63 ? */ { 3,36, 25, 0.6181102395f, 0.0000000000f, 0.6368110180f, 0.2553191483f}, + /* 64 @ */ { 3,34, 48, 0.6377952695f, 0.0000000000f, 0.6791338325f, 0.2978723347f}, + /* 65 A */ { 0,35, 33, 0.6801180840f, 0.0000000000f, 0.7116141915f, 0.2482269555f}, + /* 66 B */ { 5,35, 33, 0.7125984430f, 0.0000000000f, 0.7372047305f, 0.2482269555f}, + /* 67 C */ { 3,36, 34, 0.7381889820f, 0.0000000000f, 0.7657480240f, 0.2624113560f}, + /* 68 D */ { 5,35, 37, 0.7667322755f, 0.0000000000f, 0.7952755690f, 0.2482269555f}, + /* 69 E */ { 5,35, 30, 0.7962598205f, 0.0000000000f, 0.8188976645f, 0.2482269555f}, + /* 70 F */ { 5,35, 28, 0.8198819160f, 0.0000000000f, 0.8395669460f, 0.2482269555f}, + /* 71 G */ { 3,36, 37, 0.8405511975f, 0.0000000000f, 0.8710629940f, 0.2624113560f}, + /* 72 H */ { 5,35, 36, 0.8720472455f, 0.0000000000f, 0.8986220360f, 0.2482269555f}, + /* 73 I */ { 5,35, 14, 0.8996062875f, 0.0000000000f, 0.9035432935f, 0.2482269555f}, + /* 74 J */ {-2,35, 14, 0.9045275450f, 0.0000000000f, 0.9153543115f, 0.3191489279f}, + /* 75 K */ { 5,35, 31, 0.9163385630f, 0.0000000000f, 0.9438976645f, 0.2482269555f}, + /* 76 L */ { 5,35, 27, 0.9448819160f, 0.0000000000f, 0.9665354490f, 0.2482269555f}, + /* 77 M */ { 5,35, 41, 0.9675197005f, 0.0000000000f, 0.9990157485f, 0.2482269555f}, + /* 78 N */ { 5,35, 36, 0.0000000000f, 0.3262411356f, 0.0255905520f, 0.5744680762f}, + /* 79 O */ { 3,36, 38, 0.0265748035f, 0.3262411356f, 0.0580708645f, 0.5886524916f}, + /* 80 P */ { 5,35, 29, 0.0590551198f, 0.3262411356f, 0.0816929117f, 0.5744680762f}, + /* 81 Q */ { 3,36, 38, 0.0826771632f, 0.3262411356f, 0.1141732261f, 0.6241135001f}, + /* 82 R */ { 5,35, 33, 0.1151574776f, 0.3262411356f, 0.1427165419f, 0.5744680762f}, + /* 83 S */ { 3,36, 30, 0.1437007934f, 0.3262411356f, 0.1692913324f, 0.5886524916f}, + /* 84 T */ { 0,35, 29, 0.1702755839f, 0.3262411356f, 0.1978346407f, 0.5744680762f}, + /* 85 U */ { 5,35, 35, 0.1988188922f, 0.3262411356f, 0.2253936976f, 0.5815602541f}, + /* 86 V */ { 0,35, 33, 0.2263779491f, 0.3262411356f, 0.2578740120f, 0.5744680762f}, + /* 87 W */ { 2,35, 47, 0.2588582635f, 0.3262411356f, 0.3021653593f, 0.5744680762f}, + /* 88 X */ { 1,35, 33, 0.3031496108f, 0.3262411356f, 0.3326771557f, 0.5744680762f}, + /* 89 Y */ { 0,35, 29, 0.3336614072f, 0.3262411356f, 0.3612204790f, 0.5744680762f}, + /* 90 Z */ { 2,35, 33, 0.3622047305f, 0.3262411356f, 0.3907480240f, 0.5744680762f}, + /* 91 [ */ { 4,36, 19, 0.3917322755f, 0.3262411356f, 0.4015747905f, 0.6312056780f}, + /* 92 \ */ { 0,35, 16, 0.4025590420f, 0.3262411356f, 0.4183070958f, 0.6028369069f}, + /* 93 ] */ { 5,36, 19, 0.4192913473f, 0.3262411356f, 0.4291338623f, 0.6312056780f}, + /* 94 ^ */ { 5,35, 40, 0.4301181138f, 0.3262411356f, 0.4596456587f, 0.4184397161f}, + /* 95 _ */ { 0,-7, 24, 0.4606299102f, 0.3262411356f, 0.4842519760f, 0.3546099365f}, + /* 96 ` */ { 4,38, 24, 0.4852362275f, 0.3262411356f, 0.4960629940f, 0.3900709152f}, + /* 97 a */ { 3,27, 29, 0.4970472455f, 0.3262411356f, 0.5187007785f, 0.5248227119f}, + /* 98 b */ { 4,36, 30, 0.5196850300f, 0.3262411356f, 0.5423228145f, 0.5886524916f}, + /* 99 c */ { 3,27, 26, 0.5433070660f, 0.3262411356f, 0.5639764071f, 0.5248227119f}, + /* 100 d */ { 3,36, 30, 0.5649606586f, 0.3262411356f, 0.5875984430f, 0.5886524916f}, + /* 101 e */ { 3,27, 30, 0.5885826945f, 0.3262411356f, 0.6122047305f, 0.5248227119f}, + /* 102 f */ { 1,36, 17, 0.6131889820f, 0.3262411356f, 0.6289370060f, 0.5815602541f}, + /* 103 g */ { 3,27, 30, 0.6299212575f, 0.3262411356f, 0.6525590420f, 0.5886524916f}, + /* 104 h */ { 4,36, 30, 0.6535432935f, 0.3262411356f, 0.6751968265f, 0.5815602541f}, + /* 105 i */ { 4,36, 13, 0.6761810780f, 0.3262411356f, 0.6801180840f, 0.5815602541f}, + /* 106 j */ {-1,36, 13, 0.6811023355f, 0.3262411356f, 0.6899606586f, 0.6524822712f}, + /* 107 k */ { 4,36, 28, 0.6909449100f, 0.3262411356f, 0.7135826945f, 0.5815602541f}, + /* 108 l */ { 4,36, 13, 0.7145669460f, 0.3262411356f, 0.7185039520f, 0.5815602541f}, + /* 109 m */ { 4,27, 47, 0.7194882035f, 0.3262411356f, 0.7568897605f, 0.5177304745f}, + /* 110 n */ { 4,27, 30, 0.7578740120f, 0.3262411356f, 0.7795275450f, 0.5177304745f}, + /* 111 o */ { 3,27, 29, 0.7805117965f, 0.3262411356f, 0.8041338325f, 0.5248227119f}, + /* 112 p */ { 4,27, 30, 0.8051180840f, 0.3262411356f, 0.8277559280f, 0.5886524916f}, + /* 113 q */ { 3,27, 30, 0.8287401795f, 0.3262411356f, 0.8513779640f, 0.5886524916f}, + /* 114 r */ { 4,27, 20, 0.8523622155f, 0.3262411356f, 0.8671259880f, 0.5177304745f}, + /* 115 s */ { 3,27, 25, 0.8681102395f, 0.3262411356f, 0.8877952695f, 0.5248227119f}, + /* 116 t */ { 1,33, 19, 0.8887795210f, 0.3262411356f, 0.9045275450f, 0.5602836609f}, + /* 117 u */ { 4,27, 30, 0.9055117965f, 0.3262411356f, 0.9271653295f, 0.5248227119f}, + /* 118 v */ { 1,26, 28, 0.9281495810f, 0.3262411356f, 0.9537401795f, 0.5106382966f}, + /* 119 w */ { 2,26, 39, 0.9547244310f, 0.3262411356f, 0.9891732335f, 0.5106382966f}, + /* 120 x */ { 1,26, 28, 0.0000000000f, 0.6595744491f, 0.0246062987f, 0.8439716101f}, + /* 121 y */ { 1,26, 28, 0.0255905520f, 0.6595744491f, 0.0511811040f, 0.9148936272f}, + /* 122 z */ { 2,26, 25, 0.0521653555f, 0.6595744491f, 0.0728346482f, 0.8439716101f}, + /* 123 { */ { 6,36, 31, 0.0738188997f, 0.6595744491f, 0.0915354341f, 0.9716312289f}, + /* 124 | */ { 6,36, 16, 0.0925196856f, 0.6595744491f, 0.0964566916f, 1.0000000000f}, + /* 125 } */ { 6,36, 31, 0.0974409431f, 0.6595744491f, 0.1151574776f, 0.9716312289f}, + /* 126 ~ */ { 5,20, 40, 0.1161417291f, 0.6595744491f, 0.1456692964f, 0.7234042287f}, + /* 127 */ { 0, 0, 0, 0.0000000000f, 0.0000000000f, 0.0000000000f, 0.0000000000f}} +; + +const short DejaVu_kern45[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -1, -2, 0, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0, 0, 1, + 0, 2, 0, 0, -4, 0, -3, -2, -2, -6, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern65[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, + 0, 1, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1, + 0, -1, 0, 0, -4, 0, -3, -3, 0, -4, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -1, -1, -1, -2, 0, 0, 0, 0, 0, 0, 0, 0, -1, + 0, -1, 0, 0, -1, 0, -3, -2, 0, -3, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern66[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1, + 0, 0, 0, -1, 0, 0, -1, -2, 0, -3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern67[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern68[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, -3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern70[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, 0, 0, 0, 0, + 0, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -4, 0, 0, 0, -3, 0, 0, 0, -3, 0, 0, 0, 0, 0, -2, + 0, 0, -3, 0, 0, -3, 0, 0, 0, -4, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern71[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -2, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern72[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern74[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern75[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -1, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, + 0, 0, 0, 0, -4, -1, 0, -2, 0, -2, 0, 0, 0, 0, 0, 0, + 0, -1, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, + 0, 0, 0, 0, 0, -2, 0, 0, 0, -3, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern76[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, + 0, 0, 0, 0, -7, -2, -5, -4, 0, -6, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, + 0, 0, 0, 0, 0, -1, 0, 0, 0, -4, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern79[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, + 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, -3, -3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern80[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -7, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, + 0, -2, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, -1, -2, + 0, 0, -1, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern81[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern82[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, + 0, -2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -3, 0, -3, -2, 0, -3, 0, 0, 0, 0, 0, 0, + 0, -1, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, + 0, 0, 0, 0, 0, -2, 0, 0, 0, -3, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern83[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern84[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, -6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -5, 0, 0, 0, 0, 0, + 0, -4, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -8, 0, -8, 0, -8, 0, 0, 0, -1, 0, 0, 0, 0, 0, -8, + 0, 0, -7, -8, 0, -7, 0, -8, 0, -7, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern85[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern86[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, 0, 0, 0, 0, + 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -4, 0, 0, 0, -4, 0, 0, 0, -1, 0, 0, 0, 0, 0, -4, + 0, 0, 0, 0, 0, -3, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern87[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 0, + 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -3, 0, 0, 0, -3, 0, 0, 0, -1, 0, 0, 0, 0, 0, -3, + 0, 0, -2, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern88[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, + 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern89[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, -10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 0, 0, 0, 0, 0, + 0, -4, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -7, 0, 0, 0, -6, 0, 0, 0, -2, 0, 0, 0, 0, 0, -6, + 0, 0, 0, 0, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern90[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern101[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern102[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern107[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -1, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, + 0, 0, 0, 0, 0, -1, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern111[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern114[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -4, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -1, -1, -1, 0, -1, -1, 0, 0, 0, 0, -1, -1, -1, + 0, -1, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern118[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -4, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern119[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern120[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short DejaVu_kern121[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -7, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ; +const short* DejaVu_kern[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, DejaVu_kern45, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, DejaVu_kern65, DejaVu_kern66, DejaVu_kern67, DejaVu_kern68, 0, DejaVu_kern70, DejaVu_kern71, DejaVu_kern72, 0, DejaVu_kern74, DejaVu_kern75, DejaVu_kern76, 0, 0, DejaVu_kern79, + DejaVu_kern80, DejaVu_kern81, DejaVu_kern82, DejaVu_kern83, DejaVu_kern84, DejaVu_kern85, DejaVu_kern86, DejaVu_kern87, DejaVu_kern88, DejaVu_kern89, DejaVu_kern90, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, DejaVu_kern101, DejaVu_kern102, 0, 0, 0, 0, DejaVu_kern107, 0, 0, 0, DejaVu_kern111, + 0, 0, DejaVu_kern114, 0, 0, 0, DejaVu_kern118, DejaVu_kern119, DejaVu_kern120, DejaVu_kern121, 0, 0, 0, 0, 0, 0}; + +const unsigned char DejaVu_tex[] = { + 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 135, 255, 255, 255, 72, 0, 0, 0, 0, 0, 0, 208, 255, 255, + 241, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 59, 154, 218, 244, 243, 217, 152, 55, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 247, 255, 255, 100, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, + 97, 166, 218, 239, 250, 236, 209, 160, 97, 18, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 138, 255, 255, 255, 177, 0, 178, 255, 255, 255, 137, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 87, 255, 255, 255, 217, 0, 0, 0, 0, 0, 0, 0, 0, 31, 122, 189, + 230, 249, 249, 230, 189, 122, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 44, 111, 178, 241, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 12, 69, 126, 169, 210, 233, 248, 246, 229, + 209, 159, 102, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 28, 94, 149, 191, 225, 241, 251, 238, 223, 187, 134, 67, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 109, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 29, 111, 175, 216, 241, 252, 247, 235, 209, 178, 135, 86, 28, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 15, 100, + 159, 209, 231, 247, 247, 231, 209, 159, 101, 16, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 14, 89, 159, 210, 232, 250, 243, 218, 172, + 100, 13, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 24, 114, 208, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 208, 114, 24, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 55, 123, 184, 220, 240, 250, 233, 207, + 151, 77, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 37, 99, 155, 192, 225, 240, 251, 246, 233, + 213, 174, 132, 68, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 219, 255, 255, 255, 255, 222, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 253, 241, 228, 200, 155, 100, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 72, 134, 184, 217, 240, 251, 247, + 231, 215, 180, 130, 79, 12, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 254, 247, 240, 222, 200, 170, 130, 83, 23, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 41, 108, 162, 199, 229, 244, 252, 243, 229, 214, 185, 140, 94, 38, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 145, 255, 255, 255, 255, 254, 114, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 210, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 211, 255, 255, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 197, 255, 255, 252, 13, 0, 0, 0, 0, 0, 18, 254, 255, 255, + 183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 160, 255, 255, 255, 255, 255, 255, 255, 254, 156, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175, 255, 255, 202, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 234, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 172, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 36, 249, 255, 255, 245, 31, 0, 32, 246, 255, 255, 248, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 165, 255, 255, 255, 139, 0, 0, 0, 0, 0, 0, 15, 152, 251, 255, 255, + 255, 255, 255, 255, 255, 255, 251, 151, 15, 0, 0, 0, 0, 0, 0, 33, + 99, 166, 232, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 39, 118, 197, 251, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 249, 165, 34, 0, 0, 0, 0, 0, 0, 0, 43, 129, 214, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 221, 102, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 34, 245, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 173, + 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 15, 148, 245, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 245, 149, 14, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 8, 118, 237, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 241, 118, 4, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 88, 182, 252, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 255, 255, 252, 182, 88, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 6, 106, 212, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 219, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 24, 125, 213, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 246, 166, 65, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, + 255, 255, 255, 255, 255, 255, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 250, 167, 31, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 31, 146, 239, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 245, 168, 73, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 208, 123, 25, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 111, + 215, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 216, + 136, 45, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 171, 255, 255, 255, 255, 249, 87, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 57, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, + 255, 255, 255, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 8, 249, 255, 255, 202, 0, 0, 0, 0, 0, 0, 79, 255, 255, 255, + 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, + 183, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 179, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 255, 255, 254, 54, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 176, 255, 255, 255, 120, 0, 0, 0, 122, 255, 255, 255, 173, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, + 238, 255, 255, 255, 61, 0, 0, 0, 0, 0, 35, 217, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 216, 34, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 243, 101, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 194, 21, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 192, 255, 255, 254, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 249, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 204, 0, 0, 0, 0, 51, 227, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 227, 51, 0, 0, 0, + 0, 0, 0, 0, 24, 201, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 191, 17, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 62, 156, 241, + 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 241, 155, 61, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 75, 218, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 254, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 27, 155, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 205, 70, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, + 255, 255, 255, 255, 255, 255, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 246, 88, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 131, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 199, 66, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 249, + 155, 22, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 97, 232, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 180, 60, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 17, 194, 255, 255, 255, 255, 240, 64, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 159, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, + 255, 255, 255, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 64, 255, 255, 255, 139, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, + 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, + 255, 255, 255, 241, 98, 15, 17, 102, 243, 255, 255, 255, 116, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 5, 216, 255, 255, 157, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 55, 255, 255, 255, 226, 7, 0, 0, 0, 9, 228, 255, 255, 255, + 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, + 255, 255, 255, 236, 3, 0, 0, 0, 0, 24, 224, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 223, 23, 0, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 114, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 213, + 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 109, 255, 255, 255, 148, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 2, 156, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 97, 0, 0, 0, 44, 237, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 237, 44, 0, 0, + 0, 0, 0, 23, 224, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 201, 10, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 129, 222, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 222, 129, 35, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 119, 244, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 176, 22, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 244, + 255, 255, 255, 255, 255, 255, 245, 14, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 86, 0, 0, 0, 0, 0, + 0, 0, 0, 28, 202, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 240, 85, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 15, 175, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 30, 213, 255, 255, 255, 255, 228, 45, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 245, 15, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 246, + 255, 255, 255, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 125, 255, 255, 255, 77, 0, 0, 0, 0, 0, 0, 206, 255, 255, 246, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 243, + 255, 255, 254, 65, 0, 0, 0, 0, 72, 255, 255, 255, 241, 12, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 117, 255, 255, 240, 22, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 246, 255, 255, 255, + 255, 177, 76, 22, 5, 18, 47, 101, 168, 243, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 184, 255, 255, 255, 97, 0, 0, 0, 0, 0, 101, 255, 255, 255, + 180, 0, 0, 0, 0, 0, 12, 194, 66, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 67, 194, 12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 255, 255, 255, + 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, + 255, 255, 255, 162, 0, 0, 0, 0, 0, 187, 255, 255, 255, 255, 228, 124, + 46, 15, 16, 47, 126, 229, 255, 255, 255, 255, 186, 0, 0, 0, 0, 255, + 255, 255, 255, 224, 160, 96, 32, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 201, 140, 84, 41, 21, 5, 18, + 52, 117, 219, 255, 255, 255, 255, 255, 78, 0, 0, 0, 0, 255, 255, 255, + 224, 157, 103, 61, 27, 12, 4, 17, 45, 94, 176, 253, 255, 255, 255, 255, + 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, + 244, 255, 255, 224, 12, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, 255, + 254, 185, 106, 46, 23, 3, 9, 22, 48, 82, 132, 182, 231, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 93, 255, 255, 255, 237, 9, 0, 0, 2, 204, 255, 255, 255, 255, 240, + 146, 70, 26, 7, 8, 27, 72, 150, 243, 255, 255, 255, 255, 203, 2, 0, + 0, 0, 4, 192, 255, 255, 255, 255, 218, 108, 40, 11, 11, 40, 108, 219, + 255, 255, 255, 255, 255, 158, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 16, 103, 197, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 209, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 115, 209, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 197, 103, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 218, 143, 76, 35, 11, 9, 35, 95, + 200, 255, 255, 255, 255, 241, 23, 0, 0, 0, 0, 0, 0, 0, 0, 27, + 193, 255, 255, 255, 255, 255, 255, 251, 187, 131, 77, 40, 23, 6, 9, 21, + 51, 89, 149, 217, 255, 255, 255, 255, 255, 255, 234, 66, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 255, + 255, 255, 224, 225, 255, 255, 255, 101, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 15, 40, 80, 148, 238, 255, 255, 255, 255, 237, 22, 0, 0, 0, 0, + 0, 0, 33, 224, 255, 255, 255, 255, 255, 227, 156, 86, 47, 27, 7, 15, + 36, 57, 108, 174, 238, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 3, 10, 25, 48, 85, 130, 191, 251, 255, 255, 255, + 255, 255, 255, 124, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 206, 255, 255, 255, 255, + 255, 241, 179, 116, 60, 39, 20, 4, 10, 24, 53, 95, 147, 217, 255, 255, + 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 46, 229, 255, 255, 255, 255, 212, 28, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 225, 255, 255, 255, 108, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 255, + 255, 255, 224, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 187, 255, 255, 253, 16, 0, 0, 0, 0, 0, 16, 253, 255, 255, 191, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, + 255, 255, 178, 0, 0, 0, 0, 0, 0, 183, 255, 255, 255, 103, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 25, 242, 255, 255, 110, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 252, + 93, 0, 0, 0, 0, 0, 0, 0, 0, 8, 92, 201, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 54, 255, 255, 255, 218, 3, 0, 0, 0, 0, 0, 3, 220, 255, 255, + 255, 51, 0, 0, 0, 0, 133, 255, 255, 160, 19, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 19, 161, 255, 255, 132, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 255, 255, 255, + 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, + 255, 255, 255, 85, 0, 0, 0, 0, 91, 255, 255, 255, 255, 183, 15, 0, + 0, 0, 0, 0, 0, 16, 185, 255, 255, 255, 255, 89, 0, 0, 0, 224, + 160, 96, 32, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 216, 120, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 124, 253, 255, 255, 255, 223, 9, 0, 0, 0, 212, 125, 39, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 214, 255, 255, 255, + 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 192, + 255, 255, 255, 71, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 255, 255, 255, 255, 207, + 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 200, 255, 255, 255, 141, 0, 0, 0, 84, 255, 255, 255, 255, 191, 24, + 0, 0, 0, 0, 0, 0, 0, 0, 27, 195, 255, 255, 255, 255, 84, 0, + 0, 0, 108, 255, 255, 255, 255, 149, 5, 0, 0, 0, 0, 0, 0, 6, + 152, 255, 255, 255, 255, 255, 66, 0, 0, 0, 255, 255, 255, 255, 255, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 77, 171, 248, 255, 255, 255, 255, 255, 255, 255, 255, + 231, 142, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 48, 142, 231, 255, 255, 255, 255, 255, 255, + 255, 255, 248, 170, 76, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 254, 173, 66, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 120, 255, 255, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 45, 227, + 255, 255, 255, 255, 255, 220, 113, 22, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 54, 157, 251, 255, 255, 255, 255, 249, 91, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 255, + 255, 255, 129, 130, 255, 255, 255, 199, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 14, 161, 255, 255, 255, 255, 118, 0, 0, 0, 0, + 0, 16, 218, 255, 255, 255, 255, 224, 95, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 80, 194, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 112, 226, 255, + 255, 255, 255, 255, 109, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 204, 255, 255, 255, 255, 235, + 121, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 150, + 242, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 65, 240, 255, 255, 255, 255, 193, 16, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 125, 255, 255, 255, 209, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 255, + 255, 255, 124, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 244, 255, 255, 207, 0, 0, 0, 0, 0, 0, 78, 255, 255, 255, 129, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 101, 166, 207, + 239, 255, 255, 255, 243, 222, 200, 168, 120, 72, 24, 0, 0, 0, 170, 255, + 255, 255, 90, 0, 0, 0, 0, 0, 0, 93, 255, 255, 255, 168, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 164, 255, 255, 210, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 255, 255, 140, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 167, 255, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 108, 255, 255, + 255, 162, 0, 0, 0, 0, 155, 254, 255, 255, 235, 93, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 93, 235, 255, 255, 254, 155, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, 255, 255, 253, + 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, + 255, 255, 249, 13, 0, 0, 0, 2, 219, 255, 255, 255, 199, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 4, 200, 255, 255, 255, 218, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 197, 80, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 104, 255, 255, 255, 255, 89, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 230, 255, 255, + 255, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, 255, + 255, 255, 159, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 233, 255, 255, 255, 190, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 49, 255, 255, 255, 254, 36, 0, 0, 0, 168, 255, 255, 255, 217, 10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 220, 255, 255, 255, 167, 0, + 0, 6, 226, 255, 255, 255, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 166, 255, 255, 255, 255, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 50, 144, 234, 255, 255, 255, 255, 255, 255, 255, 255, 247, 168, 74, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 74, 168, 247, 255, 255, 255, + 255, 255, 255, 255, 255, 233, 144, 50, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 187, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 156, 255, 255, 255, 198, 0, 0, 0, 0, 0, 0, 48, 240, 255, + 255, 255, 255, 236, 111, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 34, 180, 255, 255, 255, 255, 253, 85, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 255, 255, + 255, 254, 33, 34, 254, 255, 255, 255, 40, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 183, 255, 255, 255, 197, 0, 0, 0, 0, + 0, 176, 255, 255, 255, 255, 167, 7, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 73, 222, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 141, + 255, 255, 255, 255, 250, 53, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 162, 255, 255, 255, 255, 183, 15, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 12, 123, 246, 255, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 89, 249, 255, 255, 255, 255, 170, 7, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 26, 252, 255, 255, 255, + 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 255, 255, + 255, 251, 26, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 54, 255, 255, 255, 144, 0, 0, 0, 0, 0, 0, 141, 255, 255, 255, 67, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 167, 251, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 219, 255, + 255, 255, 39, 0, 0, 0, 0, 0, 0, 41, 255, 255, 255, 218, 0, 0, + 0, 0, 0, 0, 0, 0, 60, 255, 255, 255, 64, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 235, 255, 255, 255, 38, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 26, 250, 255, 255, 235, 9, 0, 0, 0, 0, 0, 0, 0, 11, 238, 255, + 255, 248, 22, 0, 0, 0, 0, 55, 198, 255, 255, 255, 186, 34, 0, 255, + 255, 255, 0, 35, 187, 255, 255, 255, 197, 54, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217, 255, 255, 186, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 118, 255, + 255, 255, 186, 0, 0, 0, 0, 77, 255, 255, 255, 248, 35, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 35, 248, 255, 255, 255, 75, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 184, 255, 255, 255, 169, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, + 255, 214, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 244, 255, + 255, 231, 17, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 155, 255, 255, 255, 214, 10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 156, 255, 255, 255, 184, 0, 0, 0, 0, 220, 255, 255, 255, 93, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, 220, 0, + 0, 77, 255, 255, 255, 239, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 16, 240, 255, 255, 255, 255, 61, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, + 118, 212, 255, 255, 255, 255, 255, 255, 255, 255, 254, 195, 100, 14, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 100, 194, 254, + 255, 255, 255, 255, 255, 255, 255, 255, 212, 117, 26, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 46, 255, 255, 255, 233, 0, 0, 0, 0, 0, 34, 233, 255, 255, + 255, 255, 186, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 99, 249, 255, 255, 255, 248, 61, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 255, 255, + 255, 190, 0, 0, 191, 255, 255, 255, 137, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 62, 255, 255, 255, 231, 0, 0, 0, 0, + 87, 255, 255, 255, 255, 145, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 11, 160, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 101, 255, 255, 255, 255, 202, 1, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 77, 255, 255, 255, 255, 151, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 32, 178, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 116, + 254, 255, 255, 255, 255, 144, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 177, 255, 255, 255, + 158, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, + 255, 176, 0, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 116, 255, 255, 255, 81, 0, 0, 0, 0, 0, 0, 205, 255, 255, 250, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 243, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 240, 255, + 255, 255, 11, 0, 0, 0, 0, 0, 0, 12, 255, 255, 255, 238, 0, 0, + 0, 0, 0, 0, 0, 3, 207, 255, 255, 168, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 255, 255, 255, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 124, 255, 255, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, + 255, 255, 119, 0, 0, 0, 0, 0, 0, 87, 224, 255, 255, 246, 120, 255, + 255, 255, 120, 246, 255, 255, 223, 87, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 255, 255, 255, 87, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 195, 255, + 255, 255, 108, 0, 0, 0, 0, 168, 255, 255, 255, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 160, 255, 255, 255, 166, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 78, 255, 255, 255, 217, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 255, 255, + 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 191, 255, 255, + 255, 81, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 22, 250, 255, 255, 255, 63, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, + 245, 255, 255, 255, 78, 0, 0, 0, 0, 244, 255, 255, 255, 25, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 255, 255, 255, 244, 0, + 0, 144, 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 144, 255, 255, 255, 255, 155, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 91, 186, 253, + 255, 255, 255, 255, 255, 255, 255, 255, 220, 127, 33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, + 127, 219, 255, 255, 255, 255, 255, 255, 255, 255, 253, 185, 91, 10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 8, 255, 255, 255, 249, 0, 0, 0, 0, 7, 206, 255, 255, 255, + 255, 143, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 241, 255, 255, 255, 224, 13, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 229, 255, 255, + 255, 92, 0, 0, 94, 255, 255, 255, 230, 3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 14, 255, 255, 255, 250, 0, 0, 0, 4, + 221, 255, 255, 255, 192, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 137, 255, 255, 255, 255, 79, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3, 216, 255, 255, 255, 195, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 1, 144, 255, + 255, 255, 255, 254, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 75, 255, 255, 255, + 245, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 246, 255, 255, + 255, 74, 0, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 177, 255, 255, 254, 20, 0, 0, 0, 0, 0, 15, 253, 255, 255, 199, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 81, 252, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 252, 255, + 255, 255, 2, 0, 0, 0, 0, 0, 0, 3, 255, 255, 255, 251, 0, 0, + 0, 0, 0, 0, 0, 106, 255, 255, 244, 28, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 255, 255, 255, 35, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 214, 255, 255, 255, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 255, + 255, 255, 210, 0, 0, 0, 0, 0, 0, 0, 8, 121, 242, 255, 255, 255, + 255, 255, 255, 255, 242, 121, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 255, 255, 237, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 252, 255, + 255, 255, 31, 0, 0, 0, 3, 241, 255, 255, 255, 52, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 53, 255, 255, 255, 239, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 22, 255, 255, 255, 241, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 255, 255, + 255, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255, 255, 255, + 170, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 117, 255, 255, 255, 180, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, + 255, 255, 255, 224, 2, 0, 0, 0, 0, 248, 255, 255, 255, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 255, 255, 255, 246, 0, + 0, 202, 255, 255, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 74, 255, 255, 255, 255, 232, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 65, 159, 242, 255, 255, 255, + 255, 255, 255, 255, 255, 239, 153, 59, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 59, 153, 239, 255, 255, 255, 255, 255, 255, 255, 255, 242, 159, 65, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 33, 255, 255, 255, 228, 0, 0, 0, 0, 142, 255, 255, 255, 255, + 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 247, 255, 255, 255, 155, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 255, 255, 255, + 241, 9, 0, 0, 10, 242, 255, 255, 255, 76, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 13, 255, 255, 255, 239, 0, 0, 0, 90, + 255, 255, 255, 242, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 211, 255, 255, 255, 187, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 85, 255, 255, 255, 242, 30, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 7, 170, 255, 255, + 255, 255, 249, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 2, 225, 255, 255, + 255, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, 255, 255, 255, + 225, 2, 0, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 80, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 16, 235, 255, 255, 255, 255, 217, 121, 56, + 14, 255, 255, 9, 28, 46, 65, 94, 141, 187, 233, 0, 0, 0, 240, 255, + 255, 255, 10, 0, 0, 0, 0, 0, 0, 12, 255, 255, 255, 238, 0, 0, + 0, 0, 0, 0, 19, 237, 255, 255, 121, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, 255, 255, 255, 125, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 48, + 255, 255, 255, 206, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 211, + 255, 255, 255, 44, 0, 0, 0, 0, 0, 0, 0, 0, 23, 154, 253, 255, + 255, 255, 253, 154, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 158, 255, 255, 147, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 255, 255, + 255, 209, 0, 0, 0, 0, 53, 255, 255, 255, 233, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 234, 255, 255, 255, 52, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 8, 255, 255, 255, 246, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 255, 255, + 255, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 244, 255, 255, 236, + 22, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 216, 255, 255, 255, 81, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 216, + 255, 255, 255, 121, 0, 0, 0, 0, 0, 224, 255, 255, 255, 25, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 255, 255, 255, 222, 0, + 0, 227, 255, 255, 255, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 27, 255, 255, 255, 255, 255, 45, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 39, 133, 225, 255, 255, 255, 255, 255, 255, + 255, 255, 251, 180, 86, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 85, 179, 251, 255, 255, 255, 255, 255, 255, 255, 255, + 225, 133, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 125, 255, 255, 255, 184, 0, 0, 0, 47, 252, 255, 255, 255, 169, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255, 255, 255, 254, + 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 255, + 153, 0, 0, 0, 0, 155, 255, 255, 255, 174, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 62, 255, 255, 255, 205, 0, 0, 0, 190, + 255, 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 89, 255, 255, 255, 252, 18, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 187, 255, 255, 255, 144, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 16, 193, 255, 255, 255, + 255, 241, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 126, 255, 255, + 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 255, 255, 255, + 125, 0, 0, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 80, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 117, 255, 255, 255, 254, 120, 3, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 219, 255, + 255, 255, 38, 0, 0, 0, 0, 0, 0, 41, 255, 255, 255, 218, 0, 0, + 0, 0, 0, 0, 153, 255, 255, 218, 6, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 241, + 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 127, + 255, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132, + 255, 255, 255, 122, 0, 0, 0, 0, 0, 0, 0, 0, 22, 153, 252, 255, + 255, 255, 252, 153, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 255, 255, 49, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, + 255, 132, 0, 0, 0, 0, 105, 255, 255, 255, 170, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 255, 255, 255, 103, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 39, 255, 255, 255, 224, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 255, 255, + 255, 175, 0, 0, 0, 0, 0, 0, 0, 0, 1, 190, 255, 255, 255, 92, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 34, 255, 255, 255, 239, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 255, + 255, 255, 249, 21, 0, 0, 0, 0, 0, 169, 255, 255, 255, 93, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, 167, 0, + 0, 246, 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 255, 255, 255, 255, 255, 98, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 206, 112, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 21, 111, 205, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 45, 245, 255, 255, 255, 103, 0, 0, 0, 184, 255, 255, 255, 222, 10, + 0, 0, 0, 0, 0, 0, 0, 5, 94, 176, 224, 248, 244, 212, 146, 42, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 185, 255, 255, 255, + 157, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 249, 255, 255, 255, + 55, 0, 0, 0, 0, 58, 255, 255, 255, 250, 21, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 181, 255, 255, 255, 145, 0, 0, 19, 253, + 255, 255, 254, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 228, 255, 255, 255, 87, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 17, 252, 255, 255, 254, 37, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 29, 212, 255, 255, 255, 255, + 229, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 27, 252, 255, + 255, 255, 55, 0, 0, 0, 0, 0, 0, 0, 0, 56, 255, 255, 255, 252, + 26, 0, 0, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 80, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 193, 255, 255, 255, 139, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 255, + 255, 255, 88, 0, 0, 0, 0, 0, 0, 91, 255, 255, 255, 169, 0, 0, + 0, 0, 0, 50, 253, 255, 255, 74, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 252, 255, 255, 255, + 209, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 198, + 255, 255, 255, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, + 255, 255, 255, 193, 0, 0, 0, 0, 0, 0, 7, 119, 242, 255, 255, 255, + 255, 255, 255, 255, 241, 118, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 242, 255, 255, + 255, 54, 0, 0, 0, 0, 149, 255, 255, 255, 117, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 255, 255, 255, 147, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 98, 255, 255, 255, 188, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 216, 255, 255, + 255, 89, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255, 255, 255, 181, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 106, 168, 216, 237, 250, 236, 219, 193, 138, 79, 12, 0, + 0, 0, 0, 0, 0, 0, 0, 87, 255, 255, 255, 183, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 255, + 255, 255, 164, 0, 0, 0, 0, 0, 0, 79, 255, 255, 255, 217, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 220, 255, 255, 255, 76, 0, + 0, 247, 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 255, 255, 255, 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 229, 139, 44, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 137, 228, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 41, 227, 255, 255, 255, 235, 13, 0, 0, 50, 255, 255, 255, 255, 71, 0, + 0, 0, 0, 0, 0, 0, 56, 215, 255, 255, 255, 255, 255, 255, 255, 253, + 141, 4, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 56, 255, 255, 255, + 248, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 255, 213, + 0, 0, 0, 0, 0, 0, 216, 255, 255, 255, 112, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 12, 157, 255, 255, 255, 253, 46, 0, 0, 86, 255, + 255, 255, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 164, 255, 255, 255, 139, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 85, 255, 255, 255, 200, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 44, 228, 255, 255, 255, 255, 213, + 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 177, 255, + 255, 255, 158, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 255, 176, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 232, 255, 255, 255, 33, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 255, + 255, 255, 172, 0, 0, 0, 0, 0, 0, 179, 255, 255, 255, 105, 0, 0, + 0, 0, 1, 198, 255, 255, 179, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, 255, + 255, 190, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 252, + 255, 255, 240, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, + 242, 255, 255, 250, 11, 0, 0, 0, 0, 84, 222, 255, 255, 246, 121, 255, + 255, 255, 121, 246, 255, 255, 222, 84, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 255, 255, 255, + 231, 1, 0, 0, 0, 0, 183, 255, 255, 255, 84, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 255, 255, 255, 180, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 199, 255, 255, 255, 119, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 194, 255, 255, 255, + 209, 4, 0, 0, 0, 0, 0, 0, 0, 32, 243, 255, 255, 241, 28, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 240, 140, + 28, 0, 0, 0, 0, 0, 0, 140, 255, 255, 255, 127, 0, 0, 0, 67, + 155, 212, 243, 249, 231, 210, 160, 92, 16, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 251, 255, + 255, 255, 58, 0, 0, 0, 0, 0, 0, 2, 199, 255, 255, 255, 190, 24, + 0, 0, 0, 0, 0, 0, 0, 0, 27, 195, 255, 255, 255, 196, 1, 0, + 0, 228, 255, 255, 255, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 27, 255, 255, 255, 255, 255, 179, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 229, 138, 44, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 136, 228, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, + 237, 255, 255, 255, 255, 97, 0, 0, 0, 155, 255, 255, 255, 190, 0, 0, + 0, 0, 0, 0, 0, 70, 245, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 176, 3, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 198, 255, 255, + 255, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 255, 255, 116, + 0, 0, 0, 0, 0, 0, 119, 255, 255, 255, 209, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 14, 38, 79, 146, 236, 255, 255, 255, 255, 141, 0, 0, 0, 139, 255, + 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 102, 255, 255, 255, 184, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 138, 255, 255, 255, 144, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 64, 240, 255, 255, 255, 255, 195, 17, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 75, 255, + 255, 255, 245, 14, 0, 0, 0, 0, 0, 0, 15, 245, 255, 255, 255, 74, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, + 255, 255, 237, 2, 0, 0, 0, 0, 0, 51, 255, 255, 255, 152, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 250, 255, 255, 255, 6, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 245, + 255, 255, 253, 60, 0, 0, 0, 0, 67, 255, 255, 255, 243, 14, 0, 0, + 0, 0, 95, 255, 255, 248, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 255, 255, 255, + 255, 255, 183, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 255, + 255, 255, 189, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 193, 255, 255, 255, 60, 0, 0, 52, 195, 255, 255, 255, 187, 35, 0, 255, + 255, 255, 0, 35, 188, 255, 255, 255, 194, 51, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 255, 255, 255, + 155, 0, 0, 0, 0, 0, 211, 255, 255, 255, 51, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 255, 255, 255, 209, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 74, 255, 255, 255, 254, 38, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 12, 36, 82, 158, 245, 255, 255, 255, 237, + 41, 0, 0, 0, 0, 0, 0, 0, 1, 190, 255, 255, 255, 103, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 241, 92, 0, 0, 0, 0, 0, 188, 255, 255, 255, 78, 0, 48, 201, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 240, 127, 11, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 255, 255, + 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 33, 231, 255, 255, 255, 240, + 144, 69, 24, 6, 7, 25, 71, 148, 242, 255, 255, 255, 229, 30, 0, 0, + 0, 206, 255, 255, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 74, 255, 255, 255, 255, 255, 208, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 205, 111, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 21, 110, 204, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 142, 254, + 255, 255, 255, 255, 171, 0, 0, 0, 7, 240, 255, 255, 255, 72, 0, 0, + 0, 0, 0, 0, 32, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 140, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 124, 255, 255, + 255, 151, 0, 0, 0, 0, 0, 0, 0, 0, 50, 255, 255, 255, 251, 23, + 0, 0, 0, 0, 0, 0, 26, 252, 255, 255, 255, 51, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 254, 143, 2, 0, 0, 0, 183, 255, + 255, 255, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 53, 255, 255, 255, 213, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 183, 255, 255, 255, 88, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 87, 249, 255, 255, 255, 255, 173, 8, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 2, 226, + 255, 255, 255, 106, 0, 0, 0, 0, 0, 0, 107, 255, 255, 255, 225, 2, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 254, 255, 255, 255, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, + 255, 255, 168, 0, 0, 0, 0, 0, 0, 122, 255, 255, 255, 81, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 235, 255, 255, 255, 41, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, + 255, 255, 255, 238, 94, 15, 16, 98, 241, 255, 255, 255, 122, 0, 0, 0, + 0, 14, 231, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 252, 255, 255, 255, + 255, 255, 255, 179, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 255, + 255, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 141, 255, 255, 255, 111, 0, 152, 254, 255, 255, 235, 94, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 94, 235, 255, 255, 254, 151, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 255, 255, 255, + 77, 0, 0, 0, 0, 0, 230, 255, 255, 255, 28, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 255, 255, 255, 228, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 17, 227, 255, 255, 255, 172, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 209, 40, + 0, 0, 0, 0, 0, 0, 0, 0, 106, 255, 255, 255, 191, 1, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 145, 0, 0, 0, 0, 209, 255, 255, 255, 51, 87, 247, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 210, 32, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 230, 255, 255, + 255, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 202, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 200, 31, 0, 0, 0, + 0, 152, 255, 255, 255, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 143, 255, 255, 255, 255, 255, 227, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 39, 134, 225, 255, 255, 255, 255, 255, 255, + 255, 255, 251, 179, 85, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 84, 178, 251, 255, 255, 255, 255, 255, 255, 255, 255, + 225, 134, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 206, 255, 255, + 255, 255, 255, 207, 12, 0, 0, 0, 67, 255, 255, 255, 231, 2, 0, 0, + 0, 0, 0, 0, 182, 255, 255, 255, 255, 226, 112, 40, 9, 11, 42, 116, + 230, 255, 254, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 60, 255, 255, + 255, 202, 0, 0, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 177, 0, + 0, 0, 0, 0, 0, 0, 0, 181, 255, 255, 255, 148, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 247, 144, 43, 0, 0, 0, 0, 0, 213, 255, + 255, 255, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 35, 255, 255, 255, 236, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 213, 255, 255, 255, 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 254, 255, 255, 255, 255, 146, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 127, + 255, 255, 255, 208, 0, 0, 0, 0, 0, 0, 209, 255, 255, 255, 126, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 241, 255, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, + 255, 255, 96, 0, 0, 0, 0, 0, 0, 192, 255, 255, 251, 14, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 197, 255, 255, 255, 157, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, + 189, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 185, 3, 0, 0, 0, + 0, 141, 255, 255, 226, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 254, 255, 255, 245, 214, + 255, 255, 255, 255, 175, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 158, 255, + 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 98, 255, 255, 255, 156, 0, 134, 255, 255, 161, 19, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 20, 162, 255, 255, 133, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 255, 246, + 9, 0, 0, 0, 0, 0, 244, 255, 255, 255, 18, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 255, 255, 255, 243, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 181, 255, 255, 255, 246, 35, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 207, 91, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 32, 243, 255, 255, 246, 35, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 245, 179, 112, 67, 29, 13, 4, 19, 40, 91, 157, 244, 255, 255, + 255, 255, 255, 125, 0, 0, 0, 225, 255, 255, 255, 97, 251, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 234, 31, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 255, 255, 255, + 240, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 86, 196, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 196, 85, 1, 0, 0, 0, 0, + 0, 87, 255, 255, 255, 238, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 15, 240, 255, 255, 255, 255, 255, 242, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 66, 160, 243, 255, 255, 255, + 255, 255, 255, 255, 255, 239, 152, 58, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 58, 152, 238, 255, 255, 255, 255, 255, 255, 255, 255, 243, 160, 66, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 196, 255, 255, 255, + 255, 255, 209, 19, 0, 0, 0, 0, 130, 255, 255, 255, 153, 0, 0, 0, + 0, 0, 0, 44, 255, 255, 255, 255, 185, 14, 0, 0, 0, 0, 0, 0, + 18, 193, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 29, 255, 255, + 255, 225, 0, 0, 0, 0, 0, 0, 0, 7, 237, 255, 255, 255, 80, 0, + 0, 0, 0, 0, 0, 0, 0, 83, 255, 255, 255, 238, 7, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 239, 135, 14, 0, 0, 0, 0, 237, 255, + 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 18, 255, 255, 255, 246, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 237, 255, 255, 255, 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 230, 18, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 28, + 252, 255, 255, 255, 55, 0, 0, 0, 0, 56, 255, 255, 255, 252, 27, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 226, 255, 255, 255, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 184, 255, + 255, 255, 25, 0, 0, 0, 0, 0, 12, 250, 255, 255, 195, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 119, 255, 255, 255, 255, 152, 16, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 166, 255, 255, 255, 255, 255, 255, 255, 255, 162, 7, 0, 0, 0, 0, + 41, 250, 255, 255, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 252, 255, 255, 255, 114, 22, + 213, 255, 255, 255, 255, 170, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 22, 255, 255, 255, 236, 0, 0, 0, 0, 0, 0, 0, 0, 189, 255, + 255, 255, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 67, 255, 255, 255, 187, 0, 13, 195, 67, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 67, 195, 12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 255, 255, 255, 178, + 0, 0, 0, 0, 0, 0, 251, 255, 255, 255, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 255, 255, 255, 249, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 143, 255, 255, 255, 255, 111, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 195, 78, 0, + 0, 0, 0, 0, 0, 0, 1, 189, 255, 255, 255, 114, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 202, 95, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 142, 253, + 255, 255, 255, 255, 57, 0, 0, 240, 255, 255, 255, 239, 255, 255, 255, 255, + 218, 107, 39, 11, 11, 40, 108, 218, 255, 255, 255, 255, 205, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, + 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 174, 248, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 248, 174, 51, 0, 0, 0, 0, + 0, 12, 237, 255, 255, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 163, 255, 255, 255, 255, 255, 255, 249, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 92, 187, 253, + 255, 255, 255, 255, 255, 255, 255, 255, 219, 126, 33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, + 126, 219, 255, 255, 255, 255, 255, 255, 255, 255, 253, 186, 92, 10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 255, + 255, 206, 20, 0, 0, 0, 0, 0, 178, 255, 255, 255, 95, 0, 0, 0, + 0, 0, 0, 131, 255, 255, 255, 225, 13, 0, 0, 0, 0, 0, 0, 0, + 0, 17, 229, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 8, 255, 255, + 255, 246, 0, 0, 0, 0, 0, 0, 0, 86, 255, 255, 255, 233, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 6, 236, 255, 255, 255, 87, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 219, 38, 0, 0, 0, 247, 255, + 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4, 255, 255, 255, 253, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 248, 255, 255, 255, 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 201, 255, 255, 255, 255, 208, 21, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 178, 255, 255, 255, 157, 0, 0, 0, 0, 158, 255, 255, 255, 177, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 210, 255, 255, 255, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 247, 255, + 255, 208, 0, 0, 0, 0, 0, 0, 77, 255, 255, 255, 124, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 14, 233, 255, 255, 255, 255, 244, 162, 90, + 37, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 62, 157, 219, 244, 244, 219, 155, 58, 0, 0, 0, 0, 0, 0, + 188, 255, 255, 189, 0, 0, 0, 0, 0, 0, 57, 153, 218, 243, 244, 218, + 154, 58, 0, 0, 0, 0, 0, 0, 17, 233, 255, 255, 255, 201, 2, 0, + 23, 214, 255, 255, 255, 255, 166, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 67, 255, 255, 255, 196, 0, 0, 0, 0, 0, 0, 0, 0, 219, 255, + 255, 255, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 36, 255, 255, 255, 218, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, 255, 255, 255, 101, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 255, 255, 255, 254, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 117, 255, 255, 255, 255, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 174, + 14, 0, 0, 0, 0, 0, 105, 255, 255, 255, 200, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, + 249, 255, 255, 255, 198, 0, 0, 253, 255, 255, 255, 255, 255, 255, 255, 146, + 5, 0, 0, 0, 0, 0, 0, 5, 150, 255, 255, 255, 255, 119, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 255, 255, 255, 254, + 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 140, 254, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 141, 4, 0, 0, + 0, 0, 127, 255, 255, 255, 255, 146, 5, 0, 0, 0, 0, 0, 0, 5, + 149, 255, 255, 255, 255, 255, 255, 255, 253, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, + 119, 212, 255, 255, 255, 255, 255, 255, 255, 255, 254, 194, 99, 14, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 99, 193, 254, + 255, 255, 255, 255, 255, 255, 255, 255, 212, 118, 26, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 249, 255, 255, 255, 255, + 149, 7, 0, 0, 0, 0, 0, 0, 215, 255, 255, 255, 48, 0, 0, 0, + 0, 0, 0, 192, 255, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 114, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 10, 255, 255, + 255, 242, 0, 0, 0, 0, 0, 0, 0, 184, 255, 255, 255, 141, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 145, 255, 255, 255, 185, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 24, 61, 113, 200, 255, 255, 255, 255, 223, 20, 0, 0, 254, 255, + 255, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 19, 255, 255, 255, 246, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 254, 255, 255, 255, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 14, 196, 255, 255, 255, 255, 213, 24, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 76, 255, 255, 255, 244, 14, 0, 0, 15, 245, 255, 255, 255, 75, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 194, 255, 255, 255, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, + 255, 136, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 53, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 65, 248, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 120, 64, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, + 255, 255, 251, 42, 0, 0, 0, 0, 6, 159, 255, 255, 255, 255, 255, 255, + 255, 255, 160, 6, 0, 0, 0, 0, 147, 255, 255, 255, 251, 47, 0, 0, + 0, 24, 216, 255, 255, 255, 255, 161, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 113, 255, 255, 255, 157, 0, 0, 0, 0, 0, 0, 0, 0, 237, 255, + 255, 255, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 21, 255, 255, 255, 235, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 254, 255, 255, 254, 25, + 0, 0, 0, 0, 0, 0, 251, 255, 255, 255, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 255, 255, 255, 250, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 106, 255, 255, 255, 255, 205, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 4, 19, 45, 95, 173, 250, 255, 255, 255, 255, + 194, 8, 0, 0, 0, 32, 243, 255, 255, 249, 43, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 97, 255, 255, 255, 255, 55, 0, 251, 255, 255, 255, 255, 255, 255, 160, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 255, 255, 255, 233, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, 188, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 165, 255, 255, 255, 255, 237, + 140, 72, 26, 8, 9, 27, 73, 141, 238, 255, 255, 255, 255, 165, 1, 0, + 0, 0, 11, 212, 255, 255, 255, 255, 217, 107, 38, 11, 11, 39, 107, 218, + 255, 255, 255, 255, 236, 255, 255, 255, 240, 0, 255, 255, 255, 255, 255, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 51, 145, 234, 255, 255, 255, 255, 255, 255, 255, 255, 246, 167, 73, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 73, 167, 246, 255, 255, 255, + 255, 255, 255, 255, 255, 234, 145, 51, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126, 255, 255, 255, 255, 105, + 0, 0, 0, 0, 0, 0, 0, 0, 239, 255, 255, 255, 21, 0, 0, 0, + 0, 0, 0, 228, 255, 255, 255, 39, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 41, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 28, 255, 255, + 255, 220, 0, 0, 0, 0, 0, 0, 28, 253, 255, 255, 255, 43, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 47, 255, 255, 255, 253, 28, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 76, 237, 255, 255, 255, 162, 0, 0, 248, 255, + 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 255, 255, 255, 236, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 248, 255, 255, 255, 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 11, 190, 255, 255, 255, 255, 218, 28, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 3, 226, 255, 255, 255, 106, 0, 0, 107, 255, 255, 255, 226, 2, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 179, 255, 255, 255, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, + 255, 64, 0, 0, 0, 0, 0, 0, 217, 255, 255, 236, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 221, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 248, 180, 91, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 225, + 255, 255, 142, 0, 0, 0, 0, 3, 183, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 183, 3, 0, 0, 20, 246, 255, 255, 255, 141, 0, 0, 0, + 0, 0, 25, 217, 255, 255, 255, 255, 157, 1, 0, 0, 0, 0, 0, 0, + 0, 186, 255, 255, 255, 103, 0, 0, 0, 0, 0, 0, 0, 0, 247, 255, + 255, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10, 255, 255, 255, 245, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 255, 255, 255, 202, 0, + 0, 0, 0, 0, 0, 0, 244, 255, 255, 255, 18, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 255, 255, 255, 243, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, + 254, 255, 255, 255, 214, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 184, 255, 255, 255, + 255, 139, 0, 0, 1, 188, 255, 255, 255, 125, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 198, 255, 255, 255, 130, 0, 244, 255, 255, 255, 255, 255, 238, 14, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 240, 255, 255, 255, 83, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 242, 255, 255, 255, 81, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 255, 255, 255, 157, 12, + 0, 0, 0, 0, 0, 0, 0, 0, 13, 160, 255, 255, 255, 255, 111, 0, + 0, 0, 0, 38, 239, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 249, 84, 255, 255, 255, 224, 0, 255, 255, 255, 255, 255, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 78, 172, 248, 255, 255, 255, 255, 255, 255, 255, 255, + 231, 141, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 47, 141, 231, 255, 255, 255, 255, 255, 255, + 255, 255, 248, 171, 77, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 189, 255, 255, 255, 150, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 250, 255, 255, 255, 6, 0, 0, 0, + 0, 0, 0, 247, 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 10, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 72, 255, 255, + 255, 196, 0, 0, 0, 0, 0, 0, 123, 255, 255, 255, 202, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 206, 255, 255, 255, 123, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 51, 250, 255, 255, 254, 38, 0, 237, 255, + 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 55, 255, 255, 255, 212, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 238, 255, 255, 255, 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 9, 184, 255, 255, 255, 255, 223, + 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 127, 255, 255, 255, 207, 0, 0, 208, 255, 255, 255, 126, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 163, 255, 255, 255, 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 76, 0, 0, 0, 0, 0, 0, 10, 108, 218, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 225, 89, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 255, + 255, 232, 14, 0, 0, 0, 0, 119, 255, 255, 255, 240, 99, 17, 16, 99, + 242, 255, 255, 255, 118, 0, 0, 105, 255, 255, 255, 243, 17, 0, 0, 0, + 0, 0, 0, 26, 219, 255, 255, 255, 255, 152, 1, 0, 0, 0, 0, 0, + 13, 249, 255, 255, 255, 28, 0, 0, 0, 0, 0, 0, 0, 0, 253, 255, + 255, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 255, 255, 255, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 255, 255, 255, 124, 0, + 0, 0, 0, 0, 0, 0, 230, 255, 255, 255, 27, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 255, 255, 255, 228, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 254, + 255, 255, 255, 220, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 160, 255, 255, + 255, 251, 30, 0, 104, 255, 255, 255, 209, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 102, 255, 255, 255, 195, 0, 229, 255, 255, 255, 255, 255, 141, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 148, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 255, 255, 255, 227, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 17, 242, 255, 255, 255, 142, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 255, 255, 255, 242, 16, + 0, 0, 0, 0, 39, 216, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 244, 84, 52, 255, 255, 255, 208, 0, 255, 255, 255, 255, 255, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 17, 104, 198, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 209, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 115, 209, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 198, 104, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 255, 255, 255, 57, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 250, 255, 255, 255, 6, 0, 0, 0, + 0, 0, 0, 247, 255, 255, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 10, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 136, 255, 255, + 255, 136, 0, 0, 0, 0, 0, 0, 219, 255, 255, 255, 104, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 255, 255, 255, 219, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 149, 255, 255, 255, 129, 0, 214, 255, + 255, 255, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 105, 255, 255, 255, 183, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 214, 255, 255, 255, 49, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 7, 178, 255, 255, 255, 255, + 227, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 28, 252, 255, 255, 255, 54, 55, 255, 255, 255, 252, 27, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 148, 255, 255, 255, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 122, 179, + 230, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 247, 255, + 255, 95, 0, 0, 0, 0, 14, 243, 255, 255, 254, 63, 0, 0, 0, 0, + 69, 255, 255, 255, 242, 13, 0, 175, 255, 255, 255, 143, 0, 0, 0, 0, + 0, 0, 0, 0, 28, 220, 255, 255, 255, 255, 147, 0, 0, 0, 0, 0, + 100, 255, 255, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 255, + 255, 255, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 11, 255, 255, 255, 245, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 246, 255, 255, 255, 47, 0, + 0, 0, 0, 0, 0, 0, 211, 255, 255, 255, 50, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 255, 255, 255, 209, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 254, 255, + 255, 255, 225, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 218, 255, + 255, 255, 127, 0, 243, 255, 255, 252, 51, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 38, 255, 255, 255, 221, 0, 209, 255, 255, 255, 255, 255, 72, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 255, 255, 255, 204, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 255, 255, 255, 125, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 109, 255, 255, 255, 215, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 217, 255, 255, 255, 109, + 0, 0, 0, 0, 0, 14, 133, 242, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 203, 38, 0, 79, 255, 255, 255, 187, 0, 255, 255, 255, 255, 255, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 130, 222, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 222, 130, 36, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 255, 13, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 239, 255, 255, 255, 18, 0, 0, 0, + 0, 0, 0, 229, 255, 255, 255, 39, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 40, 255, 255, 255, 255, 0, 0, 0, 0, 0, 4, 230, 255, 255, + 255, 59, 0, 0, 0, 0, 0, 62, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 190, 0, 184, 255, + 255, 255, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 166, 255, 255, 255, 137, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 183, 255, 255, 255, 91, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 5, 171, 255, 255, 255, + 255, 230, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 179, 255, 255, 255, 156, 157, 255, 255, 255, 178, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 132, 255, 255, 255, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 151, 208, 255, 255, 255, 255, 255, 255, 146, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, 255, 255, + 198, 1, 0, 0, 0, 0, 103, 255, 255, 255, 176, 0, 0, 0, 0, 0, + 0, 181, 255, 255, 255, 103, 0, 222, 255, 255, 255, 67, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 29, 222, 255, 255, 255, 255, 143, 0, 0, 0, 0, + 212, 255, 255, 255, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 236, 255, + 255, 255, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 21, 255, 255, 255, 235, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 255, 255, 255, 225, 0, 0, + 0, 0, 0, 0, 0, 0, 184, 255, 255, 255, 83, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 255, 255, 255, 181, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 254, 255, 255, + 255, 228, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 255, + 255, 255, 191, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 12, 255, 255, 255, 244, 0, 180, 255, 255, 255, 255, 255, 26, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 255, 255, 255, 226, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 255, 255, 255, 250, 24, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 181, 255, 255, 255, 105, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 255, 255, 255, 180, + 0, 0, 0, 0, 0, 0, 0, 18, 94, 162, 212, 233, 250, 244, 214, 158, + 72, 1, 0, 0, 128, 255, 255, 255, 139, 0, 255, 255, 255, 255, 255, 0, + 0, 21, 255, 255, 255, 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 63, 157, 241, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 241, 156, 62, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 219, 255, 255, 255, 48, 0, 0, 0, + 0, 0, 0, 194, 255, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 111, 255, 255, 255, 255, 0, 0, 0, 0, 0, 119, 255, 255, 255, + 219, 3, 0, 0, 0, 0, 0, 159, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 21, 255, 255, 255, 231, 0, 140, 255, + 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 230, 255, 255, 255, 84, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 149, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 3, 164, 255, 255, + 255, 255, 234, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 77, 255, 255, 255, 244, 244, 255, 255, 255, 76, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 29, 135, 247, 255, 255, 255, 254, 52, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, 253, + 51, 0, 0, 0, 0, 0, 169, 255, 255, 255, 89, 0, 0, 0, 0, 0, + 0, 93, 255, 255, 255, 168, 0, 245, 255, 255, 255, 18, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 30, 223, 255, 255, 255, 255, 137, 0, 0, 77, + 255, 255, 255, 246, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 255, + 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 38, 255, 255, 255, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 255, 255, 255, 148, 0, 0, + 0, 0, 0, 0, 0, 0, 150, 255, 255, 255, 116, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 255, 255, 255, 148, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 255, 255, 255, 255, + 231, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, + 255, 255, 233, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 12, 255, 255, 255, 245, 0, 146, 255, 255, 255, 255, 255, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 255, 255, 255, 246, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 167, 255, 255, 255, 168, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 227, 255, 255, 255, 38, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 255, 255, 255, 227, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 184, 255, 255, 255, 86, 0, 255, 255, 255, 255, 255, 0, + 0, 86, 255, 255, 255, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 89, 183, 252, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 252, 183, 89, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 184, 255, 255, 255, 89, 0, 0, 0, + 0, 0, 0, 133, 255, 255, 255, 223, 12, 0, 0, 0, 0, 0, 0, 0, + 0, 13, 224, 255, 255, 255, 255, 0, 0, 0, 0, 63, 247, 255, 255, 255, + 80, 0, 0, 0, 0, 0, 12, 244, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 244, 12, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 5, 255, 255, 255, 248, 0, 87, 255, + 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 92, 255, 255, 255, 251, 16, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 86, 255, 255, 255, 209, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 2, 156, 255, + 255, 255, 255, 238, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 3, 227, 255, 255, 255, 255, 255, 255, 226, 3, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 255, 255, 254, + 18, 0, 0, 0, 0, 0, 12, 251, 255, 255, 192, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 46, 236, 255, 255, 255, 151, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 217, 255, 255, 153, + 0, 0, 0, 0, 0, 0, 219, 255, 255, 255, 39, 0, 0, 0, 0, 0, + 0, 41, 255, 255, 255, 217, 0, 248, 255, 255, 255, 15, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 31, 224, 255, 255, 255, 255, 132, 7, 219, + 255, 255, 255, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 255, + 255, 255, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 68, 255, 255, 255, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 231, 255, 255, 255, 70, 0, 0, + 0, 0, 0, 0, 0, 0, 106, 255, 255, 255, 169, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 255, 255, 255, 104, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 255, 255, 255, 255, 233, + 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 255, + 255, 255, 249, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 37, 255, 255, 255, 223, 0, 100, 255, 255, 255, 255, 255, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 255, 255, 255, 246, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 23, 249, 255, 255, 255, 62, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 247, 255, 255, 255, 11, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 255, 255, 255, 246, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 239, 255, 255, 255, 33, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 255, 255, 253, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 24, 115, 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 209, 115, 24, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 141, 255, 255, 255, 150, 0, 0, 0, + 0, 0, 0, 47, 255, 255, 255, 255, 182, 12, 0, 0, 0, 0, 0, 0, + 12, 182, 255, 255, 255, 255, 255, 0, 0, 0, 91, 243, 255, 255, 255, 180, + 0, 0, 0, 0, 0, 0, 98, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 98, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 22, 255, 255, 255, 244, 0, 19, 253, + 255, 255, 254, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 213, 255, 255, 255, 184, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 18, 252, 255, 255, 255, 47, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 1, 149, + 255, 255, 255, 255, 241, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 127, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 239, 255, 255, 211, + 0, 0, 0, 0, 0, 0, 70, 255, 255, 255, 130, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 0, 101, 255, 255, 255, 217, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 255, 255, 238, 20, + 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 11, 0, 0, 0, 0, 0, + 0, 13, 255, 255, 255, 238, 0, 226, 255, 255, 255, 44, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 33, 226, 255, 255, 255, 255, 202, 255, + 255, 255, 249, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 255, + 255, 255, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 99, 255, 255, 255, 155, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 55, 255, 255, 255, 242, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 54, 255, 255, 255, 232, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 255, 255, 255, 53, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 255, 255, 255, 255, 234, 43, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, + 255, 255, 239, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 99, 255, 255, 255, 199, 0, 47, 255, 255, 255, 255, 255, 26, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 255, 255, 255, 226, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 123, 255, 255, 255, 211, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 246, 255, 255, 255, 10, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 255, 255, 255, 246, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 83, 255, 255, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 217, 255, 255, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 81, 255, 255, 255, 223, 1, 0, 0, + 0, 0, 0, 0, 186, 255, 255, 255, 255, 224, 110, 39, 9, 8, 38, 109, + 223, 255, 254, 255, 255, 255, 255, 29, 92, 196, 255, 255, 255, 255, 199, 11, + 0, 0, 0, 0, 0, 0, 196, 255, 255, 255, 131, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 196, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 67, 255, 255, 255, 222, 0, 0, 190, + 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 139, 255, 255, 255, 255, 76, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 188, 255, 255, 255, 161, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 141, 255, 255, 255, 255, 244, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 29, 252, 255, 255, 255, 255, 252, 28, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 255, 255, 255, 149, + 0, 0, 0, 0, 0, 0, 132, 255, 255, 255, 67, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 0, 21, 255, 255, 255, 244, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 243, 255, 255, 106, 0, + 0, 0, 0, 0, 0, 0, 252, 255, 255, 255, 2, 0, 0, 0, 0, 0, + 0, 3, 255, 255, 255, 251, 0, 200, 255, 255, 255, 115, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 227, 255, 255, 255, 255, 255, + 255, 255, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 255, + 255, 255, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 143, 255, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 133, 255, 255, 255, 171, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 4, 242, 255, 255, 255, 52, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 52, 255, 255, 255, 240, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, 255, 255, 255, 235, 45, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 255, + 255, 255, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 192, 255, 255, 255, 139, 0, 1, 234, 255, 255, 255, 255, 72, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 255, 255, 255, 202, 0, + 0, 0, 0, 0, 0, 0, 0, 3, 225, 255, 255, 255, 105, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 227, 255, 255, 255, 37, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 255, 255, 255, 226, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 181, 255, 255, 255, 116, 0, 0, 0, 0, 0, 0, 0, 0, + 27, 255, 255, 255, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 17, 250, 255, 255, 255, 62, 0, 0, + 0, 0, 0, 0, 35, 243, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 138, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 187, 17, 0, + 0, 0, 0, 0, 0, 37, 255, 255, 255, 254, 34, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 255, 255, 255, 255, + 37, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 154, 255, 255, 255, 185, 0, 0, 91, + 255, 255, 255, 241, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 101, 255, 255, 255, 255, 200, 1, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 87, 255, 255, 255, 249, 44, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 132, 255, 255, 255, 255, 246, 70, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 179, 255, 255, 255, 255, 179, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 255, 255, 87, + 0, 0, 0, 0, 0, 0, 195, 255, 255, 250, 10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 0, 8, 255, 255, 255, 244, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 255, 255, 208, 3, 0, + 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 11, 0, 0, 0, 0, 0, + 0, 12, 255, 255, 255, 238, 0, 137, 255, 255, 255, 218, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 228, 255, 255, 255, 255, + 255, 227, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, + 255, 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 194, 255, 255, 255, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 210, 255, 255, 255, 93, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 169, 255, 255, 255, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 160, 255, 255, 255, 167, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 124, 255, 255, 255, 255, 237, 47, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 255, + 255, 255, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 86, 255, 255, 255, 255, 66, 0, 0, 157, 255, 255, 255, 255, 142, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, 255, 144, 0, + 0, 0, 0, 0, 0, 0, 0, 79, 255, 255, 255, 242, 12, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 202, 255, 255, 255, 104, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255, 255, 255, 200, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 65, 255, 255, 255, 250, 21, 0, 0, 0, 0, 0, 0, 0, 0, + 92, 255, 255, 237, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 255, 255, 255, 178, 0, 0, + 0, 0, 0, 0, 0, 76, 247, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 178, 3, 255, 255, 255, 255, 255, 255, 255, 255, 247, 113, 1, 0, 0, + 0, 0, 0, 0, 0, 135, 255, 255, 255, 192, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 193, 255, 255, 255, + 134, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 55, 251, 255, 255, 255, 112, 0, 0, 5, + 222, 255, 255, 255, 189, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 140, + 255, 255, 255, 255, 249, 51, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 4, 217, 255, 255, 255, 215, 12, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 125, 255, 255, 255, 255, 248, 77, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175, 255, 255, 255, 25, + 0, 0, 0, 0, 0, 8, 248, 255, 255, 199, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 0, 54, 255, 255, 255, 221, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 255, 255, 255, 60, 0, 0, + 0, 0, 0, 0, 0, 0, 220, 255, 255, 255, 39, 0, 0, 0, 0, 0, + 0, 41, 255, 255, 255, 218, 0, 60, 255, 255, 255, 255, 124, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 235, 255, 255, 255, + 255, 169, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 252, + 255, 255, 242, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, + 243, 255, 255, 250, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 32, 255, 255, 255, 252, 19, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 78, 255, 255, 255, 248, 35, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 35, 248, 255, 255, 255, 76, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 238, 49, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 230, 255, + 255, 255, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 181, + 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, + 245, 255, 255, 255, 216, 4, 0, 0, 62, 255, 255, 255, 255, 238, 14, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 240, 255, 255, 255, 78, 0, + 0, 0, 0, 0, 0, 0, 0, 185, 255, 255, 255, 148, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 140, 255, 255, 255, 214, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 218, 255, 255, 255, 139, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 215, 255, 255, 255, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 158, 255, 255, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 255, 255, 255, 255, 58, 0, + 0, 0, 0, 0, 0, 0, 61, 219, 255, 255, 255, 255, 255, 255, 255, 254, + 146, 4, 0, 255, 255, 255, 255, 255, 255, 230, 125, 21, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 229, 255, 255, 255, 95, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, + 228, 3, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 78, 238, 255, 255, 255, 246, 26, 0, 0, 0, + 88, 255, 255, 255, 255, 141, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 10, 159, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 111, 226, 255, + 255, 255, 255, 255, 106, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 79, 255, 255, 255, 255, 180, 12, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 116, 255, 255, 255, 255, 250, 84, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 236, 255, 255, 219, 0, + 0, 0, 0, 0, 0, 63, 255, 255, 255, 136, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 0, 0, 1, 177, 255, 255, 255, 168, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3, 209, 255, 255, 164, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 171, 255, 255, 255, 90, 0, 0, 0, 0, 0, + 0, 92, 255, 255, 255, 169, 0, 1, 201, 255, 255, 255, 254, 95, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 144, 254, 255, 255, 255, + 255, 255, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 255, 255, 255, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, + 255, 255, 255, 193, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 109, 255, 255, 255, 194, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2, 221, 255, 255, 255, 199, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 4, 200, 255, 255, 255, 220, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 128, 255, 255, 255, 255, 238, 50, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, 31, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 178, 255, 255, + 255, 250, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, + 249, 151, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 122, 250, + 255, 255, 255, 255, 80, 0, 0, 0, 0, 205, 255, 255, 255, 255, 160, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 255, 255, 255, 227, 6, 0, + 0, 0, 0, 0, 0, 0, 36, 254, 255, 255, 255, 42, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 69, 255, 255, 255, 255, 139, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, 255, 68, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10, 190, 255, 255, 255, 232, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 223, 255, 255, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 217, 255, 255, 255, 211, 7, + 0, 0, 0, 0, 0, 0, 0, 6, 97, 179, 226, 248, 235, 206, 139, 38, + 0, 0, 0, 245, 224, 203, 172, 114, 53, 4, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 74, 255, 255, 255, 242, 10, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 243, 255, 255, + 255, 73, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 9, 23, 60, 113, 200, 255, 255, 255, 255, 255, 129, 0, 0, 0, 0, + 0, 177, 255, 255, 255, 255, 163, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 72, 221, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 2, 9, 24, 48, 84, 129, 190, 250, 255, 255, 255, + 255, 255, 255, 121, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 165, 255, 255, 255, 255, 212, 36, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 108, 254, 255, 255, 255, 252, 91, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 255, 255, 255, 157, 0, + 0, 0, 0, 0, 0, 126, 255, 255, 255, 74, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 208, 112, 23, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 0, 0, 0, 13, 161, 255, 255, 255, 255, 88, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 108, 255, 255, 242, 25, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 106, 255, 255, 255, 177, 0, 0, 0, 0, 0, + 0, 181, 255, 255, 255, 104, 0, 0, 61, 255, 255, 255, 255, 255, 145, 13, + 0, 0, 0, 0, 0, 0, 0, 0, 2, 87, 214, 255, 255, 255, 255, 255, + 255, 255, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, + 255, 255, 255, 131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, + 255, 255, 255, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 187, 255, 255, 255, 117, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 93, 255, 255, 255, 255, 182, 15, 0, + 0, 0, 0, 0, 0, 16, 184, 255, 255, 255, 255, 91, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 128, 255, 255, 255, 255, 239, 51, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 245, 140, 37, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 200, 255, 255, 255, + 255, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 203, 137, 80, 39, 21, 4, 13, 32, 79, 142, 235, 255, 255, + 255, 255, 255, 160, 0, 0, 0, 0, 0, 69, 255, 255, 255, 255, 255, 146, + 5, 0, 0, 0, 0, 0, 0, 5, 149, 255, 255, 255, 255, 110, 0, 0, + 0, 0, 0, 0, 0, 0, 141, 255, 255, 255, 192, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 208, 255, 255, 255, 255, 153, 11, + 0, 0, 0, 0, 0, 0, 0, 0, 15, 163, 255, 255, 255, 255, 206, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, + 207, 255, 255, 255, 255, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 255, 255, 255, 255, 153, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 171, 255, 255, 255, 156, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 157, 255, 255, + 255, 170, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 181, 5, 0, 0, 0, 0, + 0, 17, 218, 255, 255, 255, 255, 222, 92, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 76, 191, 255, 255, 255, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 239, 83, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 206, 255, 255, 255, 255, 248, + 152, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, + 138, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 101, 253, 255, 255, 255, 253, 98, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255, 255, 255, 95, 0, + 0, 0, 0, 0, 0, 188, 255, 255, 252, 14, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 254, 206, 138, 86, 45, 9, 0, + 0, 255, 255, 21, 70, 143, 237, 255, 255, 255, 255, 211, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 20, 238, 255, 255, 117, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 15, 244, 255, 255, 254, 65, 0, 0, 0, 0, + 69, 255, 255, 255, 243, 14, 0, 0, 0, 128, 255, 255, 255, 255, 255, 238, + 141, 70, 26, 6, 14, 37, 75, 143, 221, 255, 255, 255, 255, 255, 249, 231, + 255, 255, 255, 255, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, + 255, 255, 255, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, + 255, 255, 255, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 14, 250, 255, 255, 255, 39, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 190, 255, 255, 255, 255, 228, 123, + 46, 14, 15, 47, 125, 229, 255, 255, 255, 255, 189, 1, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 48, 0, 255, 255, 255, 254, + 195, 132, 74, 37, 19, 4, 12, 27, 65, 114, 196, 254, 255, 255, 255, 255, + 219, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 181, 4, 0, 0, 0, 0, 0, 0, 161, 255, 255, 255, 255, 255, + 217, 107, 38, 11, 11, 39, 107, 218, 255, 255, 255, 255, 194, 4, 0, 0, + 0, 0, 0, 0, 0, 9, 237, 255, 255, 255, 85, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 254, 255, 255, 255, 255, 236, + 139, 70, 25, 7, 8, 26, 73, 143, 239, 255, 255, 255, 255, 254, 70, 0, + 0, 0, 0, 230, 179, 128, 78, 45, 21, 8, 3, 21, 45, 104, 184, 254, + 255, 255, 255, 255, 149, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 185, 255, 255, 255, 255, + 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 19, 249, 255, 255, 255, 59, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 255, 255, + 255, 248, 18, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 166, 9, 0, 0, 0, 0, 0, + 0, 0, 33, 224, 255, 255, 255, 255, 255, 226, 154, 84, 46, 25, 6, 14, + 35, 56, 105, 170, 235, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 249, + 153, 21, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 209, 255, 255, 255, 255, + 255, 253, 200, 139, 79, 46, 28, 11, 3, 13, 24, 51, 86, 135, 203, 253, + 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 93, 252, 255, 255, 255, 254, 106, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 255, 255, 255, 33, 0, + 0, 0, 0, 0, 5, 245, 255, 255, 205, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 249, + 235, 255, 255, 255, 255, 255, 255, 255, 255, 255, 241, 47, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 154, 255, 255, 216, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 123, 255, 255, 255, 240, 98, 16, 15, 98, + 241, 255, 255, 255, 119, 0, 0, 0, 0, 0, 152, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 56, 35, + 229, 255, 255, 255, 255, 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 215, 255, 255, 255, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, + 255, 255, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 86, 255, 255, 255, 218, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 226, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 25, 0, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 48, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 224, + 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 251, 126, 3, 0, 0, 0, 0, 0, 0, 0, 11, 204, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 226, 24, 0, 0, 0, + 0, 0, 0, 0, 0, 97, 255, 255, 255, 230, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 120, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 159, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 235, 255, 255, 255, + 255, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 110, 255, 255, 255, 217, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 255, + 255, 255, 109, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 205, 79, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 29, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 207, 122, 24, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 178, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 213, 65, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 6, 255, 255, + 255, 248, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 86, 251, 255, 255, 255, 255, 114, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 255, 227, 0, 0, + 0, 0, 0, 0, 57, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 227, 58, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 52, 253, 255, 255, 70, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3, 186, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 183, 3, 0, 0, 0, 0, 0, 0, 106, 248, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 148, 13, 0, 0, + 40, 233, 255, 255, 255, 255, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 123, 255, 255, 255, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 142, 255, + 255, 255, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 163, 255, 255, 255, 140, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 219, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 218, 36, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 48, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 176, 25, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 46, + 139, 226, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 248, 162, + 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 193, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 26, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 203, 255, 255, 255, 129, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 249, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 249, 115, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 250, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 251, 255, 255, + 255, 255, 161, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 69, 192, 11, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 207, 255, 255, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, + 255, 255, 207, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 249, 234, 217, 175, 122, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 132, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 198, 66, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 254, 249, 241, 224, 202, 170, 131, 83, 22, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 234, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 234, 116, 5, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 15, 255, 255, + 255, 236, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 79, 249, 255, 255, 255, 255, 123, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 44, 132, 216, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 242, 141, 18, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 199, 255, 255, 174, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 163, 255, 255, 255, 255, 255, 255, + 255, 255, 159, 6, 0, 0, 0, 0, 0, 0, 0, 0, 40, 163, 251, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 247, 158, 39, 0, 0, 0, 0, + 0, 46, 237, 255, 255, 255, 255, 129, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 250, 255, 255, 236, 9, 0, 0, 0, 0, 0, 0, 0, 10, 237, 255, + 255, 249, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3, 237, 255, 255, 255, 63, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 154, 252, 255, 255, + 255, 255, 255, 255, 255, 255, 252, 153, 16, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 48, 0, 46, 138, 225, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 189, 80, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 41, 107, 158, 199, 229, 244, 251, 238, 221, 198, 146, 90, 20, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 120, 241, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 238, 122, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 179, 253, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 179, 49, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, + 176, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 253, 255, + 255, 255, 255, 220, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 19, 150, 254, 255, 150, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 32, 147, 240, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 244, 165, 70, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 112, + 215, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 187, + 86, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 255, 255, + 255, 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 89, 139, 178, 213, 233, + 248, 255, 255, 227, 194, 150, 83, 14, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 255, 255, 247, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 155, 219, 244, 244, 219, + 155, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 102, + 167, 212, 232, 250, 248, 232, 200, 152, 88, 13, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 166, 255, 255, 255, 106, 0, 0, 0, 0, 0, 0, 0, 107, 255, 255, + 255, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 62, 255, 255, 255, 237, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 123, 191, + 232, 250, 250, 231, 190, 123, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, + 106, 157, 199, 229, 244, 251, 239, 224, 202, 154, 102, 33, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 101, + 173, 219, 245, 251, 233, 212, 162, 92, 15, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 110, + 167, 213, 231, 248, 248, 231, 213, 167, 110, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 29, 86, 136, 178, 210, 235, 248, 253, 243, 219, 177, 114, 32, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 249, + 255, 255, 255, 255, 255, 196, 83, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 32, 146, 245, 255, 255, 255, 254, 67, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 7, 75, 136, 185, 219, 241, 251, 248, + 232, 215, 179, 128, 76, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 42, 110, 163, 201, 230, 245, 253, 249, 239, 215, 185, 141, 87, 21, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, + 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 53, 255, 255, 255, 219, 3, 0, 0, 0, 0, 0, 3, 220, 255, 255, + 255, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 140, 255, 255, 255, 164, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, + 229, 255, 255, 255, 255, 255, 255, 240, 167, 107, 60, 27, 10, 2, 10, 33, + 71, 125, 196, 253, 255, 255, 255, 255, 255, 205, 34, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, 255, 255, + 255, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 183, 255, 255, 255, 99, 0, 0, 0, 0, 0, 100, 255, 255, 255, + 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 217, 255, 255, 255, 86, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 20, 171, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 250, 133, 7, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 220, 255, 255, + 255, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 55, 255, 255, 255, 226, 7, 0, 0, 0, 8, 227, 255, 255, 255, + 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 67, 202, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 175, 41, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132, 255, 255, 255, + 252, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 176, 255, 255, 255, 119, 0, 0, 0, 120, 255, 255, 255, 175, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 63, 166, 243, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 237, 151, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3, 21, 73, 179, 255, 255, 255, 255, + 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 249, 255, 255, 244, 30, 0, 31, 245, 255, 255, 249, 35, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 9, 70, 130, 178, 213, 237, 249, 252, 241, 217, + 181, 129, 66, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 252, + 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 138, 255, 255, 255, 177, 0, 178, 255, 255, 255, 137, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 107, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 233, 92, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 241, 221, 175, 103, 14, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 251, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 32, 107, 168, 208, 237, 249, 250, 237, 209, 169, 109, + 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 247, 231, 214, 172, 119, 49, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 107, + 168, 208, 237, 249, 250, 237, 209, 169, 109, 33, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 248, 233, 218, 181, 132, 70, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 10, 85, 142, 195, 222, 238, 252, 243, + 230, 207, 170, 132, 76, 17, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 207, 255, 255, 255, 120, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 121, 255, 255, 255, 207, 0, 224, 255, 255, 255, 63, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 255, 255, 255, 255, 255, 255, + 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 255, + 255, 255, 224, 0, 0, 0, 171, 255, 255, 255, 249, 43, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 249, 255, 255, 255, + 167, 0, 0, 172, 255, 255, 255, 245, 34, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 245, 255, 255, 255, 172, 0, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 217, 255, 255, 255, 86, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 191, 255, 255, 255, 255, 190, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 150, 255, 255, + 255, 216, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 53, 122, + 170, 213, 235, 250, 243, 225, 200, 142, 78, 4, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 64, 133, 193, 218, 238, 251, 237, 213, 166, 107, 26, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 87, 162, + 206, 229, 249, 238, 216, 176, 104, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 25, 124, 186, 228, 245, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 33, 120, 194, 225, 249, 243, 214, 159, 71, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 60, 150, 208, 241, 251, 236, 193, 117, 18, 0, 0, 0, 0, + 0, 0, 0, 0, 57, 148, 207, 240, 250, 230, 195, 116, 21, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 132, 198, 237, 251, 237, + 215, 160, 84, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 56, 132, 196, 221, 244, 244, 221, 195, 131, 55, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 74, 160, 215, 243, 248, 223, + 190, 110, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 23, 111, 191, 223, 248, 243, 214, 160, 73, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 142, 200, 236, 251, 255, + 252, 0, 0, 0, 0, 0, 4, 77, 147, 195, 228, 243, 252, 240, 226, 191, + 149, 89, 17, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 207, + 255, 255, 255, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 121, 255, 255, 255, 206, 0, 222, 255, 255, 255, 69, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 134, 255, 255, 255, 255, 255, 141, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 70, 255, 255, 255, 222, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 169, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 65, 189, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 254, 192, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 202, 77, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 189, 254, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 192, 68, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 229, 119, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 21, 145, 240, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 254, 205, 123, 41, 0, 0, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 110, 255, 255, 255, 216, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 217, 255, 255, 255, 109, 0, 161, 255, 255, 255, 127, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 255, 255, 255, 255, 255, 255, + 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, + 255, 255, 160, 0, 0, 0, 22, 235, 255, 255, 255, 201, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 210, 255, 255, 255, 229, + 17, 0, 0, 23, 237, 255, 255, 255, 189, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 190, 255, 255, 255, 237, 23, 0, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 140, 255, 255, 255, 164, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, + 187, 255, 255, 255, 255, 255, 255, 186, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 4, 188, 255, + 255, 255, 156, 0, 0, 0, 0, 0, 0, 0, 0, 48, 145, 234, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 222, 98, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 102, 226, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 182, 61, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 12, 130, 245, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 248, 139, 16, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 112, 249, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 10, 140, 252, 255, 255, 255, 255, 255, 255, 255, 255, 199, + 38, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 28, 186, 255, 255, 255, 255, 255, 255, 255, 255, 235, 81, 0, 0, 0, + 0, 0, 28, 184, 255, 255, 255, 255, 255, 255, 255, 255, 237, 88, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 13, 152, 252, 255, 255, 255, 255, 255, + 255, 255, 255, 211, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 202, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 200, 56, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 41, 202, 255, 255, 255, 255, 255, 255, + 255, 255, 246, 114, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 115, + 247, 255, 255, 255, 255, 255, 255, 255, 255, 202, 40, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 31, 184, 255, 255, 255, 255, 255, 255, + 253, 0, 0, 0, 1, 100, 232, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 251, 175, 58, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 108, + 255, 255, 255, 218, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 219, 255, 255, 255, 107, 0, 155, 255, 255, 255, 136, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 202, 255, 255, 255, 255, 255, 208, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 137, 255, 255, 255, 154, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 254, 51, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 22, 172, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 174, 23, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 159, + 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 172, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 174, 23, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 209, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 81, 234, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 19, 249, 255, 255, 255, 59, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 59, 255, 255, 255, 249, 19, 0, 97, 255, 255, 255, 191, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, 255, 255, 255, 255, 255, 255, + 158, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, + 255, 255, 96, 0, 0, 0, 0, 88, 255, 255, 255, 255, 119, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 255, 255, 255, 255, 70, + 0, 0, 0, 0, 93, 255, 255, 255, 255, 103, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 255, 255, 92, 0, 0, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 62, 255, 255, 255, 237, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 182, + 255, 255, 255, 255, 255, 255, 255, 255, 182, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 17, 218, + 255, 255, 255, 83, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 161, 7, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 211, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 47, 228, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 217, 33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 111, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 0, 0, 19, 210, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 239, 56, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 43, 230, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 94, 0, 0, + 0, 48, 235, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 94, 0, + 0, 0, 255, 255, 255, 255, 0, 27, 211, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 246, 71, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 124, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 57, 241, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 181, 6, 0, 0, 0, 0, 0, 0, 0, 6, 182, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 240, 57, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 50, 234, 255, 255, 255, 255, 255, 255, 255, + 254, 0, 0, 6, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 17, + 248, 255, 255, 255, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 63, 255, 255, 255, 247, 17, 0, 88, 255, 255, 255, 203, 0, + 0, 0, 0, 0, 0, 0, 0, 16, 252, 255, 255, 255, 255, 255, 254, 20, + 0, 0, 0, 0, 0, 0, 0, 0, 204, 255, 255, 255, 87, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 187, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 48, 226, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 227, 48, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 171, 2, 0, 0, 0, 0, 0, 0, 0, 48, 226, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 227, 48, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 219, 23, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 94, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 171, 255, 255, 255, 156, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 156, 255, 255, 255, 170, 0, 0, 34, 255, 255, 255, 247, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 214, 255, 255, 255, 255, 255, 255, + 220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 248, 255, + 255, 255, 32, 0, 0, 0, 0, 0, 175, 255, 255, 255, 247, 40, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 253, 255, 255, 255, 149, 0, + 0, 0, 0, 0, 0, 181, 255, 255, 255, 241, 28, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 29, 242, 255, 255, 255, 180, 0, 0, 0, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 3, 237, 255, 255, 255, 63, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 178, 255, + 255, 255, 255, 249, 249, 255, 255, 255, 255, 177, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 39, + 239, 255, 255, 238, 29, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 157, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 235, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 63, 241, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 232, 30, 0, 0, 0, 0, + 0, 0, 0, 22, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 0, 13, 207, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 234, 28, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 22, + 225, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 248, 43, 0, + 31, 235, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 248, 36, + 0, 0, 255, 255, 255, 255, 15, 212, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 239, 25, 0, 0, 0, 0, 0, 144, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 139, 0, 0, + 0, 0, 255, 255, 255, 255, 29, 234, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 172, 2, 0, 0, 0, 0, 0, 2, 173, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 234, 28, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 26, 231, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 0, 160, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 167, 255, 255, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 161, 255, 255, 255, 166, 0, 0, 22, 254, 255, 255, 253, 17, + 0, 0, 0, 0, 0, 0, 0, 81, 255, 255, 255, 255, 255, 255, 255, 86, + 0, 0, 0, 0, 0, 0, 0, 17, 253, 255, 255, 254, 21, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 69, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 49, + 239, 255, 255, 255, 255, 255, 215, 136, 65, 37, 13, 11, 36, 63, 132, 212, + 255, 255, 255, 255, 255, 239, 49, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 9, 25, 66, 126, 222, 255, 255, 255, 255, + 255, 115, 0, 0, 0, 0, 0, 0, 49, 239, 255, 255, 255, 255, 255, 215, + 136, 65, 37, 13, 11, 36, 63, 132, 212, 255, 255, 255, 255, 255, 239, 49, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 8, 21, 56, 107, 192, 255, 255, 255, 255, 255, 167, 0, 0, 0, 0, 0, + 0, 0, 0, 63, 252, 255, 255, 255, 255, 227, 140, 78, 35, 15, 4, 18, + 38, 74, 131, 191, 252, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 74, 255, 255, 255, 242, 11, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, + 242, 255, 255, 255, 73, 0, 0, 0, 225, 255, 255, 255, 62, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 21, 255, 255, 255, 252, 250, 255, 255, + 255, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 255, 255, + 255, 224, 0, 0, 0, 0, 0, 0, 23, 237, 255, 255, 255, 198, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 13, 222, 255, 255, 255, 218, 10, 0, + 0, 0, 0, 0, 0, 28, 241, 255, 255, 255, 181, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 182, 255, 255, 255, 241, 28, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 9, 204, 255, 255, 255, 252, 71, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 163, 255, 255, 255, 140, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 4, 173, 255, 255, + 255, 255, 240, 65, 65, 241, 255, 255, 255, 255, 172, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 70, 252, 255, 255, 193, 2, 0, 0, 0, 0, 0, 255, 255, 243, 175, 111, + 67, 30, 15, 3, 14, 39, 88, 170, 251, 255, 255, 255, 255, 82, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 234, 255, 255, 255, 255, + 249, 161, 89, 36, 16, 5, 21, 46, 96, 157, 236, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 29, 239, 255, 255, 255, 255, 205, 111, + 52, 20, 5, 17, 49, 116, 223, 255, 255, 255, 255, 196, 3, 0, 0, 0, + 0, 0, 0, 118, 255, 255, 255, 249, 124, 38, 7, 0, 0, 0, 0, 0, + 0, 0, 151, 255, 255, 255, 255, 250, 151, 66, 24, 6, 25, 68, 152, 250, + 255, 255, 178, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 173, + 255, 255, 241, 136, 58, 20, 6, 26, 88, 207, 255, 255, 255, 255, 182, 2, + 199, 255, 255, 241, 136, 59, 21, 5, 25, 87, 207, 255, 255, 255, 255, 176, + 0, 0, 255, 255, 255, 255, 166, 255, 255, 252, 162, 81, 29, 8, 13, 45, + 121, 236, 255, 255, 255, 255, 166, 0, 0, 0, 0, 90, 255, 255, 255, 255, + 255, 203, 106, 39, 13, 12, 39, 106, 204, 255, 255, 255, 255, 255, 84, 0, + 0, 0, 255, 255, 255, 255, 179, 255, 255, 254, 165, 73, 26, 6, 26, 73, + 165, 254, 255, 255, 255, 255, 109, 0, 0, 0, 0, 0, 111, 255, 255, 255, + 255, 254, 165, 73, 26, 6, 27, 75, 167, 254, 255, 255, 178, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 177, 255, 255, 246, 144, 65, 24, 3, 0, 0, + 0, 0, 60, 255, 255, 255, 255, 250, 163, 85, 39, 13, 3, 11, 26, 59, + 105, 166, 240, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 68, 255, 255, 255, 245, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 14, 245, 255, 255, 255, 67, 0, 0, 0, 210, 255, 255, 255, 81, + 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, 249, 255, 255, 255, 154, + 0, 0, 0, 0, 0, 0, 0, 83, 255, 255, 255, 209, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 185, 255, 255, 255, 205, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 25, 231, + 255, 255, 255, 255, 223, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 75, 221, 255, 255, 255, 255, 231, 25, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 117, 251, 255, 255, + 255, 237, 15, 0, 0, 0, 0, 25, 231, 255, 255, 255, 255, 223, 81, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 221, 255, 255, 255, 255, 231, + 25, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 65, 229, 255, 255, 255, 254, 46, 0, 0, 0, 0, + 0, 0, 4, 211, 255, 255, 255, 249, 112, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 24, 111, 209, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 3, 229, 255, 255, 255, 95, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, + 255, 255, 255, 228, 3, 0, 0, 0, 162, 255, 255, 255, 126, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 83, 255, 255, 255, 205, 198, 255, 255, + 255, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, + 255, 160, 0, 0, 0, 0, 0, 0, 0, 91, 255, 255, 255, 255, 115, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 157, 255, 255, 255, 252, 56, 0, 0, + 0, 0, 0, 0, 0, 0, 102, 255, 255, 255, 255, 94, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, 255, 101, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 168, 255, 255, 255, 255, 114, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 86, 255, 255, 255, 217, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 3, 168, 255, 255, 255, + 255, 229, 46, 0, 0, 47, 230, 255, 255, 255, 255, 167, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 109, 255, 255, 255, 124, 0, 0, 0, 0, 0, 200, 89, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 43, 213, 255, 255, 255, 206, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 196, 255, 255, 255, 255, 179, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 81, 198, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 2, 193, 255, 255, 255, 234, 82, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 142, 255, 255, 255, 255, 106, 0, 0, 0, + 0, 0, 0, 182, 255, 255, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 58, 254, 255, 255, 255, 219, 40, 0, 0, 0, 0, 0, 0, 0, 42, + 221, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, + 255, 192, 21, 0, 0, 0, 0, 0, 0, 4, 172, 255, 255, 255, 254, 141, + 255, 255, 191, 21, 0, 0, 0, 0, 0, 0, 4, 175, 255, 255, 255, 252, + 25, 0, 255, 255, 255, 255, 255, 255, 213, 40, 0, 0, 0, 0, 0, 0, + 0, 23, 211, 255, 255, 255, 250, 21, 0, 0, 24, 238, 255, 255, 255, 252, + 107, 1, 0, 0, 0, 0, 0, 0, 2, 112, 253, 255, 255, 255, 236, 22, + 0, 0, 255, 255, 255, 255, 255, 255, 237, 62, 0, 0, 0, 0, 0, 0, + 0, 63, 237, 255, 255, 255, 241, 27, 0, 0, 0, 27, 242, 255, 255, 255, + 236, 62, 0, 0, 0, 0, 0, 0, 0, 65, 239, 255, 255, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 255, 255, 206, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 156, 255, 255, 255, 231, 46, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 84, 199, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 1, 224, 255, 255, 255, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 103, 255, 255, 255, 223, 1, 0, 0, 0, 142, 255, 255, 255, 148, + 0, 0, 0, 0, 0, 0, 0, 216, 255, 255, 255, 146, 255, 255, 255, 221, + 0, 0, 0, 0, 0, 0, 0, 150, 255, 255, 255, 141, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 49, 254, 255, 255, 255, 88, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 1, 191, 255, + 255, 255, 255, 173, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 8, 169, 255, 255, 255, 255, 190, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 255, 255, + 255, 255, 97, 0, 0, 0, 1, 191, 255, 255, 255, 255, 173, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 169, 255, 255, 255, 255, + 190, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 36, 243, 255, 255, 255, 128, 0, 0, 0, 0, + 0, 0, 78, 255, 255, 255, 253, 69, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 75, 196, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 135, 255, 255, 255, 193, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, + 255, 255, 255, 134, 0, 0, 0, 0, 99, 255, 255, 255, 189, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 145, 255, 255, 255, 143, 136, 255, 255, + 255, 149, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, + 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 178, 255, 255, 255, 246, 37, + 0, 0, 0, 0, 0, 0, 0, 78, 255, 255, 255, 255, 132, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 189, 255, 255, 255, 237, 24, 0, 0, 0, + 0, 0, 0, 0, 0, 24, 238, 255, 255, 255, 189, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 124, 255, 255, 255, 255, 159, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 14, 250, 255, 255, 255, 39, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 2, 163, 255, 255, 255, 255, + 215, 31, 0, 0, 0, 0, 31, 215, 255, 255, 255, 255, 162, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 152, 255, 255, 251, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 231, 255, 255, 255, 56, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 255, 255, 255, 255, 150, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 84, 255, 255, 255, 233, 41, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 219, 2, 0, 0, + 0, 0, 0, 226, 255, 255, 255, 34, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 173, 255, 255, 255, 231, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 30, 233, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, + 202, 7, 0, 0, 0, 0, 0, 0, 0, 0, 15, 234, 255, 255, 255, 255, + 255, 199, 6, 0, 0, 0, 0, 0, 0, 0, 0, 16, 236, 255, 255, 255, + 108, 0, 255, 255, 255, 255, 255, 216, 15, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 38, 248, 255, 255, 255, 105, 0, 0, 133, 255, 255, 255, 255, 89, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 130, + 0, 0, 255, 255, 255, 255, 255, 247, 54, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 54, 247, 255, 255, 255, 139, 0, 0, 0, 141, 255, 255, 255, 247, + 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 249, 255, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 255, 219, 17, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 219, 255, 255, 255, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 127, 255, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 201, 255, 255, 255, 126, 0, 0, 0, 0, 75, 255, 255, 255, 215, + 0, 0, 0, 0, 0, 0, 27, 255, 255, 255, 251, 23, 248, 255, 255, 255, + 32, 0, 0, 0, 0, 0, 0, 217, 255, 255, 255, 74, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 166, 255, 255, 255, 220, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 98, 255, 255, + 255, 255, 167, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 164, 255, 255, 255, 255, 96, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 255, + 255, 255, 167, 0, 0, 0, 98, 255, 255, 255, 255, 167, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 164, 255, 255, 255, + 255, 97, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 195, 0, 0, 0, 0, + 0, 0, 158, 255, 255, 255, 149, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 38, 255, 255, 255, 254, 35, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 254, + 255, 255, 255, 38, 0, 0, 0, 0, 35, 255, 255, 255, 246, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 207, 255, 255, 255, 80, 75, 255, 255, + 255, 211, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 247, 255, 255, + 255, 32, 0, 0, 0, 0, 0, 0, 0, 0, 25, 238, 255, 255, 255, 195, + 2, 0, 0, 0, 0, 0, 21, 233, 255, 255, 255, 206, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 34, 245, 255, 255, 255, 173, 0, 0, 0, + 0, 0, 0, 0, 0, 174, 255, 255, 255, 244, 33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 80, 254, 255, 255, 255, 198, 6, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 187, 255, 255, 255, 117, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 1, 158, 255, 255, 255, 255, 197, + 18, 0, 0, 0, 0, 0, 0, 19, 198, 255, 255, 255, 255, 158, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 190, 255, 255, 221, 14, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 255, 255, 255, 123, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 255, 255, 255, 195, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 196, 255, 255, 255, 79, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 4, 219, 255, 255, 255, 68, 0, 0, + 0, 0, 0, 243, 255, 255, 255, 5, 0, 0, 0, 0, 0, 0, 0, 0, + 23, 250, 255, 255, 255, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 91, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 251, + 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 142, 255, 255, 255, 255, + 251, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, 255, + 161, 0, 255, 255, 255, 255, 254, 47, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 163, 255, 255, 255, 159, 0, 6, 233, 255, 255, 255, 158, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 165, 255, 255, 255, 231, + 5, 0, 255, 255, 255, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 129, 255, 255, 255, 233, 5, 0, 5, 234, 255, 255, 255, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132, 255, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 255, 65, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 244, 255, 255, 255, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 31, 253, 255, 255, 255, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 44, 255, 255, 255, 253, 30, 0, 0, 0, 0, 13, 251, 255, 255, 255, + 26, 0, 0, 0, 0, 0, 95, 255, 255, 255, 197, 0, 191, 255, 255, 255, + 99, 0, 0, 0, 0, 0, 28, 255, 255, 255, 250, 12, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 34, 250, 255, 255, 255, 106, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 6, 227, 255, 255, + 255, 211, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 209, 255, 255, 255, 225, 5, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, + 255, 255, 215, 0, 0, 6, 227, 255, 255, 255, 211, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 209, 255, 255, + 255, 226, 6, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 45, 255, 255, 255, 225, 0, 0, 0, 0, + 0, 0, 212, 255, 255, 255, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 196, 255, 255, 255, 132, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, + 255, 255, 196, 0, 0, 0, 0, 0, 0, 227, 255, 255, 255, 61, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 15, 253, 255, 255, 254, 19, 15, 253, 255, + 255, 254, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 255, + 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, 255, + 111, 0, 0, 0, 0, 0, 176, 255, 255, 255, 248, 43, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 255, 255, 255, 85, 0, 0, + 0, 0, 0, 0, 86, 255, 255, 255, 255, 111, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 46, 243, 255, 255, 255, 227, 23, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 255, 255, 255, 194, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 1, 153, 255, 255, 255, 255, 176, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 9, 177, 255, 255, 255, 255, 152, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 18, 219, 255, 255, 164, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 255, 255, 178, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 47, 255, 255, 255, 255, 44, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 41, 255, 255, 255, 201, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 255, 131, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 96, 255, 255, 255, 220, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 223, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 169, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, 255, 255, + 167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 255, 255, 255, + 207, 0, 255, 255, 255, 255, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 82, 255, 255, 255, 207, 0, 71, 255, 255, 255, 250, 23, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 251, 255, 255, 255, + 70, 0, 255, 255, 255, 255, 242, 10, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 243, 255, 255, 255, 72, 0, 72, 255, 255, 255, 242, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 244, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 205, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 247, 255, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 186, 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 143, 255, 255, 255, 185, 0, 0, 0, 0, 0, 0, 197, 255, 255, 255, + 94, 0, 0, 0, 0, 0, 162, 255, 255, 255, 129, 0, 123, 255, 255, 255, + 166, 0, 0, 0, 0, 0, 95, 255, 255, 255, 196, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 147, 255, 255, 255, 232, 11, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 96, 255, 255, 255, + 251, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 49, 251, 255, 255, 255, 93, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 255, + 255, 255, 235, 0, 0, 96, 255, 255, 255, 251, 51, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 251, 255, + 255, 255, 95, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 11, 255, 255, 255, 246, 0, 0, 0, 0, + 0, 0, 236, 255, 255, 255, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 98, 255, 255, 255, 226, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 226, 255, + 255, 255, 98, 0, 0, 0, 0, 0, 0, 164, 255, 255, 255, 125, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 255, 212, 0, 0, 207, 255, + 255, 255, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, + 161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 255, 255, 255, + 245, 35, 0, 0, 0, 97, 255, 255, 255, 255, 114, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2, 197, 255, 255, 255, 233, 19, 0, + 0, 0, 0, 19, 233, 255, 255, 255, 197, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 21, 224, 255, 255, 255, 246, 50, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 255, 255, 252, + 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 148, 255, 255, 255, 255, 153, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 153, 255, 255, 255, 255, 147, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 255, 255, 255, 217, 0, + 255, 255, 255, 255, 0, 0, 0, 1, 74, 160, 215, 243, 248, 223, 190, 110, + 23, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 255, 187, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 23, 111, 191, 223, 248, 243, 214, 160, 73, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 105, 255, 255, 255, 112, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 255, 255, 255, 189, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 151, 255, 255, 255, 131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 132, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 37, 132, 198, 237, 251, 237, 215, 160, 84, 3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 89, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 255, 255, 255, 255, + 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 255, 255, 255, + 230, 0, 255, 255, 255, 255, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 35, 255, 255, 255, 229, 0, 128, 255, 255, 255, 172, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, + 126, 0, 255, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 160, 255, 255, 255, 127, 0, 129, 255, 255, 255, 159, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 222, 255, 255, 255, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 87, 255, 255, 255, 234, 5, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 235, 255, 255, 255, 86, 0, 0, 0, 0, 0, 0, 129, 255, 255, 255, + 161, 0, 0, 0, 0, 0, 229, 255, 255, 255, 61, 0, 56, 255, 255, 255, + 232, 1, 0, 0, 0, 0, 163, 255, 255, 255, 128, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 22, 242, 255, 255, 255, 125, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 193, 255, 255, 255, + 162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 161, 255, 255, 255, 191, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 255, + 255, 255, 250, 0, 0, 193, 255, 255, 255, 162, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 255, + 255, 255, 193, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 13, 255, 255, 255, 248, 0, 0, 0, 0, + 0, 0, 250, 255, 255, 255, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 12, 244, 255, 255, 255, 71, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 255, 255, + 255, 244, 12, 0, 0, 0, 0, 0, 0, 100, 255, 255, 255, 188, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 138, 255, 255, 255, 149, 0, 0, 145, 255, + 255, 255, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 255, + 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 240, 255, 255, + 255, 192, 1, 0, 31, 241, 255, 255, 255, 192, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 248, 255, 255, 255, 165, 0, + 0, 0, 0, 166, 255, 255, 255, 247, 39, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 194, 255, 255, 255, 255, 87, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 255, 255, 255, + 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 142, 255, 255, 255, 255, 126, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, + 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 90, + 146, 194, 224, 238, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 233, 0, + 255, 255, 255, 255, 0, 0, 41, 202, 255, 255, 255, 255, 255, 255, 255, 255, + 246, 114, 2, 0, 0, 0, 0, 0, 173, 255, 255, 255, 112, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 115, 247, 255, 255, 255, 255, 255, 255, 255, 255, 202, 40, 0, + 0, 255, 255, 255, 255, 0, 165, 255, 255, 255, 59, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 255, 255, 255, 220, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 201, 255, 255, 255, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 77, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 13, 152, + 252, 255, 255, 255, 255, 255, 255, 255, 255, 211, 57, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 142, 255, 255, + 255, 255, 254, 114, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 36, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 255, 255, 255, 255, + 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 255, 255, 255, + 244, 0, 255, 255, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 14, 255, 255, 255, 244, 0, 183, 255, 255, 255, 102, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 255, 255, 255, + 181, 0, 255, 255, 255, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 95, 255, 255, 255, 180, 0, 182, 255, 255, 255, 94, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 161, 255, 255, 255, 250, 116, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 7, 237, 255, 255, 255, 83, 0, 0, 0, 0, 0, 0, 0, 0, + 84, 255, 255, 255, 237, 7, 0, 0, 0, 0, 0, 0, 62, 255, 255, 255, + 228, 0, 0, 0, 0, 41, 255, 255, 255, 244, 5, 0, 3, 241, 255, 255, + 255, 45, 0, 0, 0, 0, 229, 255, 255, 255, 61, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 127, 255, 255, 255, 241, 20, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 20, 253, 255, 255, 255, + 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 56, 255, 255, 255, 253, 19, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 255, + 255, 255, 235, 0, 20, 253, 255, 255, 255, 57, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 255, + 255, 255, 253, 19, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 47, 255, 255, 255, 230, 0, 0, 0, 0, + 0, 0, 234, 255, 255, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 159, 255, 255, 255, 168, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 255, 255, + 255, 159, 0, 0, 0, 0, 0, 0, 0, 36, 255, 255, 255, 245, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 200, 255, 255, 255, 87, 0, 0, 83, 255, + 255, 255, 203, 0, 0, 0, 0, 0, 0, 0, 0, 7, 247, 255, 255, 255, + 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 255, 255, + 255, 255, 108, 2, 192, 255, 255, 255, 241, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, 255, 255, 255, 76, + 0, 0, 77, 255, 255, 255, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 155, 255, 255, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, 255, 255, 255, + 171, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 136, 255, 255, 255, 252, 99, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 252, 255, 255, + 255, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 151, 244, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 249, 0, + 255, 255, 255, 255, 0, 57, 241, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 181, 6, 0, 0, 0, 0, 210, 255, 255, 255, 54, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 6, 182, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 240, 57, + 0, 255, 255, 255, 255, 0, 208, 255, 255, 255, 252, 252, 252, 252, 252, 253, + 253, 253, 253, 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 238, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 225, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 32, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 27, 211, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 71, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 168, 255, 255, 255, + 255, 249, 88, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 13, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255, 255, + 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 255, 255, 255, 255, 0, 215, 255, 255, 255, 50, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 255, 255, 255, + 213, 0, 255, 255, 255, 255, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 47, 255, 255, 255, 215, 0, 216, 255, 255, 255, 46, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 57, 255, 255, 255, 255, 255, 237, 153, 76, 14, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 146, 255, 255, 255, 182, 0, 0, 0, 0, 0, 0, 0, 0, + 183, 255, 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, 6, 245, 255, 255, + 255, 39, 0, 0, 0, 109, 255, 255, 255, 182, 0, 0, 0, 177, 255, 255, + 255, 112, 0, 0, 0, 41, 255, 255, 255, 244, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 12, 233, 255, 255, 255, 143, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 87, 255, 255, 255, 214, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 213, 255, 255, 255, 85, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 255, + 255, 255, 215, 0, 87, 255, 255, 255, 214, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 213, + 255, 255, 255, 86, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 189, 0, 0, 0, 0, + 0, 0, 204, 255, 255, 255, 186, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 62, 255, 255, 255, 248, 17, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 247, 255, 255, + 255, 62, 0, 0, 0, 0, 0, 0, 0, 0, 228, 255, 255, 255, 60, 0, + 0, 0, 0, 0, 0, 0, 11, 251, 255, 255, 255, 25, 0, 0, 21, 255, + 255, 255, 252, 12, 0, 0, 0, 0, 0, 0, 0, 62, 255, 255, 255, 224, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 184, 255, + 255, 255, 244, 144, 255, 255, 255, 255, 97, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 205, 255, 255, 255, 228, + 15, 15, 229, 255, 255, 255, 204, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, + 255, 255, 255, 255, 176, 1, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 255, 255, 255, + 242, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 131, 255, 255, 255, 245, 76, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 245, 255, + 255, 255, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 235, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 29, 234, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 172, 2, 0, 0, 0, 229, 255, 255, 255, 24, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 173, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 234, + 28, 255, 255, 255, 255, 0, 226, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 240, 255, 255, 255, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 15, 255, 255, 255, 255, 0, 255, 255, 255, 255, 15, 212, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 25, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 15, 191, 255, 255, 255, 255, + 241, 65, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 232, 255, 255, 255, 22, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 255, 255, 255, + 230, 0, 255, 255, 255, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 22, 255, 255, 255, 231, 0, 233, 255, 255, 255, 21, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 148, 255, 255, 255, 255, 255, 255, 255, 252, 206, 149, 87, 23, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 47, 255, 255, 255, 252, 27, 0, 0, 0, 0, 0, 0, 28, + 253, 255, 255, 255, 47, 0, 0, 0, 0, 0, 0, 0, 0, 184, 255, 255, + 255, 106, 0, 0, 0, 176, 255, 255, 255, 114, 0, 0, 0, 110, 255, 255, + 255, 179, 0, 0, 0, 108, 255, 255, 255, 183, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 108, 255, 255, 255, 248, 31, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 140, 255, 255, 255, 152, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 153, 255, 255, 255, 137, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 255, + 255, 255, 167, 0, 140, 255, 255, 255, 152, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, + 255, 255, 255, 138, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 39, 245, 255, 255, 255, 123, 0, 0, 0, 0, + 0, 0, 141, 255, 255, 255, 255, 170, 21, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 219, 255, 255, 255, 107, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 255, 255, 255, + 220, 1, 0, 0, 0, 0, 0, 0, 0, 0, 165, 255, 255, 255, 123, 0, + 0, 0, 0, 0, 0, 0, 69, 255, 255, 255, 219, 0, 0, 0, 0, 215, + 255, 255, 255, 70, 0, 0, 0, 0, 0, 0, 0, 126, 255, 255, 255, 161, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 241, + 255, 255, 255, 255, 255, 255, 255, 176, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 250, 255, 255, 255, + 156, 157, 255, 255, 255, 250, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 252, + 255, 255, 255, 210, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 231, 255, 255, + 255, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 243, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 179, 255, 255, 254, 165, 73, 26, 6, 26, 73, 165, 254, + 255, 255, 255, 255, 109, 0, 0, 0, 247, 255, 255, 255, 8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 111, 255, 255, 255, 255, 254, 165, 73, 26, 6, 27, 75, 167, 254, 255, 255, + 178, 255, 255, 255, 255, 0, 243, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 252, 255, 255, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 255, 255, 255, 255, 0, 255, 255, 255, 255, 166, 255, 255, 252, + 162, 81, 29, 8, 13, 45, 121, 236, 255, 255, 255, 255, 166, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 27, 210, 255, 255, 255, 255, 229, + 46, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 248, 255, 255, 255, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 255, 255, + 247, 0, 255, 255, 255, 255, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 255, 255, 255, 247, 0, 248, 255, 255, 255, 7, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 138, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, + 194, 99, 5, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 205, 255, 255, 255, 123, 0, 0, 0, 0, 0, 0, 124, + 255, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, + 255, 173, 0, 0, 3, 240, 255, 255, 255, 46, 0, 0, 0, 42, 255, 255, + 255, 242, 4, 0, 0, 176, 255, 255, 255, 116, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 5, 220, 255, 255, 255, 162, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 184, 255, 255, 255, 97, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 98, 255, 255, 255, 182, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 255, 255, + 255, 255, 97, 0, 184, 255, 255, 255, 97, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, + 255, 255, 255, 183, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 67, 231, 255, 255, 255, 250, 31, 0, 0, 0, 0, + 0, 0, 49, 255, 255, 255, 255, 255, 243, 156, 71, 8, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 123, 255, 255, 255, 205, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 202, 255, 255, 255, + 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 255, 255, 255, 187, 0, + 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 156, 0, 0, 0, 0, 153, + 255, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 190, 255, 255, 255, 97, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, + 255, 255, 255, 255, 255, 255, 234, 22, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 255, 255, 255, + 255, 255, 255, 255, 255, 129, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 238, 255, + 255, 255, 235, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 255, 255, + 255, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 208, 255, 255, 255, 255, 242, + 161, 102, 61, 34, 15, 8, 1, 0, 0, 0, 0, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 255, 255, 237, 62, 0, 0, 0, 0, 0, 0, 0, 63, + 237, 255, 255, 255, 241, 27, 0, 0, 247, 255, 255, 255, 8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, + 242, 255, 255, 255, 236, 62, 0, 0, 0, 0, 0, 0, 0, 65, 239, 255, + 255, 255, 255, 255, 255, 0, 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 241, 255, 255, 255, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 15, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 213, 40, + 0, 0, 0, 0, 0, 0, 0, 23, 211, 255, 255, 255, 250, 21, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 43, 226, 255, 255, 255, 255, 214, 30, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 248, 255, 255, 255, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 255, 255, + 247, 0, 255, 255, 255, 255, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 255, 255, 255, 247, 0, 248, 255, 255, 255, 7, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 48, 171, 249, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 224, 70, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 106, 255, 255, 255, 220, 1, 0, 0, 0, 0, 1, 221, + 255, 255, 255, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 255, 255, + 255, 238, 2, 0, 55, 255, 255, 255, 233, 1, 0, 0, 0, 0, 230, 255, + 255, 255, 58, 0, 3, 240, 255, 255, 255, 48, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 89, 255, 255, 255, 253, 45, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 214, 255, 255, 255, 51, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 51, 255, 255, 255, 212, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 112, 250, 255, 255, + 255, 238, 16, 0, 214, 255, 255, 255, 51, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, + 255, 255, 255, 212, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 6, 20, 55, 107, 193, 255, 255, 255, 255, 255, 141, 0, 0, 0, 0, 0, + 0, 0, 0, 172, 255, 255, 255, 255, 255, 255, 255, 247, 198, 144, 98, 54, + 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 28, 253, 255, 255, 255, + 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 255, 255, 255, 253, + 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 255, 255, 255, 245, 5, + 0, 0, 0, 0, 0, 0, 193, 255, 255, 255, 94, 0, 0, 0, 0, 91, + 255, 255, 255, 194, 0, 0, 0, 0, 0, 0, 7, 246, 255, 255, 255, 33, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 186, 255, 255, 255, 255, 255, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 212, 255, 255, + 255, 255, 255, 255, 211, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 215, 255, 255, + 255, 250, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 255, 255, + 255, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 255, 255, 255, 255, 157, 13, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 255, 247, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 54, 247, 255, 255, 255, 139, 0, 0, 229, 255, 255, 255, 24, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, + 255, 255, 255, 247, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 249, + 255, 255, 255, 255, 255, 0, 232, 255, 255, 255, 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 225, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 32, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 216, 15, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 38, 248, 255, 255, 255, 105, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 62, 239, 255, 255, 255, 255, 196, 17, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 232, 255, 255, 255, 22, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 255, 255, 255, + 230, 0, 255, 255, 255, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 22, 255, 255, 255, 231, 0, 233, 255, 255, 255, 21, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 19, 89, 155, 215, 254, 255, 255, 255, 255, + 255, 255, 255, 254, 95, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 16, 247, 255, 255, 255, 64, 0, 0, 0, 0, 65, 255, + 255, 255, 247, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 236, 255, + 255, 255, 51, 0, 123, 255, 255, 255, 167, 0, 0, 0, 0, 0, 164, 255, + 255, 255, 125, 0, 54, 255, 255, 255, 235, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 1, 205, 255, 255, 255, 181, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 237, 255, 255, 255, 32, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 33, 255, 255, 255, 236, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 7, 23, 64, 125, 219, 255, 255, 255, 255, + 255, 118, 0, 0, 237, 255, 255, 255, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, + 255, 255, 255, 236, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 188, 6, 0, 0, 0, 0, 0, + 0, 0, 0, 16, 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 254, 210, 147, 76, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 184, 255, 255, 255, + 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 255, 255, 255, 185, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 229, 255, 255, 255, 58, + 0, 0, 0, 0, 0, 7, 247, 255, 255, 255, 32, 0, 0, 0, 0, 29, + 255, 255, 255, 248, 7, 0, 0, 0, 0, 0, 62, 255, 255, 255, 224, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 81, 255, 255, 255, 255, 218, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 252, 255, + 255, 255, 255, 252, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 183, 255, 255, 255, + 255, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 246, 255, + 255, 255, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 255, 255, 255, 180, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 129, 255, 255, 255, 233, 5, 0, 210, 255, 255, 255, 54, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 234, + 255, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132, + 255, 255, 255, 255, 255, 0, 213, 255, 255, 255, 50, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 202, 255, 255, 255, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 76, 255, 255, 255, 255, 0, 255, 255, 255, 255, 254, 47, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 255, 255, 255, 159, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 84, 248, 255, 255, 255, 255, 174, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 215, 255, 255, 255, 49, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 255, 255, 255, + 213, 0, 255, 255, 255, 255, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 47, 255, 255, 255, 215, 0, 216, 255, 255, 255, 46, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 70, 129, 198, 254, + 255, 255, 255, 255, 249, 33, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 165, 255, 255, 255, 163, 0, 0, 0, 0, 164, 255, + 255, 255, 164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 255, + 255, 255, 118, 0, 190, 255, 255, 255, 99, 0, 0, 0, 0, 0, 96, 255, + 255, 255, 192, 0, 121, 255, 255, 255, 170, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 69, 255, 255, 255, 255, 62, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 248, 255, 255, 255, 16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 17, 255, 255, 255, 246, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 175, 3, 0, 0, 248, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, + 255, 255, 255, 246, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 249, 137, 7, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 20, 189, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 232, 130, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 86, 255, 255, 255, + 235, 5, 0, 0, 0, 0, 0, 0, 0, 0, 4, 232, 255, 255, 255, 87, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 255, 255, 255, 122, + 0, 0, 0, 0, 0, 62, 255, 255, 255, 225, 0, 0, 0, 0, 0, 0, + 224, 255, 255, 255, 62, 0, 0, 0, 0, 0, 126, 255, 255, 255, 161, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, + 220, 255, 255, 255, 255, 255, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 255, + 255, 255, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 255, 255, 255, 255, + 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 255, + 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 255, 255, 255, 64, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 242, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 243, 255, 255, 255, 72, 0, 171, 255, 255, 255, 112, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, + 255, 255, 242, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, + 244, 255, 255, 255, 255, 0, 181, 255, 255, 255, 103, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 151, 255, 255, 255, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 131, 255, 255, 255, 255, 0, 255, 255, 255, 255, 176, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 255, 255, 255, 207, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 110, 253, 255, 255, 255, 255, 148, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 182, 255, 255, 255, 101, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 255, + 181, 0, 255, 255, 255, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 95, 255, 255, 255, 180, 0, 182, 255, 255, 255, 94, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, + 151, 255, 255, 255, 255, 145, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 245, 255, 255, 255, 12, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 66, 255, 255, 255, 246, 15, 0, 0, 15, 246, 255, + 255, 255, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, + 255, 255, 186, 9, 248, 255, 255, 255, 31, 0, 0, 0, 0, 0, 29, 255, + 255, 255, 249, 10, 188, 255, 255, 255, 103, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 188, 255, 255, 255, 198, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 254, 255, 255, 255, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 4, 255, 255, 255, 253, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 164, + 8, 0, 0, 0, 254, 255, 255, 255, 3, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, + 255, 255, 255, 253, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 252, 93, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 91, 220, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 230, 73, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 7, 237, 255, 255, + 255, 83, 0, 0, 0, 0, 0, 0, 0, 0, 79, 255, 255, 255, 238, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 255, 255, 255, 186, + 0, 0, 0, 0, 0, 124, 255, 255, 255, 163, 0, 0, 0, 0, 0, 0, + 162, 255, 255, 255, 124, 0, 0, 0, 0, 0, 189, 255, 255, 255, 98, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, + 255, 255, 255, 255, 255, 255, 243, 31, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 255, + 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 191, + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 255, + 255, 255, 202, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 239, 255, 255, 255, 15, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 160, 255, 255, 255, 127, 0, 109, 255, 255, 255, 187, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 255, + 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 161, 255, 255, 255, 255, 0, 117, 255, 255, 255, 180, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 97, 255, 255, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 222, 255, 255, 255, 255, 0, 255, 255, 255, 255, 93, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 255, 255, 255, 229, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 1, 138, 255, 255, 255, 255, 255, 122, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 127, 255, 255, 255, 171, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175, 255, 255, 255, + 125, 0, 255, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 159, 255, 255, 255, 127, 0, 129, 255, 255, 255, 159, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 133, 255, 255, 255, 212, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 230, 255, 255, 255, 34, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 1, 222, 255, 255, 255, 104, 0, 0, 105, 255, 255, + 255, 221, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, + 255, 255, 246, 76, 255, 255, 255, 220, 0, 0, 0, 0, 0, 0, 0, 218, + 255, 255, 255, 79, 247, 255, 255, 255, 35, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 51, 254, 255, 255, 255, + 81, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 248, 255, 255, 255, 15, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 16, 255, 255, 255, 246, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 205, 80, 0, + 0, 0, 0, 0, 248, 255, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, + 255, 255, 255, 243, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 254, 114, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 66, 158, 227, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 252, 98, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 147, 255, 255, + 255, 180, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 148, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 255, 255, 255, 244, + 5, 0, 0, 0, 0, 186, 255, 255, 255, 101, 0, 0, 0, 0, 0, 0, + 100, 255, 255, 255, 186, 0, 0, 0, 0, 6, 246, 255, 255, 255, 34, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 255, + 255, 255, 255, 255, 255, 255, 255, 186, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 249, 255, 255, 255, 222, 19, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 254, + 255, 255, 254, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 255, 255, 255, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 234, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 95, 255, 255, 255, 180, 0, 45, 255, 255, 255, 255, 44, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 182, 255, + 255, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 96, 255, 255, 255, 255, 0, 52, 255, 255, 255, 252, 40, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 24, 251, 255, 255, 255, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 88, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 36, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 255, 255, 255, 244, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 5, 164, 255, 255, 255, 255, 251, 95, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 71, 255, 255, 255, 249, 21, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 251, 255, 255, 255, + 69, 0, 255, 255, 255, 255, 242, 10, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 242, 255, 255, 255, 72, 0, 73, 255, 255, 255, 242, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 243, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 255, 255, 255, 242, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 207, 255, 255, 255, 82, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 125, 255, 255, 255, 203, 0, 0, 204, 255, 255, + 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, + 255, 255, 255, 200, 255, 255, 255, 152, 0, 0, 0, 0, 0, 0, 0, 150, + 255, 255, 255, 204, 255, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 168, 255, 255, 255, + 214, 3, 0, 0, 0, 0, 255, 255, 255, 255, 0, 238, 255, 255, 255, 32, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 33, 255, 255, 255, 236, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 248, 233, 216, 174, 121, 50, 0, 0, 0, + 0, 0, 0, 0, 238, 255, 255, 255, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, + 255, 255, 255, 231, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 5, + 20, 57, 120, 215, 255, 255, 255, 255, 254, 78, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 90, 134, 178, 224, + 255, 255, 255, 255, 255, 255, 255, 255, 251, 59, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 50, 255, 255, + 255, 252, 25, 0, 0, 0, 0, 0, 0, 22, 251, 255, 255, 255, 51, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, 255, 255, 255, + 57, 0, 0, 0, 4, 243, 255, 255, 255, 39, 0, 0, 0, 0, 0, 0, + 38, 255, 255, 255, 243, 3, 0, 0, 0, 61, 255, 255, 255, 225, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 231, 255, + 255, 255, 210, 117, 255, 255, 255, 255, 101, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 29, 232, 255, 255, 255, 243, 44, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, + 255, 255, 255, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 255, 255, 255, 49, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 47, 255, 255, 255, 215, 0, 0, 200, 255, 255, 255, 195, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 255, + 255, 255, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 48, 255, 255, 255, 255, 0, 1, 209, 255, 255, 255, 182, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 174, 255, 255, 255, 230, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 28, 232, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 14, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255, 255, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 188, 255, 255, 255, 255, 243, 72, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 6, 232, 255, 255, 255, 155, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 255, 255, 255, 230, + 5, 0, 255, 255, 255, 255, 255, 126, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 127, 255, 255, 255, 233, 5, 0, 5, 234, 255, 255, 255, 126, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 9, 255, 255, 255, 245, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 255, 161, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 49, 254, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 29, 253, 255, 255, 255, 46, 46, 255, 255, 255, + 253, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 158, + 255, 255, 255, 255, 255, 255, 255, 84, 0, 0, 0, 0, 0, 0, 0, 83, + 255, 255, 255, 255, 255, 255, 255, 157, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 36, 250, 255, 255, + 255, 99, 0, 0, 0, 0, 255, 255, 255, 255, 0, 215, 255, 255, 255, 50, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 51, 255, 255, 255, 212, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 214, 255, 255, 255, 50, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, + 255, 255, 255, 215, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 122, 254, 255, 255, 255, 233, 18, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 22, 84, 162, 245, 255, 255, 255, 255, 255, 214, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, + 255, 255, 119, 0, 0, 0, 0, 0, 0, 115, 255, 255, 255, 209, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, 255, 255, 255, + 121, 0, 0, 0, 55, 255, 255, 255, 232, 0, 0, 0, 0, 0, 0, 0, + 0, 231, 255, 255, 255, 53, 0, 0, 0, 125, 255, 255, 255, 162, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, + 255, 249, 47, 2, 195, 255, 255, 255, 241, 29, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 10, 206, 255, 255, 255, 254, 78, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, + 255, 255, 255, 178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 255, 255, 255, 154, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 72, 252, 255, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 22, 255, 255, 255, 231, 0, 0, 82, 255, 255, 255, 255, 150, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 255, + 255, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 22, 255, 255, 255, 255, 0, 0, 88, 255, 255, 255, 255, 140, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 59, 254, 255, 255, 255, 217, 38, 0, 0, 0, 0, 0, 0, 0, 40, + 219, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 233, 51, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 132, 255, 255, 255, 254, 85, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 255, 255, 255, 255, 128, + 0, 0, 255, 255, 255, 255, 255, 246, 51, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 51, 247, 255, 255, 255, 139, 0, 0, 0, 141, 255, 255, 255, 246, + 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 248, 255, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 73, 255, 255, 255, 217, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 255, 247, 36, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 16, 218, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 184, 255, 255, 255, 144, 145, 255, 255, 255, + 183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, + 255, 255, 255, 255, 255, 255, 253, 18, 0, 0, 0, 0, 0, 0, 0, 17, + 253, 255, 255, 255, 255, 255, 255, 90, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 255, 255, + 255, 227, 8, 0, 0, 0, 255, 255, 255, 255, 0, 185, 255, 255, 255, 96, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 97, 255, 255, 255, 183, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 184, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, + 255, 255, 255, 178, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 116, 255, 255, 255, 255, 144, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 18, 139, 252, 255, 255, 255, 255, 77, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 247, 255, 255, 255, 14, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16, 255, 255, 255, 246, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, + 255, 255, 216, 0, 0, 0, 0, 0, 0, 212, 255, 255, 255, 112, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 255, + 185, 0, 0, 0, 117, 255, 255, 255, 170, 0, 0, 0, 0, 0, 0, 0, + 0, 170, 255, 255, 255, 115, 0, 0, 0, 189, 255, 255, 255, 98, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 255, 255, 255, + 255, 121, 0, 0, 37, 246, 255, 255, 255, 183, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 171, 255, 255, 255, 255, 123, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, + 255, 255, 255, 246, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 254, 105, 0, + 0, 0, 0, 0, 0, 0, 1, 102, 250, 255, 255, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 7, 255, 255, 255, 247, 0, 0, 2, 191, 255, 255, 255, 255, 179, + 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 81, 197, 0, 248, 255, + 255, 255, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 255, 255, 255, 255, 0, 0, 2, 198, 255, 255, 255, 255, 176, 26, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 111, 207, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 154, 255, 255, 255, 255, 249, 150, 65, 22, 5, 23, 66, 151, 250, + 255, 255, 178, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 182, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 23, 237, 255, 255, 255, 251, + 102, 1, 0, 0, 0, 0, 0, 0, 2, 109, 252, 255, 255, 255, 235, 22, + 0, 0, 255, 255, 255, 255, 255, 255, 235, 60, 0, 0, 0, 0, 0, 0, + 0, 60, 235, 255, 255, 255, 242, 27, 0, 0, 0, 28, 242, 255, 255, 255, + 235, 60, 0, 0, 0, 0, 0, 0, 0, 63, 237, 255, 255, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 209, 117, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 41, 225, 255, 255, 255, 149, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 21, 250, 255, 255, 255, 208, 21, 0, + 0, 0, 0, 0, 0, 0, 41, 215, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 85, 255, 255, 255, 236, 236, 255, 255, 255, + 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, + 255, 255, 255, 255, 255, 255, 205, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 204, 255, 255, 255, 255, 255, 255, 23, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 244, 255, + 255, 255, 118, 0, 0, 0, 255, 255, 255, 255, 0, 141, 255, 255, 255, 151, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 152, 255, 255, 255, 138, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 139, 255, 255, 255, 151, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, + 255, 255, 255, 139, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 179, 255, 255, 255, 248, 27, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 80, 252, 255, 255, 255, 153, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, 255, 255, 255, 27, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 29, 255, 255, 255, 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 249, + 255, 255, 255, 58, 0, 0, 0, 0, 53, 255, 255, 255, 250, 21, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 255, 255, + 243, 4, 0, 0, 179, 255, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, + 0, 108, 255, 255, 255, 177, 0, 0, 6, 246, 255, 255, 255, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 240, 255, 255, 255, + 197, 3, 0, 0, 0, 114, 255, 255, 255, 255, 98, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 168, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 225, 255, 255, 255, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 249, 255, 255, 255, 255, 185, + 82, 26, 5, 15, 42, 108, 206, 255, 255, 255, 171, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 7, 255, 255, 255, 247, 0, 0, 0, 25, 233, 255, 255, 255, 255, + 249, 161, 88, 36, 15, 5, 20, 46, 95, 156, 235, 255, 255, 0, 248, 255, + 255, 255, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 255, 255, 255, 255, 0, 0, 0, 27, 233, 255, 255, 255, 255, 249, 164, + 92, 38, 16, 3, 12, 25, 54, 94, 140, 207, 254, 255, 255, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 15, 209, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 235, 29, 255, 255, 255, 253, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 230, 255, 255, 255, 255, 184, 10, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 89, 255, 255, 255, 255, + 255, 201, 103, 37, 12, 11, 38, 104, 203, 255, 255, 255, 255, 255, 84, 0, + 0, 0, 255, 255, 255, 255, 179, 255, 255, 254, 164, 72, 25, 6, 25, 72, + 164, 254, 255, 255, 255, 255, 110, 0, 0, 0, 0, 0, 111, 255, 255, 255, + 255, 254, 163, 71, 24, 6, 25, 73, 166, 254, 255, 255, 178, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 216, 150, 100, 59, 27, 13, 2, 9, 33, 79, 158, + 248, 255, 255, 255, 252, 47, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 255, 255, 255, 255, 234, 119, + 43, 11, 8, 29, 81, 163, 252, 255, 255, 166, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 6, 236, 255, 255, 255, 255, 255, 255, 235, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 212, 255, 255, 255, 255, 255, 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 137, 255, 255, 255, 255, 255, 211, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 255, + 255, 255, 238, 16, 0, 0, 255, 255, 255, 255, 0, 89, 255, 255, 255, 213, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 212, 255, 255, 255, 86, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 86, 255, 255, 255, 213, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, + 255, 255, 255, 91, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 27, 246, 255, 255, 255, 137, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 149, 255, 255, 255, 211, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 214, 255, 255, 255, 58, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 60, 255, 255, 255, 212, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, + 255, 255, 255, 156, 0, 0, 0, 0, 150, 255, 255, 255, 174, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 255, + 255, 56, 0, 2, 239, 255, 255, 255, 45, 0, 0, 0, 0, 0, 0, 0, + 0, 46, 255, 255, 255, 237, 1, 0, 60, 255, 255, 255, 225, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 189, 255, 255, 255, 244, + 35, 0, 0, 0, 0, 2, 198, 255, 255, 255, 240, 27, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 83, 254, 255, 255, 255, 204, 9, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 149, 255, 255, 255, 155, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 222, 20, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 22, 255, 255, 255, 231, 0, 0, 0, 0, 55, 235, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 233, 255, + 255, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 22, 255, 255, 255, 255, 0, 0, 0, 0, 54, 233, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 21, 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 240, 57, 4, 255, 255, 255, 239, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 35, 223, 255, 255, 255, 255, 195, 14, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 144, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 139, 0, 0, + 0, 0, 255, 255, 255, 255, 29, 235, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 173, 2, 0, 0, 0, 0, 0, 2, 173, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 234, 29, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 128, 0, 0, 0, 0, 0, 253, 255, 255, 255, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 238, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 212, 15, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, 255, 255, 255, 255, 143, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 145, 255, 255, 255, 255, 255, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 69, 255, 255, 255, 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 234, + 255, 255, 255, 136, 0, 0, 255, 255, 255, 255, 0, 22, 254, 255, 255, 255, + 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 55, 255, 255, 255, 253, 20, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 19, 253, 255, 255, 255, 55, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 255, + 255, 255, 254, 21, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 134, 255, 255, 255, 239, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 51, 255, 255, 255, 235, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 185, 255, 255, 255, 107, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 109, 255, 255, 255, 183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, + 255, 255, 255, 242, 10, 0, 0, 8, 239, 255, 255, 255, 76, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 255, 255, + 255, 120, 0, 48, 255, 255, 255, 237, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 238, 255, 255, 255, 45, 0, 124, 255, 255, 255, 162, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 255, 255, 103, + 0, 0, 0, 0, 0, 0, 39, 247, 255, 255, 255, 180, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 48, 244, 255, 255, 255, 231, 28, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 71, 255, 255, 255, 231, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 175, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 222, 37, 0, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 47, 255, 255, 255, 215, 0, 0, 0, 0, 0, 35, 215, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 216, 255, + 255, 255, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 48, 255, 255, 255, 255, 0, 0, 0, 0, 0, 30, 206, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 11, 143, 252, 255, 255, 255, 255, 255, 255, 255, 255, 202, + 41, 0, 21, 255, 255, 255, 224, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 28, 215, 255, 255, 255, 255, 205, 20, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 129, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 125, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 57, 241, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 182, 6, 0, 0, 0, 0, 0, 0, 0, 6, 183, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 240, 57, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 131, 0, 0, 0, 0, 0, 0, 241, 255, 255, 255, 13, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 245, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 213, 27, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 45, 255, 255, 255, 255, 255, 255, 45, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 78, 255, 255, 255, 255, 248, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 248, 255, 255, 255, 255, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, + 255, 255, 255, 246, 27, 0, 255, 255, 255, 255, 0, 0, 195, 255, 255, 255, + 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 159, 255, 255, 255, 192, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 190, 255, 255, 255, 160, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, + 255, 255, 203, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 16, 238, 255, 255, 255, 133, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 11, 255, 255, 255, 250, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 255, 255, 255, 169, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 171, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, + 229, 255, 255, 255, 95, 0, 0, 89, 255, 255, 255, 231, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 255, 255, + 255, 183, 0, 110, 255, 255, 255, 177, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 179, 255, 255, 255, 107, 0, 188, 255, 255, 255, 98, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 247, 255, 255, 255, 182, 0, + 0, 0, 0, 0, 0, 0, 0, 117, 255, 255, 255, 255, 94, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 22, 225, 255, 255, 255, 248, 57, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 242, 255, 255, 255, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 130, 247, 255, 255, + 255, 255, 255, 255, 255, 255, 254, 164, 20, 0, 0, 255, 255, 255, 255, 0, + 255, 255, 255, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 95, 255, 255, 255, 180, 0, 0, 0, 0, 0, 0, 6, 111, 233, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 180, 60, 0, 182, 255, + 255, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 96, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 4, 98, 221, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 232, 144, 48, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 34, 122, 196, 227, 250, 245, 215, 161, 74, 1, + 0, 0, 47, 255, 255, 255, 207, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 21, 207, 255, 255, 255, 255, 213, 26, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 60, 202, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 200, 57, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 41, 202, 255, 255, 255, 255, 255, 255, + 255, 255, 246, 116, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 117, + 247, 255, 255, 255, 255, 255, 255, 255, 255, 202, 41, 0, 0, 255, 255, 255, + 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 46, 137, 224, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 203, 60, 0, 0, 0, 0, 0, 0, 0, 225, 255, 255, 255, 47, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 209, 255, 255, 255, + 255, 255, 255, 255, 255, 253, 156, 14, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, + 222, 255, 255, 255, 155, 0, 255, 255, 255, 255, 0, 0, 99, 255, 255, 255, + 250, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 47, 250, 255, 255, 255, 95, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 92, 255, 255, 255, 250, 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 250, 255, + 255, 255, 106, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 119, 255, 255, 255, 244, 23, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 16, 255, 255, 255, 235, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 255, 255, 255, 247, 16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, + 248, 255, 255, 255, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 135, 255, 255, 255, 192, 0, 0, 186, 255, 255, 255, 137, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 255, 255, + 255, 242, 4, 172, 255, 255, 255, 115, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 117, 255, 255, 255, 168, 6, 245, 255, 255, 255, 35, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 4, 204, 255, 255, 255, 237, 25, 0, + 0, 0, 0, 0, 0, 0, 0, 3, 200, 255, 255, 255, 238, 25, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 6, 196, 255, 255, 255, 255, 95, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 172, 255, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 123, 190, + 230, 249, 248, 230, 191, 128, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 159, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 8, + 73, 145, 201, 223, 244, 249, 235, 209, 163, 103, 23, 0, 0, 0, 129, 255, + 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 161, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 4, 62, 130, + 191, 218, 238, 252, 242, 229, 200, 161, 114, 47, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 102, 255, 255, 255, 163, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 15, 197, 255, 255, 255, 255, 221, 33, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 58, 134, 197, 223, 245, 245, 223, 197, 133, 57, 1, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 1, 75, 161, 216, 245, 249, 224, + 191, 111, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 23, 112, 192, 225, 249, 245, 215, 161, 74, 1, 0, 0, 0, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 40, 107, 158, 201, 231, 246, 251, 238, 220, 178, 127, + 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 185, 255, 255, 255, 127, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 81, 159, 216, + 238, 252, 239, 201, 136, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 91, 255, 255, 255, 252, 40, 255, 255, 255, 255, 0, 0, 8, 230, 255, 255, + 255, 208, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 207, 255, 255, 255, 226, 6, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 223, 255, 255, 255, 208, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 207, 255, 255, + 255, 240, 11, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 10, 230, 255, 255, 255, 147, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 58, 255, 255, 255, 212, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 235, 255, 255, 255, 138, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 142, + 255, 255, 255, 234, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 38, 255, 255, 255, 254, 35, 29, 253, 255, 255, 255, 40, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 255, + 255, 255, 55, 233, 255, 255, 255, 52, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 55, 255, 255, 255, 229, 60, 255, 255, 255, 226, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 130, 255, 255, 255, 255, 86, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 42, 248, 255, 255, 255, 177, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 158, 255, 255, 255, 255, 141, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 94, 255, 255, 255, 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 242, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 242, 255, 255, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 255, + 255, 255, 242, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, + 243, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 181, 255, 255, 255, 113, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 10, 186, 255, 255, 255, 255, 228, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 248, 125, + 44, 11, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 207, 255, 255, 255, 174, 255, 255, 255, 255, 0, 0, 0, 101, 255, 255, + 255, 255, 164, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 160, 255, 255, 255, 255, 97, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 91, 255, 255, 255, 255, 164, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 160, 255, 255, 255, + 255, 124, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 255, 255, 255, 249, 32, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 159, 255, 255, 255, 163, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 253, + 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 254, + 255, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 196, 255, 255, 255, 131, 124, 255, 255, 255, 199, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 255, + 255, 255, 160, 255, 255, 255, 242, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 244, 255, 255, 255, 160, 255, 255, 255, 163, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 55, 252, 255, 255, 255, 166, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, 255, 255, 255, 91, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 113, 255, 255, 255, 255, 183, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 20, 252, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 127, 255, 255, 255, 233, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 234, + 255, 255, 255, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 45, 254, 255, 255, 255, 53, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 7, 175, 255, 255, 255, 255, 235, 49, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 72, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 1, 195, 255, + 255, 255, 255, 171, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 7, 166, 255, 255, 255, 255, 191, 1, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 184, 255, 255, 255, 255, 171, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 166, 255, 255, 255, 255, + 216, 6, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 221, 255, 255, 255, 160, 0, + 0, 0, 201, 91, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 86, 255, 255, 255, 255, 83, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 239, 255, 255, 255, + 251, 117, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 119, 252, 255, + 255, 255, 237, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 99, 255, 255, 255, 226, 220, 255, 255, 255, 101, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255, + 255, 255, 251, 255, 255, 255, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 187, 255, 255, 255, 251, 255, 255, 255, 99, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 10, 217, 255, 255, 255, 228, 17, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 203, 255, 255, 255, 236, 23, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 71, 252, 255, 255, 255, 217, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 195, 255, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 246, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 51, 247, 255, 255, 255, 139, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, + 255, 255, 255, 246, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 248, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 11, 211, 255, 255, 255, 212, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 4, 163, 255, 255, 255, 255, 240, 59, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 158, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 190, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 27, 233, + 255, 255, 255, 255, 221, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 73, 219, 255, 255, 255, 255, 231, 25, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 21, 226, 255, 255, 255, 255, 221, 78, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 219, 255, 255, 255, 255, 247, + 54, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 255, 255, 255, 253, 42, + 0, 0, 255, 255, 232, 142, 61, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 13, 132, 253, 255, 255, 255, 221, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 255, + 255, 255, 232, 140, 79, 34, 16, 4, 16, 33, 78, 140, 232, 255, 255, 255, + 255, 254, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 12, 244, 255, 255, 255, 255, 255, 255, 246, 14, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 255, + 255, 255, 255, 255, 255, 255, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 125, 255, 255, 255, 255, 255, 255, 255, 35, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 149, 255, 255, 255, 254, 70, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 249, 255, 255, 255, 173, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 239, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 118, 255, 255, 255, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 235, 60, 0, 0, 0, 0, 0, 0, 0, 60, + 235, 255, 255, 255, 242, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, + 242, 255, 255, 255, 235, 60, 0, 0, 0, 0, 0, 0, 0, 63, 237, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 195, 75, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, + 209, 255, 255, 255, 255, 110, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 1, 150, 255, 255, 255, 255, 245, + 70, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 159, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 53, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 52, + 241, 255, 255, 255, 255, 255, 214, 134, 64, 35, 12, 10, 34, 61, 130, 210, + 255, 255, 255, 255, 255, 239, 49, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 42, 234, 255, 255, 255, 255, 255, 214, + 134, 64, 35, 12, 10, 34, 61, 130, 210, 255, 255, 255, 255, 255, 254, 85, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 211, 255, 255, 255, 174, + 0, 0, 255, 255, 255, 255, 255, 234, 170, 121, 72, 39, 23, 6, 7, 19, + 47, 89, 158, 238, 255, 255, 255, 255, 255, 73, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 160, 255, 255, 255, 255, 255, 255, 163, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 234, + 255, 255, 255, 255, 255, 255, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 63, 255, 255, 255, 255, 255, 255, 226, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 71, 255, 255, 255, 255, 148, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 255, 255, 255, 255, + 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 40, 255, 255, 255, 249, 13, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 179, 255, 255, 254, 164, 72, 25, 6, 25, 72, 164, 254, + 255, 255, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 111, 255, 255, 255, 255, 254, 163, 71, 24, 6, 25, 73, 166, 254, 255, 255, + 178, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 229, 146, 86, 39, 16, 4, 16, 38, 93, 170, 252, + 255, 255, 255, 255, 208, 5, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 137, 255, 255, 255, 255, + 249, 81, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 135, 188, + 227, 243, 254, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 171, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 51, 228, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 228, 49, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 220, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 245, 84, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 255, 255, 255, 255, + 54, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 254, 110, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 246, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 245, + 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 62, 255, 255, 255, 255, 255, 255, 65, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, + 255, 255, 255, 255, 255, 246, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 248, 255, 255, 255, 255, 255, 163, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 17, 229, 255, 255, 255, 217, 10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 206, 255, 255, 255, + 235, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 219, 255, 255, 255, 85, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 29, 235, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 173, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 173, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 234, + 29, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 242, 46, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, 255, 255, 255, + 255, 252, 93, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 37, 251, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 23, 175, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 176, 24, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 163, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 209, 50, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255, + 188, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 233, 88, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 146, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 146, 29, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 220, 255, 255, 255, 255, 223, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, + 255, 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 195, 255, 255, 255, 255, 255, 99, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 168, 255, 255, 255, 252, 55, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 250, 255, 255, + 255, 171, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 141, 255, 255, 255, 162, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 57, 241, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 182, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 6, 183, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 240, 57, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 230, 48, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 254, 255, + 255, 255, 254, 105, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 68, 191, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 192, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 180, 253, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 134, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 37, 111, 185, 245, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 227, 134, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 17, 83, 145, 199, 220, 238, 251, 239, 221, 199, 146, 83, 17, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 64, 255, 255, 255, 236, 3, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 41, 202, 255, 255, 255, 255, 255, 255, 255, 255, + 246, 116, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 117, 247, 255, 255, 255, 255, 255, 255, 255, 255, 202, 41, 0, + 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 60, 179, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 248, 147, 24, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 252, + 255, 255, 255, 255, 120, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 33, 110, 170, 210, 238, 250, 250, 238, 211, 171, 111, + 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 103, + 164, 205, 235, 249, 255, 248, 234, 255, 255, 255, 255, 220, 27, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4, 49, 104, 148, 183, 216, 234, 246, 252, 240, 227, + 209, 167, 121, 62, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4, 238, 255, 255, 255, 61, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 75, 161, 216, 245, 249, 224, 191, 111, + 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 23, 112, 192, 225, 249, 245, 215, 161, 74, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 23, 102, 161, 206, 234, 248, 248, 230, 210, 164, 98, + 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2, 255, 255, 255, 252, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3, 168, 255, 255, 255, 255, 217, 24, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 165, 255, 255, 255, 139, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 10, 255, 255, 255, 240, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 4, 171, 255, 255, 255, 255, 214, 22, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 87, 255, 255, 255, 217, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 255, 255, 226, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 4, 175, 255, 255, 255, 255, 211, 20, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, 255, 191, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 179, 255, 255, 255, 255, 208, + 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 156, 255, 255, 255, 147, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 182, 255, 255, 255, 255, + 205, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4, 34, 133, 254, 255, 255, 255, 72, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 225, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 82, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 248, 104, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 253, 237, 206, 138, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 164, 255, 255, 255, 254, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 73, 254, 255, 255, 255, 164, 0, 0, 203, 255, 255, 255, 136, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 142, + 255, 255, 255, 201, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 32, 115, 170, 209, 232, 244, 254, 255, 0, 255, 255, + 255, 255, 0, 255, 254, 244, 233, 210, 171, 118, 33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 14, 222, 255, 255, 255, 232, 22, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 23, 233, 255, 255, 255, 222, 14, 0, 0, 98, 255, 255, 255, 234, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 239, + 255, 255, 255, 92, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 9, 151, 254, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 154, 10, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 119, 188, 223, 245, 246, + 222, 178, 116, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 122, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 56, 251, 255, 255, 255, 181, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 182, 255, 255, 255, 252, 57, 0, 0, 0, 9, 238, 255, 255, 255, 92, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, + 255, 255, 233, 6, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 158, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 160, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 56, 188, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 210, 121, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 70, 209, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 124, 255, 255, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, + 111, 255, 255, 255, 255, 126, 0, 0, 0, 0, 0, 142, 255, 255, 255, 198, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 211, 255, + 255, 255, 131, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 0, 0, 0, 0, 0, 0, + 0, 0, 39, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 39, 0, 0, + 0, 0, 0, 0, 0, 0, 15, 157, 254, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 201, 127, 66, 23, 5, 23, 54, 125, 209, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 193, 255, 255, 255, 247, 45, 0, 0, 0, 0, 0, 46, + 248, 255, 255, 255, 195, 3, 0, 0, 0, 0, 0, 37, 254, 255, 255, 255, + 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 255, 255, + 255, 251, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 203, 255, 255, 255, 255, 102, 0, 0, 0, 0, 0, 0, + 0, 0, 129, 255, 255, 255, 255, 173, 75, 30, 10, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 9, 28, 73, 169, 255, 255, 255, 255, 129, 0, 0, + 0, 0, 0, 0, 0, 0, 225, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 29, 238, 255, 255, 255, 212, 8, 0, 0, 0, 9, 213, + 255, 255, 255, 240, 31, 0, 0, 0, 0, 0, 0, 0, 186, 255, 255, 255, + 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 255, 255, + 255, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 175, 255, 255, 255, 255, 141, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 181, 255, 255, 255, 169, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 168, 255, 255, 255, 180, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 215, 129, 56, 25, 5, 24, + 67, 129, 202, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 160, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 84, 255, 255, 255, 255, 149, 0, 0, 0, 150, 255, + 255, 255, 255, 88, 0, 0, 0, 0, 0, 0, 0, 0, 81, 255, 255, 255, + 244, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 252, 255, 255, + 255, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 140, 255, 255, 255, 255, 177, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 222, 255, 255, 255, 68, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 68, 255, 255, 255, 221, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 221, 81, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 34, 123, 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 193, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 156, 255, 255, 255, 255, 77, 0, 77, 255, 255, + 255, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 227, 255, 255, + 255, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 255, 255, 255, + 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 104, 255, 255, 255, 255, 206, 11, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 239, 255, 255, 255, 23, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 24, 255, 255, 255, 237, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 149, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 44, 118, 179, 223, 247, 246, 224, 190, 123, 46, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 11, 217, 255, 255, 255, 234, 50, 235, 255, 255, + 255, 220, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 255, 255, + 255, 213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 235, 255, 255, 255, + 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 71, 251, 255, 255, 255, 229, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 250, 255, 255, 255, 5, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 7, 255, 255, 255, 250, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 50, 249, 255, 255, 255, 250, 255, 255, 255, + 251, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 250, 255, + 255, 255, 64, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 238, + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, + 241, 255, 255, 255, 245, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 255, 255, 255, 255, 255, + 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 255, + 255, 255, 170, 0, 0, 0, 0, 0, 0, 0, 0, 205, 255, 255, 255, 139, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 225, + 255, 255, 255, 253, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 186, 255, 255, 255, 255, 255, 193, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 255, + 255, 255, 250, 25, 0, 0, 0, 0, 0, 0, 58, 255, 255, 255, 253, 33, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 203, 255, + 255, 255, 255, 117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, 255, 255, 254, 36, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 213, + 255, 255, 255, 125, 0, 0, 0, 0, 0, 0, 166, 255, 255, 255, 178, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 174, 255, 255, + 255, 255, 155, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 12, 218, 255, 255, 255, 255, 255, 146, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, + 255, 255, 255, 227, 3, 0, 0, 0, 0, 25, 250, 255, 255, 255, 69, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 255, 255, 255, + 255, 189, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 161, 255, 255, 255, 255, 255, 255, 255, + 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, + 243, 255, 255, 255, 81, 0, 0, 0, 0, 128, 255, 255, 255, 216, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 255, 255, 255, 255, + 216, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 255, 255, 255, 254, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 254, 255, 255, 255, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 91, 255, 255, 255, 255, 252, 255, 255, 255, + 241, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 152, 255, 255, 255, 187, 0, 0, 0, 5, 230, 255, 255, 255, 108, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 251, 255, 255, 255, 236, + 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 18, 255, 255, 255, 246, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 247, 255, 255, 255, 17, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 35, 242, 255, 255, 255, 213, 61, 249, 255, 255, + 255, 205, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 47, 255, 255, 255, 254, 37, 0, 0, 89, 255, 255, 255, 242, 13, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 241, 255, 255, 255, 249, 61, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 56, 255, 255, 255, 228, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 230, 255, 255, 255, 55, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 202, 255, 255, 255, 247, 45, 0, 107, 255, 255, + 255, 255, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 197, 255, 255, 255, 142, 0, 0, 198, 255, 255, 255, 147, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 23, 224, 255, 255, 255, 255, 94, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 128, 255, 255, 255, 194, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 196, 255, 255, 255, 127, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 139, 255, 255, 255, 255, 105, 0, 0, 0, 169, 255, + 255, 255, 255, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 91, 255, 255, 255, 238, 9, 50, 255, 255, 255, 254, 39, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 9, 202, 255, 255, 255, 255, 132, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 33, 239, 255, 255, 255, 134, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 137, 255, 255, 255, 238, 33, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 70, 254, 255, 255, 255, 174, 0, 0, 0, 0, 13, 218, + 255, 255, 255, 242, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 6, 234, 255, 255, 255, 98, 159, 255, 255, 255, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 173, 255, 255, 255, 255, 169, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 15, 51, 123, + 236, 255, 255, 255, 252, 38, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 41, 253, 255, 255, 255, 236, + 123, 51, 15, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 23, 232, 255, 255, 255, 225, 17, 0, 0, 0, 0, 0, 48, + 247, 255, 255, 255, 208, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 135, 255, 255, 255, 213, 247, 255, 255, 255, 77, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 255, 200, 8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 254, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 254, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 184, 255, 255, 255, 252, 60, 0, 0, 0, 0, 0, 0, 0, + 102, 255, 255, 255, 255, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 32, 253, 255, 255, 255, 255, 255, 255, 222, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 102, 255, 255, 255, 255, 225, 23, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 252, 187, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 187, 251, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 116, 255, 255, 255, 255, 125, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 164, 255, 255, 255, 255, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 180, 255, 255, 255, 255, 255, 255, 116, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, + 255, 248, 175, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 174, 248, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 52, 250, 255, 255, 255, 191, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 12, 215, 255, 255, 255, 243, 40, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 74, 255, 255, 255, 255, 255, 245, 17, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 251, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 252, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 13, 219, 255, 255, 255, 236, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 44, 246, 255, 255, 255, 211, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 221, 255, 255, 255, 255, 155, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 2, 16, 54, 129, + 240, 255, 255, 255, 248, 31, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 34, 249, 255, 255, 255, 239, + 127, 53, 16, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 163, 255, 255, 255, 255, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 97, 255, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 120, 255, 255, 255, 255, 46, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 40, 242, 255, 255, 255, 129, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 132, 255, 255, 255, 241, 37, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 151, 255, 255, 255, 193, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 134, 255, 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 193, 255, 255, 255, 132, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 24, 244, 255, 255, 255, 85, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 255, 255, 227, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 228, 255, 255, 255, 58, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 172, 255, 255, 255, 228, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 20, 255, 255, 255, 245, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 247, 255, 255, 255, 19, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 93, 255, 255, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 255, 255, 255, 254, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 254, 255, 255, 255, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 51, 244, 255, 255, 255, 245, 22, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 31, + 115, 240, 255, 255, 255, 255, 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 231, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 253, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 245, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 254, + 240, 205, 135, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 250, 255, 255, 255, 5, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 7, 255, 255, 255, 250, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 239, 255, 255, 255, 22, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 23, 255, 255, 255, 237, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 222, 255, 255, 255, 67, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 67, 255, 255, 255, 221, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 181, 255, 255, 255, 167, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 165, 255, 255, 255, 180, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 129, 255, 255, 255, 255, 169, 74, 28, 8, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 8, 27, 71, 166, 255, 255, 255, 255, 129, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 39, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 39, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 158, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 160, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 9, 153, 254, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 156, 10, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 32, 116, 171, 210, 234, 245, 254, 255, 0, 255, 255, + 255, 255, 0, 255, 254, 245, 235, 212, 172, 118, 35, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + +Font DejaVu = {56, 45, -11, DejaVu_chars, DejaVu_kern, 1016, 141, DejaVu_tex}; + +}} + diff --git a/Samples/CommonSrc/Render/Render_GL_Device.cpp b/Samples/CommonSrc/Render/Render_GL_Device.cpp new file mode 100644 index 0000000..d2ba42e --- /dev/null +++ b/Samples/CommonSrc/Render/Render_GL_Device.cpp @@ -0,0 +1,849 @@ +/************************************************************************************ + +Filename : Render_GL_Device.cpp +Content : RenderDevice implementation for OpenGL +Created : September 10, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#include "../Render/Render_GL_Device.h" +#include "Kernel/OVR_Log.h" + +namespace OVR { namespace Render { namespace GL { + + + +static const char* StdVertexShaderSrc = + "uniform mat4 Proj;\n" + "uniform mat4 View;\n" + "attribute vec4 Position;\n" + "attribute vec4 Color;\n" + "attribute vec2 TexCoord;\n" + "attribute vec2 TexCoord1;\n" + "attribute vec3 Normal;\n" + "varying vec4 oColor;\n" + "varying vec2 oTexCoord;\n" + "varying vec2 oTexCoord1;\n" + "varying vec3 oNormal;\n" + "varying vec3 oVPos;\n" + "void main()\n" + "{\n" + " gl_Position = Proj * (View * Position);\n" + " oNormal = vec3(View * vec4(Normal,0));\n" + " oVPos = vec3(View * Position);\n" + " oTexCoord = TexCoord;\n" + " oTexCoord1 = TexCoord1;\n" + " oColor = Color;\n" + "}\n"; + +static const char* DirectVertexShaderSrc = + "uniform mat4 View;\n" + "attribute vec4 Position;\n" + "attribute vec4 Color;\n" + "attribute vec2 TexCoord;\n" + "attribute vec3 Normal;\n" + "varying vec4 oColor;\n" + "varying vec2 oTexCoord;\n" + "varying vec3 oNormal;\n" + "void main()\n" + "{\n" + " gl_Position = View * Position;\n" + " oTexCoord = TexCoord;\n" + " oColor = Color;\n" + " oNormal = vec3(View * vec4(Normal,0));\n" + "}\n"; + +static const char* SolidFragShaderSrc = + "uniform vec4 Color;\n" + "void main()\n" + "{\n" + " gl_FragColor = Color;\n" + "}\n"; + +static const char* GouraudFragShaderSrc = + "varying vec4 oColor;\n" + "void main()\n" + "{\n" + " gl_FragColor = oColor;\n" + "}\n"; + +static const char* TextureFragShaderSrc = + "uniform sampler2D Texture0;\n" + "varying vec4 oColor;\n" + "varying vec2 oTexCoord;\n" + "void main()\n" + "{\n" + " gl_FragColor = oColor * texture2D(Texture0, oTexCoord);\n" + " if (gl_FragColor.a < 0.4)\n" + " discard;\n" + "}\n"; + +#define LIGHTING_COMMON \ + "uniform vec3 Ambient;\n" \ + "uniform vec4 LightPos[8];\n" \ + "uniform vec4 LightColor[8];\n" \ + "uniform float LightCount;\n" \ + "varying vec4 oColor;\n" \ + "varying vec2 oTexCoord;\n" \ + "varying vec3 oNormal;\n" \ + "varying vec3 oVPos;\n" \ + "vec4 DoLight()\n" \ + "{\n" \ + " vec3 norm = normalize(oNormal);\n" \ + " vec3 light = Ambient;\n" \ + " for (int i = 0; i < int(LightCount); i++)\n" \ + " {\n" \ + " vec3 ltp = (LightPos[i].xyz - oVPos);\n" \ + " float ldist = length(ltp);\n" \ + " ltp = normalize(ltp);\n" \ + " light += clamp(LightColor[i].rgb * oColor.rgb * (dot(norm, ltp) / ldist), 0.0,1.0);\n" \ + " }\n" \ + " return vec4(light, oColor.a);\n" \ + "}\n" + +static const char* LitSolidFragShaderSrc = + LIGHTING_COMMON + "void main()\n" + "{\n" + " gl_FragColor = DoLight() * oColor;\n" + "}\n"; + +static const char* LitTextureFragShaderSrc = + "uniform sampler2D Texture0;\n" + LIGHTING_COMMON + "void main()\n" + "{\n" + " gl_FragColor = DoLight() * texture2D(Texture0, oTexCoord);\n" + "}\n"; + +static const char* AlphaTextureFragShaderSrc = + "uniform sampler2D Texture0;\n" + "varying vec4 oColor;\n" + "varying vec2 oTexCoord;\n" + "void main()\n" + "{\n" + " gl_FragColor = oColor * vec4(1,1,1,texture2D(Texture0, oTexCoord).a);\n" + "}\n"; + +static const char* MultiTextureFragShaderSrc = + "uniform sampler2D Texture0;\n" + "uniform sampler2D Texture1;\n" + "varying vec4 oColor;\n" + "varying vec2 oTexCoord;\n" + "varying vec2 oTexCoord1;\n" + "void main()\n" + "{\n" + " vec4 color1 = texture2D(Texture0, oTexCoord);\n" + " vec4 color2 = texture2D(Texture1, oTexCoord1);\n" + " color2.rgb = color2.rgb * mix(1.9, 1.2, clamp(length(color2.rgb),0.0,1.0));\n" + " color2 = color1 * color2;\n" + " if (color2.a <= 0.6)\n" + " discard;\n" + " gl_FragColor = color2;\n" + "}\n"; + +static const char* PostProcessVertexShaderSrc = + "uniform mat4 View;\n" + "uniform mat4 Texm;\n" + "attribute vec4 Position;\n" + "attribute vec2 TexCoord;\n" + "varying vec2 oTexCoord;\n" + "void main()\n" + "{\n" + " gl_Position = View * Position;\n" + " oTexCoord = vec2(Texm * vec4(TexCoord,0,1));\n" + " oTexCoord.y = 1.0-oTexCoord.y;\n" + "}\n"; + +static const char* PostProcessFragShaderSrc = + "uniform vec2 LensCenter;\n" + "uniform vec2 ScreenCenter;\n" + "uniform vec2 Scale;\n" + "uniform vec2 ScaleIn;\n" + "uniform vec4 HmdWarpParam;\n" + "uniform sampler2D Texture0;\n" + "varying vec2 oTexCoord;\n" + "\n" + "vec2 HmdWarp(vec2 in01)\n" + "{\n" + " vec2 theta = (in01 - LensCenter) * ScaleIn;\n" // Scales to [-1, 1] + " float rSq = theta.x * theta.x + theta.y * theta.y;\n" + " vec2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + " + " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n" + " return LensCenter + Scale * theta1;\n" + "}\n" + "void main()\n" + "{\n" + " vec2 tc = HmdWarp(oTexCoord);\n" + " if (!all(equal(clamp(tc, ScreenCenter-vec2(0.25,0.5), ScreenCenter+vec2(0.25,0.5)), tc)))\n" + " gl_FragColor = vec4(0);\n" + " else\n" + " gl_FragColor = texture2D(Texture0, tc);\n" + "}\n"; + +// Shader with lens distortion and chromatic aberration correction. +static const char* PostProcessFullFragShaderSrc = + "uniform vec2 LensCenter;\n" + "uniform vec2 ScreenCenter;\n" + "uniform vec2 Scale;\n" + "uniform vec2 ScaleIn;\n" + "uniform vec4 HmdWarpParam;\n" + "uniform vec4 ChromAbParam;\n" + "uniform sampler2D Texture0;\n" + "varying vec2 oTexCoord;\n" + "\n" + // Scales input texture coordinates for distortion. + // ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be + // larger due to aspect ratio. + "void main()\n" + "{\n" + " vec2 theta = (oTexCoord - LensCenter) * ScaleIn;\n" // Scales to [-1, 1] + " float rSq= theta.x * theta.x + theta.y * theta.y;\n" + " vec2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + " + " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n" + " \n" + " // Detect whether blue texture coordinates are out of range since these will scaled out the furthest.\n" + " vec2 thetaBlue = theta1 * (ChromAbParam.z + ChromAbParam.w * rSq);\n" + " vec2 tcBlue = LensCenter + Scale * thetaBlue;\n" + " if (!all(equal(clamp(tcBlue, ScreenCenter-vec2(0.25,0.5), ScreenCenter+vec2(0.25,0.5)), tcBlue)))\n" + " {\n" + " gl_FragColor = vec4(0);\n" + " return;\n" + " }\n" + " \n" + " // Now do blue texture lookup.\n" + " float blue = texture2D(Texture0, tcBlue).b;\n" + " \n" + " // Do green lookup (no scaling).\n" + " vec2 tcGreen = LensCenter + Scale * theta1;\n" + " vec4 center = texture2D(Texture0, tcGreen);\n" + " \n" + " // Do red scale and lookup.\n" + " vec2 thetaRed = theta1 * (ChromAbParam.x + ChromAbParam.y * rSq);\n" + " vec2 tcRed = LensCenter + Scale * thetaRed;\n" + " float red = texture2D(Texture0, tcRed).r;\n" + " \n" + " gl_FragColor = vec4(red, center.g, blue, center.a);\n" + "}\n"; + +static const char* VShaderSrcs[VShader_Count] = +{ + DirectVertexShaderSrc, + StdVertexShaderSrc, + PostProcessVertexShaderSrc +}; +static const char* FShaderSrcs[FShader_Count] = +{ + SolidFragShaderSrc, + GouraudFragShaderSrc, + TextureFragShaderSrc, + AlphaTextureFragShaderSrc, + PostProcessFragShaderSrc, + PostProcessFullFragShaderSrc, + LitSolidFragShaderSrc, + LitTextureFragShaderSrc, + MultiTextureFragShaderSrc +}; + + + +RenderDevice::RenderDevice(const RendererParams& p) +{ + for (int i = 0; i < VShader_Count; i++) + VertexShaders[i] = *new Shader(this, Shader_Vertex, VShaderSrcs[i]); + + for (int i = 0; i < FShader_Count; i++) + FragShaders[i] = *new Shader(this, Shader_Fragment, FShaderSrcs[i]); + + Ptr gouraudShaders = *new ShaderSet(); + gouraudShaders->SetShader(VertexShaders[VShader_MVP]); + gouraudShaders->SetShader(FragShaders[FShader_Gouraud]); + DefaultFill = *new ShaderFill(gouraudShaders); + + glGenFramebuffersEXT(1, &CurrentFbo); +} + +Shader *RenderDevice::LoadBuiltinShader(ShaderStage stage, int shader) +{ + switch (stage) + { + case Shader_Vertex: return VertexShaders[shader]; + case Shader_Fragment: return FragShaders[shader]; + default: + return NULL; + } +} + + +void RenderDevice::BeginRendering() +{ + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glFrontFace(GL_CW); + + glLineWidth(3.0f); + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +void RenderDevice::SetDepthMode(bool enable, bool write, CompareFunc func) +{ + if (enable) + { + glEnable(GL_DEPTH_TEST); + glDepthMask(write); + switch (func) + { + case Compare_Always: glDepthFunc(GL_ALWAYS); break; + case Compare_Less: glDepthFunc(GL_LESS); break; + case Compare_Greater: glDepthFunc(GL_GREATER); break; + default: assert(0); + } + } + else + glDisable(GL_DEPTH_TEST); +} + +void RenderDevice::SetRealViewport(const Viewport& vp) +{ + int wh; + if (CurRenderTarget) + wh = CurRenderTarget->Height; + else + wh = WindowHeight; + glViewport(vp.x, wh-vp.y-vp.h, vp.w, vp.h); + + glEnable(GL_SCISSOR_TEST); + glScissor(vp.x, wh-vp.y-vp.h, vp.w, vp.h); +} + +void RenderDevice::Clear(float r, float g, float b, float a, float depth) +{ + glClearColor(r,g,b,a); + glClearDepth(depth); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +} + +RBuffer* RenderDevice::GetDepthBuffer(int w, int h, int ms) +{ + for (unsigned i = 0; i < DepthBuffers.GetSize(); i++) + if (w == DepthBuffers[i]->Width && h == DepthBuffers[i]->Height)// && ms == DepthBuffers[i]->Samples) + return DepthBuffers[i]; + + //Ptr newDepth = *CreateTexture(Texture_Depth|Texture_RenderTarget|ms, w, h, NULL); + Ptr newDepth = *new RBuffer(GL_DEPTH24_STENCIL8, w, h); // combined depth stencil + DepthBuffers.PushBack(newDepth); + return newDepth.GetPtr(); +} + +void RenderDevice::SetRenderTarget(Render::Texture* color, Render::Texture*, Render::Texture* stencil) +{ + OVR_UNUSED(stencil); + + CurRenderTarget = (Texture*)color; + if (color == NULL) + { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + return; + } + //if (depth == NULL) + RBuffer* depth = GetDepthBuffer(color->GetWidth(), color->GetHeight(), 0); //CurRenderTarget->Samples); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, CurrentFbo); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ((Texture*)color)->TexId, 0); + if (depth) + //glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, ((Texture*)depth)->TexId, 0); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, ((RBuffer*)depth)->BufId); + else + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); + + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) + OVR_DEBUG_LOG(("framebuffer not complete: %x", status)); +} + + +void RenderDevice::SetWorldUniforms(const Matrix4f& proj) +{ + Proj = proj.Transposed(); +} + +void RenderDevice::SetTexture(Render::ShaderStage, int slot, const Texture* t) +{ + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_2D, ((Texture*)t)->TexId); + glActiveTexture(GL_TEXTURE0); +} + +Buffer* RenderDevice::CreateBuffer() +{ + return new Buffer(this); +} + +Fill* RenderDevice::CreateSimpleFill(int flags) +{ + OVR_UNUSED(flags); + return DefaultFill; +} + + +void RenderDevice::Render(const Matrix4f& matrix, Model* model) +{ + // Store data in buffers if not already + if (!model->VertexBuffer) + { + Ptr vb = *CreateBuffer(); + vb->Data(Buffer_Vertex, &model->Vertices[0], model->Vertices.GetSize() * sizeof(Vertex)); + model->VertexBuffer = vb; + } + if (!model->IndexBuffer) + { + Ptr ib = *CreateBuffer(); + ib->Data(Buffer_Index, &model->Indices[0], model->Indices.GetSize() * 2); + model->IndexBuffer = ib; + } + + Render(model->Fill ? (const Fill*)model->Fill : (const Fill*)DefaultFill, + model->VertexBuffer, model->IndexBuffer, + matrix, 0, (int)model->Indices.GetSize(), model->GetPrimType()); +} + +void RenderDevice::Render(const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices, + const Matrix4f& matrix, int offset, int count, PrimitiveType rprim) +{ + ShaderSet* shaders = (ShaderSet*) ((ShaderFill*)fill)->GetShaders(); + + GLenum prim; + switch (rprim) + { + case Prim_Triangles: + prim = GL_TRIANGLES; + break; + case Prim_Lines: + prim = GL_LINES; + break; + case Prim_TriangleStrip: + prim = GL_TRIANGLE_STRIP; + break; + default: + assert(0); + return; + } + + fill->Set(); + if (shaders->ProjLoc >= 0) + glUniformMatrix4fv(shaders->ProjLoc, 1, 0, &Proj.M[0][0]); + if (shaders->ViewLoc >= 0) + glUniformMatrix4fv(shaders->ViewLoc, 1, 0, &matrix.Transposed().M[0][0]); + + if (shaders->UsesLighting && Lighting->Version != shaders->LightingVer) + { + shaders->LightingVer = Lighting->Version; + Lighting->Set(shaders); + } + + glBindBuffer(GL_ARRAY_BUFFER, ((Buffer*)vertices)->GLBuffer); + for (int i = 0; i < 5; i++) + glEnableVertexAttribArray(i); + + glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(Vertex), (char*)offset + offsetof(Vertex, Pos)); + glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(Vertex), (char*)offset + offsetof(Vertex, C)); + glVertexAttribPointer(2, 2, GL_FLOAT, false, sizeof(Vertex), (char*)offset + offsetof(Vertex, U)); + glVertexAttribPointer(3, 2, GL_FLOAT, false, sizeof(Vertex), (char*)offset + offsetof(Vertex, U2)); + glVertexAttribPointer(4, 3, GL_FLOAT, false, sizeof(Vertex), (char*)offset + offsetof(Vertex, Norm)); + + if (indices) + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ((Buffer*)indices)->GLBuffer); + glDrawElements(prim, count, GL_UNSIGNED_SHORT, NULL); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + else + { + glDrawArrays(prim, 0, count); + } + + for (int i = 0; i < 5; i++) + glDisableVertexAttribArray(i); +} + +void RenderDevice::RenderWithAlpha(const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices, + const Matrix4f& matrix, int offset, int count, PrimitiveType rprim) +{ + //glEnable(GL_BLEND); + Render(fill, vertices, indices, matrix, offset, count, rprim); + //glDisable(GL_BLEND); +} + +void RenderDevice::SetLighting(const LightingParams* lt) +{ + Lighting = lt; +} + +Buffer::~Buffer() +{ + if (GLBuffer) + glDeleteBuffers(1, &GLBuffer); +} + +bool Buffer::Data(int use, const void* buffer, size_t size) +{ + switch (use & Buffer_TypeMask) + { + case Buffer_Index: Use = GL_ELEMENT_ARRAY_BUFFER; break; + default: Use = GL_ARRAY_BUFFER; break; + } + + if (!GLBuffer) + glGenBuffers(1, &GLBuffer); + + int mode = GL_DYNAMIC_DRAW; + if (use & Buffer_ReadOnly) + mode = GL_STATIC_DRAW; + + glBindBuffer(Use, GLBuffer); + glBufferData(Use, size, buffer, mode); + glBindBuffer(Use, 0); + return 1; +} + +void* Buffer::Map(size_t start, size_t size, int flags) +{ + int mode = GL_WRITE_ONLY; + //if (flags & Map_Unsynchronized) + // mode |= GL_MAP_UNSYNCHRONIZED; + + glBindBuffer(Use, GLBuffer); + void* v = glMapBuffer(Use, mode); + glBindBuffer(Use, 0); + return v; +} + +bool Buffer::Unmap(void*) +{ + glBindBuffer(Use, GLBuffer); + int r = glUnmapBuffer(Use); + glBindBuffer(Use, 0); + return r; +} + +bool Shader::Compile(const char* src) +{ + if (!GLShader) + GLShader = glCreateShader(GLStage()); + + glShaderSource(GLShader, 1, &src, 0); + glCompileShader(GLShader); + GLint r; + glGetShaderiv(GLShader, GL_COMPILE_STATUS, &r); + if (!r) + { + GLchar msg[1024]; + glGetShaderInfoLog(GLShader, sizeof(msg), 0, msg); + if (msg[0]) + OVR_DEBUG_LOG(("Compiling shader\n%s\nfailed: %s\n", src, msg)); + if (!r) + return 0; + } + return 1; +} + +ShaderSet::ShaderSet() +{ + Prog = glCreateProgram(); +} +ShaderSet::~ShaderSet() +{ + glDeleteProgram(Prog); +} + +bool ShaderSet::Link() +{ + glBindAttribLocation(Prog, 0, "Position"); + glBindAttribLocation(Prog, 1, "Color"); + glBindAttribLocation(Prog, 2, "TexCoord"); + glBindAttribLocation(Prog, 3, "TexCoord1"); + glBindAttribLocation(Prog, 4, "Normal"); + + glLinkProgram(Prog); + GLint r; + glGetProgramiv(Prog, GL_LINK_STATUS, &r); + if (!r) + { + GLchar msg[1024]; + glGetProgramInfoLog(Prog, sizeof(msg), 0, msg); + OVR_DEBUG_LOG(("Linking shaders failed: %s\n", msg)); + if (!r) + return 0; + } + glUseProgram(Prog); + + UniformInfo.Clear(); + LightingVer = 0; + UsesLighting = 0; + GLuint i = 0; + for(;; i++) + { + GLsizei namelen; + GLint size = 0; + GLenum type; + GLchar name[32]; + glGetActiveUniform(Prog, i, sizeof(name), &namelen, &size, &type, name); + if (size) + { + int l = glGetUniformLocation(Prog, name); + char *np = name; + while (*np) + { + if (*np == '[') + *np = 0; + np++; + } + Uniform u; + u.Name = name; + u.Location = l; + u.Size = size; + switch (type) + { + case GL_FLOAT: u.Type = 1; break; + case GL_FLOAT_VEC2: u.Type = 2; break; + case GL_FLOAT_VEC3: u.Type = 3; break; + case GL_FLOAT_VEC4: u.Type = 4; break; + case GL_FLOAT_MAT4: u.Type = 16; break; + default: + continue; + } + UniformInfo.PushBack(u); + if (!strcmp(name, "LightCount")) + UsesLighting = 1; + } + else + break; + } + + ProjLoc = glGetUniformLocation(Prog, "Proj"); + ViewLoc = glGetUniformLocation(Prog, "View"); + for (int i = 0; i < 8; i++) + { + char texv[32]; + sprintf(texv, "Texture%d", i); + TexLoc[i] = glGetUniformLocation(Prog, texv); + if (TexLoc[i] < 0) + break; + + glUniform1i(TexLoc[i], i); + } + if (UsesLighting) + OVR_ASSERT(ProjLoc >= 0 && ViewLoc >= 0); + return 1; +} + +void ShaderSet::Set(PrimitiveType) const +{ + glUseProgram(Prog); +} + +bool ShaderSet::SetUniform(const char* name, int n, const float* v) +{ + for (int i = 0; i < UniformInfo.GetSize(); i++) + if (!strcmp(UniformInfo[i].Name.ToCStr(), name)) + { + OVR_ASSERT(UniformInfo[i].Location >= 0); + glUseProgram(Prog); + switch (UniformInfo[i].Type) + { + case 1: glUniform1fv(UniformInfo[i].Location, n, v); break; + case 2: glUniform2fv(UniformInfo[i].Location, n/2, v); break; + case 3: glUniform3fv(UniformInfo[i].Location, n/3, v); break; + case 4: glUniform4fv(UniformInfo[i].Location, n/4, v); break; + default: OVR_ASSERT(0); + } + return 1; + } + + OVR_DEBUG_LOG(("Warning: uniform %s not present in selected shader", name)); + return 0; +} + +bool ShaderSet::SetUniform4x4f(const char* name, const Matrix4f& m) +{ + for (int i = 0; i < UniformInfo.GetSize(); i++) + if (!strcmp(UniformInfo[i].Name.ToCStr(), name)) + { + glUseProgram(Prog); + glUniformMatrix4fv(UniformInfo[i].Location, 1, 1, &m.M[0][0]); + return 1; + } + + OVR_DEBUG_LOG(("Warning: uniform %s not present in selected shader", name)); + return 0; +} + +Texture::Texture(RenderDevice* r, int w, int h) : Ren(r), Width(w), Height(h) +{ + glGenTextures(1, &TexId); +} + +Texture::~Texture() +{ + if (TexId) + glDeleteTextures(1, &TexId); +} + +void Texture::Set(int slot, Render::ShaderStage stage) const +{ + Ren->SetTexture(stage, slot, this); +} + +void Texture::SetSampleMode(int sm) +{ + glBindTexture(GL_TEXTURE_2D, TexId); + switch (sm & Sample_FilterMask) + { + case Sample_Linear: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0); + break; + + case Sample_Anisotropic: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8); + break; + + case Sample_Nearest: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0); + break; + } + + switch (sm & Sample_AddressMask) + { + case Sample_Repeat: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + break; + + case Sample_Clamp: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + break; + + case Sample_ClampBorder: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + break; + } + glBindTexture(GL_TEXTURE_2D, 0); +} + +Texture* RenderDevice::CreateTexture(int format, int width, int height, const void* data, int mipcount) +{ + GLenum glformat, gltype = GL_UNSIGNED_BYTE; + switch(format & Texture_TypeMask) + { + case Texture_RGBA: glformat = GL_RGBA; break; + case Texture_R: glformat = GL_ALPHA; break; + case Texture_Depth: glformat = GL_DEPTH; gltype = GL_DEPTH_COMPONENT; break; + case Texture_DXT1: glformat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; + case Texture_DXT3: glformat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; + case Texture_DXT5: glformat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; + default: + return NULL; + } + Texture* NewTex = new Texture(this, width, height); + glBindTexture(GL_TEXTURE_2D, NewTex->TexId); + glGetError(); + + if (format & Texture_Compressed) + { + const unsigned char* level = (const unsigned char*)data; + int w = width, h = height; + for (int i = 0; i < mipcount; i++) + { + int mipsize = GetTextureSize(format, w, h); + glCompressedTexImage2D(GL_TEXTURE_2D, i, glformat, w, h, 0, mipsize, level); + + level += mipsize; + w >>= 1; + h >>= 1; + if (w < 1) w = 1; + if (h < 1) h = 1; + } + } + else + glTexImage2D(GL_TEXTURE_2D, 0, glformat, width, height, 0, glformat, gltype, data); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if (format == (Texture_RGBA|Texture_GenMipmaps)) // not render target + { + int srcw = width, srch = height; + int level = 0; + UByte* mipmaps = NULL; + do + { + level++; + int mipw = srcw >> 1; if (mipw < 1) mipw = 1; + int miph = srch >> 1; if (miph < 1) miph = 1; + if (mipmaps == NULL) + mipmaps = (UByte*)OVR_ALLOC(mipw * miph * 4); + FilterRgba2x2(level == 1 ? (const UByte*)data : mipmaps, srcw, srch, mipmaps); + glTexImage2D(GL_TEXTURE_2D, level, glformat, mipw, miph, 0, glformat, gltype, mipmaps); + srcw = mipw; + srch = miph; + } while (srcw > 1 || srch > 1); + if (mipmaps) + OVR_FREE(mipmaps); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level); + } + else + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipcount-1); + } + + OVR_ASSERT(!glGetError()); + glBindTexture(GL_TEXTURE_2D, 0); + return NewTex; +} + +RBuffer::RBuffer(GLenum format, GLint w, GLint h) +{ + Width = w; + Height = h; + glGenRenderbuffersEXT(1, &BufId); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, BufId); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format, w, h); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); +} + +RBuffer::~RBuffer() +{ + glDeleteRenderbuffersEXT(1, &BufId); +} + +}}} diff --git a/Samples/CommonSrc/Render/Render_GL_Device.h b/Samples/CommonSrc/Render/Render_GL_Device.h new file mode 100644 index 0000000..37cd4f0 --- /dev/null +++ b/Samples/CommonSrc/Render/Render_GL_Device.h @@ -0,0 +1,230 @@ +/************************************************************************************ + +Filename : Render_GL_Device.h +Content : RenderDevice implementation header for OpenGL +Created : September 10, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef OVR_Render_GL_Device_h +#define OVR_Render_GL_Device_h + +#include "../Render/Render_Device.h" + +#if defined(OVR_OS_WIN32) +#include +#endif + +#if defined(OVR_OS_MAC) +#include +#include +#else +#define GL_GLEXT_PROTOTYPES +#include +#include +#endif + +namespace OVR { namespace Render { namespace GL { + +class RenderDevice; + +class Buffer : public Render::Buffer +{ +public: + RenderDevice* Ren; + size_t Size; + GLenum Use; + GLuint GLBuffer; + +public: + Buffer(RenderDevice* r) : Ren(r), Size(0), Use(0), GLBuffer(0) {} + ~Buffer(); + + GLuint GetBuffer() { return GLBuffer; } + + virtual size_t GetSize() { return Size; } + virtual void* Map(size_t start, size_t size, int flags = 0); + virtual bool Unmap(void *m); + virtual bool Data(int use, const void* buffer, size_t size); +}; + +class Texture : public Render::Texture +{ +public: + RenderDevice* Ren; + GLuint TexId; + int Width, Height; + + Texture(RenderDevice* r, int w, int h); + ~Texture(); + + virtual int GetWidth() const { return Width; } + virtual int GetHeight() const { return Height; } + + virtual void SetSampleMode(int); + + virtual void Set(int slot, ShaderStage stage = Shader_Fragment) const; +}; + +class Shader : public Render::Shader +{ +public: + GLuint GLShader; + + Shader(RenderDevice*, ShaderStage st, GLuint s) : Render::Shader(st), GLShader(s) {} + Shader(RenderDevice*, ShaderStage st, const char* src) : Render::Shader(st), GLShader(0) + { + Compile(src); + } + ~Shader() + { + if (GLShader) + glDeleteShader(GLShader); + } + bool Compile(const char* src); + + GLenum GLStage() const + { + switch (Stage) + { + default: OVR_ASSERT(0); return GL_NONE; + case Shader_Vertex: return GL_VERTEX_SHADER; + case Shader_Fragment: return GL_FRAGMENT_SHADER; + } + } + + //void Set(PrimitiveType prim) const; + //void SetUniformBuffer(Render::Buffer* buffers, int i = 0); +}; + +class ShaderSet : public Render::ShaderSet +{ +public: + GLuint Prog; + + struct Uniform + { + String Name; + int Location, Size; + int Type; // currently number of floats in vector + }; + Array UniformInfo; + + int ProjLoc, ViewLoc; + int TexLoc[8]; + bool UsesLighting; + int LightingVer; + + ShaderSet(); + ~ShaderSet(); + + virtual void SetShader(Render::Shader *s) + { + Shaders[s->GetStage()] = s; + Shader* gls = (Shader*)s; + glAttachShader(Prog, gls->GLShader); + if (Shaders[Shader_Vertex] && Shaders[Shader_Fragment]) + Link(); + } + virtual void UnsetShader(int stage) + { + Shader* gls = (Shader*)(Render::Shader*)Shaders[stage]; + if (gls) + glDetachShader(Prog, gls->GLShader); + Shaders[stage] = NULL; + Link(); + } + + virtual void Set(PrimitiveType prim) const; + + // Set a uniform (other than the standard matrices). It is undefined whether the + // uniforms from one shader occupy the same space as those in other shaders + // (unless a buffer is used, then each buffer is independent). + virtual bool SetUniform(const char* name, int n, const float* v); + virtual bool SetUniform4x4f(const char* name, const Matrix4f& m); + + bool Link(); +}; + + class RBuffer : public RefCountBase +{ + public: + int Width, Height; + GLuint BufId; + + RBuffer(GLenum format, GLint w, GLint h); + ~RBuffer(); +}; + +class RenderDevice : public Render::RenderDevice +{ + Ptr VertexShaders[VShader_Count]; + Ptr FragShaders[FShader_Count]; + + Ptr DefaultFill; + + Matrix4f Proj; + + Ptr CurRenderTarget; + Array > DepthBuffers; + GLuint CurrentFbo; + + const LightingParams* Lighting; + + +public: + RenderDevice(const RendererParams& p); + + virtual void SetRealViewport(const Viewport& vp); + + //virtual void SetScissor(int x, int y, int w, int h); + + virtual void Clear(float r = 0, float g = 0, float b = 0, float a = 1, float depth = 1); + virtual void Rect(float left, float top, float right, float bottom) { OVR_UNUSED4(left,top,right,bottom); } + + virtual void BeginRendering(); + virtual void SetDepthMode(bool enable, bool write, CompareFunc func = Compare_Less); + virtual void SetWorldUniforms(const Matrix4f& proj); + + RBuffer* GetDepthBuffer(int w, int h, int ms); + + virtual void SetRenderTarget(Render::Texture* color, + Render::Texture* depth = NULL, Render::Texture* stencil = NULL); + + virtual void SetLighting(const LightingParams* lt); + + virtual void Render(const Matrix4f& matrix, Model* model); + virtual void Render(const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices, + const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles); + virtual void RenderWithAlpha(const Fill* fill, Render::Buffer* vertices, Render::Buffer* indices, + const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles); + + virtual Buffer* CreateBuffer(); + virtual Texture* CreateTexture(int format, int width, int height, const void* data, int mipcount=1); + virtual ShaderSet* CreateShaderSet() { return new ShaderSet; } + + virtual Fill *CreateSimpleFill(int flags = Fill::F_Solid); + + virtual Shader *LoadBuiltinShader(ShaderStage stage, int shader); + + void SetTexture(Render::ShaderStage, int slot, const Texture* t); +}; + +}}} + +#endif diff --git a/Samples/CommonSrc/Render/Render_GL_Win32_Device.cpp b/Samples/CommonSrc/Render/Render_GL_Win32_Device.cpp new file mode 100644 index 0000000..8bbbf88 --- /dev/null +++ b/Samples/CommonSrc/Render/Render_GL_Win32_Device.cpp @@ -0,0 +1,90 @@ +/************************************************************************************ + +Filename : Render_GL_Win32 Device.cpp +Content : Win32 OpenGL Device implementation +Created : September 10, 2012 +Authors : Andrew Reisse, Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#include "Render_GL_Win32_Device.h" + +namespace OVR { namespace Render { namespace GL { namespace Win32 { + + +// ***** GL::Win32::RenderDevice + +// Implement static initializer function to create this class. +Render::RenderDevice* RenderDevice::CreateDevice(const RendererParams&, void* oswnd) +{ + HWND hwnd = (HWND)oswnd; + + PIXELFORMATDESCRIPTOR pfd; + memset(&pfd, 0, sizeof(pfd)); + + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER; + pfd.cColorBits = 32; + pfd.cDepthBits = 16; + + HDC dc = GetDC(hwnd); + int pf = ChoosePixelFormat(dc, &pfd); + if (!pf) + { + ReleaseDC(hwnd, dc); + return NULL; + } + if (!SetPixelFormat(dc, pf, &pfd)) + { + ReleaseDC(hwnd, dc); + return NULL; + } + HGLRC context = wglCreateContext(dc); + if (!wglMakeCurrent(dc, context)) + { + wglDeleteContext(context); + ReleaseDC(hwnd, dc); + return NULL; + } + + // return new RenderDevice(rp, hwnd, dc, context); + return 0; +} + + +void RenderDevice::Present() +{ + SwapBuffers(GdiDc); +} + +void RenderDevice::Shutdown() +{ + if (WglContext) + { + wglMakeCurrent(NULL,NULL); + wglDeleteContext(WglContext); + ReleaseDC(Window, GdiDc); + WglContext = NULL; + GdiDc = NULL; + Window = NULL; + } +} + +}}}} + diff --git a/Samples/CommonSrc/Render/Render_GL_Win32_Device.h b/Samples/CommonSrc/Render/Render_GL_Win32_Device.h new file mode 100644 index 0000000..f58a625 --- /dev/null +++ b/Samples/CommonSrc/Render/Render_GL_Win32_Device.h @@ -0,0 +1,59 @@ +/************************************************************************************ + +Filename : Render_GL_Win32 Device.h +Content : Win32 OpenGL Device implementation header +Created : September 10, 2012 +Authors : Andrew Reisse, Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef OVR_Render_GL_Win32_Device_h +#define OVR_Render_GL_Win32_Device_h + +#include "Render_GL_Device.h" + +#ifdef WIN32 +#include +#endif + + +namespace OVR { namespace Render { namespace GL { namespace Win32 { + +// ***** GL::Win32::RenderDevice + +// Win32-Specific GL Render Device, used to create OpenGL under Windows. +class RenderDevice : public GL::RenderDevice +{ + HWND Window; + HGLRC WglContext; + HDC GdiDc; + +public: + RenderDevice(const Render::RendererParams& p, HWND win, HDC dc, HGLRC gl) + : GL::RenderDevice(p), Window(win), WglContext(gl), GdiDc(dc) { OVR_UNUSED(p); } + + // Implement static initializer function to create this class. + static Render::RenderDevice* CreateDevice(const RendererParams& rp, void* oswnd); + + virtual void Shutdown(); + virtual void Present(); +}; + + +}}}} // OVR::Render::GL::Win32 + +#endif diff --git a/Samples/CommonSrc/Render/Render_LoadTextureDDS.cpp b/Samples/CommonSrc/Render/Render_LoadTextureDDS.cpp new file mode 100644 index 0000000..e11b07f --- /dev/null +++ b/Samples/CommonSrc/Render/Render_LoadTextureDDS.cpp @@ -0,0 +1,121 @@ +/************************************************************************************ + +Filename : WavPlayer_OSX.h +Content : A DDS file loader for cross-platform compressed texture support. +Created : March 5, 2013 +Authors : Peter Hoff, Dan Goodman, Bryan Croteau + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ +#include "Render_Device.h" + +#ifdef OVR_DEFINE_NEW +#undef new +#endif + +namespace OVR { namespace Render { + +static const UPInt OVR_DDS_PF_FOURCC = 0x4; +static const UInt32 OVR_DTX1_MAGIC_NUMBER = 827611204; +static const UInt32 OVR_DTX5_MAGIC_NUMBER = 894720068; + +struct OVR_DDS_PIXELFORMAT +{ + UInt32 Size; + UInt32 Flags; + UInt32 FourCC; + UInt32 RGBBitCount; + UInt32 RBitMask; + UInt32 GBitMask; + UInt32 BBitMask; + UInt32 ABitMask; +}; + +struct OVR_DDS_HEADER +{ + UInt32 Size; + UInt32 Flags; + UInt32 Height; + UInt32 Width; + UInt32 PitchOrLinearSize; + UInt32 Depth; + UInt32 MipMapCount; + UInt32 Reserved1[11]; + OVR_DDS_PIXELFORMAT PixelFormat; + UInt32 Caps; + UInt32 Caps2; + UInt32 Caps3; + UInt32 Caps4; + UInt32 Reserved2; +}; + +Texture* LoadTextureDDS(RenderDevice* ren, File* f) +{ + OVR_DDS_HEADER header; + unsigned char filecode[4]; + + f->Read(filecode, 4); + if (strncmp((const char*)filecode, "DDS ", 4) != 0) + { + return NULL; + } + + f->Read((unsigned char*)(&header), sizeof(header)); + + int width = header.Width; + int height = header.Height; + + int format = 0; + + UInt32 mipCount = header.MipMapCount; + if(mipCount <= 0) + { + mipCount = 1; + } + if(header.PixelFormat.Flags & OVR_DDS_PF_FOURCC) + { + if(header.PixelFormat.FourCC == OVR_DTX1_MAGIC_NUMBER) + { + format = Texture_DXT1; + } + else if(header.PixelFormat.FourCC == OVR_DTX5_MAGIC_NUMBER) + { + format = Texture_DXT5; + } + else + { + return NULL; + } + } + + int byteLen = f->BytesAvailable(); + unsigned char* bytes = new unsigned char[byteLen]; + f->Read(bytes, byteLen); + Texture* out = ren->CreateTexture(format, (int)width, (int)height, bytes, mipCount); + if(strstr(f->GetFilePath(), "_c.")) + { + out->SetSampleMode(Sample_Clamp); + } + OVR_FREE(bytes); + return out; +} + + +}} + +#ifdef OVR_DEFINE_NEW +#define new OVR_DEFINE_NEW +#endif \ No newline at end of file diff --git a/Samples/CommonSrc/Render/Render_LoadTextureTGA.cpp b/Samples/CommonSrc/Render/Render_LoadTextureTGA.cpp new file mode 100644 index 0000000..0331540 --- /dev/null +++ b/Samples/CommonSrc/Render/Render_LoadTextureTGA.cpp @@ -0,0 +1,102 @@ +/************************************************************************************ + +Filename : Render_LoadeTextureTGA.cpp +Content : Loading of TGA implementation +Created : October, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + + +#include "Render_Device.h" + +namespace OVR { namespace Render { + +Texture* LoadTextureTga(RenderDevice* ren, File* f) +{ + int desclen = f->ReadUByte(); + int palette = f->ReadUByte(); + OVR_UNUSED(palette); + int imgtype = f->ReadUByte(); + f->ReadUInt16(); + int palCount = f->ReadUInt16(); + int palSize = f->ReadUByte(); + f->ReadUInt16(); + f->ReadUInt16(); + int width = f->ReadUInt16(); + int height = f->ReadUInt16(); + int bpp = f->ReadUByte(); + f->ReadUByte(); + int imgsize = width * height * 4; + unsigned char* imgdata = (unsigned char*) OVR_ALLOC(imgsize); + unsigned char buf[16]; + f->Read(imgdata, desclen); + f->Read(imgdata, palCount * (palSize + 7) >> 3); + int bpl = width * 4; + + switch (imgtype) + { + case 2: + switch (bpp) + { + case 24: + for (int y = 0; y < height; y++) + for (int x = 0; x < width; x++) + { + f->Read(buf, 3); + imgdata[y*bpl+x*4+0] = buf[2]; + imgdata[y*bpl+x*4+1] = buf[1]; + imgdata[y*bpl+x*4+2] = buf[0]; + imgdata[y*bpl+x*4+3] = 255; + } + break; + case 32: + for (int y = 0; y < height; y++) + for (int x = 0; x < width; x++) + { + f->Read(buf, 4); + imgdata[y*bpl+x*4+0] = buf[2]; + imgdata[y*bpl+x*4+1] = buf[1]; + imgdata[y*bpl+x*4+2] = buf[0]; + imgdata[y*bpl+x*4+3] = buf[3]; + } + break; + + default: + OVR_FREE(imgdata); + return NULL; + } + break; + + default: + OVR_FREE(imgdata); + return NULL; + } + + Texture* out = ren->CreateTexture(Texture_RGBA|Texture_GenMipmaps, width, height, imgdata); + + // check for clamp based on texture name + if(strstr(f->GetFilePath(), "_c.")) + { + out->SetSampleMode(Sample_Clamp); + } + + OVR_FREE(imgdata); + return out; +} + +}} diff --git a/Samples/CommonSrc/Render/Render_XmlSceneLoader.cpp b/Samples/CommonSrc/Render/Render_XmlSceneLoader.cpp new file mode 100644 index 0000000..cc0eb47 --- /dev/null +++ b/Samples/CommonSrc/Render/Render_XmlSceneLoader.cpp @@ -0,0 +1,390 @@ +/************************************************************************************ + +Filename : Render_XmlSceneLoader.cpp +Content : Imports and exports XML files - implementation +Created : January 21, 2013 +Authors : Robotic Arm Software - Peter Hoff, Dan Goodman, Bryan Croteau + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#include "Render_XmlSceneLoader.h" +#include + +#ifdef OVR_DEFINE_NEW +#undef new +#endif + +namespace OVR { namespace Render { + +XmlHandler::XmlHandler() : pXmlDocument(NULL) +{ + pXmlDocument = new tinyxml2::XMLDocument(); +} + +XmlHandler::~XmlHandler() +{ + delete pXmlDocument; +} + +bool XmlHandler::ReadFile(const char* fileName, OVR::Render::RenderDevice* pRender, + OVR::Render::Scene* pScene, + OVR::Array >* pCollisions, + OVR::Array >* pGroundCollisions) +{ + if(pXmlDocument->LoadFile(fileName) != 0) + { + return false; + } + + // Extract the relative path to our working directory for loading textures + filePath[0] = 0; + SPInt pos = 0; + SPInt len = strlen(fileName); + for(SPInt i = len; i > 0; i--) + { + if (fileName[i-1]=='\\' || fileName[i-1]=='/') + { + memcpy(filePath, fileName, i); + filePath[i] = 0; + break; + } + } + + // Load the textures + OVR_DEBUG_LOG_TEXT(("Loading textures...")); + XMLElement* pXmlTexture = pXmlDocument->FirstChildElement("scene")->FirstChildElement("textures"); + if (pXmlTexture) + { + pXmlTexture->QueryIntAttribute("count", &textureCount); + pXmlTexture = pXmlTexture->FirstChildElement("texture"); + } + + for(int i = 0; i < textureCount; ++i) + { + const char* textureName = pXmlTexture->Attribute("fileName"); + SPInt dotpos = strcspn(textureName, "."); + char fname[300]; + + if (pos == len) + { + OVR_sprintf(fname, 300, "%s", textureName); + } + else + { + OVR_sprintf(fname, 300, "%s%s", filePath, textureName); + } + + SysFile* pFile = new SysFile(fname); + Ptr texture; + if (textureName[dotpos + 1] == 'd' || textureName[dotpos + 1] == 'D') + { + // DDS file + texture.SetPtr(*LoadTextureDDS(pRender, pFile)); + } + else + { + texture.SetPtr(*LoadTextureTga(pRender, pFile)); + } + + Textures.PushBack(texture); + pFile->Close(); + pFile->Release(); + pXmlTexture = pXmlTexture->NextSiblingElement("texture"); + } + OVR_DEBUG_LOG_TEXT(("Done.\n")); + + // Load the models + pXmlDocument->FirstChildElement("scene")->FirstChildElement("models")-> + QueryIntAttribute("count", &modelCount); + + OVR_DEBUG_LOG(("Loading models... %i models to load...", modelCount)); + XMLElement* pXmlModel = pXmlDocument->FirstChildElement("scene")-> + FirstChildElement("models")->FirstChildElement("model"); + for(int i = 0; i < modelCount; ++i) + { + if (i % 15 == 0) + { + OVR_DEBUG_LOG_TEXT(("%i models remaining...", modelCount - i)); + } + Models.PushBack(*new Model(Prim_Triangles)); + bool isCollisionModel = false; + pXmlModel->QueryBoolAttribute("isCollisionModel", &isCollisionModel); + Models[i]->IsCollisionModel = isCollisionModel; + if (isCollisionModel) + { + Models[i]->Visible = false; + } + + //read the vertices + OVR::Array *vertices = new OVR::Array(); + ParseVectorString(pXmlModel->FirstChildElement("vertices")->FirstChild()-> + ToText()->Value(), vertices); + + for (unsigned int vertexIndex = 0; vertexIndex < vertices->GetSize(); ++vertexIndex) + { + vertices->At(vertexIndex).x *= -1.0f; + } + + //read the normals + OVR::Array *normals = new OVR::Array(); + ParseVectorString(pXmlModel->FirstChildElement("normals")->FirstChild()-> + ToText()->Value(), normals); + + for (unsigned int normalIndex = 0; normalIndex < normals->GetSize(); ++normalIndex) + { + normals->At(normalIndex).z *= -1.0f; + } + + //read the textures + OVR::Array *diffuseUVs = new OVR::Array(); + OVR::Array *lightmapUVs = new OVR::Array(); + int diffuseTextureIndex = -1; + int lightmapTextureIndex = -1; + XMLElement* pXmlCurMaterial = pXmlModel->FirstChildElement("material"); + + while(pXmlCurMaterial != NULL) + { + if(pXmlCurMaterial->Attribute("name", "diffuse")) + { + pXmlCurMaterial->FirstChildElement("texture")-> + QueryIntAttribute("index", &diffuseTextureIndex); + if(diffuseTextureIndex > -1) + { + ParseVectorString(pXmlCurMaterial->FirstChildElement("texture")-> + FirstChild()->ToText()->Value(), diffuseUVs, true); + } + } + else if(pXmlCurMaterial->Attribute("name", "lightmap")) + { + pXmlCurMaterial->FirstChildElement("texture")-> + QueryIntAttribute("index", &lightmapTextureIndex); + if(lightmapTextureIndex > -1) + { + XMLElement* firstChildElement = pXmlCurMaterial->FirstChildElement("texture"); + XMLNode* firstChild = firstChildElement->FirstChild(); + XMLText* text = firstChild->ToText(); + const char* value = text->Value(); + ParseVectorString(value, lightmapUVs, true); + } + } + + pXmlCurMaterial = pXmlCurMaterial->NextSiblingElement("material"); + } + + //set up the shader + Ptr shader = *new ShaderFill(*pRender->CreateShaderSet()); + shader->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Vertex, VShader_MVP)); + if(diffuseTextureIndex > -1) + { + shader->SetTexture(0, Textures[diffuseTextureIndex]); + if(lightmapTextureIndex > -1) + { + shader->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Fragment, FShader_MultiTexture)); + shader->SetTexture(1, Textures[lightmapTextureIndex]); + } + else + { + shader->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Fragment, FShader_Texture)); + } + } + else + { + shader->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Fragment, FShader_LitGouraud)); + } + Models[i]->Fill = shader; + + //add all the vertices to the model + const UPInt numVerts = vertices->GetSize(); + for(UPInt v = 0; v < numVerts; ++v) + { + if(diffuseTextureIndex > -1) + { + if(lightmapTextureIndex > -1) + { + Models[i]->AddVertex(vertices->At(v).z, vertices->At(v).y, vertices->At(v).x, Color(255, 255, 255), + diffuseUVs->At(v).x, diffuseUVs->At(v).y, lightmapUVs->At(v).x, lightmapUVs->At(v).y, + normals->At(v).x, normals->At(v).y, normals->At(v).z); + } + else + { + Models[i]->AddVertex(vertices->At(v).z, vertices->At(v).y, vertices->At(v).x, Color(255, 255, 255), + diffuseUVs->At(v).x, diffuseUVs->At(v).y, 0, 0, + normals->At(v).x, normals->At(v).y, normals->At(v).z); + } + } + else + { + Models[i]->AddVertex(vertices->At(v).z, vertices->At(v).y, vertices->At(v).x, Color(255, 0, 0, 128), + 0, 0, 0, 0, + normals->At(v).x, normals->At(v).y, normals->At(v).z); + } + } + + // Read the vertex indices for the triangles + const char* indexStr = pXmlModel->FirstChildElement("indices")-> + FirstChild()->ToText()->Value(); + UPInt stringLength = strlen(indexStr); + + for(UPInt j = 0; j < stringLength;) + { + UPInt k = j + 1; + for(; k < stringLength; ++k) + { + if(indexStr[k] == ' ') + { + break; + } + } + char text[20]; + for(UPInt l = 0; l < k - j; ++l) + { + text[l] = indexStr[j + l]; + } + text[k - j] = '\0'; + Models[i]->Indices.InsertAt(0, (unsigned short)atoi(text)); + j = k + 1; + } + + delete vertices; + delete normals; + delete diffuseUVs; + delete lightmapUVs; + + pScene->World.Add(Models[i]); + pScene->Models.PushBack(Models[i]); + pXmlModel = pXmlModel->NextSiblingElement("model"); + } + OVR_DEBUG_LOG(("Done.")); + + //load the collision models + OVR_DEBUG_LOG(("Loading collision models... ")); + XMLElement* pXmlCollisionModel = pXmlDocument->FirstChildElement("scene")->FirstChildElement("collisionModels"); + if (pXmlCollisionModel) + { + pXmlCollisionModel->QueryIntAttribute("count", &collisionModelCount); + pXmlCollisionModel = pXmlCollisionModel->FirstChildElement("collisionModel"); + } + + XMLElement* pXmlPlane = NULL; + for(int i = 0; i < collisionModelCount; ++i) + { + Ptr cm = *new CollisionModel(); + int planeCount = 0; + pXmlCollisionModel->QueryIntAttribute("planeCount", &planeCount); + + pXmlPlane = pXmlCollisionModel->FirstChildElement("plane"); + for(int j = 0; j < planeCount; ++j) + { + Vector3f norm; + pXmlPlane->QueryFloatAttribute("nx", &norm.x); + pXmlPlane->QueryFloatAttribute("ny", &norm.y); + pXmlPlane->QueryFloatAttribute("nz", &norm.z); + float D; + pXmlPlane->QueryFloatAttribute("d", &D); + D -= 0.5f; + Planef p(norm.z, norm.y, norm.x * -1.0f, D); + cm->Add(p); + pXmlPlane = pXmlPlane->NextSiblingElement("plane"); + } + + pCollisions->PushBack(cm); + pXmlCollisionModel = pXmlCollisionModel->NextSiblingElement("collisionModel"); + } + OVR_DEBUG_LOG(("done.")); + + //load the ground collision models + OVR_DEBUG_LOG(("Loading ground collision models...")); + pXmlCollisionModel = pXmlDocument->FirstChildElement("scene")->FirstChildElement("groundCollisionModels"); + if (pXmlCollisionModel) + { + pXmlCollisionModel->QueryIntAttribute("count", &groundCollisionModelCount); + pXmlCollisionModel = pXmlCollisionModel->FirstChildElement("collisionModel"); + } + pXmlPlane = NULL; + for(int i = 0; i < groundCollisionModelCount; ++i) + { + Ptr cm = *new CollisionModel(); + int planeCount = 0; + pXmlCollisionModel->QueryIntAttribute("planeCount", &planeCount); + + pXmlPlane = pXmlCollisionModel->FirstChildElement("plane"); + for(int j = 0; j < planeCount; ++j) + { + Vector3f norm; + pXmlPlane->QueryFloatAttribute("nx", &norm.x); + pXmlPlane->QueryFloatAttribute("ny", &norm.y); + pXmlPlane->QueryFloatAttribute("nz", &norm.z); + float D; + pXmlPlane->QueryFloatAttribute("d", &D); + Planef p(norm.z, norm.y, norm.x * -1.0f, D); + cm->Add(p); + pXmlPlane = pXmlPlane->NextSiblingElement("plane"); + } + + pGroundCollisions->PushBack(cm); + pXmlCollisionModel = pXmlCollisionModel->NextSiblingElement("collisionModel"); + } + OVR_DEBUG_LOG(("done.")); + return true; +} + +void XmlHandler::ParseVectorString(const char* str, OVR::Array *array, + bool is2element) +{ + UPInt stride = is2element ? 2 : 3; + UPInt stringLength = strlen(str); + UPInt element = 0; + float v[3]; + + for(UPInt j = 0; j < stringLength;) + { + UPInt k = j + 1; + for(; k < stringLength; ++k) + { + if(str[k] == ' ') + { + break; + } + } + char text[20]; + for(UPInt l = 0; l < k - j; ++l) + { + text[l] = str[j + l]; + } + text[k - j] = '\0'; + v[element] = (float)atof(text); + + if(element == (stride - 1)) + { + //we've got all the elements of our vertex, so store them + OVR::Vector3f vect; + vect.x = v[0]; + vect.y = v[1]; + vect.z = is2element ? 0.0f : v[2]; + array->PushBack(vect); + } + + j = k + 1; + element = (element + 1) % stride; + } +} + +}} // OVR::Render + +#ifdef OVR_DEFINE_NEW +#define new OVR_DEFINE_NEW +#endif diff --git a/Samples/CommonSrc/Render/Render_XmlSceneLoader.h b/Samples/CommonSrc/Render/Render_XmlSceneLoader.h new file mode 100644 index 0000000..2b888c5 --- /dev/null +++ b/Samples/CommonSrc/Render/Render_XmlSceneLoader.h @@ -0,0 +1,74 @@ +/************************************************************************************ + +Filename : Render_XmlSceneLoader.h +Content : Imports and exports XML files +Created : January 21, 2013 +Authors : Robotic Arm Software - Peter Hoff, Dan Goodman, Bryan Croteau + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef INC_Render_XMLSceneLoader_h +#define INC_Render_XMLSceneLoader_h + +#include "Render_Device.h" +#include +using namespace OVR; +using namespace OVR::Render; + +#ifdef OVR_DEFINE_NEW +#undef new +#endif + +#include "../../../3rdParty/TinyXml/tinyxml2.h" + +namespace OVR { namespace Render { + +using namespace tinyxml2; + +class XmlHandler +{ +public: + XmlHandler(); + ~XmlHandler(); + + bool ReadFile(const char* fileName, OVR::Render::RenderDevice* pRender, + OVR::Render::Scene* pScene, + OVR::Array >* pColisions, + OVR::Array >* pGroundCollisions); + +protected: + void ParseVectorString(const char* str, OVR::Array *array, + bool is2element = false); + +private: + tinyxml2::XMLDocument* pXmlDocument; + char filePath[250]; + int textureCount; + OVR::Array > Textures; + int modelCount; + OVR::Array > Models; + int collisionModelCount; + int groundCollisionModelCount; +}; + +}} // OVR::Render + +#ifdef OVR_DEFINE_NEW +#define new OVR_DEFINE_NEW +#endif + +#endif // INC_Render_XMLSceneLoader_h diff --git a/Samples/LibOVR_Samples_Msvc2010.sln b/Samples/LibOVR_Samples_Msvc2010.sln new file mode 100644 index 0000000..b3ad124 --- /dev/null +++ b/Samples/LibOVR_Samples_Msvc2010.sln @@ -0,0 +1,43 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OculusWorldDemo", "OculusWorldDemo\OculusWorldDemo_Msvc2010.vcxproj", "{8051B877-2992-4F64-8C3B-FAF88B6D83AA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SensorBoxTest", "SensorBox\SensorBoxTest_Msvc2010.vcxproj", "{8051B837-2982-4F64-8C3B-FAF88B6D83AB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OculusRoomTiny", "OculusRoomTiny\OculusRoomTiny_Msvc2010.vcxproj", "{80523489-2881-4F64-8C3B-FAF88B60ABCD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Debug|Win32.ActiveCfg = Debug|Win32 + {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Debug|Win32.Build.0 = Debug|Win32 + {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Debug|x64.ActiveCfg = Debug|x64 + {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Debug|x64.Build.0 = Debug|x64 + {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Release|Win32.ActiveCfg = Release|Win32 + {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Release|Win32.Build.0 = Release|Win32 + {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Release|x64.ActiveCfg = Release|x64 + {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Release|x64.Build.0 = Release|x64 + {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Debug|Win32.ActiveCfg = Debug|Win32 + {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Debug|Win32.Build.0 = Debug|Win32 + {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Debug|x64.ActiveCfg = Debug|Win32 + {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Release|Win32.ActiveCfg = Release|Win32 + {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Release|Win32.Build.0 = Release|Win32 + {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Release|x64.ActiveCfg = Release|Win32 + {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Debug|Win32.ActiveCfg = Debug|Win32 + {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Debug|Win32.Build.0 = Debug|Win32 + {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Debug|x64.ActiveCfg = Debug|x64 + {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Debug|x64.Build.0 = Debug|x64 + {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Release|Win32.ActiveCfg = Release|Win32 + {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Release|Win32.Build.0 = Release|Win32 + {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Release|x64.ActiveCfg = Release|x64 + {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/LibOVR_With_Samples.xcodeproj/OculusRoomTiny-Info.plist b/Samples/LibOVR_With_Samples.xcodeproj/OculusRoomTiny-Info.plist new file mode 100644 index 0000000..1882911 --- /dev/null +++ b/Samples/LibOVR_With_Samples.xcodeproj/OculusRoomTiny-Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.oculusvr.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSHumanReadableCopyright + Copyright © 2013 Oculus VR. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/Samples/LibOVR_With_Samples.xcodeproj/OculusWorldDemo-Info.plist b/Samples/LibOVR_With_Samples.xcodeproj/OculusWorldDemo-Info.plist new file mode 100644 index 0000000..2464c75 --- /dev/null +++ b/Samples/LibOVR_With_Samples.xcodeproj/OculusWorldDemo-Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + Oculus + CFBundleIdentifier + com.oculusvr.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSHumanReadableCopyright + Copyright © 2013 Oculus VR. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/Samples/LibOVR_With_Samples.xcodeproj/SensorBoxTest-Info.plist b/Samples/LibOVR_With_Samples.xcodeproj/SensorBoxTest-Info.plist new file mode 100644 index 0000000..2464c75 --- /dev/null +++ b/Samples/LibOVR_With_Samples.xcodeproj/SensorBoxTest-Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + Oculus + CFBundleIdentifier + com.oculusvr.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSHumanReadableCopyright + Copyright © 2013 Oculus VR. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/Samples/LibOVR_With_Samples.xcodeproj/project.pbxproj b/Samples/LibOVR_With_Samples.xcodeproj/project.pbxproj new file mode 100644 index 0000000..af9448d --- /dev/null +++ b/Samples/LibOVR_With_Samples.xcodeproj/project.pbxproj @@ -0,0 +1,1092 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 37973B391739D78B0093BBB8 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4945072216E55A0300B9FF78 /* Cocoa.framework */; }; + 37973B501739E1B60093BBB8 /* OculusRoomModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37973B4F1739E1B60093BBB8 /* OculusRoomModel.cpp */; }; + 37973B531739E1D20093BBB8 /* RenderTiny_Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37973B511739E1D20093BBB8 /* RenderTiny_Device.cpp */; }; + 37973B561739E9230093BBB8 /* RenderTiny_GL_Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37973B541739E9230093BBB8 /* RenderTiny_GL_Device.cpp */; }; + 37973B591739E9E80093BBB8 /* OSX_OculusRoomTiny.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37973B581739E9E80093BBB8 /* OSX_OculusRoomTiny.mm */; }; + 37973B5C1739FA620093BBB8 /* libovr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 49A5336D16E544BE0039CB59 /* libovr.a */; }; + 37973B5D1739FA920093BBB8 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 499ED4C516E6A179008EA2ED /* IOKit.framework */; }; + 37973B5E1739FAB60093BBB8 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4945071616E546F700B9FF78 /* OpenGL.framework */; }; + 37973B5F1739FAC80093BBB8 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49DB65F01718B1E10097A8DD /* ApplicationServices.framework */; }; + 37973B601739FAD50093BBB8 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 499ED4C716E6A187008EA2ED /* CoreFoundation.framework */; }; + 494506DD16E5461F00B9FF78 /* OVR_Alg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5337116E544E30039CB59 /* OVR_Alg.cpp */; }; + 494506DF16E5461F00B9FF78 /* OVR_Allocator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5337316E544E30039CB59 /* OVR_Allocator.cpp */; }; + 494506E216E5461F00B9FF78 /* OVR_Atomic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5337616E544E30039CB59 /* OVR_Atomic.cpp */; }; + 494506E616E5461F00B9FF78 /* OVR_File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5337A16E544E30039CB59 /* OVR_File.cpp */; }; + 494506E816E5461F00B9FF78 /* OVR_FileFILE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5337C16E544E30039CB59 /* OVR_FileFILE.cpp */; }; + 494506EC16E5461F00B9FF78 /* OVR_Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338016E544E30039CB59 /* OVR_Log.cpp */; }; + 494506EE16E5461F00B9FF78 /* OVR_Math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338216E544E30039CB59 /* OVR_Math.cpp */; }; + 494506F016E5461F00B9FF78 /* OVR_RefCount.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338416E544E30039CB59 /* OVR_RefCount.cpp */; }; + 494506F216E5461F00B9FF78 /* OVR_Std.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338616E544E30039CB59 /* OVR_Std.cpp */; }; + 494506F416E5461F00B9FF78 /* OVR_String.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338816E544E30039CB59 /* OVR_String.cpp */; }; + 494506F616E5461F00B9FF78 /* OVR_String_FormatUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338A16E544E30039CB59 /* OVR_String_FormatUtil.cpp */; }; + 494506F716E5461F00B9FF78 /* OVR_String_PathUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338B16E544E30039CB59 /* OVR_String_PathUtil.cpp */; }; + 494506F916E5461F00B9FF78 /* OVR_SysFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338D16E544E30039CB59 /* OVR_SysFile.cpp */; }; + 494506FB16E5461F00B9FF78 /* OVR_System.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5338F16E544E30039CB59 /* OVR_System.cpp */; }; + 494506FE16E5461F00B9FF78 /* OVR_ThreadsPthread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5339216E544E30039CB59 /* OVR_ThreadsPthread.cpp */; }; + 494506FF16E5461F00B9FF78 /* OVR_Timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5339316E544E30039CB59 /* OVR_Timer.cpp */; }; + 4945070216E5461F00B9FF78 /* OVR_UTF8Util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5339616E544E30039CB59 /* OVR_UTF8Util.cpp */; }; + 4945070616E5462A00B9FF78 /* OVR_DeviceHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5339B16E544E30039CB59 /* OVR_DeviceHandle.cpp */; }; + 4945070816E5462A00B9FF78 /* OVR_DeviceImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5339D16E544E30039CB59 /* OVR_DeviceImpl.cpp */; }; + 4945070F16E5462A00B9FF78 /* OVR_SensorFusion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A533A416E544E30039CB59 /* OVR_SensorFusion.cpp */; }; + 4945071116E5462A00B9FF78 /* OVR_ThreadCommandQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A533A616E544E30039CB59 /* OVR_ThreadCommandQueue.cpp */; }; + 4985385E16ECFE92008D0727 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4945072216E55A0300B9FF78 /* Cocoa.framework */; }; + 4985387416ECFED8008D0727 /* Render_Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5335016E527820039CB59 /* Render_Device.cpp */; }; + 4985387816ECFED8008D0727 /* Render_GL_Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5335416E527820039CB59 /* Render_GL_Device.cpp */; }; + 4985387A16ECFED8008D0727 /* Render_LoadTextureTGA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5335816E527820039CB59 /* Render_LoadTextureTGA.cpp */; }; + 4985388016ECFEE5008D0727 /* Platform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5334216E527820039CB59 /* Platform.cpp */; }; + 4985388616ECFF2D008D0727 /* OculusWorldDemo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4985388116ECFF23008D0727 /* OculusWorldDemo.cpp */; }; + 4985388716ECFF2D008D0727 /* Player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4985388216ECFF23008D0727 /* Player.cpp */; }; + 4985388A16ECFF53008D0727 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 499ED4C716E6A187008EA2ED /* CoreFoundation.framework */; }; + 4985388C16ECFF53008D0727 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 499ED4C516E6A179008EA2ED /* IOKit.framework */; }; + 4985388D16ECFF53008D0727 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 499ED4C916E9880D008EA2ED /* IOSurface.framework */; }; + 4985388E16ECFF53008D0727 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4945071616E546F700B9FF78 /* OpenGL.framework */; }; + 4985389216ED0204008D0727 /* tinyxml2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4985389116ED0204008D0727 /* tinyxml2.cpp */; }; + 4985389516ED0218008D0727 /* Render_LoadTextureDDS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4985389316ED0218008D0727 /* Render_LoadTextureDDS.cpp */; }; + 4985389716ED1AA7008D0727 /* Assets in Resources */ = {isa = PBXBuildFile; fileRef = 4985389616ED1AA7008D0727 /* Assets */; }; + 49ABA2E91718A38100E288A7 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49ABA2E81718A38100E288A7 /* AudioToolbox.framework */; }; + 49DB65F11718B1E10097A8DD /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49DB65F01718B1E10097A8DD /* ApplicationServices.framework */; }; + 49DB65F71726F0C30097A8DD /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4945072216E55A0300B9FF78 /* Cocoa.framework */; }; + 49DB660F1726F0EA0097A8DD /* OSX_Platform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BBB8902171E2BE200563901 /* OSX_Platform.mm */; }; + 49DB66101726F0EA0097A8DD /* Platform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5334216E527820039CB59 /* Platform.cpp */; }; + 49DB66111726F0F80097A8DD /* Render_LoadTextureDDS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4985389316ED0218008D0727 /* Render_LoadTextureDDS.cpp */; }; + 49DB66121726F0F80097A8DD /* Render_Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5335016E527820039CB59 /* Render_Device.cpp */; }; + 49DB66131726F0F80097A8DD /* Render_GL_Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5335416E527820039CB59 /* Render_GL_Device.cpp */; }; + 49DB66141726F0F80097A8DD /* Render_LoadTextureTGA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A5335816E527820039CB59 /* Render_LoadTextureTGA.cpp */; }; + 49DB66171726F1350097A8DD /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49DB65F01718B1E10097A8DD /* ApplicationServices.framework */; }; + 49DB66181726F13B0097A8DD /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 499ED4C516E6A179008EA2ED /* IOKit.framework */; }; + 49DB66191726F1530097A8DD /* libovr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 49A5336D16E544BE0039CB59 /* libovr.a */; }; + 49DB661A1726F1650097A8DD /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4945071616E546F700B9FF78 /* OpenGL.framework */; }; + 9B6DEF851728A6560071E76B /* SensorBoxTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9B6DEF841728A6560071E76B /* SensorBoxTest.cpp */; }; + 9BA4DDB61721E12400CF7715 /* OSX_Platform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BBB8902171E2BE200563901 /* OSX_Platform.mm */; }; + 9BA4DDB81727061100CF7715 /* Oculus.icns in Resources */ = {isa = PBXBuildFile; fileRef = 9BA4DDB71727061100CF7715 /* Oculus.icns */; }; + 9BA4DDB91727083500CF7715 /* Oculus.icns in Resources */ = {isa = PBXBuildFile; fileRef = 9BA4DDB71727061100CF7715 /* Oculus.icns */; }; + 9BAB70F6170E69530006FE98 /* libovr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 49A5336D16E544BE0039CB59 /* libovr.a */; }; + 9BB10AF1173C4918009ED618 /* OSX_Gamepad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BB10AEF173C4918009ED618 /* OSX_Gamepad.cpp */; }; + 9BB86B6D1747178B0071C537 /* OSX_Gamepad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BB10AEF173C4918009ED618 /* OSX_Gamepad.cpp */; }; + 9BCE53F916F0293A007A23FF /* OVR_HIDDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BCE53F416F0293A007A23FF /* OVR_HIDDevice.h */; }; + 9BCE53FA16F0293A007A23FF /* OVR_HIDDeviceBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BCE53F516F0293A007A23FF /* OVR_HIDDeviceBase.h */; }; + 9BCE53FB16F0293A007A23FF /* OVR_HIDDeviceImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BCE53F616F0293A007A23FF /* OVR_HIDDeviceImpl.h */; }; + 9BCE53FC16F0293A007A23FF /* OVR_LatencyTestImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BCE53F716F0293A007A23FF /* OVR_LatencyTestImpl.cpp */; }; + 9BCE53FD16F0293A007A23FF /* OVR_LatencyTestImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BCE53F816F0293A007A23FF /* OVR_LatencyTestImpl.h */; }; + 9BCE540016F02A56007A23FF /* OVR_SensorImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BCE53FE16F02A56007A23FF /* OVR_SensorImpl.cpp */; }; + 9BCE540116F02A56007A23FF /* OVR_SensorImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BCE53FF16F02A56007A23FF /* OVR_SensorImpl.h */; }; + 9BCE541616F02ABC007A23FF /* OVR_OSX_DeviceManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BCE53EB16F028A9007A23FF /* OVR_OSX_DeviceManager.cpp */; }; + 9BCE541716F02AD5007A23FF /* OVR_OSX_HIDDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BCE53ED16F028AA007A23FF /* OVR_OSX_HIDDevice.cpp */; }; + 9BCE541816F02ADB007A23FF /* OVR_OSX_DeviceManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BCE53EC16F028AA007A23FF /* OVR_OSX_DeviceManager.h */; }; + 9BCE541916F02AE6007A23FF /* OVR_OSX_HIDDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BCE53EE16F028AA007A23FF /* OVR_OSX_HIDDevice.h */; }; + 9BCE541A16F02AEB007A23FF /* OVR_OSX_HMDDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BCE53EF16F028AA007A23FF /* OVR_OSX_HMDDevice.cpp */; }; + 9BCE541B16F02AF1007A23FF /* OVR_OSX_HMDDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BCE53F016F028AA007A23FF /* OVR_OSX_HMDDevice.h */; }; + 9BCE541C16F02B04007A23FF /* OVR_Device.h in Headers */ = {isa = PBXBuildFile; fileRef = 499ED4B216E5703B008EA2ED /* OVR_Device.h */; }; + 9BCE541D16F02B13007A23FF /* OVR_DeviceConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 49A5339A16E544E30039CB59 /* OVR_DeviceConstants.h */; }; + 9BCE541E16F02B1D007A23FF /* OVR_DeviceHandle.h in Headers */ = {isa = PBXBuildFile; fileRef = 49A5339C16E544E30039CB59 /* OVR_DeviceHandle.h */; }; + 9BCE541F16F02B26007A23FF /* OVR_DeviceImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 49A5339E16E544E30039CB59 /* OVR_DeviceImpl.h */; }; + 9BCE542016F02B2A007A23FF /* OVR_DeviceMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = 49A5339F16E544E30039CB59 /* OVR_DeviceMessages.h */; }; + 9BCE542216F02B88007A23FF /* OVR_SensorFusion.h in Headers */ = {isa = PBXBuildFile; fileRef = 49A533A516E544E30039CB59 /* OVR_SensorFusion.h */; }; + 9BCE542316F02B91007A23FF /* OVR_ThreadCommandQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 49A533A716E544E30039CB59 /* OVR_ThreadCommandQueue.h */; }; + 9BCE542516F2694F007A23FF /* OVR_OSX_SensorDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BCE542416F2694E007A23FF /* OVR_OSX_SensorDevice.cpp */; }; + 9BD2A642172069B300C3C389 /* Util_MagCalibration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BD2A640172069B300C3C389 /* Util_MagCalibration.cpp */; }; + 9BD2A643172069B300C3C389 /* Util_MagCalibration.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BD2A641172069B300C3C389 /* Util_MagCalibration.h */; }; + 9BD2A646172069BF00C3C389 /* OVR_SensorFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BD2A644172069BF00C3C389 /* OVR_SensorFilter.cpp */; }; + 9BD2A647172069BF00C3C389 /* OVR_SensorFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BD2A645172069BF00C3C389 /* OVR_SensorFilter.h */; }; + 9BEAD55F17187B8A00A8AA1D /* Util_LatencyTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BEAD55B17187B8A00A8AA1D /* Util_LatencyTest.cpp */; }; + 9BEAD56017187B8A00A8AA1D /* Util_LatencyTest.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BEAD55C17187B8A00A8AA1D /* Util_LatencyTest.h */; }; + 9BEAD56117187B8A00A8AA1D /* Util_Render_Stereo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BEAD55D17187B8A00A8AA1D /* Util_Render_Stereo.cpp */; }; + 9BEAD56217187B8A00A8AA1D /* Util_Render_Stereo.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BEAD55E17187B8A00A8AA1D /* Util_Render_Stereo.h */; }; + 9BEAD56517187CFF00A8AA1D /* OSX_WavPlayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BEAD56317187CFF00A8AA1D /* OSX_WavPlayer.cpp */; }; + 9BEAD56717187E7500A8AA1D /* Render_XmlSceneLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BEAD56617187E7500A8AA1D /* Render_XmlSceneLoader.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 37973B5A1739FA100093BBB8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 49A5332B16E527490039CB59 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 49A5336C16E544BE0039CB59; + remoteInfo = ovr; + }; + 4985388F16ECFF5C008D0727 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 49A5332B16E527490039CB59 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 49A5336C16E544BE0039CB59; + remoteInfo = ovr; + }; + 49DB660D1726F0CD0097A8DD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 49A5332B16E527490039CB59 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 49A5336C16E544BE0039CB59; + remoteInfo = ovr; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 37973B381739D78B0093BBB8 /* OculusRoomTiny.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OculusRoomTiny.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 37973B4F1739E1B60093BBB8 /* OculusRoomModel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OculusRoomModel.cpp; sourceTree = ""; }; + 37973B511739E1D20093BBB8 /* RenderTiny_Device.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderTiny_Device.cpp; sourceTree = ""; }; + 37973B521739E1D20093BBB8 /* RenderTiny_Device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderTiny_Device.h; sourceTree = ""; }; + 37973B541739E9230093BBB8 /* RenderTiny_GL_Device.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderTiny_GL_Device.cpp; sourceTree = ""; }; + 37973B551739E9230093BBB8 /* RenderTiny_GL_Device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderTiny_GL_Device.h; sourceTree = ""; }; + 37973B571739E9E80093BBB8 /* OSX_OculusRoomTiny.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSX_OculusRoomTiny.h; sourceTree = ""; }; + 37973B581739E9E80093BBB8 /* OSX_OculusRoomTiny.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OSX_OculusRoomTiny.mm; sourceTree = ""; }; + 4945071616E546F700B9FF78 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; }; + 4945071816E5474000B9FF78 /* libGL.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libGL.dylib; path = ../../../../../../../opt/X11/lib/libGL.dylib; sourceTree = ""; }; + 4945071A16E5474A00B9FF78 /* libX11.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libX11.dylib; path = ../../../../../../../opt/X11/lib/libX11.dylib; sourceTree = ""; }; + 4945072216E55A0300B9FF78 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + 4945072516E55A0300B9FF78 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; + 4945072616E55A0300B9FF78 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + 4945072716E55A0300B9FF78 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 4985385D16ECFE92008D0727 /* OculusWorldDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OculusWorldDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 4985388116ECFF23008D0727 /* OculusWorldDemo.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = OculusWorldDemo.cpp; path = OculusWorldDemo/OculusWorldDemo.cpp; sourceTree = ""; }; + 4985388216ECFF23008D0727 /* Player.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Player.cpp; path = OculusWorldDemo/Player.cpp; sourceTree = ""; }; + 4985388316ECFF23008D0727 /* Player.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Player.h; path = OculusWorldDemo/Player.h; sourceTree = ""; }; + 4985389116ED0204008D0727 /* tinyxml2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tinyxml2.cpp; path = ../3rdParty/TinyXml/tinyxml2.cpp; sourceTree = ""; }; + 4985389316ED0218008D0727 /* Render_LoadTextureDDS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Render_LoadTextureDDS.cpp; sourceTree = ""; }; + 4985389616ED1AA7008D0727 /* Assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Assets; path = OculusWorldDemo/Assets; sourceTree = ""; }; + 499ED49B16E57027008EA2ED /* OVR_Alg.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Alg.h; sourceTree = ""; }; + 499ED49C16E57027008EA2ED /* OVR_Allocator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Allocator.h; sourceTree = ""; }; + 499ED49D16E57027008EA2ED /* OVR_Array.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Array.h; sourceTree = ""; }; + 499ED49E16E57027008EA2ED /* OVR_Atomic.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Atomic.h; sourceTree = ""; }; + 499ED49F16E57027008EA2ED /* OVR_Color.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Color.h; sourceTree = ""; }; + 499ED4A016E57027008EA2ED /* OVR_ContainerAllocator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_ContainerAllocator.h; sourceTree = ""; }; + 499ED4A116E57027008EA2ED /* OVR_File.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_File.h; sourceTree = ""; }; + 499ED4A216E57027008EA2ED /* OVR_Hash.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Hash.h; sourceTree = ""; }; + 499ED4A316E57027008EA2ED /* OVR_KeyCodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_KeyCodes.h; sourceTree = ""; }; + 499ED4A416E57027008EA2ED /* OVR_List.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_List.h; sourceTree = ""; }; + 499ED4A516E57027008EA2ED /* OVR_Log.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Log.h; sourceTree = ""; }; + 499ED4A616E57027008EA2ED /* OVR_Math.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Math.h; sourceTree = ""; }; + 499ED4A716E57027008EA2ED /* OVR_RefCount.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_RefCount.h; sourceTree = ""; }; + 499ED4A816E57027008EA2ED /* OVR_Std.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Std.h; sourceTree = ""; }; + 499ED4A916E57027008EA2ED /* OVR_String.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_String.h; sourceTree = ""; }; + 499ED4AA16E57027008EA2ED /* OVR_StringHash.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_StringHash.h; sourceTree = ""; }; + 499ED4AB16E57027008EA2ED /* OVR_SysFile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_SysFile.h; sourceTree = ""; }; + 499ED4AC16E57027008EA2ED /* OVR_System.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_System.h; sourceTree = ""; }; + 499ED4AD16E57027008EA2ED /* OVR_Threads.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Threads.h; sourceTree = ""; }; + 499ED4AE16E57027008EA2ED /* OVR_ThreadsWinAPI.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_ThreadsWinAPI.cpp; sourceTree = ""; }; + 499ED4AF16E57027008EA2ED /* OVR_Timer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Timer.h; sourceTree = ""; }; + 499ED4B016E57027008EA2ED /* OVR_Types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Types.h; sourceTree = ""; }; + 499ED4B116E57027008EA2ED /* OVR_UTF8Util.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_UTF8Util.h; sourceTree = ""; }; + 499ED4B216E5703B008EA2ED /* OVR_Device.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OVR_Device.h; sourceTree = ""; }; + 499ED4C516E6A179008EA2ED /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; + 499ED4C716E6A187008EA2ED /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 499ED4C916E9880D008EA2ED /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = System/Library/Frameworks/IOSurface.framework; sourceTree = SDKROOT; }; + 49A5334216E527820039CB59 /* Platform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Platform.cpp; sourceTree = ""; }; + 49A5334316E527820039CB59 /* Platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Platform.h; sourceTree = ""; }; + 49A5334416E527820039CB59 /* Platform_Default.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Platform_Default.h; sourceTree = ""; }; + 49A5335016E527820039CB59 /* Render_Device.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Render_Device.cpp; sourceTree = ""; }; + 49A5335116E527820039CB59 /* Render_Device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Render_Device.h; sourceTree = ""; }; + 49A5335216E527820039CB59 /* Render_Font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Render_Font.h; sourceTree = ""; }; + 49A5335316E527820039CB59 /* Render_FontEmbed_DejaVu48.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Render_FontEmbed_DejaVu48.h; sourceTree = ""; }; + 49A5335416E527820039CB59 /* Render_GL_Device.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Render_GL_Device.cpp; sourceTree = ""; }; + 49A5335516E527820039CB59 /* Render_GL_Device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Render_GL_Device.h; sourceTree = ""; }; + 49A5335816E527820039CB59 /* Render_LoadTextureTGA.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Render_LoadTextureTGA.cpp; sourceTree = ""; }; + 49A5336D16E544BE0039CB59 /* libovr.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libovr.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 49A5337116E544E30039CB59 /* OVR_Alg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Alg.cpp; sourceTree = ""; }; + 49A5337316E544E30039CB59 /* OVR_Allocator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Allocator.cpp; sourceTree = ""; }; + 49A5337616E544E30039CB59 /* OVR_Atomic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Atomic.cpp; sourceTree = ""; }; + 49A5337A16E544E30039CB59 /* OVR_File.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_File.cpp; sourceTree = ""; }; + 49A5337C16E544E30039CB59 /* OVR_FileFILE.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_FileFILE.cpp; sourceTree = ""; }; + 49A5338016E544E30039CB59 /* OVR_Log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Log.cpp; sourceTree = ""; }; + 49A5338216E544E30039CB59 /* OVR_Math.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Math.cpp; sourceTree = ""; }; + 49A5338416E544E30039CB59 /* OVR_RefCount.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_RefCount.cpp; sourceTree = ""; }; + 49A5338616E544E30039CB59 /* OVR_Std.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Std.cpp; sourceTree = ""; }; + 49A5338816E544E30039CB59 /* OVR_String.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_String.cpp; sourceTree = ""; }; + 49A5338A16E544E30039CB59 /* OVR_String_FormatUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_String_FormatUtil.cpp; sourceTree = ""; }; + 49A5338B16E544E30039CB59 /* OVR_String_PathUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_String_PathUtil.cpp; sourceTree = ""; }; + 49A5338D16E544E30039CB59 /* OVR_SysFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_SysFile.cpp; sourceTree = ""; }; + 49A5338F16E544E30039CB59 /* OVR_System.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_System.cpp; sourceTree = ""; }; + 49A5339216E544E30039CB59 /* OVR_ThreadsPthread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_ThreadsPthread.cpp; sourceTree = ""; }; + 49A5339316E544E30039CB59 /* OVR_Timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Timer.cpp; sourceTree = ""; }; + 49A5339616E544E30039CB59 /* OVR_UTF8Util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_UTF8Util.cpp; sourceTree = ""; }; + 49A5339A16E544E30039CB59 /* OVR_DeviceConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_DeviceConstants.h; sourceTree = ""; }; + 49A5339B16E544E30039CB59 /* OVR_DeviceHandle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_DeviceHandle.cpp; sourceTree = ""; }; + 49A5339C16E544E30039CB59 /* OVR_DeviceHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_DeviceHandle.h; sourceTree = ""; }; + 49A5339D16E544E30039CB59 /* OVR_DeviceImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_DeviceImpl.cpp; sourceTree = ""; }; + 49A5339E16E544E30039CB59 /* OVR_DeviceImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_DeviceImpl.h; sourceTree = ""; }; + 49A5339F16E544E30039CB59 /* OVR_DeviceMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_DeviceMessages.h; sourceTree = ""; }; + 49A533A416E544E30039CB59 /* OVR_SensorFusion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_SensorFusion.cpp; sourceTree = ""; }; + 49A533A516E544E30039CB59 /* OVR_SensorFusion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_SensorFusion.h; sourceTree = ""; }; + 49A533A616E544E30039CB59 /* OVR_ThreadCommandQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_ThreadCommandQueue.cpp; sourceTree = ""; }; + 49A533A716E544E30039CB59 /* OVR_ThreadCommandQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_ThreadCommandQueue.h; sourceTree = ""; }; + 49ABA2E81718A38100E288A7 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = ../../../../../../../System/Library/Frameworks/AudioToolbox.framework; sourceTree = ""; }; + 49DB65F01718B1E10097A8DD /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = System/Library/Frameworks/ApplicationServices.framework; sourceTree = SDKROOT; }; + 49DB65F61726F0C30097A8DD /* SensorBoxTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SensorBoxTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 9B22CBB517187F5C0046D43D /* tinyxml2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tinyxml2.h; path = ../3rdParty/TinyXml/tinyxml2.h; sourceTree = ""; }; + 9B6DEF841728A6560071E76B /* SensorBoxTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SensorBoxTest.cpp; path = SensorBox/SensorBoxTest.cpp; sourceTree = ""; }; + 9BA4DDB71727061100CF7715 /* Oculus.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = Oculus.icns; sourceTree = ""; }; + 9BB10AEF173C4918009ED618 /* OSX_Gamepad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OSX_Gamepad.cpp; sourceTree = ""; }; + 9BB10AF0173C4918009ED618 /* OSX_Gamepad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSX_Gamepad.h; sourceTree = ""; }; + 9BB10AF2173C49AC009ED618 /* Gamepad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Gamepad.h; sourceTree = ""; }; + 9BBB8901171E2BE200563901 /* OSX_Platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSX_Platform.h; sourceTree = ""; }; + 9BBB8902171E2BE200563901 /* OSX_Platform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OSX_Platform.mm; sourceTree = ""; }; + 9BBB8903171E2BE200563901 /* OSX_PlatformObjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSX_PlatformObjc.h; sourceTree = ""; }; + 9BCE53EB16F028A9007A23FF /* OVR_OSX_DeviceManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_OSX_DeviceManager.cpp; sourceTree = ""; }; + 9BCE53EC16F028AA007A23FF /* OVR_OSX_DeviceManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_OSX_DeviceManager.h; sourceTree = ""; }; + 9BCE53ED16F028AA007A23FF /* OVR_OSX_HIDDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_OSX_HIDDevice.cpp; sourceTree = ""; }; + 9BCE53EE16F028AA007A23FF /* OVR_OSX_HIDDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_OSX_HIDDevice.h; sourceTree = ""; }; + 9BCE53EF16F028AA007A23FF /* OVR_OSX_HMDDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_OSX_HMDDevice.cpp; sourceTree = ""; }; + 9BCE53F016F028AA007A23FF /* OVR_OSX_HMDDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_OSX_HMDDevice.h; sourceTree = ""; }; + 9BCE53F416F0293A007A23FF /* OVR_HIDDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_HIDDevice.h; sourceTree = ""; }; + 9BCE53F516F0293A007A23FF /* OVR_HIDDeviceBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_HIDDeviceBase.h; sourceTree = ""; }; + 9BCE53F616F0293A007A23FF /* OVR_HIDDeviceImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_HIDDeviceImpl.h; sourceTree = ""; }; + 9BCE53F716F0293A007A23FF /* OVR_LatencyTestImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_LatencyTestImpl.cpp; sourceTree = ""; }; + 9BCE53F816F0293A007A23FF /* OVR_LatencyTestImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_LatencyTestImpl.h; sourceTree = ""; }; + 9BCE53FE16F02A56007A23FF /* OVR_SensorImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_SensorImpl.cpp; sourceTree = ""; }; + 9BCE53FF16F02A56007A23FF /* OVR_SensorImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_SensorImpl.h; sourceTree = ""; }; + 9BCE542416F2694E007A23FF /* OVR_OSX_SensorDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_OSX_SensorDevice.cpp; sourceTree = ""; }; + 9BD2A640172069B300C3C389 /* Util_MagCalibration.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Util_MagCalibration.cpp; path = Util/Util_MagCalibration.cpp; sourceTree = ""; }; + 9BD2A641172069B300C3C389 /* Util_MagCalibration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Util_MagCalibration.h; path = Util/Util_MagCalibration.h; sourceTree = ""; }; + 9BD2A644172069BF00C3C389 /* OVR_SensorFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_SensorFilter.cpp; sourceTree = ""; }; + 9BD2A645172069BF00C3C389 /* OVR_SensorFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_SensorFilter.h; sourceTree = ""; }; + 9BEAD55B17187B8A00A8AA1D /* Util_LatencyTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Util_LatencyTest.cpp; path = Util/Util_LatencyTest.cpp; sourceTree = ""; }; + 9BEAD55C17187B8A00A8AA1D /* Util_LatencyTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Util_LatencyTest.h; path = Util/Util_LatencyTest.h; sourceTree = ""; }; + 9BEAD55D17187B8A00A8AA1D /* Util_Render_Stereo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Util_Render_Stereo.cpp; path = Util/Util_Render_Stereo.cpp; sourceTree = ""; }; + 9BEAD55E17187B8A00A8AA1D /* Util_Render_Stereo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Util_Render_Stereo.h; path = Util/Util_Render_Stereo.h; sourceTree = ""; }; + 9BEAD56317187CFF00A8AA1D /* OSX_WavPlayer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OSX_WavPlayer.cpp; sourceTree = ""; }; + 9BEAD56417187CFF00A8AA1D /* OSX_WavPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSX_WavPlayer.h; sourceTree = ""; }; + 9BEAD56617187E7500A8AA1D /* Render_XmlSceneLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Render_XmlSceneLoader.cpp; sourceTree = ""; }; + 9BEAD56817187E8300A8AA1D /* Render_XmlSceneLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Render_XmlSceneLoader.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 37973B351739D78B0093BBB8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 37973B601739FAD50093BBB8 /* CoreFoundation.framework in Frameworks */, + 37973B5F1739FAC80093BBB8 /* ApplicationServices.framework in Frameworks */, + 37973B5E1739FAB60093BBB8 /* OpenGL.framework in Frameworks */, + 37973B5D1739FA920093BBB8 /* IOKit.framework in Frameworks */, + 37973B391739D78B0093BBB8 /* Cocoa.framework in Frameworks */, + 37973B5C1739FA620093BBB8 /* libovr.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4985385A16ECFE92008D0727 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 49DB65F11718B1E10097A8DD /* ApplicationServices.framework in Frameworks */, + 49ABA2E91718A38100E288A7 /* AudioToolbox.framework in Frameworks */, + 4985388A16ECFF53008D0727 /* CoreFoundation.framework in Frameworks */, + 4985388C16ECFF53008D0727 /* IOKit.framework in Frameworks */, + 4985388D16ECFF53008D0727 /* IOSurface.framework in Frameworks */, + 4985388E16ECFF53008D0727 /* OpenGL.framework in Frameworks */, + 4985385E16ECFE92008D0727 /* Cocoa.framework in Frameworks */, + 9BAB70F6170E69530006FE98 /* libovr.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 49A5336A16E544BE0039CB59 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 49DB65F31726F0C30097A8DD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 49DB661A1726F1650097A8DD /* OpenGL.framework in Frameworks */, + 49DB66191726F1530097A8DD /* libovr.a in Frameworks */, + 49DB66181726F13B0097A8DD /* IOKit.framework in Frameworks */, + 49DB66171726F1350097A8DD /* ApplicationServices.framework in Frameworks */, + 49DB65F71726F0C30097A8DD /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 37973B3A1739D78B0093BBB8 /* OculusRoomTiny */ = { + isa = PBXGroup; + children = ( + 37973B571739E9E80093BBB8 /* OSX_OculusRoomTiny.h */, + 37973B581739E9E80093BBB8 /* OSX_OculusRoomTiny.mm */, + 37973B541739E9230093BBB8 /* RenderTiny_GL_Device.cpp */, + 37973B551739E9230093BBB8 /* RenderTiny_GL_Device.h */, + 37973B511739E1D20093BBB8 /* RenderTiny_Device.cpp */, + 37973B521739E1D20093BBB8 /* RenderTiny_Device.h */, + 37973B4F1739E1B60093BBB8 /* OculusRoomModel.cpp */, + ); + path = OculusRoomTiny; + sourceTree = ""; + }; + 4945072116E55A0300B9FF78 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 4945072216E55A0300B9FF78 /* Cocoa.framework */, + 4945072416E55A0300B9FF78 /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; + 4945072416E55A0300B9FF78 /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + 4945072516E55A0300B9FF78 /* AppKit.framework */, + 4945072616E55A0300B9FF78 /* CoreData.framework */, + 4945072716E55A0300B9FF78 /* Foundation.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 49A5332A16E527490039CB59 = { + isa = PBXGroup; + children = ( + 9BA4DDB71727061100CF7715 /* Oculus.icns */, + 49DB65F01718B1E10097A8DD /* ApplicationServices.framework */, + 49ABA2E81718A38100E288A7 /* AudioToolbox.framework */, + 9B22CBB417187F380046D43D /* 3rdParty */, + 9B6DEF831728A60A0071E76B /* SensorBox */, + 9B22CBB317187EC70046D43D /* OculusWorldDemo */, + 4985389616ED1AA7008D0727 /* Assets */, + 37973B3A1739D78B0093BBB8 /* OculusRoomTiny */, + 49A5333416E527490039CB59 /* Products */, + 49A533A816E544E30039CB59 /* Src */, + 49A5334116E527820039CB59 /* Platform */, + 49A5334916E527820039CB59 /* Render */, + 4945071816E5474000B9FF78 /* libGL.dylib */, + 4945071A16E5474A00B9FF78 /* libX11.dylib */, + 4945072116E55A0300B9FF78 /* Frameworks */, + 499ED4C716E6A187008EA2ED /* CoreFoundation.framework */, + 4945071616E546F700B9FF78 /* OpenGL.framework */, + 499ED4C516E6A179008EA2ED /* IOKit.framework */, + 499ED4C916E9880D008EA2ED /* IOSurface.framework */, + ); + sourceTree = ""; + }; + 49A5333416E527490039CB59 /* Products */ = { + isa = PBXGroup; + children = ( + 49A5336D16E544BE0039CB59 /* libovr.a */, + 4985385D16ECFE92008D0727 /* OculusWorldDemo.app */, + 49DB65F61726F0C30097A8DD /* SensorBoxTest.app */, + 37973B381739D78B0093BBB8 /* OculusRoomTiny.app */, + ); + name = Products; + sourceTree = ""; + }; + 49A5334116E527820039CB59 /* Platform */ = { + isa = PBXGroup; + children = ( + 9BB10AF2173C49AC009ED618 /* Gamepad.h */, + 9BB10AEF173C4918009ED618 /* OSX_Gamepad.cpp */, + 9BB10AF0173C4918009ED618 /* OSX_Gamepad.h */, + 9BEAD56317187CFF00A8AA1D /* OSX_WavPlayer.cpp */, + 9BEAD56417187CFF00A8AA1D /* OSX_WavPlayer.h */, + 9BBB8901171E2BE200563901 /* OSX_Platform.h */, + 9BBB8902171E2BE200563901 /* OSX_Platform.mm */, + 9BBB8903171E2BE200563901 /* OSX_PlatformObjc.h */, + 49A5334216E527820039CB59 /* Platform.cpp */, + 49A5334316E527820039CB59 /* Platform.h */, + 49A5334416E527820039CB59 /* Platform_Default.h */, + ); + name = Platform; + path = CommonSrc/Platform; + sourceTree = ""; + }; + 49A5334916E527820039CB59 /* Render */ = { + isa = PBXGroup; + children = ( + 9BEAD56817187E8300A8AA1D /* Render_XmlSceneLoader.h */, + 9BEAD56617187E7500A8AA1D /* Render_XmlSceneLoader.cpp */, + 4985389316ED0218008D0727 /* Render_LoadTextureDDS.cpp */, + 49A5335016E527820039CB59 /* Render_Device.cpp */, + 49A5335116E527820039CB59 /* Render_Device.h */, + 49A5335216E527820039CB59 /* Render_Font.h */, + 49A5335316E527820039CB59 /* Render_FontEmbed_DejaVu48.h */, + 49A5335416E527820039CB59 /* Render_GL_Device.cpp */, + 49A5335516E527820039CB59 /* Render_GL_Device.h */, + 49A5335816E527820039CB59 /* Render_LoadTextureTGA.cpp */, + ); + name = Render; + path = CommonSrc/Render; + sourceTree = ""; + }; + 49A5339816E544E30039CB59 /* Kernel */ = { + isa = PBXGroup; + children = ( + 499ED49B16E57027008EA2ED /* OVR_Alg.h */, + 499ED49C16E57027008EA2ED /* OVR_Allocator.h */, + 499ED49D16E57027008EA2ED /* OVR_Array.h */, + 499ED49E16E57027008EA2ED /* OVR_Atomic.h */, + 499ED49F16E57027008EA2ED /* OVR_Color.h */, + 499ED4A016E57027008EA2ED /* OVR_ContainerAllocator.h */, + 499ED4A116E57027008EA2ED /* OVR_File.h */, + 499ED4A216E57027008EA2ED /* OVR_Hash.h */, + 499ED4A316E57027008EA2ED /* OVR_KeyCodes.h */, + 499ED4A416E57027008EA2ED /* OVR_List.h */, + 499ED4A516E57027008EA2ED /* OVR_Log.h */, + 499ED4A616E57027008EA2ED /* OVR_Math.h */, + 499ED4A716E57027008EA2ED /* OVR_RefCount.h */, + 499ED4A816E57027008EA2ED /* OVR_Std.h */, + 499ED4A916E57027008EA2ED /* OVR_String.h */, + 499ED4AA16E57027008EA2ED /* OVR_StringHash.h */, + 499ED4AB16E57027008EA2ED /* OVR_SysFile.h */, + 499ED4AC16E57027008EA2ED /* OVR_System.h */, + 499ED4AD16E57027008EA2ED /* OVR_Threads.h */, + 499ED4AE16E57027008EA2ED /* OVR_ThreadsWinAPI.cpp */, + 499ED4AF16E57027008EA2ED /* OVR_Timer.h */, + 499ED4B016E57027008EA2ED /* OVR_Types.h */, + 499ED4B116E57027008EA2ED /* OVR_UTF8Util.h */, + 49A5337116E544E30039CB59 /* OVR_Alg.cpp */, + 49A5337316E544E30039CB59 /* OVR_Allocator.cpp */, + 49A5337616E544E30039CB59 /* OVR_Atomic.cpp */, + 49A5337A16E544E30039CB59 /* OVR_File.cpp */, + 49A5337C16E544E30039CB59 /* OVR_FileFILE.cpp */, + 49A5338016E544E30039CB59 /* OVR_Log.cpp */, + 49A5338216E544E30039CB59 /* OVR_Math.cpp */, + 49A5338416E544E30039CB59 /* OVR_RefCount.cpp */, + 49A5338616E544E30039CB59 /* OVR_Std.cpp */, + 49A5338816E544E30039CB59 /* OVR_String.cpp */, + 49A5338A16E544E30039CB59 /* OVR_String_FormatUtil.cpp */, + 49A5338B16E544E30039CB59 /* OVR_String_PathUtil.cpp */, + 49A5338D16E544E30039CB59 /* OVR_SysFile.cpp */, + 49A5338F16E544E30039CB59 /* OVR_System.cpp */, + 49A5339216E544E30039CB59 /* OVR_ThreadsPthread.cpp */, + 49A5339316E544E30039CB59 /* OVR_Timer.cpp */, + 49A5339616E544E30039CB59 /* OVR_UTF8Util.cpp */, + ); + path = Kernel; + sourceTree = ""; + }; + 49A533A816E544E30039CB59 /* Src */ = { + isa = PBXGroup; + children = ( + 9BD2A644172069BF00C3C389 /* OVR_SensorFilter.cpp */, + 9BD2A645172069BF00C3C389 /* OVR_SensorFilter.h */, + 9BEAD55A17187B5500A8AA1D /* Util */, + 49A5339816E544E30039CB59 /* Kernel */, + 9BCE542416F2694E007A23FF /* OVR_OSX_SensorDevice.cpp */, + 9BCE53EB16F028A9007A23FF /* OVR_OSX_DeviceManager.cpp */, + 9BCE53EC16F028AA007A23FF /* OVR_OSX_DeviceManager.h */, + 9BCE53ED16F028AA007A23FF /* OVR_OSX_HIDDevice.cpp */, + 9BCE53EE16F028AA007A23FF /* OVR_OSX_HIDDevice.h */, + 9BCE53EF16F028AA007A23FF /* OVR_OSX_HMDDevice.cpp */, + 9BCE53F016F028AA007A23FF /* OVR_OSX_HMDDevice.h */, + 499ED4B216E5703B008EA2ED /* OVR_Device.h */, + 49A5339A16E544E30039CB59 /* OVR_DeviceConstants.h */, + 49A5339B16E544E30039CB59 /* OVR_DeviceHandle.cpp */, + 49A5339C16E544E30039CB59 /* OVR_DeviceHandle.h */, + 49A5339D16E544E30039CB59 /* OVR_DeviceImpl.cpp */, + 49A5339E16E544E30039CB59 /* OVR_DeviceImpl.h */, + 49A5339F16E544E30039CB59 /* OVR_DeviceMessages.h */, + 9BCE53FE16F02A56007A23FF /* OVR_SensorImpl.cpp */, + 9BCE53FF16F02A56007A23FF /* OVR_SensorImpl.h */, + 9BCE53F416F0293A007A23FF /* OVR_HIDDevice.h */, + 9BCE53F516F0293A007A23FF /* OVR_HIDDeviceBase.h */, + 9BCE53F616F0293A007A23FF /* OVR_HIDDeviceImpl.h */, + 9BCE53F716F0293A007A23FF /* OVR_LatencyTestImpl.cpp */, + 9BCE53F816F0293A007A23FF /* OVR_LatencyTestImpl.h */, + 49A533A416E544E30039CB59 /* OVR_SensorFusion.cpp */, + 49A533A516E544E30039CB59 /* OVR_SensorFusion.h */, + 49A533A616E544E30039CB59 /* OVR_ThreadCommandQueue.cpp */, + 49A533A716E544E30039CB59 /* OVR_ThreadCommandQueue.h */, + ); + name = Src; + path = ../LibOVR/Src; + sourceTree = ""; + }; + 9B22CBB317187EC70046D43D /* OculusWorldDemo */ = { + isa = PBXGroup; + children = ( + 4985388216ECFF23008D0727 /* Player.cpp */, + 4985388316ECFF23008D0727 /* Player.h */, + 4985388116ECFF23008D0727 /* OculusWorldDemo.cpp */, + ); + name = OculusWorldDemo; + sourceTree = ""; + }; + 9B22CBB417187F380046D43D /* 3rdParty */ = { + isa = PBXGroup; + children = ( + 9B22CBB517187F5C0046D43D /* tinyxml2.h */, + 4985389116ED0204008D0727 /* tinyxml2.cpp */, + ); + name = 3rdParty; + sourceTree = ""; + }; + 9B6DEF831728A60A0071E76B /* SensorBox */ = { + isa = PBXGroup; + children = ( + 9B6DEF841728A6560071E76B /* SensorBoxTest.cpp */, + ); + name = SensorBox; + sourceTree = ""; + }; + 9BEAD55A17187B5500A8AA1D /* Util */ = { + isa = PBXGroup; + children = ( + 9BD2A640172069B300C3C389 /* Util_MagCalibration.cpp */, + 9BD2A641172069B300C3C389 /* Util_MagCalibration.h */, + 9BEAD55B17187B8A00A8AA1D /* Util_LatencyTest.cpp */, + 9BEAD55C17187B8A00A8AA1D /* Util_LatencyTest.h */, + 9BEAD55D17187B8A00A8AA1D /* Util_Render_Stereo.cpp */, + 9BEAD55E17187B8A00A8AA1D /* Util_Render_Stereo.h */, + ); + name = Util; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 49A5336B16E544BE0039CB59 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 9BCE53F916F0293A007A23FF /* OVR_HIDDevice.h in Headers */, + 9BCE53FA16F0293A007A23FF /* OVR_HIDDeviceBase.h in Headers */, + 9BCE53FB16F0293A007A23FF /* OVR_HIDDeviceImpl.h in Headers */, + 9BCE53FD16F0293A007A23FF /* OVR_LatencyTestImpl.h in Headers */, + 9BCE540116F02A56007A23FF /* OVR_SensorImpl.h in Headers */, + 9BCE541816F02ADB007A23FF /* OVR_OSX_DeviceManager.h in Headers */, + 9BCE541916F02AE6007A23FF /* OVR_OSX_HIDDevice.h in Headers */, + 9BCE541B16F02AF1007A23FF /* OVR_OSX_HMDDevice.h in Headers */, + 9BCE541C16F02B04007A23FF /* OVR_Device.h in Headers */, + 9BCE541D16F02B13007A23FF /* OVR_DeviceConstants.h in Headers */, + 9BCE541E16F02B1D007A23FF /* OVR_DeviceHandle.h in Headers */, + 9BCE541F16F02B26007A23FF /* OVR_DeviceImpl.h in Headers */, + 9BCE542016F02B2A007A23FF /* OVR_DeviceMessages.h in Headers */, + 9BCE542216F02B88007A23FF /* OVR_SensorFusion.h in Headers */, + 9BCE542316F02B91007A23FF /* OVR_ThreadCommandQueue.h in Headers */, + 9BEAD56017187B8A00A8AA1D /* Util_LatencyTest.h in Headers */, + 9BEAD56217187B8A00A8AA1D /* Util_Render_Stereo.h in Headers */, + 9BD2A643172069B300C3C389 /* Util_MagCalibration.h in Headers */, + 9BD2A647172069BF00C3C389 /* OVR_SensorFilter.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 37973B371739D78B0093BBB8 /* OculusRoomTiny */ = { + isa = PBXNativeTarget; + buildConfigurationList = 37973B4E1739D7930093BBB8 /* Build configuration list for PBXNativeTarget "OculusRoomTiny" */; + buildPhases = ( + 37973B341739D78B0093BBB8 /* Sources */, + 37973B351739D78B0093BBB8 /* Frameworks */, + 37973B361739D78B0093BBB8 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 37973B5B1739FA100093BBB8 /* PBXTargetDependency */, + ); + name = OculusRoomTiny; + productName = OculusRoomTiny; + productReference = 37973B381739D78B0093BBB8 /* OculusRoomTiny.app */; + productType = "com.apple.product-type.application"; + }; + 4985385C16ECFE92008D0727 /* OculusWorldDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4985387116ECFE92008D0727 /* Build configuration list for PBXNativeTarget "OculusWorldDemo" */; + buildPhases = ( + 4985385916ECFE92008D0727 /* Sources */, + 4985385A16ECFE92008D0727 /* Frameworks */, + 4985385B16ECFE92008D0727 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 4985389016ECFF5C008D0727 /* PBXTargetDependency */, + ); + name = OculusWorldDemo; + productName = OculusWorldDemo; + productReference = 4985385D16ECFE92008D0727 /* OculusWorldDemo.app */; + productType = "com.apple.product-type.application"; + }; + 49A5336C16E544BE0039CB59 /* ovr */ = { + isa = PBXNativeTarget; + buildConfigurationList = 49A5336E16E544BE0039CB59 /* Build configuration list for PBXNativeTarget "ovr" */; + buildPhases = ( + 49A5336916E544BE0039CB59 /* Sources */, + 49A5336A16E544BE0039CB59 /* Frameworks */, + 49A5336B16E544BE0039CB59 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ovr; + productName = ovr; + productReference = 49A5336D16E544BE0039CB59 /* libovr.a */; + productType = "com.apple.product-type.library.static"; + }; + 49DB65F51726F0C30097A8DD /* SensorBoxTest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 49DB660C1726F0C30097A8DD /* Build configuration list for PBXNativeTarget "SensorBoxTest" */; + buildPhases = ( + 49DB65F21726F0C30097A8DD /* Sources */, + 49DB65F31726F0C30097A8DD /* Frameworks */, + 49DB65F41726F0C30097A8DD /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 49DB660E1726F0CD0097A8DD /* PBXTargetDependency */, + ); + name = SensorBoxTest; + productName = SensorBoxTest; + productReference = 49DB65F61726F0C30097A8DD /* SensorBoxTest.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 49A5332B16E527490039CB59 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0460; + ORGANIZATIONNAME = "Oculus VR"; + }; + buildConfigurationList = 49A5332E16E527490039CB59 /* Build configuration list for PBXProject "LibOVR_With_Samples" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 49A5332A16E527490039CB59; + productRefGroup = 49A5333416E527490039CB59 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 49A5336C16E544BE0039CB59 /* ovr */, + 4985385C16ECFE92008D0727 /* OculusWorldDemo */, + 49DB65F51726F0C30097A8DD /* SensorBoxTest */, + 37973B371739D78B0093BBB8 /* OculusRoomTiny */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 37973B361739D78B0093BBB8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4985385B16ECFE92008D0727 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4985389716ED1AA7008D0727 /* Assets in Resources */, + 9BA4DDB81727061100CF7715 /* Oculus.icns in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 49DB65F41726F0C30097A8DD /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9BA4DDB91727083500CF7715 /* Oculus.icns in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 37973B341739D78B0093BBB8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 37973B501739E1B60093BBB8 /* OculusRoomModel.cpp in Sources */, + 37973B531739E1D20093BBB8 /* RenderTiny_Device.cpp in Sources */, + 37973B561739E9230093BBB8 /* RenderTiny_GL_Device.cpp in Sources */, + 37973B591739E9E80093BBB8 /* OSX_OculusRoomTiny.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4985385916ECFE92008D0727 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4985388616ECFF2D008D0727 /* OculusWorldDemo.cpp in Sources */, + 4985388716ECFF2D008D0727 /* Player.cpp in Sources */, + 4985388016ECFEE5008D0727 /* Platform.cpp in Sources */, + 4985387416ECFED8008D0727 /* Render_Device.cpp in Sources */, + 4985387816ECFED8008D0727 /* Render_GL_Device.cpp in Sources */, + 4985387A16ECFED8008D0727 /* Render_LoadTextureTGA.cpp in Sources */, + 4985389216ED0204008D0727 /* tinyxml2.cpp in Sources */, + 4985389516ED0218008D0727 /* Render_LoadTextureDDS.cpp in Sources */, + 9BEAD56517187CFF00A8AA1D /* OSX_WavPlayer.cpp in Sources */, + 9BEAD56717187E7500A8AA1D /* Render_XmlSceneLoader.cpp in Sources */, + 9BA4DDB61721E12400CF7715 /* OSX_Platform.mm in Sources */, + 9BB10AF1173C4918009ED618 /* OSX_Gamepad.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 49A5336916E544BE0039CB59 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4945070616E5462A00B9FF78 /* OVR_DeviceHandle.cpp in Sources */, + 4945070816E5462A00B9FF78 /* OVR_DeviceImpl.cpp in Sources */, + 4945070F16E5462A00B9FF78 /* OVR_SensorFusion.cpp in Sources */, + 4945071116E5462A00B9FF78 /* OVR_ThreadCommandQueue.cpp in Sources */, + 494506DD16E5461F00B9FF78 /* OVR_Alg.cpp in Sources */, + 494506DF16E5461F00B9FF78 /* OVR_Allocator.cpp in Sources */, + 494506E216E5461F00B9FF78 /* OVR_Atomic.cpp in Sources */, + 494506E616E5461F00B9FF78 /* OVR_File.cpp in Sources */, + 494506E816E5461F00B9FF78 /* OVR_FileFILE.cpp in Sources */, + 494506EC16E5461F00B9FF78 /* OVR_Log.cpp in Sources */, + 494506EE16E5461F00B9FF78 /* OVR_Math.cpp in Sources */, + 494506F016E5461F00B9FF78 /* OVR_RefCount.cpp in Sources */, + 494506F216E5461F00B9FF78 /* OVR_Std.cpp in Sources */, + 494506F416E5461F00B9FF78 /* OVR_String.cpp in Sources */, + 494506F616E5461F00B9FF78 /* OVR_String_FormatUtil.cpp in Sources */, + 494506F716E5461F00B9FF78 /* OVR_String_PathUtil.cpp in Sources */, + 494506F916E5461F00B9FF78 /* OVR_SysFile.cpp in Sources */, + 494506FB16E5461F00B9FF78 /* OVR_System.cpp in Sources */, + 494506FE16E5461F00B9FF78 /* OVR_ThreadsPthread.cpp in Sources */, + 494506FF16E5461F00B9FF78 /* OVR_Timer.cpp in Sources */, + 4945070216E5461F00B9FF78 /* OVR_UTF8Util.cpp in Sources */, + 9BCE53FC16F0293A007A23FF /* OVR_LatencyTestImpl.cpp in Sources */, + 9BCE540016F02A56007A23FF /* OVR_SensorImpl.cpp in Sources */, + 9BCE541616F02ABC007A23FF /* OVR_OSX_DeviceManager.cpp in Sources */, + 9BCE541716F02AD5007A23FF /* OVR_OSX_HIDDevice.cpp in Sources */, + 9BCE541A16F02AEB007A23FF /* OVR_OSX_HMDDevice.cpp in Sources */, + 9BCE542516F2694F007A23FF /* OVR_OSX_SensorDevice.cpp in Sources */, + 9BEAD55F17187B8A00A8AA1D /* Util_LatencyTest.cpp in Sources */, + 9BEAD56117187B8A00A8AA1D /* Util_Render_Stereo.cpp in Sources */, + 9BD2A642172069B300C3C389 /* Util_MagCalibration.cpp in Sources */, + 9BD2A646172069BF00C3C389 /* OVR_SensorFilter.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 49DB65F21726F0C30097A8DD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9BB86B6D1747178B0071C537 /* OSX_Gamepad.cpp in Sources */, + 49DB66111726F0F80097A8DD /* Render_LoadTextureDDS.cpp in Sources */, + 49DB66121726F0F80097A8DD /* Render_Device.cpp in Sources */, + 49DB66131726F0F80097A8DD /* Render_GL_Device.cpp in Sources */, + 49DB66141726F0F80097A8DD /* Render_LoadTextureTGA.cpp in Sources */, + 49DB660F1726F0EA0097A8DD /* OSX_Platform.mm in Sources */, + 49DB66101726F0EA0097A8DD /* Platform.cpp in Sources */, + 9B6DEF851728A6560071E76B /* SensorBoxTest.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 37973B5B1739FA100093BBB8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 49A5336C16E544BE0039CB59 /* ovr */; + targetProxy = 37973B5A1739FA100093BBB8 /* PBXContainerItemProxy */; + }; + 4985389016ECFF5C008D0727 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 49A5336C16E544BE0039CB59 /* ovr */; + targetProxy = 4985388F16ECFF5C008D0727 /* PBXContainerItemProxy */; + }; + 49DB660E1726F0CD0097A8DD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 49A5336C16E544BE0039CB59 /* ovr */; + targetProxy = 49DB660D1726F0CD0097A8DD /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 37973B4C1739D78C0093BBB8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LIBRARY = "libstdc++"; + COMBINE_HIDPI_IMAGES = YES; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + HEADER_SEARCH_PATHS = ( + ../LibOVR/Src, + ../LibOVR/Include, + ); + INFOPLIST_FILE = "LibOVR_With_Samples.xcodeproj/OculusRoomTiny-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.6; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SYMROOT = OculusRoomTiny/MacOS/Debug; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 37973B4D1739D78C0093BBB8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LIBRARY = "libstdc++"; + COMBINE_HIDPI_IMAGES = YES; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + HEADER_SEARCH_PATHS = ( + ../LibOVR/Src, + ../LibOVR/Include, + ); + INFOPLIST_FILE = "LibOVR_With_Samples.xcodeproj/OculusRoomTiny-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.6; + PRODUCT_NAME = "$(TARGET_NAME)"; + SYMROOT = OculusRoomTiny/MacOS/Release; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + 4985387216ECFE92008D0727 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + COMBINE_HIDPI_IMAGES = YES; + CONFIGURATION_BUILD_DIR = OculusWorldDemo/MacOS/Debug; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + "GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = ( + "DEBUG=1", + "$(inherited)", + ); + HEADER_SEARCH_PATHS = ( + ../LibOVR/Src, + ../LibOVR/Include, + CommonSrc, + ../3rdParty/TinyXml, + ); + INFOPLIST_FILE = "LibOVR_With_Samples.xcodeproj/OculusWorldDemo-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SYMROOT = OculusWorldDemo/MacOS/Debug; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 4985387316ECFE92008D0727 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + COMBINE_HIDPI_IMAGES = YES; + CONFIGURATION_BUILD_DIR = OculusWorldDemo/MacOS/Release; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + HEADER_SEARCH_PATHS = ( + ../LibOVR/Src, + ../LibOVR/Include, + CommonSrc, + ../3rdParty/TinyXml, + ); + INFOPLIST_FILE = "LibOVR_With_Samples.xcodeproj/OculusWorldDemo-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SYMROOT = OculusWorldDemo/MacOS/Release; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + 49A5333A16E527490039CB59 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libstdc++"; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CONFIGURATION_BUILD_DIR = ../LibOVR/Lib/MacOS/Debug; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_CPP_EXCEPTIONS = NO; + GCC_ENABLE_CPP_RTTI = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + OVR_BUILD_DEBUG, + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + ../LibOVR/Src, + ../LibOVR/Include, + CommonSrc, + ); + LIBRARY_SEARCH_PATHS = "../LibOVR/Lib/MacOS/$(CONFIGURATION)"; + MACOSX_DEPLOYMENT_TARGET = 10.6; + ONLY_ACTIVE_ARCH = NO; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = macosx; + }; + name = Debug; + }; + 49A5333B16E527490039CB59 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libstdc++"; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CONFIGURATION_BUILD_DIR = ../LibOVR/Lib/MacOS/Release; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_CPP_EXCEPTIONS = NO; + GCC_ENABLE_CPP_RTTI = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + ../LibOVR/Src, + ../LibOVR/Include, + CommonSrc, + ); + LIBRARY_SEARCH_PATHS = "../LibOVR/Lib/MacOS/$(CONFIGURATION)"; + MACOSX_DEPLOYMENT_TARGET = 10.6; + ONLY_ACTIVE_ARCH = NO; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = macosx; + }; + name = Release; + }; + 49A5336F16E544BE0039CB59 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + COMBINE_HIDPI_IMAGES = YES; + EXECUTABLE_PREFIX = lib; + GCC_ENABLE_CPP_EXCEPTIONS = NO; + GCC_ENABLE_CPP_RTTI = NO; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + LIBRARY_SEARCH_PATHS = "../LIbOVR/MacOS/$(CONFIGURATION)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SYMROOT = ../LibOVR/Lib/MacOS; + }; + name = Debug; + }; + 49A5337016E544BE0039CB59 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + COMBINE_HIDPI_IMAGES = YES; + EXECUTABLE_PREFIX = lib; + GCC_ENABLE_CPP_EXCEPTIONS = NO; + GCC_ENABLE_CPP_RTTI = NO; + LIBRARY_SEARCH_PATHS = "../LIbOVR/MacOS/$(CONFIGURATION)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SYMROOT = ../LibOVR/Lib/MacOS; + }; + name = Release; + }; + 49DB660A1726F0C30097A8DD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LIBRARY = "libstdc++"; + COMBINE_HIDPI_IMAGES = YES; + CONFIGURATION_BUILD_DIR = Samples/SensorBoxTest/MacOS/Debug; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "LibOVR_With_Samples.xcodeproj/SensorBoxTest-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.6; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 49DB660B1726F0C30097A8DD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LIBRARY = "libstdc++"; + COMBINE_HIDPI_IMAGES = YES; + CONFIGURATION_BUILD_DIR = Samples/SensorBoxTest/MacOS/Release; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + INFOPLIST_FILE = "LibOVR_With_Samples.xcodeproj/SensorBoxTest-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.6; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 37973B4E1739D7930093BBB8 /* Build configuration list for PBXNativeTarget "OculusRoomTiny" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 37973B4C1739D78C0093BBB8 /* Debug */, + 37973B4D1739D78C0093BBB8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4985387116ECFE92008D0727 /* Build configuration list for PBXNativeTarget "OculusWorldDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4985387216ECFE92008D0727 /* Debug */, + 4985387316ECFE92008D0727 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 49A5332E16E527490039CB59 /* Build configuration list for PBXProject "LibOVR_With_Samples" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 49A5333A16E527490039CB59 /* Debug */, + 49A5333B16E527490039CB59 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 49A5336E16E544BE0039CB59 /* Build configuration list for PBXNativeTarget "ovr" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 49A5336F16E544BE0039CB59 /* Debug */, + 49A5337016E544BE0039CB59 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 49DB660C1726F0C30097A8DD /* Build configuration list for PBXNativeTarget "SensorBoxTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 49DB660A1726F0C30097A8DD /* Debug */, + 49DB660B1726F0C30097A8DD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 49A5332B16E527490039CB59 /* Project object */; +} diff --git a/Samples/LibOVR_With_Samples_Msvc2010.sln b/Samples/LibOVR_With_Samples_Msvc2010.sln new file mode 100644 index 0000000..eb7c50d --- /dev/null +++ b/Samples/LibOVR_With_Samples_Msvc2010.sln @@ -0,0 +1,67 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibOVR", "..\LibOVR\Projects\Win32\LibOVR_Msvc2010.vcxproj", "{934B40C7-F40A-4E4C-97A7-B9659BE0A441}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OculusWorldDemo", "OculusWorldDemo\OculusWorldDemo_Msvc2010.vcxproj", "{8051B877-2992-4F64-8C3B-FAF88B6D83AA}" + ProjectSection(ProjectDependencies) = postProject + {934B40C7-F40A-4E4C-97A7-B9659BE0A441} = {934B40C7-F40A-4E4C-97A7-B9659BE0A441} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SensorBoxTest", "SensorBox\SensorBoxTest_Msvc2010.vcxproj", "{8051B837-2982-4F64-8C3B-FAF88B6D83AB}" + ProjectSection(ProjectDependencies) = postProject + {934B40C7-F40A-4E4C-97A7-B9659BE0A441} = {934B40C7-F40A-4E4C-97A7-B9659BE0A441} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OculusRoomTiny", "OculusRoomTiny\OculusRoomTiny_Msvc2010.vcxproj", "{80523489-2881-4F64-8C3B-FAF88B60ABCD}" + ProjectSection(ProjectDependencies) = postProject + {934B40C7-F40A-4E4C-97A7-B9659BE0A441} = {934B40C7-F40A-4E4C-97A7-B9659BE0A441} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Debug|Win32.ActiveCfg = Debug|Win32 + {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Debug|Win32.Build.0 = Debug|Win32 + {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Debug|x64.ActiveCfg = Debug|x64 + {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Debug|x64.Build.0 = Debug|x64 + {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Release|Win32.ActiveCfg = Release|Win32 + {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Release|Win32.Build.0 = Release|Win32 + {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Release|x64.ActiveCfg = Release|x64 + {934B40C7-F40A-4E4C-97A7-B9659BE0A441}.Release|x64.Build.0 = Release|x64 + {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Debug|Win32.ActiveCfg = Debug|Win32 + {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Debug|Win32.Build.0 = Debug|Win32 + {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Debug|x64.ActiveCfg = Debug|x64 + {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Debug|x64.Build.0 = Debug|x64 + {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Release|Win32.ActiveCfg = Release|Win32 + {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Release|Win32.Build.0 = Release|Win32 + {8051B877-2992-4F64-8C3B-FAF88B6D83AA}.Release|x64.ActiveCfg = Release|x64 + {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Debug|Win32.ActiveCfg = Debug|Win32 + {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Debug|Win32.Build.0 = Debug|Win32 + {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Debug|x64.ActiveCfg = Debug|Win32 + {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Release|Win32.ActiveCfg = Release|Win32 + {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Release|Win32.Build.0 = Release|Win32 + {8051B837-2982-4F64-8C3B-FAF88B6D83AB}.Release|x64.ActiveCfg = Release|Win32 + {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Debug|Win32.ActiveCfg = Debug|Win32 + {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Debug|Win32.Build.0 = Debug|Win32 + {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Debug|x64.ActiveCfg = Debug|x64 + {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Debug|x64.Build.0 = Debug|x64 + {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Release|Win32.ActiveCfg = Release|Win32 + {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Release|Win32.Build.0 = Release|Win32 + {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Release|x64.ActiveCfg = Release|x64 + {80523489-2881-4F64-8C3B-FAF88B60ABCD}.Release|x64.Build.0 = Release|x64 + {9ABD1D70-F80B-466F-B097-821EBAA00ED6}.Debug|Win32.ActiveCfg = Debug|Win32 + {9ABD1D70-F80B-466F-B097-821EBAA00ED6}.Debug|Win32.Build.0 = Debug|Win32 + {9ABD1D70-F80B-466F-B097-821EBAA00ED6}.Debug|x64.ActiveCfg = Debug|Win32 + {9ABD1D70-F80B-466F-B097-821EBAA00ED6}.Release|Win32.ActiveCfg = Release|Win32 + {9ABD1D70-F80B-466F-B097-821EBAA00ED6}.Release|Win32.Build.0 = Release|Win32 + {9ABD1D70-F80B-466F-B097-821EBAA00ED6}.Release|x64.ActiveCfg = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/Oculus.icns b/Samples/Oculus.icns new file mode 100644 index 0000000..955a8fe Binary files /dev/null and b/Samples/Oculus.icns differ diff --git a/Samples/OculusRoomTiny/OSX_OculusRoomTiny.h b/Samples/OculusRoomTiny/OSX_OculusRoomTiny.h new file mode 100644 index 0000000..dc51509 --- /dev/null +++ b/Samples/OculusRoomTiny/OSX_OculusRoomTiny.h @@ -0,0 +1,223 @@ +/************************************************************************************ + + Filename : OSX_OculusRoomTiny.h + Content : Simplest possible first-person view test application for Oculus Rift + Created : May 7, 2013 + Authors : Michael Antonov, Andrew Reisse, Artem Bolgar + + Copyright : Copyright 2013 Oculus, Inc. All Rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + *************************************************************************************/ +#ifndef INC_OSX_OculusRoomTiny_h +#define INC_OSX_OculusRoomTiny_h + +#import + +#import +#import + + +#include "OVR.h" +#include "Util/Util_Render_Stereo.h" +#include "../../LibOVR/Src/Kernel/OVR_Timer.h" +#include "RenderTiny_GL_Device.h" + +using namespace OVR; +using namespace OVR::RenderTiny; + +class OculusRoomTinyApp; + +@interface OVRApp : NSApplication + +@property (assign) IBOutlet NSWindow* win; +@property (assign) OculusRoomTinyApp* App; + +-(void) run; + +@end + +@interface OVRView : NSOpenGLView + +//@property (assign) OVR::Platform::OSX::PlatformCore* Platform; +@property (assign) OculusRoomTinyApp* App; +@property unsigned long Modifiers; + +-(void)ProcessMouse:(NSEvent*)event; +-(void)warpMouseToCenter; + ++(CGDirectDisplayID) displayFromScreen:(NSScreen*)s; + +@end + +//------------------------------------------------------------------------------------- +// ***** OculusRoomTiny Description + +// This app renders a simple flat-shaded room allowing the user to move along the +// floor and look around with an HMD, mouse, keyboard and gamepad. +// By default, the application will start full-screen on Oculus Rift. +// +// The following keys work: +// +// 'W', 'S', 'A', 'D' - Move forward, back; strafe left/right. +// F1 - No stereo, no distortion. +// F2 - Stereo, no distortion. +// F3 - Stereo and distortion. +// + +// The world RHS coordinate system is defines as follows (as seen in perspective view): +// Y - Up +// Z - Back +// X - Right +const Vector3f UpVector(0.0f, 1.0f, 0.0f); +const Vector3f ForwardVector(0.0f, 0.0f, -1.0f); +const Vector3f RightVector(1.0f, 0.0f, 0.0f); + +// We start out looking in the positive Z (180 degree rotation). +const float YawInitial = 3.141592f; +const float Sensitivity = 1.0f; +const float MoveSpeed = 3.0f; // m/s + +namespace OSX +{ + class RenderDevice : public GL::RenderDevice + { + public: + void* Context; // NSOpenGLContext + + // osview = NSView* + RenderDevice(const RendererParams& p, void* osview, void* context); + + virtual void Shutdown(); + virtual void Present(); + + virtual bool SetFullscreen(DisplayMode fullscreen); + + // osview = NSView* + static RenderDevice* CreateDevice(const RendererParams& rp, void* osview); + }; +} + +//------------------------------------------------------------------------------------- +// ***** OculusRoomTiny Application class + +// An instance of this class is created on application startup (main/WinMain). +// +// It then works as follows: +// +// OnStartup - Window, graphics and HMD setup is done here. +// This function will initialize OVR::DeviceManager and HMD, +// creating SensorDevice and attaching it to SensorFusion. +// This needs to be done before obtaining sensor data. +// +// OnIdle - Does per-frame processing, processing SensorFusion and +// movement input and rendering the frame. + +class OculusRoomTinyApp : public MessageHandler +{ + friend class OSX::RenderDevice; +public: + OculusRoomTinyApp(OVRApp* nsapp); + ~OculusRoomTinyApp(); + + // Initializes graphics, Rift input and creates world model. + virtual int OnStartup(const char* args); + // Called per frame to sample SensorFucion and render the world. + virtual void OnIdle(); + + // Installed for Oculus device messages. Optional. + virtual void OnMessage(const Message& msg); + + // Handle input events for movement. + virtual void OnMouseMove(int x, int y, int modifiers); + virtual void OnKey(unsigned vk, bool down); + + // Render the view for one eye. + void Render(const StereoEyeParams& stereo); + + // Main application loop. + int Run(); + void Exit(); + + // Return amount of time passed since application started in seconds. + double GetAppTime() const + { + return (OVR::Timer::GetTicks() - StartupTicks) * (1.0 / (double)OVR::Timer::MksPerSecond); + } + bool IsQuiting() const { return Quit; } + + int GetWidth() const { return Width; } + int GetHeight() const { return Height; } + + bool SetFullscreen(const RendererParams& rp, int fullscreen); + +protected: + bool setupWindow(); + void destroyWindow(); + + NSView* View; + NSWindow* Win; + OVRApp* NsApp; + + static OculusRoomTinyApp* pApp; + + // *** Rendering Variables + Ptr pRender; + RendererParams RenderParams; + int Width, Height; + + bool Quit; + + // *** Oculus HMD Variables + + Ptr pManager; + Ptr pSensor; + Ptr pHMD; + SensorFusion SFusion; + OVR::HMDInfo HMDInfo; + + // Last update seconds, used for move speed timing. + double LastUpdate; + OVR::UInt64 StartupTicks; + + // Position and look. The following apply: + Vector3f EyePos; + float EyeYaw; // Rotation around Y, CCW positive when looking at RHS (X,Z) plane. + float EyePitch; // Pitch. If sensor is plugged in, only read from sensor. + float EyeRoll; // Roll, only accessible from Sensor. + float LastSensorYaw; // Stores previous Yaw value from to support computing delta. + + // Movement state; different bits may be set based on the state of keys. + UByte MoveForward; + UByte MoveBack; + UByte MoveLeft; + UByte MoveRight; + + Matrix4f ViewMat; + RenderTiny::Scene Scene; + + // Stereo view parameters. + StereoConfig SConfig; + PostProcessType PostProcess; + + // Shift accelerates movement/adjustment velocity. + bool ShiftDown; + bool ControlDown; +}; + +// Adds sample models and lights to the argument scene. +void PopulateRoomScene(Scene* scene, RenderDevice* render); + + +#endif diff --git a/Samples/OculusRoomTiny/OSX_OculusRoomTiny.mm b/Samples/OculusRoomTiny/OSX_OculusRoomTiny.mm new file mode 100644 index 0000000..0a59afd --- /dev/null +++ b/Samples/OculusRoomTiny/OSX_OculusRoomTiny.mm @@ -0,0 +1,861 @@ +/************************************************************************************ + + Filename : OSX_OculusRoomTiny.mm + Content : Simplest possible first-person view test application for Oculus Rift + Created : May 7, 2013 + Authors : Michael Antonov, Andrew Reisse, Artem Bolgar + + Copyright : Copyright 2013 Oculus, Inc. All Rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + *************************************************************************************/ + +#import "OSX_OculusRoomTiny.h" +#include "RenderTiny_GL_Device.h" + +#include "Kernel/OVR_KeyCodes.h" + +using namespace OVR; + +//------------------------------------------------------------------------------------- +// ***** OculusRoomTiny Class + +// Static pApp simplifies routing the window function. +OculusRoomTinyApp* OculusRoomTinyApp::pApp = 0; + + +OculusRoomTinyApp::OculusRoomTinyApp(OVRApp* nsapp) + : pRender(0), + LastUpdate(0), + NsApp(nsapp), + Quit(0), + + // Initial location + EyePos(0.0f, 1.6f, -5.0f), + EyeYaw(YawInitial), EyePitch(0), EyeRoll(0), + LastSensorYaw(0), + SConfig(), + PostProcess(PostProcess_Distortion), + ShiftDown(false), + ControlDown(false) +{ + pApp = this; + + Width = 1280; + Height = 800; + + StartupTicks = OVR::Timer::GetTicks(); + + MoveForward = MoveBack = MoveLeft = MoveRight = 0; +} + +OculusRoomTinyApp::~OculusRoomTinyApp() +{ + RemoveHandlerFromDevices(); + pSensor.Clear(); + pHMD.Clear(); + destroyWindow(); + pApp = 0; +} + + +int OculusRoomTinyApp::OnStartup(const char* args) +{ + OVR_UNUSED(args); + + + // *** Oculus HMD & Sensor Initialization + + // Create DeviceManager and first available HMDDevice from it. + // Sensor object is created from the HMD, to ensure that it is on the + // correct device. + + pManager = *DeviceManager::Create(); + + // We'll handle it's messages in this case. + pManager->SetMessageHandler(this); + + CFOptionFlags detectionResult; + const char* detectionMessage; + + do + { + // Release Sensor/HMD in case this is a retry. + pSensor.Clear(); + pHMD.Clear(); + RenderParams.MonitorName.Clear(); + + pHMD = *pManager->EnumerateDevices().CreateDevice(); + if (pHMD) + { + pSensor = *pHMD->GetSensor(); + + // This will initialize HMDInfo with information about configured IPD, + // screen size and other variables needed for correct projection. + // We pass HMD DisplayDeviceName into the renderer to select the + // correct monitor in full-screen mode. + if (pHMD->GetDeviceInfo(&HMDInfo)) + { + RenderParams.MonitorName = HMDInfo.DisplayDeviceName; + RenderParams.DisplayId = HMDInfo.DisplayId; + SConfig.SetHMDInfo(HMDInfo); + } + } + else + { + // If we didn't detect an HMD, try to create the sensor directly. + // This is useful for debugging sensor interaction; it is not needed in + // a shipping app. + pSensor = *pManager->EnumerateDevices().CreateDevice(); + } + + + // If there was a problem detecting the Rift, display appropriate message. + detectionResult = kCFUserNotificationAlternateResponse; + + if (!pHMD && !pSensor) + detectionMessage = "Oculus Rift not detected."; + else if (!pHMD) + detectionMessage = "Oculus Sensor detected; HMD Display not detected."; + else if (!pSensor) + detectionMessage = "Oculus HMD Display detected; Sensor not detected."; + else if (HMDInfo.DisplayDeviceName[0] == '\0') + detectionMessage = "Oculus Sensor detected; HMD display EDID not detected."; + else + detectionMessage = 0; + + if (detectionMessage) + { + String messageText(detectionMessage); + messageText += "\n\n" + "Press 'Try Again' to run retry detection.\n" + "Press 'Continue' to run full-screen anyway."; + + CFStringRef headerStrRef = CFStringCreateWithCString(NULL, "Oculus Rift Detection", kCFStringEncodingMacRoman); + CFStringRef messageStrRef = CFStringCreateWithCString(NULL, messageText, kCFStringEncodingMacRoman); + + //launch the message box + CFUserNotificationDisplayAlert(0, + kCFUserNotificationNoteAlertLevel, + NULL, NULL, NULL, + headerStrRef, // header text + messageStrRef, // message text + CFSTR("Try again"), + CFSTR("Continue"), + CFSTR("Cancel"), + &detectionResult); + + //Clean up the strings + CFRelease(headerStrRef); + CFRelease(messageStrRef); + + if (detectionResult == kCFUserNotificationCancelResponse || + detectionResult == kCFUserNotificationOtherResponse) + return 1; + } + + } while (detectionResult != kCFUserNotificationAlternateResponse); + + + if (HMDInfo.HResolution > 0) + { + Width = HMDInfo.HResolution; + Height = HMDInfo.VResolution; + } + + + if (!setupWindow()) + return 1; + + if (pSensor) + { + // We need to attach sensor to SensorFusion object for it to receive + // body frame messages and update orientation. SFusion.GetOrientation() + // is used in OnIdle() to orient the view. + SFusion.AttachToSensor(pSensor); + SFusion.SetDelegateMessageHandler(this); + SFusion.SetPredictionEnabled(true); + } + + + // *** Initialize Rendering + + // Enable multi-sampling by default. + RenderParams.Multisample = 4; + RenderParams.Fullscreen = false;//true; //? + + // Setup Graphics. + pRender = *OSX::RenderDevice::CreateDevice(RenderParams, (void*)View); + if (!pRender) + return 1; + + + // *** Configure Stereo settings. + + SConfig.SetFullViewport(Viewport(0,0, Width, Height)); + SConfig.SetStereoMode(Stereo_LeftRight_Multipass); + + // Configure proper Distortion Fit. + // For 7" screen, fit to touch left side of the view, leaving a bit of invisible + // screen on the top (saves on rendering cost). + // For smaller screens (5.5"), fit to the top. + if (HMDInfo.HScreenSize > 0.0f) + { + if (HMDInfo.HScreenSize > 0.140f) // 7" + SConfig.SetDistortionFitPointVP(-1.0f, 0.0f); + else + SConfig.SetDistortionFitPointVP(0.0f, 1.0f); + } + + pRender->SetSceneRenderScale(SConfig.GetDistortionScale()); + + SConfig.Set2DAreaFov(DegreeToRad(85.0f)); + + + // *** Populate Room Scene + + // This creates lights and models. + PopulateRoomScene(&Scene, pRender); + + + LastUpdate = GetAppTime(); + return 0; +} + +void OculusRoomTinyApp::OnMessage(const Message& msg) +{ + if (msg.Type == Message_DeviceAdded && msg.pDevice == pManager) + { + LogText("DeviceManager reported device added.\n"); + } + else if (msg.Type == Message_DeviceRemoved && msg.pDevice == pManager) + { + LogText("DeviceManager reported device removed.\n"); + } + else if (msg.Type == Message_DeviceAdded && msg.pDevice == pSensor) + { + LogText("Sensor reported device added.\n"); + } + else if (msg.Type == Message_DeviceRemoved && msg.pDevice == pSensor) + { + LogText("Sensor reported device removed.\n"); + } +} + +bool OculusRoomTinyApp::setupWindow() +{ + NSRect winrect; + winrect.origin.x = 0; + winrect.origin.y = 1000; + winrect.size.width = Width; + winrect.size.height = Height; + NSWindow* win = [[NSWindow alloc] initWithContentRect:winrect styleMask:NSTitledWindowMask|NSClosableWindowMask backing:NSBackingStoreBuffered defer:NO]; + + OVRView* view = [[OVRView alloc] initWithFrame:winrect]; + [win setContentView:view]; + [win setAcceptsMouseMovedEvents:YES]; + [win setDelegate:view]; + [view setApp:pApp]; + Win = win; + View = view; + + const char* title = "OculusRoomTiny"; + [((NSWindow*)Win) setTitle:[[NSString alloc] initWithBytes:title length:strlen(title) encoding:NSUTF8StringEncoding]]; + + [NSCursor hide]; + [view warpMouseToCenter]; + CGAssociateMouseAndMouseCursorPosition(false); + + SetFullscreen(RenderParams, true); + return true; +} + +void OculusRoomTinyApp::destroyWindow() +{ + SetFullscreen(RenderParams, false); + [((NSWindow*)Win) close]; +} + +void OculusRoomTinyApp::OnMouseMove(int x, int y, int modifiers) +{ + OVR_UNUSED(modifiers); + + // Mouse motion here is always relative. + int dx = x, dy = y; + const float maxPitch = ((3.1415f/2)*0.98f); + + // Apply to rotation. Subtract for right body frame rotation, + // since yaw rotation is positive CCW when looking down on XZ plane. + EyeYaw -= (Sensitivity * dx)/ 360.0f; + + if (!pSensor) + { + EyePitch -= (Sensitivity * dy)/ 360.0f; + + if (EyePitch > maxPitch) + EyePitch = maxPitch; + if (EyePitch < -maxPitch) + EyePitch = -maxPitch; + } +} + + +void OculusRoomTinyApp::OnKey(unsigned vk, bool down) +{ + switch (vk) + { + case 'Q': + if (down && ControlDown) + Exit();; + break; + case Key_Escape: + if (!down) + Exit(); + break; + + // Handle player movement keys. + // We just update movement state here, while the actual translation is done in OnIdle() + // based on time. + case 'W': MoveForward = down ? (MoveForward | 1) : (MoveForward & ~1); break; + case 'S': MoveBack = down ? (MoveBack | 1) : (MoveBack & ~1); break; + case 'A': MoveLeft = down ? (MoveLeft | 1) : (MoveLeft & ~1); break; + case 'D': MoveRight = down ? (MoveRight | 1) : (MoveRight & ~1); break; + case Key_Up: MoveForward = down ? (MoveForward | 2) : (MoveForward & ~2); break; + case Key_Down: MoveBack = down ? (MoveBack | 2) : (MoveBack & ~2); break; + + case 'R': + SFusion.Reset(); + break; + + case 'P': + if (down) + { + // Toggle chromatic aberration correction on/off. + RenderDevice::PostProcessShader shader = pRender->GetPostProcessShader(); + + if (shader == RenderDevice::PostProcessShader_Distortion) + { + pRender->SetPostProcessShader(RenderDevice::PostProcessShader_DistortionAndChromAb); + } + else if (shader == RenderDevice::PostProcessShader_DistortionAndChromAb) + { + pRender->SetPostProcessShader(RenderDevice::PostProcessShader_Distortion); + } + else + OVR_ASSERT(false); + } + break; + + // Switch rendering modes/distortion. + case Key_F1: + SConfig.SetStereoMode(Stereo_None); + PostProcess = PostProcess_None; + break; + case Key_F2: + SConfig.SetStereoMode(Stereo_LeftRight_Multipass); + PostProcess = PostProcess_None; + break; + case Key_F3: + SConfig.SetStereoMode(Stereo_LeftRight_Multipass); + PostProcess = PostProcess_Distortion; + break; + + // Stereo IPD adjustments, in meter (default IPD is 64mm). + case '+': + case '=': + if (down) + SConfig.SetIPD(SConfig.GetIPD() + 0.0005f * (ShiftDown ? 5.0f : 1.0f)); + break; + case '-': + case '_': + if (down) + SConfig.SetIPD(SConfig.GetIPD() - 0.0005f * (ShiftDown ? 5.0f : 1.0f)); + break; + + // Holding down Shift key accelerates adjustment velocity. + case Key_Shift: + ShiftDown = down; + break; + case Key_Meta: + ControlDown = down; + break; + } +} + + +void OculusRoomTinyApp::OnIdle() +{ + double curtime = GetAppTime(); + float dt = float(curtime - LastUpdate); + LastUpdate = curtime; + + + // Handle Sensor motion. + // We extract Yaw, Pitch, Roll instead of directly using the orientation + // to allow "additional" yaw manipulation with mouse/controller. + if (pSensor) + { + Quatf hmdOrient = SFusion.GetOrientation(); + float yaw = 0.0f; + + hmdOrient.GetEulerAngles(&yaw, &EyePitch, &EyeRoll); + + EyeYaw += (yaw - LastSensorYaw); + LastSensorYaw = yaw; + } + + + if (!pSensor) + { + const float maxPitch = ((3.1415f/2)*0.98f); + if (EyePitch > maxPitch) + EyePitch = maxPitch; + if (EyePitch < -maxPitch) + EyePitch = -maxPitch; + } + + // Handle keyboard movement. + // This translates EyePos based on Yaw vector direction and keys pressed. + // Note that Pitch and Roll do not affect movement (they only affect view). + if (MoveForward || MoveBack || MoveLeft || MoveRight) + { + Vector3f localMoveVector(0,0,0); + Matrix4f yawRotate = Matrix4f::RotationY(EyeYaw); + + if (MoveForward) + localMoveVector = ForwardVector; + else if (MoveBack) + localMoveVector = -ForwardVector; + + if (MoveRight) + localMoveVector += RightVector; + else if (MoveLeft) + localMoveVector -= RightVector; + + // Normalize vector so we don't move faster diagonally. + localMoveVector.Normalize(); + Vector3f orientationVector = yawRotate.Transform(localMoveVector); + orientationVector *= MoveSpeed * dt * (ShiftDown ? 3.0f : 1.0f); + + EyePos += orientationVector; + } + + // Rotate and position View Camera, using YawPitchRoll in BodyFrame coordinates. + // + Matrix4f rollPitchYaw = Matrix4f::RotationY(EyeYaw) * Matrix4f::RotationX(EyePitch) * + Matrix4f::RotationZ(EyeRoll); + Vector3f up = rollPitchYaw.Transform(UpVector); + Vector3f forward = rollPitchYaw.Transform(ForwardVector); + + + // Minimal head modelling. + float headBaseToEyeHeight = 0.15f; // Vertical height of eye from base of head + float headBaseToEyeProtrusion = 0.09f; // Distance forward of eye from base of head + + Vector3f eyeCenterInHeadFrame(0.0f, headBaseToEyeHeight, -headBaseToEyeProtrusion); + Vector3f shiftedEyePos = EyePos + rollPitchYaw.Transform(eyeCenterInHeadFrame); + shiftedEyePos.y -= eyeCenterInHeadFrame.y; // Bring the head back down to original height + + ViewMat = Matrix4f::LookAtRH(shiftedEyePos, shiftedEyePos + forward, up); + + // This is what transformation would be without head modeling. + // View = Matrix4f::LookAtRH(EyePos, EyePos + forward, up); + + switch(SConfig.GetStereoMode()) + { + case Stereo_None: + Render(SConfig.GetEyeRenderParams(StereoEye_Center)); + break; + + case Stereo_LeftRight_Multipass: + Render(SConfig.GetEyeRenderParams(StereoEye_Left)); + Render(SConfig.GetEyeRenderParams(StereoEye_Right)); + break; + } + + pRender->Present(); + // Force GPU to flush the scene, resulting in the lowest possible latency. + pRender->ForceFlushGPU(); +} + + +// Render the scene for one eye. +void OculusRoomTinyApp::Render(const StereoEyeParams& stereo) +{ + pRender->BeginScene(PostProcess); + + // Apply Viewport/Projection for the eye. + pRender->ApplyStereoParams(stereo); + pRender->Clear(); + pRender->SetDepthMode(true, true); + + Scene.Render(pRender, stereo.ViewAdjust * ViewMat); + + pRender->FinishScene(); +} + +void OculusRoomTinyApp::Exit() +{ + [NsApp stop:nil]; + Quit = true; +} + +bool OculusRoomTinyApp::SetFullscreen(const RenderTiny::RendererParams& rp, int fullscreen) +{ + if (fullscreen == RenderTiny::Display_Window) + [(OVRView*)View exitFullScreenModeWithOptions:nil]; + else + { + NSScreen* usescreen = [NSScreen mainScreen]; + NSArray* screens = [NSScreen screens]; + for (int i = 0; i < [screens count]; i++) + { + NSScreen* s = (NSScreen*)[screens objectAtIndex:i]; + CGDirectDisplayID disp = [OVRView displayFromScreen:s]; + + if (disp == rp.DisplayId) + usescreen = s; + } + + [(OVRView*)View enterFullScreenMode:usescreen withOptions:nil]; + } + + if (pRender) + pRender->SetFullscreen((RenderTiny::DisplayMode)fullscreen); + return 1; +} + +//------------------------------------------------------------------------------------- +// ***** OS X-Specific Logic + +@implementation OVRApp + +- (void)dealloc +{ + [super dealloc]; +} + +- (void)run +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + _running = YES; + OculusRoomTinyApp* app; + + // Initializes LibOVR. This LogMask_All enables maximum logging. + // Custom allocator can also be specified here. + OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); + + int exitCode = 0; + do + { + { + using namespace OVR; + + // CreateApplication must be the first call since it does OVR::System::Initialize. + app = new OculusRoomTinyApp(self); + // The platform attached to an app will be deleted by DestroyApplication. + + [self setApp:app]; + + const char* argv[] = {"OVRApp"}; + exitCode = app->OnStartup(argv[0]); + if (exitCode) + break; + } + [self finishLaunching]; + [pool drain]; + + while ([self isRunning]) + { + pool = [[NSAutoreleasePool alloc] init]; + NSEvent* event = [self nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES]; + if (event) + { + [self sendEvent:event]; + } + _App->OnIdle(); + [pool drain]; + } + } while(0); + + delete app; + + // No OVR functions involving memory are allowed after this. + OVR::System::Destroy(); +} + +@end + +static int KeyMap[][2] = +{ + { NSDeleteFunctionKey, OVR::Key_Delete }, + { '\t', OVR::Key_Tab }, + { '\n', OVR::Key_Return }, + { NSPauseFunctionKey, OVR::Key_Pause }, + { 27, OVR::Key_Escape }, + { 127, OVR::Key_Backspace }, + { ' ', OVR::Key_Space }, + { NSPageUpFunctionKey, OVR::Key_PageUp }, + { NSPageDownFunctionKey, OVR::Key_PageDown }, + { NSNextFunctionKey, OVR::Key_PageDown }, + { NSEndFunctionKey, OVR::Key_End }, + { NSHomeFunctionKey, OVR::Key_Home }, + { NSLeftArrowFunctionKey, OVR::Key_Left }, + { NSUpArrowFunctionKey, OVR::Key_Up }, + { NSRightArrowFunctionKey, OVR::Key_Right }, + { NSDownArrowFunctionKey, OVR::Key_Down }, + { NSInsertFunctionKey, OVR::Key_Insert }, + { NSDeleteFunctionKey, OVR::Key_Delete }, + { NSHelpFunctionKey, OVR::Key_Insert }, +}; + + +static KeyCode MapToKeyCode(wchar_t vk) +{ + unsigned key = Key_None; + + if ((vk >= 'a') && (vk <= 'z')) + { + key = vk - 'a' + Key_A; + } + else if ((vk >= ' ') && (vk <= '~')) + { + key = vk; + } + else if ((vk >= '0') && (vk <= '9')) + { + key = vk - '0' + Key_Num0; + } + else if ((vk >= NSF1FunctionKey) && (vk <= NSF15FunctionKey)) + { + key = vk - NSF1FunctionKey + Key_F1; + } + else + { + for (unsigned i = 0; i< (sizeof(KeyMap) / sizeof(KeyMap[1])); i++) + { + if (vk == KeyMap[i][0]) + { + key = KeyMap[i][1]; + break; + } + } + } + + return (KeyCode)key; +} + +@implementation OVRView + +-(BOOL) acceptsFirstResponder +{ + return YES; +} +-(BOOL) acceptsFirstMouse:(NSEvent *)ev +{ + return YES; +} + ++(CGDirectDisplayID) displayFromScreen:(NSScreen *)s +{ + NSNumber* didref = (NSNumber*)[[s deviceDescription] objectForKey:@"NSScreenNumber"]; + CGDirectDisplayID disp = (CGDirectDisplayID)[didref longValue]; + return disp; +} + +-(void) warpMouseToCenter +{ + NSPoint w; + w.x = _App->GetWidth()/2.0f; + w.y = _App->GetHeight()/2.0f; + w = [[self window] convertBaseToScreen:w]; + CGDirectDisplayID disp = [OVRView displayFromScreen:[[self window] screen]]; + CGPoint p = {w.x, CGDisplayPixelsHigh(disp)-w.y}; + CGDisplayMoveCursorToPoint(disp, p); +} + +-(void) keyDown:(NSEvent*)ev +{ + NSString* chars = [ev charactersIgnoringModifiers]; + if ([chars length]) + { + wchar_t ch = [chars characterAtIndex:0]; + OVR::KeyCode key = MapToKeyCode(ch); + _App->OnKey(key, true); + } +} +-(void) keyUp:(NSEvent*)ev +{ + NSString* chars = [ev charactersIgnoringModifiers]; + if ([chars length]) + { + wchar_t ch = [chars characterAtIndex:0]; + OVR::KeyCode key = MapToKeyCode(ch); + _App->OnKey(key, false); + + } +} + + +-(void)flagsChanged:(NSEvent *)ev +{ + static const OVR::KeyCode ModifierKeys[] = {OVR::Key_None, OVR::Key_Shift, OVR::Key_Control, OVR::Key_Alt, OVR::Key_Meta}; + + unsigned long cmods = [ev modifierFlags]; + if ((cmods & 0xffff0000) != _Modifiers) + { + uint32_t mods = 0; + if (cmods & NSShiftKeyMask) + mods |= 0x01; + if (cmods & NSControlKeyMask) + mods |= 0x02; + if (cmods & NSAlternateKeyMask) + mods |= 0x04; + if (cmods & NSCommandKeyMask) + mods |= 0x08; + + for (int i = 1; i <= 4; i++) + { + unsigned long m = (1 << (16+i)); + if ((cmods & m) != (_Modifiers & m)) + { + if (cmods & m) + _App->OnKey(ModifierKeys[i], true); + else + _App->OnKey(ModifierKeys[i], false); + } + } + _Modifiers = cmods & 0xffff0000; + } +} + +-(void)ProcessMouse:(NSEvent*)ev +{ + switch ([ev type]) + { + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDragged: + case NSMouseMoved: + { + int dx = [ev deltaX]; + int dy = [ev deltaY]; + + if (dx != 0 || dy != 0) + { + _App->OnMouseMove(dx, dy, 0); + [self warpMouseToCenter]; + } + } + break; + case NSLeftMouseDown: + case NSRightMouseDown: + case NSOtherMouseDown: + break; + } +} + +-(void) mouseMoved:(NSEvent*)ev +{ + [self ProcessMouse:ev]; +} +-(void) mouseDragged:(NSEvent*)ev +{ + [self ProcessMouse:ev]; +} + +-(void) mouseDown:(NSEvent*)ev +{ + [self warpMouseToCenter]; + CGAssociateMouseAndMouseCursorPosition(false); + [NSCursor hide]; +} + +//-(void) + +-(id) initWithFrame:(NSRect)frameRect +{ + NSOpenGLPixelFormatAttribute attr[] = + {NSOpenGLPFAWindow, NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, 24, nil}; + + NSOpenGLPixelFormat *pf = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attr] autorelease]; + + self = [super initWithFrame:frameRect pixelFormat:pf]; + GLint swap = 0; + [[self openGLContext] setValues:&swap forParameter:NSOpenGLCPSwapInterval]; + //[self setWantsBestResolutionOpenGLSurface:YES]; + return self; +} + +-(BOOL)windowShouldClose:(id)sender +{ + _App->Exit(); + return 1; +} + +@end + +// GL OSX-specific logic +namespace OSX { + + RenderDevice::RenderDevice(const RendererParams& p, void* osview, void* context) + : GL::RenderDevice(p), Context(context) + { + OVRView* view = (OVRView*)osview; + NSRect bounds = [view bounds]; + WindowWidth = bounds.size.width; + WindowHeight= bounds.size.height; + + } + + RenderDevice* RenderDevice::CreateDevice(const RendererParams& rp, void* osview) + { + OVRView* view = (OVRView*)osview; + NSOpenGLContext *context = [view openGLContext]; + if (!context) + return NULL; + + [context makeCurrentContext]; + [[view window] makeKeyAndOrderFront:nil]; + + return new OSX::RenderDevice(rp, osview, context); + } + + void RenderDevice::Present() + { + NSOpenGLContext *context = (NSOpenGLContext*)Context; + [context flushBuffer]; + } + + void RenderDevice::Shutdown() + { + Context = NULL; + } + + bool RenderDevice::SetFullscreen(DisplayMode fullscreen) + { + Params.Fullscreen = fullscreen; + return 1; + } + +} + + +int main(int argc, char *argv[]) +{ + NSApplication* nsapp = [OVRApp sharedApplication]; + [nsapp run]; + return 0; +} + diff --git a/Samples/OculusRoomTiny/OculusRoomModel.cpp b/Samples/OculusRoomTiny/OculusRoomModel.cpp new file mode 100644 index 0000000..74e60cf --- /dev/null +++ b/Samples/OculusRoomTiny/OculusRoomModel.cpp @@ -0,0 +1,260 @@ +/************************************************************************************ + +Filename : OculusRoomModel.cpp +Content : Creates a simple room scene from hard-coded geometry +Created : October 4, 2012 + +Copyright : Copyright 2012-2013 Oculus, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#include "RenderTiny_Device.h" + +using namespace OVR; +using namespace OVR::RenderTiny; + + +//------------------------------------------------------------------------------------- +// ***** Room Model + +// This model is hard-coded out of axis-aligned solid-colored slabs. +// Room unit dimensions are in meters. Player starts in the middle. +// + +enum BuiltinTexture +{ + Tex_None, + Tex_Checker, + Tex_Block, + Tex_Panel, + Tex_Count +}; + +struct Slab +{ + float x1, y1, z1; + float x2, y2, z2; + Color c; +}; + +struct SlabModel +{ + int Count; + Slab* pSlabs; + BuiltinTexture tex; +}; + +Slab FloorSlabs[] = +{ + // Floor + { -10.0f, -0.1f, -20.0f, 10.0f, 0.0f, 20.1f, Color(128,128,128) } +}; + +SlabModel Floor = {sizeof(FloorSlabs)/sizeof(Slab), FloorSlabs, Tex_Checker}; + +Slab CeilingSlabs[] = +{ + { -10.0f, 4.0f, -20.0f, 10.0f, 4.1f, 20.1f, Color(128,128,128) } +}; + +SlabModel Ceiling = {sizeof(FloorSlabs)/sizeof(Slab), CeilingSlabs, Tex_Panel}; + +Slab RoomSlabs[] = +{ + // Left Wall + { -10.1f, 0.0f, -20.0f, -10.0f, 4.0f, 20.0f, Color(128,128,128) }, + // Back Wall + { -10.0f, -0.1f, -20.1f, 10.0f, 4.0f, -20.0f, Color(128,128,128) }, + + // Right Wall + { 10.0f, -0.1f, -20.0f, 10.1f, 4.0f, 20.0f, Color(128,128,128) }, +}; + +SlabModel Room = {sizeof(RoomSlabs)/sizeof(Slab), RoomSlabs, Tex_Block}; + +Slab FixtureSlabs[] = +{ + // Right side shelf + { 9.5f, 0.75f, 3.0f, 10.1f, 2.5f, 3.1f, Color(128,128,128) }, // Verticals + { 9.5f, 0.95f, 3.7f, 10.1f, 2.75f, 3.8f, Color(128,128,128) }, + { 9.5f, 1.20f, 2.5f, 10.1f, 1.30f, 3.8f, Color(128,128,128) }, // Horizontals + { 9.5f, 2.00f, 3.0f, 10.1f, 2.10f, 4.2f, Color(128,128,128) }, + + // Right railing + { 5.0f, 1.1f, 20.0f, 10.0f, 1.2f, 20.1f, Color(128,128,128) }, + // Bars + { 9.0f, 1.1f, 20.0f, 9.1f, 0.0f, 20.1f, Color(128,128,128) }, + { 8.0f, 1.1f, 20.0f, 8.1f, 0.0f, 20.1f, Color(128,128,128) }, + { 7.0f, 1.1f, 20.0f, 7.1f, 0.0f, 20.1f, Color(128,128,128) }, + { 6.0f, 1.1f, 20.0f, 6.1f, 0.0f, 20.1f, Color(128,128,128) }, + { 5.0f, 1.1f, 20.0f, 5.1f, 0.0f, 20.1f, Color(128,128,128) }, + + // Left railing + { -10.0f, 1.1f, 20.0f, -5.0f, 1.2f, 20.1f, Color(128,128,128) }, + // Bars + { -9.0f, 1.1f, 20.0f, -9.1f, 0.0f, 20.1f, Color(128,128,128) }, + { -8.0f, 1.1f, 20.0f, -8.1f, 0.0f, 20.1f, Color(128,128,128) }, + { -7.0f, 1.1f, 20.0f, -7.1f, 0.0f, 20.1f, Color(128,128,128) }, + { -6.0f, 1.1f, 20.0f, -6.1f, 0.0f, 20.1f, Color(128,128,128) }, + { -5.0f, 1.1f, 20.0f, -5.1f, 0.0f, 20.1f, Color(128,128,128) }, + + // Bottom Floor 2 + { -15.0f, -6.1f, 18.0f, 15.0f, -6.0f, 30.0f, Color(128,128,128) }, +}; + +SlabModel Fixtures = {sizeof(FixtureSlabs)/sizeof(Slab), FixtureSlabs}; + +Slab FurnitureSlabs[] = +{ + // Table + { -1.8f, 0.7f, 1.0f, 0.0f, 0.8f, 0.0f, Color(128,128,88) }, + { -1.8f, 0.7f, 0.0f, -1.8f+0.1f, 0.0f, 0.0f+0.1f, Color(128,128,88) }, // Leg 1 + { -1.8f, 0.7f, 1.0f, -1.8f+0.1f, 0.0f, 1.0f-0.1f, Color(128,128,88) }, // Leg 2 + { 0.0f, 0.7f, 1.0f, 0.0f-0.1f, 0.0f, 1.0f-0.1f, Color(128,128,88) }, // Leg 2 + { 0.0f, 0.7f, 0.0f, 0.0f-0.1f, 0.0f, 0.0f+0.1f, Color(128,128,88) }, // Leg 2 + + // Chair + { -1.4f, 0.5f, -1.1f, -0.8f, 0.55f, -0.5f, Color(88,88,128) }, // Set + { -1.4f, 1.0f, -1.1f, -1.4f+0.06f, 0.0f, -1.1f+0.06f, Color(88,88,128) }, // Leg 1 + { -1.4f, 0.5f, -0.5f, -1.4f+0.06f, 0.0f, -0.5f-0.06f, Color(88,88,128) }, // Leg 2 + { -0.8f, 0.5f, -0.5f, -0.8f-0.06f, 0.0f, -0.5f-0.06f, Color(88,88,128) }, // Leg 2 + { -0.8f, 1.0f, -1.1f, -0.8f-0.06f, 0.0f, -1.1f+0.06f, Color(88,88,128) }, // Leg 2 + { -1.4f, 0.97f,-1.05f,-0.8f, 0.92f, -1.10f, Color(88,88,128) }, // Back high bar +}; + +SlabModel Furniture = {sizeof(FurnitureSlabs)/sizeof(Slab), FurnitureSlabs}; + +Slab PostsSlabs[] = +{ + // Posts + { 0, 0.0f, 0.0f, 0.1f, 1.3f, 0.1f, Color(128,128,128) }, + { 0, 0.0f, 0.4f, 0.1f, 1.3f, 0.5f, Color(128,128,128) }, + { 0, 0.0f, 0.8f, 0.1f, 1.3f, 0.9f, Color(128,128,128) }, + { 0, 0.0f, 1.2f, 0.1f, 1.3f, 1.3f, Color(128,128,128) }, + { 0, 0.0f, 1.6f, 0.1f, 1.3f, 1.7f, Color(128,128,128) }, + { 0, 0.0f, 2.0f, 0.1f, 1.3f, 2.1f, Color(128,128,128) }, + { 0, 0.0f, 2.4f, 0.1f, 1.3f, 2.5f, Color(128,128,128) }, + { 0, 0.0f, 2.8f, 0.1f, 1.3f, 2.9f, Color(128,128,128) }, + { 0, 0.0f, 3.2f, 0.1f, 1.3f, 3.3f, Color(128,128,128) }, + { 0, 0.0f, 3.6f, 0.1f, 1.3f, 3.7f, Color(128,128,128) }, +}; + +SlabModel Posts = {sizeof(PostsSlabs)/sizeof(Slab), PostsSlabs}; + + +// Temporary helper class used to initialize fills used by model. +class FillCollection +{ +public: + Ptr LitSolid; + Ptr LitTextures[4]; + + FillCollection(RenderDevice* render); + +}; + +FillCollection::FillCollection(RenderDevice* render) +{ + Ptr builtinTextures[Tex_Count]; + + // Create floor checkerboard texture. + { + Color checker[256*256]; + for (int j = 0; j < 256; j++) + for (int i = 0; i < 256; i++) + checker[j*256+i] = (((i/4 >> 5) ^ (j/4 >> 5)) & 1) ? + Color(180,180,180,255) : Color(80,80,80,255); + builtinTextures[Tex_Checker] = *render->CreateTexture(Texture_RGBA|Texture_GenMipmaps, 256, 256, checker); + builtinTextures[Tex_Checker]->SetSampleMode(Sample_Anisotropic|Sample_Repeat); + } + + // Ceiling panel texture. + { + Color panel[256*256]; + for (int j = 0; j < 256; j++) + for (int i = 0; i < 256; i++) + panel[j*256+i] = (i/4 == 0 || j/4 == 0) ? + Color(80,80,80,255) : Color(180,180,180,255); + builtinTextures[Tex_Panel] = *render->CreateTexture(Texture_RGBA|Texture_GenMipmaps, 256, 256, panel); + builtinTextures[Tex_Panel]->SetSampleMode(Sample_Anisotropic|Sample_Repeat); + } + + // Wall brick textures. + { + Color block[256*256]; + for (int j = 0; j < 256; j++) + for (int i = 0; i < 256; i++) + block[j*256+i] = (((j/4 & 15) == 0) || (((i/4 & 15) == 0) && ((((i/4 & 31) == 0) ^ ((j/4 >> 4) & 1)) == 0))) ? + Color(60,60,60,255) : Color(180,180,180,255); + builtinTextures[Tex_Block] = *render->CreateTexture(Texture_RGBA|Texture_GenMipmaps, 256, 256, block); + builtinTextures[Tex_Block]->SetSampleMode(Sample_Anisotropic|Sample_Repeat); + } + + LitSolid = *new ShaderFill(*render->CreateShaderSet()); + LitSolid->GetShaders()->SetShader(render->LoadBuiltinShader(Shader_Vertex, VShader_MVP)); + LitSolid->GetShaders()->SetShader(render->LoadBuiltinShader(Shader_Fragment, FShader_LitGouraud)); + + for (int i = 1; i < Tex_Count; i++) + { + LitTextures[i] = *new ShaderFill(*render->CreateShaderSet()); + LitTextures[i]->GetShaders()->SetShader(render->LoadBuiltinShader(Shader_Vertex, VShader_MVP)); + LitTextures[i]->GetShaders()->SetShader(render->LoadBuiltinShader(Shader_Fragment, FShader_LitTexture)); + LitTextures[i]->SetTexture(0, builtinTextures[i]); + } + +} + + + +// Helper function to create a model out of Slab arrays. +Model* CreateModel(Vector3f pos, SlabModel* sm, const FillCollection& fills) +{ + Model* m = new Model(Prim_Triangles); + m->SetPosition(pos); + + for(int i=0; i< sm->Count; i++) + { + Slab &s = sm->pSlabs[i]; + m->AddSolidColorBox(s.x1, s.y1, s.z1, s.x2, s.y2, s.z2, s.c); + } + + if (sm->tex > 0) + m->Fill = fills.LitTextures[sm->tex]; + else + m->Fill = fills.LitSolid; + return m; +} + + +// Adds sample models and lights to the argument scene. +void PopulateRoomScene(Scene* scene, RenderDevice* render) +{ + FillCollection fills(render); + + scene->World.Add(Ptr(*CreateModel(Vector3f(0,0,0), &Room, fills))); + scene->World.Add(Ptr(*CreateModel(Vector3f(0,0,0), &Floor, fills))); + scene->World.Add(Ptr(*CreateModel(Vector3f(0,0,0), &Ceiling, fills))); + scene->World.Add(Ptr(*CreateModel(Vector3f(0,0,0), &Fixtures, fills))); + scene->World.Add(Ptr(*CreateModel(Vector3f(0,0,0), &Furniture, fills))); + scene->World.Add(Ptr(*CreateModel(Vector3f(0,0,4), &Furniture, fills))); + scene->World.Add(Ptr(*CreateModel(Vector3f(-3,0,3), &Posts, fills))); + + + scene->SetAmbient(Vector4f(0.65f,0.65f,0.65f,1)); + scene->AddLight(Vector3f(-2,4,-2), Vector4f(8,8,8,1)); + scene->AddLight(Vector3f(3,4,-3), Vector4f(2,1,1,1)); + scene->AddLight(Vector3f(-4,3,25), Vector4f(3,6,3,1)); +} + diff --git a/Samples/OculusRoomTiny/OculusRoomTiny.rc b/Samples/OculusRoomTiny/OculusRoomTiny.rc new file mode 100644 index 0000000..49a6a5a Binary files /dev/null and b/Samples/OculusRoomTiny/OculusRoomTiny.rc differ diff --git a/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj b/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj new file mode 100644 index 0000000..c031772 --- /dev/null +++ b/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj @@ -0,0 +1,187 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {80523489-2881-4F64-8C3B-FAF88B60ABCD} + Win32Proj + OculusRoomTiny + OculusRoomTiny + + + + Application + true + Unicode + + + Application + true + Unicode + + + Application + false + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + + + + + + + true + $(Configuration)\Obj\ + $(ProjectDir)$(Configuration)\ + + + true + $(Configuration)\Obj\ + $(ProjectDir)$(Configuration)\ + + + false + $(Configuration)\Obj\ + $(ProjectDir)$(Configuration)\ + + + false + $(Configuration)\Obj\ + $(ProjectDir)$(Configuration)\ + + + + + + Level4 + Disabled + OVR_BUILD_DEBUG;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + ../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories) + OldStyle + true + false + + + Windows + true + libovrd.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + ../../LibOVR/Lib/Win32;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories) + + + + + + + Level4 + Disabled + OVR_BUILD_DEBUG;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + ../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories) + OldStyle + true + false + + + Windows + true + libovr64d.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + ../../LibOVR/Lib/x64;$(DXSDK_DIR)/Lib/x64;%(AdditionalLibraryDirectories) + + + + + Level4 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + ../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories) + MultiThreaded + true + OldStyle + + + Windows + false + true + true + ../../LibOVR/Lib/Win32;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories) + libovr.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level4 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + ../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories) + MultiThreaded + true + OldStyle + + + Windows + true + true + true + ../../LibOVR/Lib/x64;$(DXSDK_DIR)/Lib/x64;%(AdditionalLibraryDirectories) + libovr64.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj.filters b/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj.filters new file mode 100644 index 0000000..f7ec303 --- /dev/null +++ b/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj.filters @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp b/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp new file mode 100644 index 0000000..64887dd --- /dev/null +++ b/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp @@ -0,0 +1,1311 @@ +/************************************************************************************ + +Filename : RenderTiny_D3D1x.cpp +Content : RenderDevice implementation for D3DX10/11. +Created : September 10, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#include "Kernel/OVR_Log.h" +#include "Kernel/OVR_Std.h" + +#include "RenderTiny_D3D1X_Device.h" + +#include + +namespace OVR { namespace RenderTiny { namespace D3D10 { + + +//------------------------------------------------------------------------------------- +// Vertex format +static D3D1x_(INPUT_ELEMENT_DESC) ModelVertexDesc[] = +{ + {"Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, Pos), D3D1x_(INPUT_PER_VERTEX_DATA), 0}, + {"Color", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(Vertex, C), D3D1x_(INPUT_PER_VERTEX_DATA), 0}, + {"TexCoord", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(Vertex, U), D3D1x_(INPUT_PER_VERTEX_DATA), 0}, + {"Normal", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, Norm), D3D1x_(INPUT_PER_VERTEX_DATA), 0}, +}; + +// These shaders are used to render the world, including lit vertex-colored and textured geometry. + +// Used for world geometry; has projection matrix. +static const char* StdVertexShaderSrc = + "float4x4 Proj;\n" + "float4x4 View;\n" + "struct Varyings\n" + "{\n" + " float4 Position : SV_Position;\n" + " float4 Color : COLOR0;\n" + " float2 TexCoord : TEXCOORD0;\n" + " float3 Normal : NORMAL;\n" + " float3 VPos : TEXCOORD4;\n" + "};\n" + "void main(in float4 Position : POSITION, in float4 Color : COLOR0, in float2 TexCoord : TEXCOORD0," + " in float3 Normal : NORMAL,\n" + " out Varyings ov)\n" + "{\n" + " ov.Position = mul(Proj, mul(View, Position));\n" + " ov.Normal = mul(View, Normal);\n" + " ov.VPos = mul(View, Position);\n" + " ov.TexCoord = TexCoord;\n" + " ov.Color = Color;\n" + "}\n"; + +// Used for text/clearing; no projection. +static const char* DirectVertexShaderSrc = + "float4x4 View : register(c4);\n" + "void main(in float4 Position : POSITION, in float4 Color : COLOR0,\n" + " in float2 TexCoord : TEXCOORD0, in float3 Normal : NORMAL,\n" + " out float4 oPosition : SV_Position, out float4 oColor : COLOR,\n" + " out float2 oTexCoord : TEXCOORD0," + " out float3 oNormal : NORMAL)\n" + "{\n" + " oPosition = mul(View, Position);\n" + " oTexCoord = TexCoord;\n" + " oColor = Color;\n" + " oNormal = mul(View, Normal);\n" + "}\n"; + +static const char* SolidPixelShaderSrc = + "float4 Color;\n" + "struct Varyings\n" + "{\n" + " float4 Position : SV_Position;\n" + " float4 Color : COLOR0;\n" + " float2 TexCoord : TEXCOORD0;\n" + "};\n" + "float4 main(in Varyings ov) : SV_Target\n" + "{\n" + " return Color;\n" + "}\n"; + +static const char* GouraudPixelShaderSrc = + "struct Varyings\n" + "{\n" + " float4 Position : SV_Position;\n" + " float4 Color : COLOR0;\n" + " float2 TexCoord : TEXCOORD0;\n" + "};\n" + "float4 main(in Varyings ov) : SV_Target\n" + "{\n" + " return ov.Color;\n" + "}\n"; + +static const char* TexturePixelShaderSrc = + "Texture2D Texture : register(t0);\n" + "SamplerState Linear : register(s0);\n" + "struct Varyings\n" + "{\n" + " float4 Position : SV_Position;\n" + " float4 Color : COLOR0;\n" + " float2 TexCoord : TEXCOORD0;\n" + "};\n" + "float4 main(in Varyings ov) : SV_Target\n" + "{\n" + " float4 color2 = ov.Color * Texture.Sample(Linear, ov.TexCoord);\n" + " if (color2.a <= 0.4)\n" + " discard;\n" + " return color2;\n" + "}\n"; + + +#define LIGHTING_COMMON \ + "cbuffer Lighting : register(b1)\n" \ + "{\n" \ + " float3 Ambient;\n" \ + " float3 LightPos[8];\n" \ + " float4 LightColor[8];\n" \ + " float LightCount;\n" \ + "};\n" \ + "struct Varyings\n" \ + "{\n" \ + " float4 Position : SV_Position;\n" \ + " float4 Color : COLOR0;\n" \ + " float2 TexCoord : TEXCOORD0;\n" \ + " float3 Normal : NORMAL;\n" \ + " float3 VPos : TEXCOORD4;\n" \ + "};\n" \ + "float4 DoLight(Varyings v)\n" \ + "{\n" \ + " float3 norm = normalize(v.Normal);\n" \ + " float3 light = Ambient;\n" \ + " for (uint i = 0; i < LightCount; i++)\n"\ + " {\n" \ + " float3 ltp = (LightPos[i] - v.VPos);\n" \ + " float ldist = dot(ltp,ltp);\n" \ + " ltp = normalize(ltp);\n" \ + " light += saturate(LightColor[i] * v.Color.rgb * dot(norm, ltp) / sqrt(ldist));\n"\ + " }\n" \ + " return float4(light, v.Color.a);\n" \ + "}\n" + +static const char* LitSolidPixelShaderSrc = + LIGHTING_COMMON + "float4 main(in Varyings ov) : SV_Target\n" + "{\n" + " return DoLight(ov) * ov.Color;\n" + "}\n"; + +static const char* LitTexturePixelShaderSrc = + "Texture2D Texture : register(t0);\n" + "SamplerState Linear : register(s0);\n" + LIGHTING_COMMON + "float4 main(in Varyings ov) : SV_Target\n" + "{\n" + " return DoLight(ov) * Texture.Sample(Linear, ov.TexCoord);\n" + "}\n"; + + +//------------------------------------------------------------------------------------- +// ***** Distortion Post-process Shaders + +static const char* PostProcessVertexShaderSrc = + "float4x4 View : register(c4);\n" + "float4x4 Texm : register(c8);\n" + "void main(in float4 Position : POSITION, in float4 Color : COLOR0, in float2 TexCoord : TEXCOORD0,\n" + " out float4 oPosition : SV_Position, out float4 oColor : COLOR, out float2 oTexCoord : TEXCOORD0)\n" + "{\n" + " oPosition = mul(View, Position);\n" + " oTexCoord = mul(Texm, float4(TexCoord,0,1));\n" + " oColor = Color;\n" + "}\n"; + +// Shader with just lens distortion correction. +static const char* PostProcessPixelShaderSrc = + "Texture2D Texture : register(t0);\n" + "SamplerState Linear : register(s0);\n" + "float2 LensCenter;\n" + "float2 ScreenCenter;\n" + "float2 Scale;\n" + "float2 ScaleIn;\n" + "float4 HmdWarpParam;\n" + "\n" + + // Scales input texture coordinates for distortion. + // ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be + // larger due to aspect ratio. + "float2 HmdWarp(float2 in01)\n" + "{\n" + " float2 theta = (in01 - LensCenter) * ScaleIn;\n" // Scales to [-1, 1] + " float rSq = theta.x * theta.x + theta.y * theta.y;\n" + " float2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + " + " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n" + " return LensCenter + Scale * theta1;\n" + "}\n" + + "float4 main(in float4 oPosition : SV_Position, in float4 oColor : COLOR,\n" + " in float2 oTexCoord : TEXCOORD0) : SV_Target\n" + "{\n" + " float2 tc = HmdWarp(oTexCoord);\n" + " if (any(clamp(tc, ScreenCenter-float2(0.25,0.5), ScreenCenter+float2(0.25, 0.5)) - tc))\n" + " return 0;\n" + " return Texture.Sample(Linear, tc);\n" + "}\n"; + +// Shader with lens distortion and chromatic aberration correction. +static const char* PostProcessPixelShaderWithChromAbSrc = + "Texture2D Texture : register(t0);\n" + "SamplerState Linear : register(s0);\n" + "float2 LensCenter;\n" + "float2 ScreenCenter;\n" + "float2 Scale;\n" + "float2 ScaleIn;\n" + "float4 HmdWarpParam;\n" + "float4 ChromAbParam;\n" + "\n" + + // Scales input texture coordinates for distortion. + // ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be + // larger due to aspect ratio. + "float4 main(in float4 oPosition : SV_Position, in float4 oColor : COLOR,\n" + " in float2 oTexCoord : TEXCOORD0) : SV_Target\n" + "{\n" + " float2 theta = (oTexCoord - LensCenter) * ScaleIn;\n" // Scales to [-1, 1] + " float rSq= theta.x * theta.x + theta.y * theta.y;\n" + " float2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + " + " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n" + " \n" + " // Detect whether blue texture coordinates are out of range since these will scaled out the furthest.\n" + " float2 thetaBlue = theta1 * (ChromAbParam.z + ChromAbParam.w * rSq);\n" + " float2 tcBlue = LensCenter + Scale * thetaBlue;\n" + " if (any(clamp(tcBlue, ScreenCenter-float2(0.25,0.5), ScreenCenter+float2(0.25, 0.5)) - tcBlue))\n" + " return 0;\n" + " \n" + " // Now do blue texture lookup.\n" + " float blue = Texture.Sample(Linear, tcBlue).b;\n" + " \n" + " // Do green lookup (no scaling).\n" + " float2 tcGreen = LensCenter + Scale * theta1;\n" + " float green = Texture.Sample(Linear, tcGreen).g;\n" + " \n" + " // Do red scale and lookup.\n" + " float2 thetaRed = theta1 * (ChromAbParam.x + ChromAbParam.y * rSq);\n" + " float2 tcRed = LensCenter + Scale * thetaRed;\n" + " float red = Texture.Sample(Linear, tcRed).r;\n" + " \n" + " return float4(red, green, blue, 1);\n" + "}\n"; + + +static const char* VShaderSrcs[VShader_Count] = +{ + DirectVertexShaderSrc, + StdVertexShaderSrc, + PostProcessVertexShaderSrc +}; +static const char* FShaderSrcs[FShader_Count] = +{ + SolidPixelShaderSrc, + GouraudPixelShaderSrc, + TexturePixelShaderSrc, + PostProcessPixelShaderSrc, + PostProcessPixelShaderWithChromAbSrc, + LitSolidPixelShaderSrc, + LitTexturePixelShaderSrc +}; + + +//------------------------------------------------------------------------------------- +// ***** Buffer + +Buffer::~Buffer() +{ +} + +bool Buffer::Data(int use, const void *buffer, size_t size) +{ + if (D3DBuffer && Size >= size) + { + if (Dynamic) + { + if (!buffer) + return true; + + void* v = Map(0, size, Map_Discard); + if (v) + { + memcpy(v, buffer, size); + Unmap(v); + return true; + } + } + else + { + Ren->Context->UpdateSubresource(D3DBuffer, 0, NULL, buffer, 0, 0); + return true; + } + } + if (D3DBuffer) + { + D3DBuffer = NULL; + Size = 0; + Use = 0; + Dynamic = 0; + } + + D3D1x_(BUFFER_DESC) desc; + memset(&desc, 0, sizeof(desc)); + if (use & Buffer_ReadOnly) + { + desc.Usage = D3D1x_(USAGE_IMMUTABLE); + desc.CPUAccessFlags = 0; + } + else + { + desc.Usage = D3D1x_(USAGE_DYNAMIC); + desc.CPUAccessFlags = D3D1x_(CPU_ACCESS_WRITE); + Dynamic = 1; + } + + switch(use & Buffer_TypeMask) + { + case Buffer_Vertex: desc.BindFlags = D3D1x_(BIND_VERTEX_BUFFER); break; + case Buffer_Index: desc.BindFlags = D3D1x_(BIND_INDEX_BUFFER); break; + case Buffer_Uniform: + desc.BindFlags = D3D1x_(BIND_CONSTANT_BUFFER); + size += ((size + 15) & ~15) - size; + break; + } + + desc.ByteWidth = (unsigned)size; + + D3D1x_(SUBRESOURCE_DATA) sr; + sr.pSysMem = buffer; + sr.SysMemPitch = 0; + sr.SysMemSlicePitch = 0; + + HRESULT hr = Ren->Device->CreateBuffer(&desc, buffer ? &sr : NULL, &D3DBuffer.GetRawRef()); + if (SUCCEEDED(hr)) + { + Use = use; + Size = desc.ByteWidth; + return 1; + } + return 0; +} + +void* Buffer::Map(size_t start, size_t size, int flags) +{ + OVR_UNUSED(size); + + D3D1x_(MAP) mapFlags = D3D1x_(MAP_WRITE); + if (flags & Map_Discard) + mapFlags = D3D1x_(MAP_WRITE_DISCARD); + if (flags & Map_Unsynchronized) + mapFlags = D3D1x_(MAP_WRITE_NO_OVERWRITE); + + void* map = 0; + if (SUCCEEDED(D3DBuffer->Map(mapFlags, 0, &map))) + return ((char*)map) + start; + return NULL; +} + +bool Buffer::Unmap(void *m) +{ + OVR_UNUSED(m); + + D3DBuffer->Unmap(); + return true; +} + + +//------------------------------------------------------------------------------------- +// Shaders + +template<> bool Shader::Load(void* shader, size_t size) +{ + return SUCCEEDED(Ren->Device->CreateVertexShader(shader, size, &D3DShader)); +} +template<> bool Shader::Load(void* shader, size_t size) +{ + return SUCCEEDED(Ren->Device->CreatePixelShader(shader, size, &D3DShader)); +} + +template<> void Shader::Set(PrimitiveType) const +{ + Ren->Context->VSSetShader(D3DShader); +} +template<> void Shader::Set(PrimitiveType) const +{ + Ren->Context->PSSetShader(D3DShader); +} + +template<> void Shader::SetUniformBuffer(RenderTiny::Buffer* buffer, int i) +{ + Ren->Context->VSSetConstantBuffers(i, 1, &((Buffer*)buffer)->D3DBuffer.GetRawRef()); +} +template<> void Shader::SetUniformBuffer(RenderTiny::Buffer* buffer, int i) +{ + Ren->Context->PSSetConstantBuffers(i, 1, &((Buffer*)buffer)->D3DBuffer.GetRawRef()); +} + + +//------------------------------------------------------------------------------------- +// ***** Shader Base + +ShaderBase::ShaderBase(RenderDevice* r, ShaderStage stage) + : RenderTiny::Shader(stage), Ren(r), UniformData(0) +{ +} +ShaderBase::~ShaderBase() +{ + if (UniformData) + OVR_FREE(UniformData); +} + +bool ShaderBase::SetUniform(const char* name, int n, const float* v) +{ + for(unsigned i = 0; i < UniformInfo.GetSize(); i++) + { + if (!strcmp(UniformInfo[i].Name.ToCStr(), name)) + { + memcpy(UniformData + UniformInfo[i].Offset, v, n * sizeof(float)); + return 1; + } + } + return 0; +} + +void ShaderBase::InitUniforms(ID3D10Blob* s) +{ + ID3D10ShaderReflection* ref = NULL; + D3D10ReflectShader(s->GetBufferPointer(), s->GetBufferSize(), &ref); + ID3D10ShaderReflectionConstantBuffer* buf = ref->GetConstantBufferByIndex(0); + D3D10_SHADER_BUFFER_DESC bufd; + if (FAILED(buf->GetDesc(&bufd))) + { + UniformsSize = 0; + if (UniformData) + { + OVR_FREE(UniformData); + UniformData = 0; + } + return; + } + + for(unsigned i = 0; i < bufd.Variables; i++) + { + ID3D10ShaderReflectionVariable* var = buf->GetVariableByIndex(i); + if (var) + { + D3D10_SHADER_VARIABLE_DESC vd; + if (SUCCEEDED(var->GetDesc(&vd))) + { + Uniform u; + u.Name = vd.Name; + u.Offset = vd.StartOffset; + u.Size = vd.Size; + UniformInfo.PushBack(u); + } + } + } + + UniformsSize = bufd.Size; + UniformData = (unsigned char*)OVR_ALLOC(bufd.Size); +} + +void ShaderBase::UpdateBuffer(Buffer* buf) +{ + if (UniformsSize) + { + buf->Data(Buffer_Uniform, UniformData, UniformsSize); + } +} + + +//------------------------------------------------------------------------------------- +// ***** Texture +// +Texture::Texture(RenderDevice* ren, int fmt, int w, int h) + : Ren(ren), Tex(NULL), TexSv(NULL), TexRtv(NULL), TexDsv(NULL), Width(w), Height(h) +{ + OVR_UNUSED(fmt); + Sampler = Ren->GetSamplerState(0); +} + +Texture::~Texture() +{ +} + +void Texture::Set(int slot, RenderTiny::ShaderStage stage) const +{ + Ren->SetTexture(stage, slot, this); +} + +void Texture::SetSampleMode(int sm) +{ + Sampler = Ren->GetSamplerState(sm); +} + + + +//------------------------------------------------------------------------------------- +// ***** Render Device + +RenderDevice::RenderDevice(const RendererParams& p, HWND window) +{ + RECT rc; + GetClientRect(window, &rc); + UINT width = rc.right - rc.left; + UINT height = rc.bottom - rc.top; + WindowWidth = width; + WindowHeight = height; + Window = window; + Params = p; + + HRESULT hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)(&DXGIFactory.GetRawRef())); + if (FAILED(hr)) + return; + + // Find the adapter & output (monitor) to use for fullscreen, based on the reported name of the HMD's monitor. + if (Params.MonitorName.GetLength() > 0) + { + for(UINT AdapterIndex = 0; ; AdapterIndex++) + { + HRESULT hr = DXGIFactory->EnumAdapters(AdapterIndex, &Adapter.GetRawRef()); + if (hr == DXGI_ERROR_NOT_FOUND) + break; + + DXGI_ADAPTER_DESC Desc; + Adapter->GetDesc(&Desc); + + UpdateMonitorOutputs(); + + if (FullscreenOutput) + break; + } + + if (!FullscreenOutput) + Adapter = NULL; + } + + if (!Adapter) + { + DXGIFactory->EnumAdapters(0, &Adapter.GetRawRef()); + } + + int flags = 0; + + hr = D3D10CreateDevice(Adapter, D3D10_DRIVER_TYPE_HARDWARE, NULL, flags, D3D1x_(SDK_VERSION), + &Device.GetRawRef()); + Context = Device; + Context->AddRef(); + + if (FAILED(hr)) + return; + + if (!RecreateSwapChain()) + return; + + if (Params.Fullscreen) + SwapChain->SetFullscreenState(1, FullscreenOutput); + + CurRenderTarget = NULL; + for(int i = 0; i < Shader_Count; i++) + { + UniformBuffers[i] = *CreateBuffer(); + MaxTextureSet[i] = 0; + } + + ID3D10Blob* vsData = CompileShader("vs_4_0", DirectVertexShaderSrc); + VertexShaders[VShader_MV] = *new VertexShader(this, vsData); + for(int i = 1; i < VShader_Count; i++) + { + VertexShaders[i] = *new VertexShader(this, CompileShader("vs_4_0", VShaderSrcs[i])); + } + + for(int i = 0; i < FShader_Count; i++) + { + PixelShaders[i] = *new PixelShader(this, CompileShader("ps_4_0", FShaderSrcs[i])); + } + + SPInt bufferSize = vsData->GetBufferSize(); + const void* buffer = vsData->GetBufferPointer(); + ID3D1xInputLayout** objRef = &ModelVertexIL.GetRawRef(); + + HRESULT validate = Device->CreateInputLayout(ModelVertexDesc, sizeof(ModelVertexDesc)/sizeof(D3D1x_(INPUT_ELEMENT_DESC)), + buffer, bufferSize, objRef); + OVR_UNUSED(validate); + + Ptr gouraudShaders = *new ShaderSet(); + gouraudShaders->SetShader(VertexShaders[VShader_MVP]); + gouraudShaders->SetShader(PixelShaders[FShader_Gouraud]); + DefaultFill = *new ShaderFill(gouraudShaders); + + D3D1x_(BLEND_DESC) bm; + memset(&bm, 0, sizeof(bm)); + bm.BlendEnable[0] = true; + bm.BlendOp = bm.BlendOpAlpha = D3D1x_(BLEND_OP_ADD); + bm.SrcBlend = bm.SrcBlendAlpha = D3D1x_(BLEND_SRC_ALPHA); + bm.DestBlend = bm.DestBlendAlpha = D3D1x_(BLEND_INV_SRC_ALPHA); + bm.RenderTargetWriteMask[0] = D3D1x_(COLOR_WRITE_ENABLE_ALL); + Device->CreateBlendState(&bm, &BlendState.GetRawRef()); + + D3D1x_(RASTERIZER_DESC) rs; + memset(&rs, 0, sizeof(rs)); + rs.AntialiasedLineEnable = true; + rs.CullMode = D3D1x_(CULL_BACK); + rs.DepthClipEnable = true; + rs.FillMode = D3D1x_(FILL_SOLID); + Device->CreateRasterizerState(&rs, &Rasterizer.GetRawRef()); + + QuadVertexBuffer = *CreateBuffer(); + const RenderTiny::Vertex QuadVertices[] = + { Vertex(Vector3f(0, 1, 0)), Vertex(Vector3f(1, 1, 0)), + Vertex(Vector3f(0, 0, 0)), Vertex(Vector3f(1, 0, 0)) }; + QuadVertexBuffer->Data(Buffer_Vertex, QuadVertices, sizeof(QuadVertices)); + + SetDepthMode(0, 0); +} + +RenderDevice::~RenderDevice() +{ + if (SwapChain && Params.Fullscreen) + { + SwapChain->SetFullscreenState(false, NULL); + } +} + + +// Implement static initializer function to create this class. +RenderTiny::RenderDevice* RenderDevice::CreateDevice(const RendererParams& rp, void* oswnd) +{ + return new RenderDevice(rp, (HWND)oswnd); +} + + +// Fallback monitor enumeration in case newly plugged in monitor wasn't detected. +// Added originally for the FactoryTest app. +// New Outputs don't seem to be detected unless adapter is re-created, but that would also +// require us to re-initialize D3D10 (recreating objects, etc). This bypasses that for "fake" +// fullscreen modes. +BOOL CALLBACK MonitorEnumFunc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData) +{ + RenderDevice* renderer = (RenderDevice*)dwData; + + MONITORINFOEX monitor; + monitor.cbSize = sizeof(monitor); + + if (::GetMonitorInfo(hMonitor, &monitor) && monitor.szDevice[0]) + { + DISPLAY_DEVICE dispDev; + memset(&dispDev, 0, sizeof(dispDev)); + dispDev.cb = sizeof(dispDev); + + if (::EnumDisplayDevices(monitor.szDevice, 0, &dispDev, 0)) + { + if (strstr(String(dispDev.DeviceName).ToCStr(), renderer->GetParams().MonitorName.ToCStr())) + { + renderer->FSDesktopX = monitor.rcMonitor.left; + renderer->FSDesktopY = monitor.rcMonitor.top; + return FALSE; + } + } + } + + return TRUE; +} + + +void RenderDevice::UpdateMonitorOutputs() +{ + HRESULT hr; + + bool deviceNameFound = false; + + for(UINT OutputIndex = 0; ; OutputIndex++) + { + Ptr Output; + hr = Adapter->EnumOutputs(OutputIndex, &Output.GetRawRef()); + if (hr == DXGI_ERROR_NOT_FOUND) + { + break; + } + + DXGI_OUTPUT_DESC OutDesc; + Output->GetDesc(&OutDesc); + + MONITORINFOEX monitor; + monitor.cbSize = sizeof(monitor); + if (::GetMonitorInfo(OutDesc.Monitor, &monitor) && monitor.szDevice[0]) + { + DISPLAY_DEVICE dispDev; + memset(&dispDev, 0, sizeof(dispDev)); + dispDev.cb = sizeof(dispDev); + + if (::EnumDisplayDevices(monitor.szDevice, 0, &dispDev, 0)) + { + if (strstr(String(dispDev.DeviceName).ToCStr(), Params.MonitorName.ToCStr())) + { + deviceNameFound = true; + FullscreenOutput = Output; + FSDesktopX = monitor.rcMonitor.left; + FSDesktopY = monitor.rcMonitor.top; + break; + } + } + } + } + + if (!deviceNameFound && !Params.MonitorName.IsEmpty()) + { + EnumDisplayMonitors(0, 0, MonitorEnumFunc, (LPARAM)this); + } +} + +bool RenderDevice::RecreateSwapChain() +{ + DXGI_SWAP_CHAIN_DESC scDesc; + memset(&scDesc, 0, sizeof(scDesc)); + scDesc.BufferCount = 1; + scDesc.BufferDesc.Width = WindowWidth; + scDesc.BufferDesc.Height = WindowHeight; + scDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + scDesc.BufferDesc.RefreshRate.Numerator = 60; + scDesc.BufferDesc.RefreshRate.Denominator = 1; + scDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + scDesc.OutputWindow = Window; + scDesc.SampleDesc.Count = Params.Multisample; + scDesc.SampleDesc.Quality = 0; + scDesc.Windowed = (Params.Fullscreen != Display_Fullscreen); + scDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + + if (SwapChain) + { + SwapChain->SetFullscreenState(FALSE, NULL); + SwapChain->Release(); + SwapChain = NULL; + } + + Ptr newSC; + if (FAILED(DXGIFactory->CreateSwapChain(Device, &scDesc, &newSC.GetRawRef()))) + return false; + SwapChain = newSC; + + BackBuffer = NULL; + BackBufferRT = NULL; + HRESULT hr = SwapChain->GetBuffer(0, __uuidof(ID3D1xTexture2D), (void**)&BackBuffer.GetRawRef()); + if (FAILED(hr)) + return false; + + hr = Device->CreateRenderTargetView(BackBuffer, NULL, &BackBufferRT.GetRawRef()); + if (FAILED(hr)) + return false; + + Texture* depthBuffer = GetDepthBuffer(WindowWidth, WindowHeight, Params.Multisample); + CurDepthBuffer = depthBuffer; + if (CurRenderTarget == NULL) + { + Context->OMSetRenderTargets(1, &BackBufferRT.GetRawRef(), depthBuffer->TexDsv); + } + return true; +} + +bool RenderDevice::SetParams(const RendererParams& newParams) +{ + String oldMonitor = Params.MonitorName; + + Params = newParams; + if (newParams.MonitorName != oldMonitor) + { + UpdateMonitorOutputs(); + } + + // Cause this to be recreated with the new multisample mode. + pSceneColorTex = NULL; + return RecreateSwapChain(); +} + + +bool RenderDevice::SetFullscreen(DisplayMode fullscreen) +{ + if (fullscreen == Params.Fullscreen) + return true; + + HRESULT hr = SwapChain->SetFullscreenState(fullscreen, fullscreen ? FullscreenOutput : NULL); + if (FAILED(hr)) + { + return false; + } + + Params.Fullscreen = fullscreen; + return true; +} + +void RenderDevice::SetRealViewport(const Viewport& vp) +{ + D3DViewport.Width = vp.w; + D3DViewport.Height = vp.h; + D3DViewport.MinDepth = 0; + D3DViewport.MaxDepth = 1; + D3DViewport.TopLeftX = vp.x; + D3DViewport.TopLeftY = vp.y; + Context->RSSetViewports(1, &D3DViewport); +} + +static int GetDepthStateIndex(bool enable, bool write, RenderDevice::CompareFunc func) +{ + if (!enable) + return 0; + return 1 + int(func) * 2 + write; +} + +void RenderDevice::SetDepthMode(bool enable, bool write, CompareFunc func) +{ + int index = GetDepthStateIndex(enable, write, func); + if (DepthStates[index]) + { + CurDepthState = DepthStates[index]; + Context->OMSetDepthStencilState(DepthStates[index], 0); + return; + } + + D3D1x_(DEPTH_STENCIL_DESC) dss; + memset(&dss, 0, sizeof(dss)); + dss.DepthEnable = enable; + switch(func) + { + case Compare_Always: dss.DepthFunc = D3D1x_(COMPARISON_ALWAYS); break; + case Compare_Less: dss.DepthFunc = D3D1x_(COMPARISON_LESS); break; + case Compare_Greater: dss.DepthFunc = D3D1x_(COMPARISON_GREATER); break; + default: + assert(0); + } + dss.DepthWriteMask = write ? D3D1x_(DEPTH_WRITE_MASK_ALL) : D3D1x_(DEPTH_WRITE_MASK_ZERO); + Device->CreateDepthStencilState(&dss, &DepthStates[index].GetRawRef()); + Context->OMSetDepthStencilState(DepthStates[index], 0); + CurDepthState = DepthStates[index]; +} + +Texture* RenderDevice::GetDepthBuffer(int w, int h, int ms) +{ + for(unsigned i = 0; i < DepthBuffers.GetSize(); i++) + { + if (w == DepthBuffers[i]->Width && h == DepthBuffers[i]->Height && + ms == DepthBuffers[i]->Samples) + return DepthBuffers[i]; + } + + Ptr newDepth = *CreateTexture(Texture_Depth | Texture_RenderTarget | ms, w, h, NULL); + if (newDepth == NULL) + { + OVR_DEBUG_LOG(("Failed to get depth buffer.")); + return NULL; + } + + DepthBuffers.PushBack(newDepth); + return newDepth.GetPtr(); +} + +void RenderDevice::Clear(float r, float g, float b, float a, float depth) +{ + const float color[] = {r, g, b, a}; + + // Needed for each eye to do its own clear, since ClearRenderTargetView doesn't honor viewport. + + // Save state that is affected by clearing this way + ID3D1xDepthStencilState* oldDepthState = CurDepthState; + StandardUniformData clearUniforms; + + SetDepthMode(true, true, Compare_Always); + + Context->IASetInputLayout(ModelVertexIL); + Context->GSSetShader(NULL); + + ID3D1xShaderResourceView* sv[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + if (MaxTextureSet[Shader_Fragment]) + { + Context->PSSetShaderResources(0, MaxTextureSet[Shader_Fragment], sv); + } + + ID3D1xBuffer* vertexBuffer = QuadVertexBuffer->GetBuffer(); + UINT vertexStride = sizeof(Vertex); + UINT vertexOffset = 0; + Context->IASetVertexBuffers(0, 1, &vertexBuffer, &vertexStride, &vertexOffset); + + clearUniforms.View = Matrix4f(2, 0, 0, 0, + 0, 2, 0, 0, + 0, 0, 0, 0, + -1, -1, depth, 1); + UniformBuffers[Shader_Vertex]->Data(Buffer_Uniform, &clearUniforms, sizeof(clearUniforms)); + + ID3D1xBuffer* vertexConstants = UniformBuffers[Shader_Vertex]->GetBuffer(); + Context->VSSetConstantBuffers(0, 1, &vertexConstants); + Context->IASetPrimitiveTopology(D3D1x_(PRIMITIVE_TOPOLOGY_TRIANGLESTRIP)); + VertexShaders[VShader_MV]->Set(Prim_TriangleStrip); + PixelShaders[FShader_Solid]->Set(Prim_TriangleStrip); + + UniformBuffers[Shader_Pixel]->Data(Buffer_Uniform, color, sizeof(color)); + PixelShaders[FShader_Solid]->SetUniformBuffer(UniformBuffers[Shader_Pixel]); + + // Clear Viewport + Context->OMSetBlendState(NULL, NULL, 0xffffffff); + Context->Draw(4, 0); + + // reset + CurDepthState = oldDepthState; + Context->OMSetDepthStencilState(CurDepthState, 0); +} + +// Buffers + +Buffer* RenderDevice::CreateBuffer() +{ + return new Buffer(this); +} + + +ID3D10Blob* RenderDevice::CompileShader(const char* profile, const char* src, const char* mainName) +{ + ID3D10Blob* shader; + ID3D10Blob* errors; + HRESULT hr = D3DCompile(src, strlen(src), NULL, NULL, NULL, mainName, profile, + 0, 0, &shader, &errors); + if (FAILED(hr)) + { + OVR_DEBUG_LOG(("Compiling D3D shader for %s failed\n%s\n\n%s", + profile, src, errors->GetBufferPointer())); + OutputDebugStringA((char*)errors->GetBufferPointer()); + return NULL; + } + if (errors) + { + errors->Release(); + } + return shader; +} + +void RenderDevice::SetCommonUniformBuffer(int i, RenderTiny::Buffer* buffer) +{ + CommonUniforms[i] = (Buffer*)buffer; + + Context->PSSetConstantBuffers(1, 1, &CommonUniforms[1]->D3DBuffer.GetRawRef()); + Context->VSSetConstantBuffers(1, 1, &CommonUniforms[1]->D3DBuffer.GetRawRef()); +} + +RenderTiny::Shader *RenderDevice::LoadBuiltinShader(ShaderStage stage, int shader) +{ + switch(stage) + { + case Shader_Vertex: + return VertexShaders[shader]; + case Shader_Pixel: + return PixelShaders[shader]; + default: + return NULL; + } +} + + +ID3D1xSamplerState* RenderDevice::GetSamplerState(int sm) +{ + if (SamplerStates[sm]) + return SamplerStates[sm]; + + D3D1x_(SAMPLER_DESC) ss; + memset(&ss, 0, sizeof(ss)); + if (sm & Sample_Clamp) + ss.AddressU = ss.AddressV = ss.AddressW = D3D1x_(TEXTURE_ADDRESS_CLAMP); + else if (sm & Sample_ClampBorder) + ss.AddressU = ss.AddressV = ss.AddressW = D3D1x_(TEXTURE_ADDRESS_BORDER); + else + ss.AddressU = ss.AddressV = ss.AddressW = D3D1x_(TEXTURE_ADDRESS_WRAP); + + if (sm & Sample_Nearest) + { + ss.Filter = D3D1x_(FILTER_MIN_MAG_MIP_POINT); + } + else if (sm & Sample_Anisotropic) + { + ss.Filter = D3D1x_(FILTER_ANISOTROPIC); + ss.MaxAnisotropy = 8; + } + else + { + ss.Filter = D3D1x_(FILTER_MIN_MAG_MIP_LINEAR); + } + ss.MaxLOD = 15; + Device->CreateSamplerState(&ss, &SamplerStates[sm].GetRawRef()); + return SamplerStates[sm]; +} + + +void RenderDevice::SetTexture(RenderTiny::ShaderStage stage, int slot, const Texture* t) +{ + if (MaxTextureSet[stage] <= slot) + MaxTextureSet[stage] = slot + 1; + + ID3D1xShaderResourceView* sv = t ? t->TexSv : NULL; + switch(stage) + { + case Shader_Fragment: + Context->PSSetShaderResources(slot, 1, &sv); + if (t) + { + Context->PSSetSamplers(slot, 1, &t->Sampler.GetRawRef()); + } + break; + + case Shader_Vertex: + Context->VSSetShaderResources(slot, 1, &sv); + break; + } +} + +Texture* RenderDevice::CreateTexture(int format, int width, int height, const void* data, int mipcount) +{ + OVR_UNUSED(mipcount); + + DXGI_FORMAT d3dformat; + int bpp; + switch(format & Texture_TypeMask) + { + case Texture_RGBA: + bpp = 4; + d3dformat = DXGI_FORMAT_R8G8B8A8_UNORM; + break; + case Texture_Depth: + bpp = 0; + d3dformat = DXGI_FORMAT_D32_FLOAT; + break; + default: + return NULL; + } + + int samples = (format & Texture_SamplesMask); + if (samples < 1) + { + samples = 1; + } + + Texture* NewTex = new Texture(this, format, width, height); + NewTex->Samples = samples; + + D3D1x_(TEXTURE2D_DESC) dsDesc; + dsDesc.Width = width; + dsDesc.Height = height; + dsDesc.MipLevels = (format == (Texture_RGBA | Texture_GenMipmaps) && data) ? GetNumMipLevels(width, height) : 1; + dsDesc.ArraySize = 1; + dsDesc.Format = d3dformat; + dsDesc.SampleDesc.Count = samples; + dsDesc.SampleDesc.Quality = 0; + dsDesc.Usage = D3D1x_(USAGE_DEFAULT); + dsDesc.BindFlags = D3D1x_(BIND_SHADER_RESOURCE); + dsDesc.CPUAccessFlags = 0; + dsDesc.MiscFlags = 0; + + if (format & Texture_RenderTarget) + { + if ((format & Texture_TypeMask) == Texture_Depth) + { // We don't use depth textures, and creating them in d3d10 requires different options. + dsDesc.BindFlags = D3D1x_(BIND_DEPTH_STENCIL); + } + else + { + dsDesc.BindFlags |= D3D1x_(BIND_RENDER_TARGET); + } + } + + HRESULT hr = Device->CreateTexture2D(&dsDesc, NULL, &NewTex->Tex.GetRawRef()); + if (FAILED(hr)) + { + OVR_DEBUG_LOG_TEXT(("Failed to create 2D D3D texture.")); + NewTex->Release(); + return NULL; + } + if (dsDesc.BindFlags & D3D1x_(BIND_SHADER_RESOURCE)) + { + Device->CreateShaderResourceView(NewTex->Tex, NULL, &NewTex->TexSv.GetRawRef()); + } + + if (data) + { + Context->UpdateSubresource(NewTex->Tex, 0, NULL, data, width * bpp, width * height * bpp); + if (format == (Texture_RGBA | Texture_GenMipmaps)) + { + int srcw = width, srch = height; + int level = 0; + UByte* mipmaps = NULL; + do + { + level++; + int mipw = srcw >> 1; + if (mipw < 1) + { + mipw = 1; + } + int miph = srch >> 1; + if (miph < 1) + { + miph = 1; + } + if (mipmaps == NULL) + { + mipmaps = (UByte*)OVR_ALLOC(mipw * miph * 4); + } + FilterRgba2x2(level == 1 ? (const UByte*)data : mipmaps, srcw, srch, mipmaps); + Context->UpdateSubresource(NewTex->Tex, level, NULL, mipmaps, mipw * bpp, miph * bpp); + srcw = mipw; + srch = miph; + } + while(srcw > 1 || srch > 1); + + if (mipmaps != NULL) + { + OVR_FREE(mipmaps); + } + } + } + + if (format & Texture_RenderTarget) + { + if ((format & Texture_TypeMask) == Texture_Depth) + { + Device->CreateDepthStencilView(NewTex->Tex, NULL, &NewTex->TexDsv.GetRawRef()); + } + else + { + Device->CreateRenderTargetView(NewTex->Tex, NULL, &NewTex->TexRtv.GetRawRef()); + } + } + + return NewTex; +} + + +// Rendering + +void RenderDevice::BeginRendering() +{ + Context->RSSetState(Rasterizer); +} + +void RenderDevice::SetRenderTarget(RenderTiny::Texture* colorTex, + RenderTiny::Texture* depth, RenderTiny::Texture* stencil) +{ + OVR_UNUSED(stencil); + + CurRenderTarget = (Texture*)colorTex; + if (colorTex == NULL) + { + Texture* newDepthBuffer = GetDepthBuffer(WindowWidth, WindowHeight, Params.Multisample); + if (newDepthBuffer == NULL) + { + OVR_DEBUG_LOG(("New depth buffer creation failed.")); + } + if (newDepthBuffer != NULL) + { + CurDepthBuffer = GetDepthBuffer(WindowWidth, WindowHeight, Params.Multisample); + Context->OMSetRenderTargets(1, &BackBufferRT.GetRawRef(), CurDepthBuffer->TexDsv); + } + return; + } + if (depth == NULL) + { + depth = GetDepthBuffer(colorTex->GetWidth(), colorTex->GetHeight(), CurRenderTarget->Samples); + } + + ID3D1xShaderResourceView* sv[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + if (MaxTextureSet[Shader_Fragment]) + { + Context->PSSetShaderResources(0, MaxTextureSet[Shader_Fragment], sv); + } + memset(MaxTextureSet, 0, sizeof(MaxTextureSet)); + + CurDepthBuffer = (Texture*)depth; + Context->OMSetRenderTargets(1, &((Texture*)colorTex)->TexRtv.GetRawRef(), ((Texture*)depth)->TexDsv); +} + + +void RenderDevice::SetWorldUniforms(const Matrix4f& proj) +{ + StdUniforms.Proj = proj.Transposed(); + // Shader constant buffers cannot be partially updated. +} + + +void RenderDevice::Render(const Matrix4f& matrix, Model* model) +{ + // Store data in buffers if not already + if (!model->VertexBuffer) + { + Ptr vb = *CreateBuffer(); + vb->Data(Buffer_Vertex, &model->Vertices[0], model->Vertices.GetSize() * sizeof(Vertex)); + model->VertexBuffer = vb; + } + if (!model->IndexBuffer) + { + Ptr ib = *CreateBuffer(); + ib->Data(Buffer_Index, &model->Indices[0], model->Indices.GetSize() * 2); + model->IndexBuffer = ib; + } + + Render(model->Fill ? model->Fill : DefaultFill, + model->VertexBuffer, model->IndexBuffer, + matrix, 0, (unsigned)model->Indices.GetSize(), model->GetPrimType()); +} + +void RenderDevice::Render(const ShaderFill* fill, RenderTiny::Buffer* vertices, RenderTiny::Buffer* indices, + const Matrix4f& matrix, int offset, int count, PrimitiveType rprim) +{ + Context->IASetInputLayout(ModelVertexIL); + if (indices) + { + Context->IASetIndexBuffer(((Buffer*)indices)->GetBuffer(), DXGI_FORMAT_R16_UINT, 0); + } + + ID3D1xBuffer* vertexBuffer = ((Buffer*)vertices)->GetBuffer(); + UINT vertexStride = sizeof(Vertex); + UINT vertexOffset = offset; + Context->IASetVertexBuffers(0, 1, &vertexBuffer, &vertexStride, &vertexOffset); + + ShaderSet* shaders = ((ShaderFill*)fill)->GetShaders(); + + ShaderBase* vshader = ((ShaderBase*)shaders->GetShader(Shader_Vertex)); + unsigned char* vertexData = vshader->UniformData; + if (vertexData) + { + StandardUniformData* stdUniforms = (StandardUniformData*) vertexData; + stdUniforms->View = matrix.Transposed(); + stdUniforms->Proj = StdUniforms.Proj; + UniformBuffers[Shader_Vertex]->Data(Buffer_Uniform, vertexData, vshader->UniformsSize); + vshader->SetUniformBuffer(UniformBuffers[Shader_Vertex]); + } + + for(int i = Shader_Vertex + 1; i < Shader_Count; i++) + if (shaders->GetShader(i)) + { + ((ShaderBase*)shaders->GetShader(i))->UpdateBuffer(UniformBuffers[i]); + ((ShaderBase*)shaders->GetShader(i))->SetUniformBuffer(UniformBuffers[i]); + } + + D3D1x_(PRIMITIVE_TOPOLOGY) prim; + switch(rprim) + { + case Prim_Triangles: + prim = D3D1x_(PRIMITIVE_TOPOLOGY_TRIANGLELIST); + break; + case Prim_Lines: + prim = D3D1x_(PRIMITIVE_TOPOLOGY_LINELIST); + break; + case Prim_TriangleStrip: + prim = D3D1x_(PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + break; + default: + assert(0); + return; + } + Context->IASetPrimitiveTopology(prim); + + fill->Set(rprim); + + if (indices) + { + Context->DrawIndexed(count, 0, 0); + } + else + { + Context->Draw(count, 0); + } +} + + +void RenderDevice::Present() +{ + SwapChain->Present(0, 0); +} + +void RenderDevice::ForceFlushGPU() +{ + D3D1x_QUERY_DESC queryDesc = { D3D1x_(QUERY_EVENT), 0 }; + Ptr query; + BOOL done = FALSE; + + if (Device->CreateQuery(&queryDesc, &query.GetRawRef()) == S_OK) + { + // Begin() not used for EVENT query. + query->End(); + // GetData will returns S_OK for both done == TRUE or FALSE. + // Exit on failure to avoid infinite loop. + do { } + while(!done && !FAILED(query->GetData(&done, sizeof(BOOL), 0))); + } +} + +}}} + diff --git a/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.h b/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.h new file mode 100644 index 0000000..e608498 --- /dev/null +++ b/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.h @@ -0,0 +1,273 @@ +/************************************************************************************ + +Filename : RenderTiny_D3D1X_Device.h +Content : RenderDevice implementation header for D3DX10. +Created : September 10, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef INC_RenderTiny_D3D1X_Device_h +#define INC_RenderTiny_D3D1X_Device_h + +#include "Kernel/OVR_String.h" +#include "Kernel/OVR_Array.h" + +#include "RenderTiny_Device.h" +#include + +#define _OVR_RENDERER_D3D10 +#include + +namespace OVR { namespace RenderTiny { namespace D3D10 { + +class RenderDevice; +class Buffer; + +typedef ID3D10Device ID3D1xDevice; +typedef ID3D10Device ID3D1xDeviceContext; +typedef ID3D10RenderTargetView ID3D1xRenderTargetView; +typedef ID3D10Texture2D ID3D1xTexture2D; +typedef ID3D10ShaderResourceView ID3D1xShaderResourceView; +typedef ID3D10DepthStencilView ID3D1xDepthStencilView; +typedef ID3D10DepthStencilState ID3D1xDepthStencilState; +typedef ID3D10InputLayout ID3D1xInputLayout; +typedef ID3D10Buffer ID3D1xBuffer; +typedef ID3D10VertexShader ID3D1xVertexShader; +typedef ID3D10PixelShader ID3D1xPixelShader; +typedef ID3D10GeometryShader ID3D1xGeometryShader; +typedef ID3D10BlendState ID3D1xBlendState; +typedef ID3D10RasterizerState ID3D1xRasterizerState; +typedef ID3D10SamplerState ID3D1xSamplerState; +typedef ID3D10Query ID3D1xQuery; +typedef ID3D10Blob ID3D1xBlob; +typedef D3D10_VIEWPORT D3D1x_VIEWPORT; +typedef D3D10_QUERY_DESC D3D1x_QUERY_DESC; +#define D3D1x_(x) D3D10_##x +#define ID3D1x(x) ID3D10##x + + + +class ShaderBase : public RenderTiny::Shader +{ +public: + RenderDevice* Ren; + unsigned char* UniformData; + int UniformsSize; + + struct Uniform + { + String Name; + int Offset, Size; + }; + Array UniformInfo; + + ShaderBase(RenderDevice* r, ShaderStage stage); + ~ShaderBase(); + + void InitUniforms(ID3D10Blob* s); + bool SetUniform(const char* name, int n, const float* v); + + void UpdateBuffer(Buffer* b); +}; + +template +class Shader : public ShaderBase +{ +public: + D3DShaderType* D3DShader; + + Shader(RenderDevice* r, D3DShaderType* s) : ShaderBase(r, SStage), D3DShader(s) {} + Shader(RenderDevice* r, ID3D1xBlob* s) : ShaderBase(r, SStage) + { + Load(s); + InitUniforms(s); + } + ~Shader() + { + if (D3DShader) + D3DShader->Release(); + } + bool Load(ID3D1xBlob* shader) + { + return Load(shader->GetBufferPointer(), shader->GetBufferSize()); + } + + // These functions have specializations. + bool Load(void* shader, size_t size); + void Set(PrimitiveType prim) const; + void SetUniformBuffer(RenderTiny::Buffer* buffers, int i = 0); +}; + +typedef Shader VertexShader; +typedef Shader PixelShader; + + +class Buffer : public RenderTiny::Buffer +{ +public: + RenderDevice* Ren; + Ptr D3DBuffer; + size_t Size; + int Use; + bool Dynamic; + +public: + Buffer(RenderDevice* r) : Ren(r), Size(0), Use(0) {} + ~Buffer(); + + ID3D1xBuffer* GetBuffer() + { + return D3DBuffer; + } + + virtual size_t GetSize() + { + return Size; + } + virtual void* Map(size_t start, size_t size, int flags = 0); + virtual bool Unmap(void *m); + virtual bool Data(int use, const void* buffer, size_t size); +}; + +class Texture : public RenderTiny::Texture +{ +public: + RenderDevice* Ren; + Ptr Tex; + Ptr TexSv; + Ptr TexRtv; + Ptr TexDsv; + mutable Ptr Sampler; + int Width, Height; + int Samples; + + Texture(RenderDevice* r, int fmt, int w, int h); + ~Texture(); + + virtual int GetWidth() const + { + return Width; + } + virtual int GetHeight() const + { + return Height; + } + virtual int GetSamples() const + { + return Samples; + } + + virtual void SetSampleMode(int sm); + + virtual void Set(int slot, RenderTiny::ShaderStage stage = RenderTiny::Shader_Fragment) const; +}; + +class RenderDevice : public RenderTiny::RenderDevice +{ +public: + Ptr DXGIFactory; + HWND Window; + + Ptr Device; + Ptr Context; + Ptr SwapChain; + Ptr Adapter; + Ptr FullscreenOutput; + int FSDesktopX, FSDesktopY; + + Ptr BackBuffer; + Ptr BackBufferRT; + Ptr CurRenderTarget; + Ptr CurDepthBuffer; + Ptr Rasterizer; + Ptr BlendState; + D3D1x_VIEWPORT D3DViewport; + + Ptr DepthStates[1 + 2 * Compare_Count]; + Ptr CurDepthState; + Ptr ModelVertexIL; + + Ptr SamplerStates[Sample_Count]; + + struct StandardUniformData + { + Matrix4f Proj; + Matrix4f View; + } StdUniforms; + Ptr UniformBuffers[Shader_Count]; + int MaxTextureSet[Shader_Count]; + + Ptr VertexShaders[VShader_Count]; + Ptr PixelShaders[FShader_Count]; + Ptr CommonUniforms[8]; + Ptr DefaultFill; + + Ptr QuadVertexBuffer; + + Array > DepthBuffers; + +public: + RenderDevice(const RendererParams& p, HWND window); + ~RenderDevice(); + + // Implement static initializer function to create this class. + static RenderTiny::RenderDevice* CreateDevice(const RendererParams& rp, void* oswnd); + + void UpdateMonitorOutputs(); + + virtual void SetRealViewport(const Viewport& vp); + virtual bool SetParams(const RendererParams& newParams); + + virtual void Present(); + virtual void ForceFlushGPU(); + + virtual bool SetFullscreen(DisplayMode fullscreen); + + virtual void Clear(float r = 0, float g = 0, float b = 0, float a = 1, float depth = 1); + + virtual Buffer* CreateBuffer(); + virtual Texture* CreateTexture(int format, int width, int height, const void* data, int mipcount=1); + + Texture* GetDepthBuffer(int w, int h, int ms); + + virtual void BeginRendering(); + virtual void SetRenderTarget(RenderTiny::Texture* color, + RenderTiny::Texture* depth = NULL, RenderTiny::Texture* stencil = NULL); + virtual void SetDepthMode(bool enable, bool write, CompareFunc func = Compare_Less); + virtual void SetWorldUniforms(const Matrix4f& proj); + virtual void SetCommonUniformBuffer(int i, RenderTiny::Buffer* buffer); + + virtual void Render(const Matrix4f& matrix, Model* model); + virtual void Render(const ShaderFill* fill, RenderTiny::Buffer* vertices, RenderTiny::Buffer* indices, + const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles); + + virtual ShaderFill *CreateSimpleFill() { return DefaultFill; } + + virtual RenderTiny::Shader *LoadBuiltinShader(ShaderStage stage, int shader); + + bool RecreateSwapChain(); + virtual ID3D10Blob* CompileShader(const char* profile, const char* src, const char* mainName = "main"); + + ID3D1xSamplerState* GetSamplerState(int sm); + + void SetTexture(RenderTiny::ShaderStage stage, int slot, const Texture* t); +}; + +}}} // Render::D3D10 + +#endif diff --git a/Samples/OculusRoomTiny/RenderTiny_Device.cpp b/Samples/OculusRoomTiny/RenderTiny_Device.cpp new file mode 100644 index 0000000..1d6cdde --- /dev/null +++ b/Samples/OculusRoomTiny/RenderTiny_Device.cpp @@ -0,0 +1,442 @@ +/************************************************************************************ + +Filename : RenderTiny_Device.cpp +Content : Platform renderer for simple scene graph - implementation +Created : September 6, 2012 +Authors : Andrew Reisse + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#include "RenderTiny_Device.h" + +#include "Kernel/OVR_Log.h" + +namespace OVR { namespace RenderTiny { + +void Model::Render(const Matrix4f& ltw, RenderDevice* ren) +{ + if (Visible) + { + Matrix4f m = ltw * GetMatrix(); + ren->Render(m, this); + } +} + +void Container::Render(const Matrix4f& ltw, RenderDevice* ren) +{ + Matrix4f m = ltw * GetMatrix(); + for(unsigned i = 0; i < Nodes.GetSize(); i++) + { + Nodes[i]->Render(m, ren); + } +} + +void Scene::Render(RenderDevice* ren, const Matrix4f& view) +{ + Lighting.Update(view, LightPos); + + ren->SetLighting(&Lighting); + + World.Render(view, ren); +} + + + +UInt16 CubeIndices[] = +{ + 0, 1, 3, + 3, 1, 2, + + 5, 4, 6, + 6, 4, 7, + + 8, 9, 11, + 11, 9, 10, + + 13, 12, 14, + 14, 12, 15, + + 16, 17, 19, + 19, 17, 18, + + 21, 20, 22, + 22, 20, 23 +}; + + +void Model::AddSolidColorBox(float x1, float y1, float z1, + float x2, float y2, float z2, + Color c) +{ + float t; + + if(x1 > x2) + { + t = x1; + x1 = x2; + x2 = t; + } + if(y1 > y2) + { + t = y1; + y1 = y2; + y2 = t; + } + if(z1 > z2) + { + t = z1; + z1 = z2; + z2 = t; + } + + // Cube vertices and their normals. + Vector3f CubeVertices[][3] = + { + Vector3f(x1, y2, z1), Vector3f(z1, x1), Vector3f(0.0f, 1.0f, 0.0f), + Vector3f(x2, y2, z1), Vector3f(z1, x2), Vector3f(0.0f, 1.0f, 0.0f), + Vector3f(x2, y2, z2), Vector3f(z2, x2), Vector3f(0.0f, 1.0f, 0.0f), + Vector3f(x1, y2, z2), Vector3f(z2, x1), Vector3f(0.0f, 1.0f, 0.0f), + + Vector3f(x1, y1, z1), Vector3f(z1, x1), Vector3f(0.0f, -1.0f, 0.0f), + Vector3f(x2, y1, z1), Vector3f(z1, x2), Vector3f(0.0f, -1.0f, 0.0f), + Vector3f(x2, y1, z2), Vector3f(z2, x2), Vector3f(0.0f, -1.0f, 0.0f), + Vector3f(x1, y1, z2), Vector3f(z2, x1), Vector3f(0.0f, -1.0f, 0.0f), + + Vector3f(x1, y1, z2), Vector3f(z2, y1), Vector3f(-1.0f, 0.0f, 0.0f), + Vector3f(x1, y1, z1), Vector3f(z1, y1), Vector3f(-1.0f, 0.0f, 0.0f), + Vector3f(x1, y2, z1), Vector3f(z1, y2), Vector3f(-1.0f, 0.0f, 0.0f), + Vector3f(x1, y2, z2), Vector3f(z2, y2), Vector3f(-1.0f, 0.0f, 0.0f), + + Vector3f(x2, y1, z2), Vector3f(z2, y1), Vector3f(1.0f, 0.0f, 0.0f), + Vector3f(x2, y1, z1), Vector3f(z1, y1), Vector3f(1.0f, 0.0f, 0.0f), + Vector3f(x2, y2, z1), Vector3f(z1, y2), Vector3f(1.0f, 0.0f, 0.0f), + Vector3f(x2, y2, z2), Vector3f(z2, y2), Vector3f(1.0f, 0.0f, 0.0f), + + Vector3f(x1, y1, z1), Vector3f(x1, y1), Vector3f(0.0f, 0.0f, -1.0f), + Vector3f(x2, y1, z1), Vector3f(x2, y1), Vector3f(0.0f, 0.0f, -1.0f), + Vector3f(x2, y2, z1), Vector3f(x2, y2), Vector3f(0.0f, 0.0f, -1.0f), + Vector3f(x1, y2, z1), Vector3f(x1, y2), Vector3f(0.0f, 0.0f, -1.0f), + + Vector3f(x1, y1, z2), Vector3f(x1, y1), Vector3f(0.0f, 0.0f, 1.0f), + Vector3f(x2, y1, z2), Vector3f(x2, y1), Vector3f(0.0f, 0.0f, 1.0f), + Vector3f(x2, y2, z2), Vector3f(x2, y2), Vector3f(0.0f, 0.0f, 1.0f), + Vector3f(x1, y2, z2), Vector3f(x1, y2), Vector3f(0.0f, 0.0f, 1.0f) + }; + + + UInt16 startIndex = GetNextVertexIndex(); + + enum + { + CubeVertexCount = sizeof(CubeVertices) / sizeof(CubeVertices[0]), + CubeIndexCount = sizeof(CubeIndices) / sizeof(CubeIndices[0]) + }; + + for(int v = 0; v < CubeVertexCount; v++) + { + AddVertex(Vertex(CubeVertices[v][0], c, CubeVertices[v][1].x, CubeVertices[v][1].y, CubeVertices[v][2])); + } + + // Renumber indices + for(int i = 0; i < CubeIndexCount / 3; i++) + { + AddTriangle(CubeIndices[i * 3] + startIndex, + CubeIndices[i * 3 + 1] + startIndex, + CubeIndices[i * 3 + 2] + startIndex); + } +} + + +//------------------------------------------------------------------------------------- + + +void ShaderFill::Set(PrimitiveType prim) const +{ + Shaders->Set(prim); + for(int i = 0; i < 8; i++) + { + if(Textures[i]) + { + Textures[i]->Set(i); + } + } +} + + + +//------------------------------------------------------------------------------------- +// ***** Rendering + + +RenderDevice::RenderDevice() + : CurPostProcess(PostProcess_None), + SceneColorTexW(0), SceneColorTexH(0), + SceneRenderScale(1), + Distortion(1.0f, 0.18f, 0.115f), + PostProcessShaderActive(PostProcessShader_DistortionAndChromAb) +{ + PostProcessShaderRequested = PostProcessShaderActive; +} + +ShaderFill* RenderDevice::CreateTextureFill(RenderTiny::Texture* t) +{ + ShaderSet* shaders = CreateShaderSet(); + shaders->SetShader(LoadBuiltinShader(Shader_Vertex, VShader_MVP)); + shaders->SetShader(LoadBuiltinShader(Shader_Fragment, FShader_Texture)); + ShaderFill* f = new ShaderFill(*shaders); + f->SetTexture(0, t); + return f; +} + +void RenderDevice::SetLighting(const LightingParams* lt) +{ + if (!LightingBuffer) + LightingBuffer = *CreateBuffer(); + + LightingBuffer->Data(Buffer_Uniform, lt, sizeof(LightingParams)); + SetCommonUniformBuffer(1, LightingBuffer); +} + + + +void RenderDevice::SetSceneRenderScale(float ss) +{ + SceneRenderScale = ss; + pSceneColorTex = NULL; +} + +void RenderDevice::SetViewport(const Viewport& vp) +{ + VP = vp; + + if (CurPostProcess == PostProcess_Distortion) + { + Viewport svp = vp; + svp.w = (int)ceil(SceneRenderScale * vp.w); + svp.h = (int)ceil(SceneRenderScale * vp.h); + svp.x = (int)ceil(SceneRenderScale * vp.x); + svp.y = (int)ceil(SceneRenderScale * vp.y); + SetRealViewport(svp); + } + else + { + SetRealViewport(vp); + } +} + + +bool RenderDevice::initPostProcessSupport(PostProcessType pptype) +{ + if (pptype != PostProcess_Distortion) + return true; + + + if (PostProcessShaderRequested != PostProcessShaderActive) + { + pPostProcessShader.Clear(); + PostProcessShaderActive = PostProcessShaderRequested; + } + + if (!pPostProcessShader) + { + Shader *vs = LoadBuiltinShader(Shader_Vertex, VShader_PostProcess); + + Shader *ppfs = NULL; + if (PostProcessShaderActive == PostProcessShader_Distortion) + { + ppfs = LoadBuiltinShader(Shader_Fragment, FShader_PostProcess); + } + else if (PostProcessShaderActive == PostProcessShader_DistortionAndChromAb) + { + ppfs = LoadBuiltinShader(Shader_Fragment, FShader_PostProcessWithChromAb); + } + else + OVR_ASSERT(false); + + pPostProcessShader = *CreateShaderSet(); + pPostProcessShader->SetShader(vs); + pPostProcessShader->SetShader(ppfs); + } + + + int texw = (int)ceil(SceneRenderScale * WindowWidth), + texh = (int)ceil(SceneRenderScale * WindowHeight); + + // If pSceneColorTex is already created and is of correct size, we are done. + // It's important to check width/height in case window size changed. + if (pSceneColorTex && (texw == SceneColorTexW) && (texh == SceneColorTexH)) + { + return true; + } + + pSceneColorTex = *CreateTexture(Texture_RGBA | Texture_RenderTarget | Params.Multisample, + texw, texh, NULL); + if (!pSceneColorTex) + { + return false; + } + SceneColorTexW = texw; + SceneColorTexH = texh; + pSceneColorTex->SetSampleMode(Sample_ClampBorder | Sample_Linear); + + + if (!pFullScreenVertexBuffer) + { + pFullScreenVertexBuffer = *CreateBuffer(); + const RenderTiny::Vertex QuadVertices[] = + { + Vertex(Vector3f(0, 1, 0), Color(1, 1, 1, 1), 0, 0), + Vertex(Vector3f(1, 1, 0), Color(1, 1, 1, 1), 1, 0), + Vertex(Vector3f(0, 0, 0), Color(1, 1, 1, 1), 0, 1), + Vertex(Vector3f(1, 0, 0), Color(1, 1, 1, 1), 1, 1) + }; + pFullScreenVertexBuffer->Data(Buffer_Vertex, QuadVertices, sizeof(QuadVertices)); + } + return true; +} + +void RenderDevice::SetProjection(const Matrix4f& proj) +{ + Proj = proj; + SetWorldUniforms(proj); +} + +void RenderDevice::BeginScene(PostProcessType pptype) +{ + BeginRendering(); + + if ((pptype != PostProcess_None) && initPostProcessSupport(pptype)) + { + CurPostProcess = pptype; + } + else + { + CurPostProcess = PostProcess_None; + } + + if (CurPostProcess == PostProcess_Distortion) + { + SetRenderTarget(pSceneColorTex); + SetViewport(VP); + } + else + { + SetRenderTarget(0); + } + + SetWorldUniforms(Proj); +} + +void RenderDevice::FinishScene() +{ + if (CurPostProcess == PostProcess_None) + return; + + SetRenderTarget(0); + SetRealViewport(VP); + FinishScene1(); + + CurPostProcess = PostProcess_None; +} + + + +void RenderDevice::FinishScene1() +{ + // Clear with black + Clear(0.0f, 0.0f, 0.0f, 1.0f); + + float w = float(VP.w) / float(WindowWidth), + h = float(VP.h) / float(WindowHeight), + x = float(VP.x) / float(WindowWidth), + y = float(VP.y) / float(WindowHeight); + + float as = float(VP.w) / float(VP.h); + + // We are using 1/4 of DistortionCenter offset value here, since it is + // relative to [-1,1] range that gets mapped to [0, 0.5]. + pPostProcessShader->SetUniform2f("LensCenter", + x + (w + Distortion.XCenterOffset * 0.5f)*0.5f, y + h*0.5f); + pPostProcessShader->SetUniform2f("ScreenCenter", x + w*0.5f, y + h*0.5f); + + // MA: This is more correct but we would need higher-res texture vertically; we should adopt this + // once we have asymmetric input texture scale. + float scaleFactor = 1.0f / Distortion.Scale; + + pPostProcessShader->SetUniform2f("Scale", (w/2) * scaleFactor, (h/2) * scaleFactor * as); + pPostProcessShader->SetUniform2f("ScaleIn", (2/w), (2/h) / as); + + pPostProcessShader->SetUniform4f("HmdWarpParam", + Distortion.K[0], Distortion.K[1], Distortion.K[2], Distortion.K[3]); + + if (PostProcessShaderRequested == PostProcessShader_DistortionAndChromAb) + { + pPostProcessShader->SetUniform4f("ChromAbParam", + Distortion.ChromaticAberration[0], + Distortion.ChromaticAberration[1], + Distortion.ChromaticAberration[2], + Distortion.ChromaticAberration[3]); + } + + Matrix4f texm(w, 0, 0, x, + 0, h, 0, y, + 0, 0, 0, 0, + 0, 0, 0, 1); + pPostProcessShader->SetUniform4x4f("Texm", texm); + + Matrix4f view(2, 0, 0, -1, + 0, 2, 0, -1, + 0, 0, 0, 0, + 0, 0, 0, 1); + + ShaderFill fill(pPostProcessShader); + fill.SetTexture(0, pSceneColorTex); + Render(&fill, pFullScreenVertexBuffer, NULL, view, 0, 4, Prim_TriangleStrip); +} + + +int GetNumMipLevels(int w, int h) +{ + int n = 1; + while(w > 1 || h > 1) + { + w >>= 1; + h >>= 1; + n++; + } + return n; +} + +void FilterRgba2x2(const UByte* src, int w, int h, UByte* dest) +{ + for(int j = 0; j < (h & ~1); j += 2) + { + const UByte* psrc = src + (w * j * 4); + UByte* pdest = dest + ((w >> 1) * (j >> 1) * 4); + + for(int i = 0; i < w >> 1; i++, psrc += 8, pdest += 4) + { + pdest[0] = (((int)psrc[0]) + psrc[4] + psrc[w * 4 + 0] + psrc[w * 4 + 4]) >> 2; + pdest[1] = (((int)psrc[1]) + psrc[5] + psrc[w * 4 + 1] + psrc[w * 4 + 5]) >> 2; + pdest[2] = (((int)psrc[2]) + psrc[6] + psrc[w * 4 + 2] + psrc[w * 4 + 6]) >> 2; + pdest[3] = (((int)psrc[3]) + psrc[7] + psrc[w * 4 + 3] + psrc[w * 4 + 7]) >> 2; + } + } +} + + +}} diff --git a/Samples/OculusRoomTiny/RenderTiny_Device.h b/Samples/OculusRoomTiny/RenderTiny_Device.h new file mode 100644 index 0000000..74ad7ec --- /dev/null +++ b/Samples/OculusRoomTiny/RenderTiny_Device.h @@ -0,0 +1,724 @@ +/************************************************************************************ + +Filename : RenderTiny_Device.h +Content : Minimal possible renderer for RoomTiny sample +Created : September 6, 2012 +Authors : Andrew Reisse, Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef OVR_RenderTiny_Device_h +#define OVR_RenderTiny_Device_h + +#include "Kernel/OVR_Math.h" +#include "Kernel/OVR_Array.h" +#include "Kernel/OVR_RefCount.h" +#include "Kernel/OVR_String.h" +#include "Kernel/OVR_File.h" +#include "Kernel/OVR_Color.h" + +#include "Util/Util_Render_Stereo.h" + +namespace OVR { namespace RenderTiny { + +using namespace OVR::Util::Render; + +class RenderDevice; + + +//----------------------------------------------------------------------------------- + +// Rendering primitive type used to render Model. +enum PrimitiveType +{ + Prim_Triangles, + Prim_Lines, + Prim_TriangleStrip, + Prim_Unknown, + Prim_Count +}; + +// Types of shaders taht can be stored together in a ShaderSet. +enum ShaderStage +{ + Shader_Vertex = 0, + Shader_Fragment = 2, + Shader_Pixel = 2, + Shader_Count = 3, +}; + +// Built-in shader types; used by LoadBuiltinShader. +enum BuiltinShaders +{ + VShader_MV = 0, + VShader_MVP = 1, + VShader_PostProcess = 2, + VShader_Count = 3, + + FShader_Solid = 0, + FShader_Gouraud = 1, + FShader_Texture = 2, + FShader_PostProcess = 3, + FShader_PostProcessWithChromAb = 4, + FShader_LitGouraud = 5, + FShader_LitTexture = 6, + FShader_Count +}; + + +enum MapFlags +{ + Map_Discard = 1, + Map_Read = 2, // do not use + Map_Unsynchronized = 4, // like D3D11_MAP_NO_OVERWRITE +}; + +// Buffer types used for uploading geometry & constants. +enum BufferUsage +{ + Buffer_Unknown = 0, + Buffer_Vertex = 1, + Buffer_Index = 2, + Buffer_Uniform = 4, + Buffer_TypeMask = 0xff, + Buffer_ReadOnly = 0x100, // Buffer must be created with Data(). +}; + +enum TextureFormat +{ + Texture_RGBA = 0x0100, + Texture_Depth = 0x8000, + Texture_TypeMask = 0xff00, + Texture_SamplesMask = 0x00ff, + Texture_RenderTarget = 0x10000, + Texture_GenMipmaps = 0x20000, +}; + +// Texture sampling modes. +enum SampleMode +{ + Sample_Linear = 0, + Sample_Nearest = 1, + Sample_Anisotropic = 2, + Sample_FilterMask = 3, + + Sample_Repeat = 0, + Sample_Clamp = 4, + Sample_ClampBorder = 8, // If unsupported Clamp is used instead. + Sample_AddressMask =12, + + Sample_Count =13, +}; + +// A vector with a dummy w component for alignment in uniform buffers (and for float colors). +// The w component is not used in any calculations. +struct Vector4f : public Vector3f +{ + float w; + + Vector4f() : w(1) {} + Vector4f(const Vector3f& v) : Vector3f(v), w(1) {} + Vector4f(float r, float g, float b, float a) : Vector3f(r,g,b), w(a) {} +}; + + +// Base class for vertex and pixel shaders. Stored in ShaderSet. +class Shader : public RefCountBase +{ + friend class ShaderSet; + +protected: + ShaderStage Stage; + +public: + Shader(ShaderStage s) : Stage(s) {} + virtual ~Shader() {} + + ShaderStage GetStage() const { return Stage; } + + virtual void Set(PrimitiveType) const { } + virtual void SetUniformBuffer(class Buffer* buffers, int i = 0) { OVR_UNUSED2(buffers, i); } + +protected: + virtual bool SetUniform(const char* name, int n, const float* v) { OVR_UNUSED3(name, n, v); return false; } +}; + + +// A group of shaders, one per stage. +// A ShaderSet is applied to a RenderDevice for rendering with a given fill. +class ShaderSet : public RefCountBase +{ + protected: + Ptr Shaders[Shader_Count]; + +public: + ShaderSet() { } + ~ShaderSet() { } + + virtual void SetShader(Shader *s) + { + Shaders[s->GetStage()] = s; + } + virtual void UnsetShader(int stage) + { + Shaders[stage] = NULL; + } + Shader* GetShader(int stage) { return Shaders[stage]; } + + virtual void Set(PrimitiveType prim) const + { + for (int i = 0; i < Shader_Count; i++) + if (Shaders[i]) + Shaders[i]->Set(prim); + } + + // Set a uniform (other than the standard matrices). It is undefined whether the + // uniforms from one shader occupy the same space as those in other shaders + // (unless a buffer is used, then each buffer is independent). + virtual bool SetUniform(const char* name, int n, const float* v) + { + bool result = 0; + for (int i = 0; i < Shader_Count; i++) + if (Shaders[i]) + result |= Shaders[i]->SetUniform(name, n, v); + + return result; + } + bool SetUniform1f(const char* name, float x) + { + const float v[] = {x}; + return SetUniform(name, 1, v); + } + bool SetUniform2f(const char* name, float x, float y) + { + const float v[] = {x,y}; + return SetUniform(name, 2, v); + } + bool SetUniform4f(const char* name, float x, float y, float z, float w = 1) + { + const float v[] = {x,y,z,w}; + return SetUniform(name, 4, v); + } + bool SetUniformv(const char* name, const Vector3f& v) + { + const float a[] = {v.x,v.y,v.z,1}; + return SetUniform(name, 4, a); + } + bool SetUniform4fv(const char* name, int n, const Vector4f* v) + { + return SetUniform(name, 4*n, &v[0].x); + } + virtual bool SetUniform4x4f(const char* name, const Matrix4f& m) + { + Matrix4f mt = m.Transposed(); + return SetUniform(name, 16, &mt.M[0][0]); + } +}; + + +// Fill combines a ShaderSet (vertex, pixel) with textures, if any. +// Every model has a fill. +class ShaderFill : public RefCountBase +{ + Ptr Shaders; + Ptr Textures[8]; + +public: + ShaderFill(ShaderSet* sh) : Shaders(sh) { } + ShaderFill(ShaderSet& sh) : Shaders(sh) { } + + ShaderSet* GetShaders() { return Shaders; } + + virtual void Set(PrimitiveType prim = Prim_Unknown) const; + virtual void SetTexture(int i, class Texture* tex) { if (i < 8) Textures[i] = tex; } +}; + + +// Buffer for vertex or index data. Some renderers require separate buffers, so that +// is recommended. Some renderers cannot have high-performance buffers which are readable, +// so reading in Map should not be relied on. +// +// Constraints on buffers, such as ReadOnly, are not enforced by the API but may result in +// rendering-system dependent undesirable behavior, such as terrible performance or unreported failure. +// +// Use of a buffer inconsistent with usage is also not checked by the API, but it may result in bad +// performance or even failure. +// +// Use the Data() function to set buffer data the first time, if possible (it may be faster). + +class Buffer : public RefCountBase +{ +public: + virtual ~Buffer() {} + + virtual size_t GetSize() = 0; + virtual void* Map(size_t start, size_t size, int flags = 0) = 0; + virtual bool Unmap(void *m) = 0; + + // Allocates a buffer, optionally filling it with data. + virtual bool Data(int use, const void* buffer, size_t size) = 0; +}; + +class Texture : public RefCountBase +{ +public: + virtual ~Texture() {} + + virtual int GetWidth() const = 0; + virtual int GetHeight() const = 0; + virtual int GetSamples() const { return 1; } + + virtual void SetSampleMode(int sm) = 0; + virtual void Set(int slot, ShaderStage stage = Shader_Fragment) const = 0; +}; + + + +//----------------------------------------------------------------------------------- + +// Node is a base class for geometry in a Scene, it contains base position +// and orientation data. +// Model and Container both derive from it. +// +class Node : public RefCountBase +{ + Vector3f Pos; + Quatf Rot; + + mutable Matrix4f Mat; + mutable bool MatCurrent; + +public: + Node() : Pos(Vector3f(0)), MatCurrent(1) { } + virtual ~Node() { } + + enum NodeType + { + Node_NonDisplay, + Node_Container, + Node_Model + }; + virtual NodeType GetType() const { return Node_NonDisplay; } + + const Vector3f& GetPosition() const { return Pos; } + const Quatf& GetOrientation() const { return Rot; } + void SetPosition(Vector3f p) { Pos = p; MatCurrent = 0; } + void SetOrientation(Quatf q) { Rot = q; MatCurrent = 0; } + + void Move(Vector3f p) { Pos += p; MatCurrent = 0; } + void Rotate(Quatf q) { Rot = q * Rot; MatCurrent = 0; } + + + // For testing only; causes Position an Orientation + void SetMatrix(const Matrix4f& m) + { + MatCurrent = true; + Mat = m; + } + + const Matrix4f& GetMatrix() const + { + if (!MatCurrent) + { + Mat = Rot; + Mat = Matrix4f::Translation(Pos) * Mat; + MatCurrent = 1; + } + return Mat; + } + + virtual void Render(const Matrix4f& ltw, RenderDevice* ren) { OVR_UNUSED2(ltw, ren); } +}; + + +// Vertex type; same format is used for all shapes for simplicity. +// Shapes are built by adding vertices to Model. +struct Vertex +{ + Vector3f Pos; + Color C; + float U, V; + Vector3f Norm; + + Vertex (const Vector3f& p, const Color& c = Color(64,0,0,255), + float u = 0, float v = 0, Vector3f n = Vector3f(1,0,0)) + : Pos(p), C(c), U(u), V(v), Norm(n) + {} + Vertex(float x, float y, float z, const Color& c = Color(64,0,0,255), + float u = 0, float v = 0) : Pos(x,y,z), C(c), U(u), V(v) + { } + + bool operator==(const Vertex& b) const + { + return Pos == b.Pos && C == b.C && U == b.U && V == b.V; + } +}; + +// LightingParams are stored in a uniform buffer, don't change it without fixing all renderers +// Scene contains a set of LightingParams that is uses for rendering. +struct LightingParams +{ + Vector4f Ambient; + Vector4f LightPos[8]; + Vector4f LightColor[8]; + float LightCount; + int Version; + + LightingParams() : LightCount(0), Version(0) {} + + + void Update(const Matrix4f& view, const Vector4f* SceneLightPos) + { + Version++; + for (int i = 0; i < LightCount; i++) + { + LightPos[i] = view.Transform(SceneLightPos[i]); + } + } + + void Set(ShaderSet* s) const + { + s->SetUniform4fv("Ambient", 1, &Ambient); + s->SetUniform1f("LightCount", LightCount); + s->SetUniform4fv("LightPos", (int)LightCount, LightPos); + s->SetUniform4fv("LightColor", (int)LightCount, LightColor); + } + +}; + +//----------------------------------------------------------------------------------- + +// Model is a triangular mesh with a fill that can be added to scene. +// +class Model : public Node +{ +public: + Array Vertices; + Array Indices; + PrimitiveType Type; + Ptr Fill; + bool Visible; + + // Some renderers will create these if they didn't exist before rendering. + // Currently they are not updated, so vertex data should not be changed after rendering. + Ptr VertexBuffer; + Ptr IndexBuffer; + + Model(PrimitiveType t = Prim_Triangles) : Type(t), Fill(NULL), Visible(true) { } + ~Model() { } + + PrimitiveType GetPrimType() const { return Type; } + + void SetVisible(bool visible) { Visible = visible; } + bool IsVisible() const { return Visible; } + + + // Node implementation. + virtual NodeType GetType() const { return Node_Model; } + virtual void Render(const Matrix4f& ltw, RenderDevice* ren); + + + // Returns the index next added vertex will have. + UInt16 GetNextVertexIndex() const + { + return (UInt16)Vertices.GetSize(); + } + + UInt16 AddVertex(const Vertex& v) + { + assert(!VertexBuffer && !IndexBuffer); + UInt16 index = (UInt16)Vertices.GetSize(); + Vertices.PushBack(v); + return index; + } + + void AddTriangle(UInt16 a, UInt16 b, UInt16 c) + { + Indices.PushBack(a); + Indices.PushBack(b); + Indices.PushBack(c); + } + + // Uses texture coordinates for uniform world scaling (must use a repeat sampler). + void AddSolidColorBox(float x1, float y1, float z1, + float x2, float y2, float z2, + Color c); +}; + + +// Container stores a collection of rendering nodes (Models or other containers). +class Container : public Node +{ +public: + Array > Nodes; + + Container() { } + ~Container() { } + + virtual NodeType GetType() const { return Node_Container; } + + virtual void Render(const Matrix4f& ltw, RenderDevice* ren); + + void Add(Node *n) { Nodes.PushBack(n); } + void Clear() { Nodes.Clear(); } +}; + + +// Scene combines a collection of model +class Scene +{ +public: + Container World; + Vector4f LightPos[8]; + LightingParams Lighting; + +public: + void Render(RenderDevice* ren, const Matrix4f& view); + + void SetAmbient(Vector4f color) + { + Lighting.Ambient = color; + } + + void AddLight(Vector3f pos, Vector4f color) + { + int n = (int)Lighting.LightCount; + OVR_ASSERT(n < 8); + LightPos[n] = pos; + Lighting.LightColor[n] = color; + Lighting.LightCount++; + } + + void Clear() + { + World.Clear(); + Lighting.Ambient = Vector4f(0.0f, 0.0f, 0.0f, 0.0f); + Lighting.LightCount = 0; + } + }; + + +//----------------------------------------------------------------------------------- + +// Post-processing type to apply to scene after rendering. PostProcess_Distortion +// applied distortion as described by DistortionConfig. +enum PostProcessType +{ + PostProcess_None, + PostProcess_Distortion +}; + +enum DisplayMode +{ + Display_Window = 0, + Display_Fullscreen = 1 +}; + + +// Rendering parameters used by RenderDevice::CreateDevice. +struct RendererParams +{ + int Multisample; + int Fullscreen; + + // Windows - Monitor name for fullscreen mode. + String MonitorName; + // MacOS + long DisplayId; + + RendererParams(int ms = 1) : Multisample(ms), Fullscreen(0) {} + + bool IsDisplaySet() const + { + return MonitorName.GetLength() || DisplayId; + } +}; + + + +//----------------------------------------------------------------------------------- +// ***** RenderDevice + +// Rendering device abstraction. +// Provides platform-independent part of implementation, with platform-specific +// part being in a separate derived class/file, such as D3D10::RenderDevice. +// +class RenderDevice : public RefCountBase +{ +protected: + int WindowWidth, WindowHeight; + RendererParams Params; + Viewport VP; + + Matrix4f Proj; + Ptr pTextVertexBuffer; + + // For rendering with lens warping + PostProcessType CurPostProcess; + Ptr pSceneColorTex; // Distortion render target, both eyes. + int SceneColorTexW; + int SceneColorTexH; + Ptr pPostProcessShader; + Ptr pFullScreenVertexBuffer; + float SceneRenderScale; + DistortionConfig Distortion; + + // For lighting on platforms with uniform buffers + Ptr LightingBuffer; + + void FinishScene1(); + +public: + enum CompareFunc + { + Compare_Always = 0, + Compare_Less = 1, + Compare_Greater = 2, + Compare_Count + }; + RenderDevice(); + virtual ~RenderDevice() { Shutdown(); } + + // This static function is implemented in each derived class + // to support a specific renderer type. + //static RenderDevice* CreateDevice(const RendererParams& rp, void* oswnd); + + + virtual void Init() {} + virtual void Shutdown() {} + virtual bool SetParams(const RendererParams&) { return 0; } + + const RendererParams& GetParams() const { return Params; } + + + // StereoParams apply Viewport, Projection and Distortion simultaneously, + // doing full configuration for one eye. + void ApplyStereoParams(const StereoEyeParams& params) + { + SetViewport(params.VP); + SetProjection(params.Projection); + if (params.pDistortion) + SetDistortionConfig(*params.pDistortion, params.Eye); + } + + virtual void SetViewport(const Viewport& vp); + void SetViewport(int x, int y, int w, int h) { SetViewport(Viewport(x,y,w,h)); } + + // PostProcess distortion + void SetSceneRenderScale(float ss); + + void SetDistortionConfig(const DistortionConfig& config, StereoEye eye = StereoEye_Left) + { + Distortion = config; + if (eye == StereoEye_Right) + Distortion.XCenterOffset = -Distortion.XCenterOffset; + } + + // Set viewport ignoring any adjustments used for the stereo mode. + virtual void SetRealViewport(const Viewport& vp) = 0; + + virtual void Clear(float r = 0, float g = 0, float b = 0, float a = 1, float depth = 1) = 0; + + virtual bool IsFullscreen() const { return Params.Fullscreen != Display_Window; } + virtual void Present() = 0; + // Waits for rendering to complete; important for reducing latency. + virtual void ForceFlushGPU() { } + + // Resources + virtual Buffer* CreateBuffer() { return NULL; } + virtual Texture* CreateTexture(int format, int width, int height, const void* data, int mipcount=1) + { OVR_UNUSED5(format,width,height,data, mipcount); return NULL; } + + + virtual ShaderSet* CreateShaderSet() { return new ShaderSet; } + virtual Shader* LoadBuiltinShader(ShaderStage stage, int shader) = 0; + + // Rendering + + // Begin drawing directly to the currently selected render target, no post-processing. + virtual void BeginRendering() {} + // Begin drawing the primary scene. This will have post-processing applied (if enabled) + // during FinishScene. + virtual void BeginScene(PostProcessType pp = PostProcess_None); + // Postprocess the scene and return to the screen render target. + virtual void FinishScene(); + + // Texture must have been created with Texture_RenderTarget. Use NULL for the default render target. + // NULL depth buffer means use an internal, temporary one. + virtual void SetRenderTarget(Texture* color, Texture* depth = NULL, Texture* stencil = NULL) + { OVR_UNUSED3(color, depth, stencil); } + virtual void SetDepthMode(bool enable, bool write, CompareFunc func = Compare_Less) = 0; + virtual void SetProjection(const Matrix4f& proj); + virtual void SetWorldUniforms(const Matrix4f& proj) = 0; + + // The data is not copied, it must remain valid until the end of the frame + virtual void SetLighting(const LightingParams* light); + + // The index 0 is reserved for non-buffer uniforms, and so cannot be used with this function. + virtual void SetCommonUniformBuffer(int i, Buffer* buffer) { OVR_UNUSED2(i, buffer); } + + virtual Matrix4f GetProjection() const { return Proj; } + + // This is a View matrix only, it will be combined with the projection matrix from SetProjection + virtual void Render(const Matrix4f& matrix, Model* model) = 0; + // offset is in bytes; indices can be null. + virtual void Render(const ShaderFill* fill, Buffer* vertices, Buffer* indices, + const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles) = 0; + + virtual ShaderFill *CreateSimpleFill() = 0; + ShaderFill * CreateTextureFill(Texture* tex); + + + // Don't call these directly, use App/Platform instead + virtual bool SetFullscreen(DisplayMode fullscreen) { OVR_UNUSED(fullscreen); return false; } + + + enum PostProcessShader + { + PostProcessShader_Distortion = 0, + PostProcessShader_DistortionAndChromAb = 1, + PostProcessShader_Count + }; + + PostProcessShader GetPostProcessShader() + { + return PostProcessShaderActive; + } + + void SetPostProcessShader(PostProcessShader newShader) + { + PostProcessShaderRequested = newShader; + } + +protected: + // Stereo & post-processing + virtual bool initPostProcessSupport(PostProcessType pptype); + +private: + PostProcessShader PostProcessShaderRequested; + PostProcessShader PostProcessShaderActive; +}; + +int GetNumMipLevels(int w, int h); + +// Filter an rgba image with a 2x2 box filter, for mipmaps. +// Image size must be a power of 2. +void FilterRgba2x2(const UByte* src, int w, int h, UByte* dest); + +}} // OVR::RenderTiny + +#endif diff --git a/Samples/OculusRoomTiny/RenderTiny_GL_Device.cpp b/Samples/OculusRoomTiny/RenderTiny_GL_Device.cpp new file mode 100644 index 0000000..197c62a --- /dev/null +++ b/Samples/OculusRoomTiny/RenderTiny_GL_Device.cpp @@ -0,0 +1,784 @@ +/************************************************************************************ + +Filename : RenderTiny_GL_Device.cpp +Content : RenderDevice implementation for OpenGL (tiny version) +Created : September 10, 2012 +Authors : Andrew Reisse, Artem Bolgar + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#include "RenderTiny_GL_Device.h" +#include "Kernel/OVR_Log.h" + +namespace OVR { namespace RenderTiny { namespace GL { + + + +static const char* StdVertexShaderSrc = + "uniform mat4 Proj;\n" + "uniform mat4 View;\n" + "attribute vec4 Position;\n" + "attribute vec4 Color;\n" + "attribute vec2 TexCoord;\n" + "attribute vec3 Normal;\n" + "varying vec4 oColor;\n" + "varying vec2 oTexCoord;\n" + "varying vec3 oNormal;\n" + "varying vec3 oVPos;\n" + "void main()\n" + "{\n" + " gl_Position = Proj * (View * Position);\n" + " oNormal = vec3(View * vec4(Normal,0));\n" + " oVPos = vec3(View * Position);\n" + " oTexCoord = TexCoord;\n" + " oColor = Color;\n" + "}\n"; + +static const char* DirectVertexShaderSrc = + "uniform mat4 View;\n" + "attribute vec4 Position;\n" + "attribute vec4 Color;\n" + "attribute vec2 TexCoord;\n" + "attribute vec3 Normal;\n" + "varying vec4 oColor;\n" + "varying vec2 oTexCoord;\n" + "varying vec3 oNormal;\n" + "void main()\n" + "{\n" + " gl_Position = View * Position;\n" + " oTexCoord = TexCoord;\n" + " oColor = Color;\n" + " oNormal = vec3(View * vec4(Normal,0));\n" + "}\n"; + +static const char* SolidFragShaderSrc = + "uniform vec4 Color;\n" + "void main()\n" + "{\n" + " gl_FragColor = Color;\n" + "}\n"; + +static const char* GouraudFragShaderSrc = + "varying vec4 oColor;\n" + "void main()\n" + "{\n" + " gl_FragColor = oColor;\n" + "}\n"; + +static const char* TextureFragShaderSrc = + "uniform sampler2D Texture0;\n" + "varying vec4 oColor;\n" + "varying vec2 oTexCoord;\n" + "void main()\n" + "{\n" + " gl_FragColor = oColor * texture2D(Texture0, oTexCoord);\n" + " if (gl_FragColor.a < 0.4)\n" + " discard;\n" + "}\n"; + +#define LIGHTING_COMMON \ + "uniform vec3 Ambient;\n" \ + "uniform vec4 LightPos[8];\n" \ + "uniform vec4 LightColor[8];\n" \ + "uniform float LightCount;\n" \ + "varying vec4 oColor;\n" \ + "varying vec2 oTexCoord;\n" \ + "varying vec3 oNormal;\n" \ + "varying vec3 oVPos;\n" \ + "vec4 DoLight()\n" \ + "{\n" \ + " vec3 norm = normalize(oNormal);\n" \ + " vec3 light = Ambient;\n" \ + " for (int i = 0; i < int(LightCount); i++)\n" \ + " {\n" \ + " vec3 ltp = (LightPos[i].xyz - oVPos);\n" \ + " float ldist = length(ltp);\n" \ + " ltp = normalize(ltp);\n" \ + " light += clamp(LightColor[i].rgb * oColor.rgb * (dot(norm, ltp) / ldist), 0.0,1.0);\n" \ + " }\n" \ + " return vec4(light, oColor.a);\n" \ + "}\n" + +static const char* LitSolidFragShaderSrc = + LIGHTING_COMMON + "void main()\n" + "{\n" + " gl_FragColor = DoLight() * oColor;\n" + "}\n"; + +static const char* LitTextureFragShaderSrc = + "uniform sampler2D Texture0;\n" + LIGHTING_COMMON + "void main()\n" + "{\n" + " gl_FragColor = DoLight() * texture2D(Texture0, oTexCoord);\n" + "}\n"; + +static const char* PostProcessVertexShaderSrc = + "uniform mat4 View;\n" + "uniform mat4 Texm;\n" + "attribute vec4 Position;\n" + "attribute vec2 TexCoord;\n" + "varying vec2 oTexCoord;\n" + "void main()\n" + "{\n" + " gl_Position = View * Position;\n" + " oTexCoord = vec2(Texm * vec4(TexCoord,0,1));\n" + " oTexCoord.y = 1.0-oTexCoord.y;\n" + "}\n"; + +static const char* PostProcessFragShaderSrc = + "uniform vec2 LensCenter;\n" + "uniform vec2 ScreenCenter;\n" + "uniform vec2 Scale;\n" + "uniform vec2 ScaleIn;\n" + "uniform vec4 HmdWarpParam;\n" + "uniform sampler2D Texture0;\n" + "varying vec2 oTexCoord;\n" + "\n" + "vec2 HmdWarp(vec2 in01)\n" + "{\n" + " vec2 theta = (in01 - LensCenter) * ScaleIn;\n" // Scales to [-1, 1] + " float rSq = theta.x * theta.x + theta.y * theta.y;\n" + " vec2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + " + " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n" + " return LensCenter + Scale * theta1;\n" + "}\n" + "void main()\n" + "{\n" + " vec2 tc = HmdWarp(oTexCoord);\n" + " if (!all(equal(clamp(tc, ScreenCenter-vec2(0.25,0.5), ScreenCenter+vec2(0.25,0.5)), tc)))\n" + " gl_FragColor = vec4(0);\n" + " else\n" + " gl_FragColor = texture2D(Texture0, tc);\n" + "}\n"; + +// Shader with lens distortion and chromatic aberration correction. +static const char* PostProcessFullFragShaderSrc = + "uniform vec2 LensCenter;\n" + "uniform vec2 ScreenCenter;\n" + "uniform vec2 Scale;\n" + "uniform vec2 ScaleIn;\n" + "uniform vec4 HmdWarpParam;\n" + "uniform vec4 ChromAbParam;\n" + "uniform sampler2D Texture0;\n" + "varying vec2 oTexCoord;\n" + "\n" + // Scales input texture coordinates for distortion. + // ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be + // larger due to aspect ratio. + "void main()\n" + "{\n" + " vec2 theta = (oTexCoord - LensCenter) * ScaleIn;\n" // Scales to [-1, 1] + " float rSq= theta.x * theta.x + theta.y * theta.y;\n" + " vec2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + " + " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n" + " \n" + " // Detect whether blue texture coordinates are out of range since these will scaled out the furthest.\n" + " vec2 thetaBlue = theta1 * (ChromAbParam.z + ChromAbParam.w * rSq);\n" + " vec2 tcBlue = LensCenter + Scale * thetaBlue;\n" + " if (!all(equal(clamp(tcBlue, ScreenCenter-vec2(0.25,0.5), ScreenCenter+vec2(0.25,0.5)), tcBlue)))\n" + " {\n" + " gl_FragColor = vec4(0);\n" + " return;\n" + " }\n" + " \n" + " // Now do blue texture lookup.\n" + " float blue = texture2D(Texture0, tcBlue).b;\n" + " \n" + " // Do green lookup (no scaling).\n" + " vec2 tcGreen = LensCenter + Scale * theta1;\n" + " vec4 center = texture2D(Texture0, tcGreen);\n" + " \n" + " // Do red scale and lookup.\n" + " vec2 thetaRed = theta1 * (ChromAbParam.x + ChromAbParam.y * rSq);\n" + " vec2 tcRed = LensCenter + Scale * thetaRed;\n" + " float red = texture2D(Texture0, tcRed).r;\n" + " \n" + " gl_FragColor = vec4(red, center.g, blue, 1);\n" + "}\n"; + +static const char* VShaderSrcs[VShader_Count] = +{ + DirectVertexShaderSrc, + StdVertexShaderSrc, + PostProcessVertexShaderSrc +}; +static const char* FShaderSrcs[FShader_Count] = +{ + SolidFragShaderSrc, + GouraudFragShaderSrc, + TextureFragShaderSrc, + PostProcessFragShaderSrc, + PostProcessFullFragShaderSrc, + LitSolidFragShaderSrc, + LitTextureFragShaderSrc +}; + + + +RenderDevice::RenderDevice(const RendererParams& p) +{ + for (int i = 0; i < VShader_Count; i++) + VertexShaders[i] = *new Shader(this, Shader_Vertex, VShaderSrcs[i]); + + for (int i = 0; i < FShader_Count; i++) + FragShaders[i] = *new Shader(this, Shader_Fragment, FShaderSrcs[i]); + + Ptr gouraudShaders = *new ShaderSet(); + gouraudShaders->SetShader(VertexShaders[VShader_MVP]); + gouraudShaders->SetShader(FragShaders[FShader_Gouraud]); + DefaultFill = *new ShaderFill(gouraudShaders); + + glGenFramebuffersEXT(1, &CurrentFbo); +} + +Shader *RenderDevice::LoadBuiltinShader(ShaderStage stage, int shader) +{ + switch (stage) + { + case Shader_Vertex: return VertexShaders[shader]; + case Shader_Fragment: return FragShaders[shader]; + default: + return NULL; + } +} + + +void RenderDevice::BeginRendering() +{ + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glFrontFace(GL_CW); + + glLineWidth(3.0f); + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +void RenderDevice::SetDepthMode(bool enable, bool write, CompareFunc func) +{ + if (enable) + { + glEnable(GL_DEPTH_TEST); + glDepthMask(write); + switch (func) + { + case Compare_Always: glDepthFunc(GL_ALWAYS); break; + case Compare_Less: glDepthFunc(GL_LESS); break; + case Compare_Greater: glDepthFunc(GL_GREATER); break; + default: assert(0); + } + } + else + glDisable(GL_DEPTH_TEST); +} + +void RenderDevice::SetRealViewport(const Viewport& vp) +{ + int wh; + if (CurRenderTarget) + wh = CurRenderTarget->Height; + else + wh = WindowHeight; + glViewport(vp.x, wh-vp.y-vp.h, vp.w, vp.h); + + glEnable(GL_SCISSOR_TEST); + glScissor(vp.x, wh-vp.y-vp.h, vp.w, vp.h); +} + +void RenderDevice::Clear(float r, float g, float b, float a, float depth) +{ + glClearColor(r,g,b,a); + glClearDepth(depth); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +} + +RBuffer* RenderDevice::GetDepthBuffer(int w, int h, int ms) +{ + for (unsigned i = 0; i < DepthBuffers.GetSize(); i++) + if (w == DepthBuffers[i]->Width && h == DepthBuffers[i]->Height)// && ms == DepthBuffers[i]->Samples) + return DepthBuffers[i]; + + //Ptr newDepth = *CreateTexture(Texture_Depth|Texture_RenderTarget|ms, w, h, NULL); + Ptr newDepth = *new RBuffer(GL_DEPTH24_STENCIL8, w, h); // combined depth stencil + DepthBuffers.PushBack(newDepth); + return newDepth.GetPtr(); +} + +void RenderDevice::SetRenderTarget(RenderTiny::Texture* color, RenderTiny::Texture*, RenderTiny::Texture* stencil) +{ + OVR_UNUSED(stencil); + + CurRenderTarget = (Texture*)color; + if (color == NULL) + { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + return; + } + //if (depth == NULL) + RBuffer* depth = GetDepthBuffer(color->GetWidth(), color->GetHeight(), 0); //CurRenderTarget->Samples); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, CurrentFbo); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ((Texture*)color)->TexId, 0); + if (depth) + //glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, ((Texture*)depth)->TexId, 0); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, ((RBuffer*)depth)->BufId); + else + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); + + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) + OVR_DEBUG_LOG(("framebuffer not complete: %x", status)); +} + + +void RenderDevice::SetWorldUniforms(const Matrix4f& proj) +{ + Proj = proj.Transposed(); +} + +void RenderDevice::SetTexture(RenderTiny::ShaderStage, int slot, const Texture* t) +{ + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_2D, ((Texture*)t)->TexId); + glActiveTexture(GL_TEXTURE0); +} + +Buffer* RenderDevice::CreateBuffer() +{ + return new Buffer(this); +} + +void RenderDevice::Render(const Matrix4f& matrix, Model* model) +{ + // Store data in buffers if not already + if (!model->VertexBuffer) + { + Ptr vb = *CreateBuffer(); + vb->Data(Buffer_Vertex, &model->Vertices[0], model->Vertices.GetSize() * sizeof(Vertex)); + model->VertexBuffer = vb; + } + if (!model->IndexBuffer) + { + Ptr ib = *CreateBuffer(); + ib->Data(Buffer_Index, &model->Indices[0], model->Indices.GetSize() * 2); + model->IndexBuffer = ib; + } + + Render(model->Fill ? (const ShaderFill*)model->Fill : (const ShaderFill*)DefaultFill, + model->VertexBuffer, model->IndexBuffer, + matrix, 0, (int)model->Indices.GetSize(), model->GetPrimType()); +} + +void RenderDevice::Render(const ShaderFill* fill, RenderTiny::Buffer* vertices, RenderTiny::Buffer* indices, + const Matrix4f& matrix, int offset, int count, PrimitiveType rprim) +{ + ShaderSet* shaders = (ShaderSet*) ((ShaderFill*)fill)->GetShaders(); + + GLenum prim; + switch (rprim) + { + case Prim_Triangles: + prim = GL_TRIANGLES; + break; + case Prim_Lines: + prim = GL_LINES; + break; + case Prim_TriangleStrip: + prim = GL_TRIANGLE_STRIP; + break; + default: + assert(0); + return; + } + + fill->Set(); + if (shaders->ProjLoc >= 0) + glUniformMatrix4fv(shaders->ProjLoc, 1, 0, &Proj.M[0][0]); + if (shaders->ViewLoc >= 0) + glUniformMatrix4fv(shaders->ViewLoc, 1, 0, &matrix.Transposed().M[0][0]); + + if (shaders->UsesLighting && Lighting->Version != shaders->LightingVer) + { + shaders->LightingVer = Lighting->Version; + Lighting->Set(shaders); + } + + glBindBuffer(GL_ARRAY_BUFFER, ((Buffer*)vertices)->GLBuffer); + for (int i = 0; i < 4; i++) + glEnableVertexAttribArray(i); + + glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(Vertex), (char*)offset + offsetof(Vertex, Pos)); + glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(Vertex), (char*)offset + offsetof(Vertex, C)); + glVertexAttribPointer(2, 2, GL_FLOAT, false, sizeof(Vertex), (char*)offset + offsetof(Vertex, U)); + glVertexAttribPointer(3, 3, GL_FLOAT, false, sizeof(Vertex), (char*)offset + offsetof(Vertex, Norm)); + + if (indices) + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ((Buffer*)indices)->GLBuffer); + glDrawElements(prim, count, GL_UNSIGNED_SHORT, NULL); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + else + { + glDrawArrays(prim, 0, count); + } + + for (int i = 0; i < 4; i++) + glDisableVertexAttribArray(i); +} + +void RenderDevice::SetLighting(const LightingParams* lt) +{ + Lighting = lt; +} + +Buffer::~Buffer() +{ + if (GLBuffer) + glDeleteBuffers(1, &GLBuffer); +} + +bool Buffer::Data(int use, const void* buffer, size_t size) +{ + switch (use & Buffer_TypeMask) + { + case Buffer_Index: Use = GL_ELEMENT_ARRAY_BUFFER; break; + default: Use = GL_ARRAY_BUFFER; break; + } + + if (!GLBuffer) + glGenBuffers(1, &GLBuffer); + + int mode = GL_DYNAMIC_DRAW; + if (use & Buffer_ReadOnly) + mode = GL_STATIC_DRAW; + + glBindBuffer(Use, GLBuffer); + glBufferData(Use, size, buffer, mode); + glBindBuffer(Use, 0); + return 1; +} + +void* Buffer::Map(size_t start, size_t size, int flags) +{ + int mode = GL_WRITE_ONLY; + //if (flags & Map_Unsynchronized) + // mode |= GL_MAP_UNSYNCHRONIZED; + + glBindBuffer(Use, GLBuffer); + void* v = glMapBuffer(Use, mode); + glBindBuffer(Use, 0); + return v; +} + +bool Buffer::Unmap(void*) +{ + glBindBuffer(Use, GLBuffer); + int r = glUnmapBuffer(Use); + glBindBuffer(Use, 0); + return r; +} + +bool Shader::Compile(const char* src) +{ + if (!GLShader) + GLShader = glCreateShader(GLStage()); + + glShaderSource(GLShader, 1, &src, 0); + glCompileShader(GLShader); + GLint r; + glGetShaderiv(GLShader, GL_COMPILE_STATUS, &r); + if (!r) + { + GLchar msg[1024]; + glGetShaderInfoLog(GLShader, sizeof(msg), 0, msg); + if (msg[0]) + OVR_DEBUG_LOG(("Compiling shader\n%s\nfailed: %s\n", src, msg)); + if (!r) + return 0; + } + return 1; +} + +ShaderSet::ShaderSet() +{ + Prog = glCreateProgram(); +} +ShaderSet::~ShaderSet() +{ + glDeleteProgram(Prog); +} + +bool ShaderSet::Link() +{ + glBindAttribLocation(Prog, 0, "Position"); + glBindAttribLocation(Prog, 1, "Color"); + glBindAttribLocation(Prog, 2, "TexCoord"); + glBindAttribLocation(Prog, 3, "Normal"); + + glLinkProgram(Prog); + GLint r; + glGetProgramiv(Prog, GL_LINK_STATUS, &r); + if (!r) + { + GLchar msg[1024]; + glGetProgramInfoLog(Prog, sizeof(msg), 0, msg); + OVR_DEBUG_LOG(("Linking shaders failed: %s\n", msg)); + if (!r) + return 0; + } + glUseProgram(Prog); + + UniformInfo.Clear(); + LightingVer = 0; + UsesLighting = 0; + GLuint i = 0; + for(;; i++) + { + GLsizei namelen; + GLint size = 0; + GLenum type; + GLchar name[32]; + glGetActiveUniform(Prog, i, sizeof(name), &namelen, &size, &type, name); + if (size) + { + int l = glGetUniformLocation(Prog, name); + char *np = name; + while (*np) + { + if (*np == '[') + *np = 0; + np++; + } + Uniform u; + u.Name = name; + u.Location = l; + u.Size = size; + switch (type) + { + case GL_FLOAT: u.Type = 1; break; + case GL_FLOAT_VEC2: u.Type = 2; break; + case GL_FLOAT_VEC3: u.Type = 3; break; + case GL_FLOAT_VEC4: u.Type = 4; break; + case GL_FLOAT_MAT4: u.Type = 16; break; + default: + continue; + } + UniformInfo.PushBack(u); + if (!strcmp(name, "LightCount")) + UsesLighting = 1; + } + else + break; + } + + ProjLoc = glGetUniformLocation(Prog, "Proj"); + ViewLoc = glGetUniformLocation(Prog, "View"); + for (int i = 0; i < 8; i++) + { + char texv[32]; + sprintf(texv, "Texture%d", i); + TexLoc[i] = glGetUniformLocation(Prog, texv); + if (TexLoc[i] < 0) + break; + + glUniform1i(TexLoc[i], i); + } + if (UsesLighting) + OVR_ASSERT(ProjLoc >= 0 && ViewLoc >= 0); + return 1; +} + +void ShaderSet::Set(PrimitiveType) const +{ + glUseProgram(Prog); +} + +bool ShaderSet::SetUniform(const char* name, int n, const float* v) +{ + for (int i = 0; i < UniformInfo.GetSize(); i++) + if (!strcmp(UniformInfo[i].Name.ToCStr(), name)) + { + OVR_ASSERT(UniformInfo[i].Location >= 0); + glUseProgram(Prog); + switch (UniformInfo[i].Type) + { + case 1: glUniform1fv(UniformInfo[i].Location, n, v); break; + case 2: glUniform2fv(UniformInfo[i].Location, n/2, v); break; + case 3: glUniform3fv(UniformInfo[i].Location, n/3, v); break; + case 4: glUniform4fv(UniformInfo[i].Location, n/4, v); break; + default: OVR_ASSERT(0); + } + return 1; + } + + OVR_DEBUG_LOG(("Warning: uniform %s not present in selected shader", name)); + return 0; +} + +bool ShaderSet::SetUniform4x4f(const char* name, const Matrix4f& m) +{ + for (int i = 0; i < UniformInfo.GetSize(); i++) + if (!strcmp(UniformInfo[i].Name.ToCStr(), name)) + { + glUseProgram(Prog); + glUniformMatrix4fv(UniformInfo[i].Location, 1, 1, &m.M[0][0]); + return 1; + } + + OVR_DEBUG_LOG(("Warning: uniform %s not present in selected shader", name)); + return 0; +} + +Texture::Texture(RenderDevice* r, int w, int h) : Ren(r), Width(w), Height(h) +{ + glGenTextures(1, &TexId); +} + +Texture::~Texture() +{ + if (TexId) + glDeleteTextures(1, &TexId); +} + +void Texture::Set(int slot, RenderTiny::ShaderStage stage) const +{ + Ren->SetTexture(stage, slot, this); +} + +void Texture::SetSampleMode(int sm) +{ + glBindTexture(GL_TEXTURE_2D, TexId); + switch (sm & Sample_FilterMask) + { + case Sample_Linear: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0); + break; + + case Sample_Anisotropic: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8); + break; + + case Sample_Nearest: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0); + break; + } + + switch (sm & Sample_AddressMask) + { + case Sample_Repeat: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + break; + + case Sample_Clamp: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + break; + + case Sample_ClampBorder: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + break; + } + glBindTexture(GL_TEXTURE_2D, 0); +} + +Texture* RenderDevice::CreateTexture(int format, int width, int height, const void* data, int mipcount) +{ + GLenum glformat, gltype = GL_UNSIGNED_BYTE; + switch(format & Texture_TypeMask) + { + case Texture_RGBA: glformat = GL_RGBA; break; + case Texture_Depth: glformat = GL_DEPTH; gltype = GL_DEPTH_COMPONENT; break; + default: + return NULL; + } + Texture* NewTex = new Texture(this, width, height); + glBindTexture(GL_TEXTURE_2D, NewTex->TexId); + glGetError(); + + glTexImage2D(GL_TEXTURE_2D, 0, glformat, width, height, 0, glformat, gltype, data); + OVR_ASSERT(!glGetError()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if (format == (Texture_RGBA|Texture_GenMipmaps)) // not render target + { + int srcw = width, srch = height; + int level = 0; + UByte* mipmaps = NULL; + do + { + level++; + int mipw = srcw >> 1; if (mipw < 1) mipw = 1; + int miph = srch >> 1; if (miph < 1) miph = 1; + if (mipmaps == NULL) + mipmaps = (UByte*)OVR_ALLOC(mipw * miph * 4); + FilterRgba2x2(level == 1 ? (const UByte*)data : mipmaps, srcw, srch, mipmaps); + glTexImage2D(GL_TEXTURE_2D, level, glformat, mipw, miph, 0, glformat, gltype, mipmaps); + OVR_ASSERT(!glGetError()); + srcw = mipw; + srch = miph; + } while (srcw > 1 || srch > 1); + if (mipmaps) + OVR_FREE(mipmaps); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level); + OVR_ASSERT(!glGetError()); + } + else + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipcount-1); + OVR_ASSERT(!glGetError()); + } + + OVR_ASSERT(!glGetError()); + glBindTexture(GL_TEXTURE_2D, 0); + return NewTex; +} + +RBuffer::RBuffer(GLenum format, GLint w, GLint h) +{ + Width = w; + Height = h; + glGenRenderbuffersEXT(1, &BufId); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, BufId); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format, w, h); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); +} + +RBuffer::~RBuffer() +{ + glDeleteRenderbuffersEXT(1, &BufId); +} + +}}} diff --git a/Samples/OculusRoomTiny/RenderTiny_GL_Device.h b/Samples/OculusRoomTiny/RenderTiny_GL_Device.h new file mode 100644 index 0000000..feb504f --- /dev/null +++ b/Samples/OculusRoomTiny/RenderTiny_GL_Device.h @@ -0,0 +1,228 @@ +/************************************************************************************ + +Filename : RenderTiny_GL_Device.h +Content : RenderDevice implementation header for OpenGL (tiny version) +Created : September 10, 2012 +Authors : Andrew Reisse, Artem Bolgar + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef OVR_Render_GL_Device_h +#define OVR_Render_GL_Device_h + +#include "RenderTiny_Device.h" + +#if defined(OVR_OS_WIN32) +#include +#endif + +#if defined(OVR_OS_MAC) +#include +#include +#else +#define GL_GLEXT_PROTOTYPES +#include +#include +#endif + +namespace OVR { namespace RenderTiny { namespace GL { + +class RenderDevice; + +class Buffer : public RenderTiny::Buffer +{ +public: + RenderDevice* Ren; + size_t Size; + GLenum Use; + GLuint GLBuffer; + +public: + Buffer(RenderDevice* r) : Ren(r), Size(0), Use(0), GLBuffer(0) {} + ~Buffer(); + + GLuint GetBuffer() { return GLBuffer; } + + virtual size_t GetSize() { return Size; } + virtual void* Map(size_t start, size_t size, int flags = 0); + virtual bool Unmap(void *m); + virtual bool Data(int use, const void* buffer, size_t size); +}; + +class Texture : public RenderTiny::Texture +{ +public: + RenderDevice* Ren; + GLuint TexId; + int Width, Height; + + Texture(RenderDevice* r, int w, int h); + ~Texture(); + + virtual int GetWidth() const { return Width; } + virtual int GetHeight() const { return Height; } + + virtual void SetSampleMode(int); + + virtual void Set(int slot, ShaderStage stage = Shader_Fragment) const; +}; + +class Shader : public RenderTiny::Shader +{ +public: + GLuint GLShader; + + Shader(RenderDevice*, ShaderStage st, GLuint s) : RenderTiny::Shader(st), GLShader(s) {} + Shader(RenderDevice*, ShaderStage st, const char* src) : RenderTiny::Shader(st), GLShader(0) + { + Compile(src); + } + ~Shader() + { + if (GLShader) + glDeleteShader(GLShader); + } + bool Compile(const char* src); + + GLenum GLStage() const + { + switch (Stage) + { + default: OVR_ASSERT(0); return GL_NONE; + case Shader_Vertex: return GL_VERTEX_SHADER; + case Shader_Fragment: return GL_FRAGMENT_SHADER; + } + } + + //void Set(PrimitiveType prim) const; + //void SetUniformBuffer(Render::Buffer* buffers, int i = 0); +}; + +class ShaderSet : public RenderTiny::ShaderSet +{ +public: + GLuint Prog; + + struct Uniform + { + String Name; + int Location, Size; + int Type; // currently number of floats in vector + }; + Array UniformInfo; + + int ProjLoc, ViewLoc; + int TexLoc[8]; + bool UsesLighting; + int LightingVer; + + ShaderSet(); + ~ShaderSet(); + + virtual void SetShader(RenderTiny::Shader *s) + { + Shaders[s->GetStage()] = s; + Shader* gls = (Shader*)s; + glAttachShader(Prog, gls->GLShader); + if (Shaders[Shader_Vertex] && Shaders[Shader_Fragment]) + Link(); + } + virtual void UnsetShader(int stage) + { + Shader* gls = (Shader*)(RenderTiny::Shader*)Shaders[stage]; + if (gls) + glDetachShader(Prog, gls->GLShader); + Shaders[stage] = NULL; + Link(); + } + + virtual void Set(PrimitiveType prim) const; + + // Set a uniform (other than the standard matrices). It is undefined whether the + // uniforms from one shader occupy the same space as those in other shaders + // (unless a buffer is used, then each buffer is independent). + virtual bool SetUniform(const char* name, int n, const float* v); + virtual bool SetUniform4x4f(const char* name, const Matrix4f& m); + + bool Link(); +}; + + class RBuffer : public RefCountBase +{ + public: + int Width, Height; + GLuint BufId; + + RBuffer(GLenum format, GLint w, GLint h); + ~RBuffer(); +}; + +class RenderDevice : public RenderTiny::RenderDevice +{ + Ptr VertexShaders[VShader_Count]; + Ptr FragShaders[FShader_Count]; + + Ptr DefaultFill; + + Matrix4f Proj; + + Ptr CurRenderTarget; + Array > DepthBuffers; + GLuint CurrentFbo; + + const LightingParams* Lighting; + + +public: + RenderDevice(const RendererParams& p); + + virtual void SetRealViewport(const Viewport& vp); + + //virtual void SetScissor(int x, int y, int w, int h); + + virtual void Clear(float r = 0, float g = 0, float b = 0, float a = 1, float depth = 1); + virtual void Rect(float left, float top, float right, float bottom) { OVR_UNUSED4(left,top,right,bottom); } + + virtual void BeginRendering(); + virtual void SetDepthMode(bool enable, bool write, CompareFunc func = Compare_Less); + virtual void SetWorldUniforms(const Matrix4f& proj); + + RBuffer* GetDepthBuffer(int w, int h, int ms); + + virtual void SetRenderTarget(RenderTiny::Texture* color, + RenderTiny::Texture* depth = NULL, RenderTiny::Texture* stencil = NULL); + + virtual void SetLighting(const LightingParams* lt); + + virtual void Render(const Matrix4f& matrix, Model* model); + virtual void Render(const ShaderFill* fill, RenderTiny::Buffer* vertices, RenderTiny::Buffer* indices, + const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles); + + virtual Buffer* CreateBuffer(); + virtual Texture* CreateTexture(int format, int width, int height, const void* data, int mipcount=1); + virtual ShaderSet* CreateShaderSet() { return new ShaderSet; } + + virtual ShaderFill *CreateSimpleFill() { return DefaultFill; } + + virtual Shader *LoadBuiltinShader(ShaderStage stage, int shader); + + void SetTexture(RenderTiny::ShaderStage, int slot, const Texture* t); +}; + +}}} + +#endif diff --git a/Samples/OculusRoomTiny/Win32_OculusRoomTiny.cpp b/Samples/OculusRoomTiny/Win32_OculusRoomTiny.cpp new file mode 100644 index 0000000..2e6dd3e --- /dev/null +++ b/Samples/OculusRoomTiny/Win32_OculusRoomTiny.cpp @@ -0,0 +1,712 @@ +/************************************************************************************ + +Filename : Win32_OculusRoomTiny.cpp +Content : First-person view test application for Oculus Rift +Created : October 4, 2012 +Authors : Michael Antonov, Andrew Reisse + +Copyright : Copyright 2012 Oculus, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#include "Win32_OculusRoomTiny.h" +#include "RenderTiny_D3D1X_Device.h" + +//------------------------------------------------------------------------------------- +// ***** OculusRoomTiny Class + +// Static pApp simplifies routing the window function. +OculusRoomTinyApp* OculusRoomTinyApp::pApp = 0; + + +OculusRoomTinyApp::OculusRoomTinyApp(HINSTANCE hinst) + : pRender(0), + LastUpdate(0), + + // Win32 + hWnd(NULL), + hInstance(hinst), Quit(0), MouseCaptured(true), + hXInputModule(0), pXInputGetState(0), + + // Initial location + EyePos(0.0f, 1.6f, -5.0f), + EyeYaw(YawInitial), EyePitch(0), EyeRoll(0), + LastSensorYaw(0), + SConfig(), + PostProcess(PostProcess_Distortion), + ShiftDown(false), + ControlDown(false) +{ + pApp = this; + + Width = 1280; + Height = 800; + + StartupTicks = OVR::Timer::GetTicks(); + LastPadPacketNo = 0; + + MoveForward = MoveBack = MoveLeft = MoveRight = 0; + GamepadMove = Vector3f(0); + GamepadRotate = Vector3f(0); +} + +OculusRoomTinyApp::~OculusRoomTinyApp() +{ + RemoveHandlerFromDevices(); + pSensor.Clear(); + pHMD.Clear(); + destroyWindow(); + pApp = 0; +} + + +int OculusRoomTinyApp::OnStartup(const char* args) +{ + OVR_UNUSED(args); + + + // *** Oculus HMD & Sensor Initialization + + // Create DeviceManager and first available HMDDevice from it. + // Sensor object is created from the HMD, to ensure that it is on the + // correct device. + + pManager = *DeviceManager::Create(); + + // We'll handle it's messages in this case. + pManager->SetMessageHandler(this); + + + int detectionResult = IDCONTINUE; + const char* detectionMessage; + + do + { + // Release Sensor/HMD in case this is a retry. + pSensor.Clear(); + pHMD.Clear(); + RenderParams.MonitorName.Clear(); + + pHMD = *pManager->EnumerateDevices().CreateDevice(); + if (pHMD) + { + pSensor = *pHMD->GetSensor(); + + // This will initialize HMDInfo with information about configured IPD, + // screen size and other variables needed for correct projection. + // We pass HMD DisplayDeviceName into the renderer to select the + // correct monitor in full-screen mode. + if (pHMD->GetDeviceInfo(&HMDInfo)) + { + RenderParams.MonitorName = HMDInfo.DisplayDeviceName; + RenderParams.DisplayId = HMDInfo.DisplayId; + SConfig.SetHMDInfo(HMDInfo); + } + } + else + { + // If we didn't detect an HMD, try to create the sensor directly. + // This is useful for debugging sensor interaction; it is not needed in + // a shipping app. + pSensor = *pManager->EnumerateDevices().CreateDevice(); + } + + + // If there was a problem detecting the Rift, display appropriate message. + detectionResult = IDCONTINUE; + + if (!pHMD && !pSensor) + detectionMessage = "Oculus Rift not detected."; + else if (!pHMD) + detectionMessage = "Oculus Sensor detected; HMD Display not detected."; + else if (!pSensor) + detectionMessage = "Oculus HMD Display detected; Sensor not detected."; + else if (HMDInfo.DisplayDeviceName[0] == '\0') + detectionMessage = "Oculus Sensor detected; HMD display EDID not detected."; + else + detectionMessage = 0; + + if (detectionMessage) + { + String messageText(detectionMessage); + messageText += "\n\n" + "Press 'Try Again' to run retry detection.\n" + "Press 'Continue' to run full-screen anyway."; + + detectionResult = ::MessageBoxA(0, messageText.ToCStr(), "Oculus Rift Detection", + MB_CANCELTRYCONTINUE|MB_ICONWARNING); + + if (detectionResult == IDCANCEL) + return 1; + } + + } while (detectionResult != IDCONTINUE); + + + if (HMDInfo.HResolution > 0) + { + Width = HMDInfo.HResolution; + Height = HMDInfo.VResolution; + } + + + if (!setupWindow()) + return 1; + + if (pSensor) + { + // We need to attach sensor to SensorFusion object for it to receive + // body frame messages and update orientation. SFusion.GetOrientation() + // is used in OnIdle() to orient the view. + SFusion.AttachToSensor(pSensor); + SFusion.SetDelegateMessageHandler(this); + SFusion.SetPredictionEnabled(true); + } + + + // *** Initialize Rendering + + // Enable multi-sampling by default. + RenderParams.Multisample = 4; + RenderParams.Fullscreen = true; + + // Setup Graphics. + pRender = *RenderTiny::D3D10::RenderDevice::CreateDevice(RenderParams, (void*)hWnd); + if (!pRender) + return 1; + + + // *** Configure Stereo settings. + + SConfig.SetFullViewport(Viewport(0,0, Width, Height)); + SConfig.SetStereoMode(Stereo_LeftRight_Multipass); + + // Configure proper Distortion Fit. + // For 7" screen, fit to touch left side of the view, leaving a bit of invisible + // screen on the top (saves on rendering cost). + // For smaller screens (5.5"), fit to the top. + if (HMDInfo.HScreenSize > 0.0f) + { + if (HMDInfo.HScreenSize > 0.140f) // 7" + SConfig.SetDistortionFitPointVP(-1.0f, 0.0f); + else + SConfig.SetDistortionFitPointVP(0.0f, 1.0f); + } + + pRender->SetSceneRenderScale(SConfig.GetDistortionScale()); + + SConfig.Set2DAreaFov(DegreeToRad(85.0f)); + + + // *** Populate Room Scene + + // This creates lights and models. + PopulateRoomScene(&Scene, pRender); + + + LastUpdate = GetAppTime(); + return 0; +} + +void OculusRoomTinyApp::OnMessage(const Message& msg) +{ + if (msg.Type == Message_DeviceAdded && msg.pDevice == pManager) + { + LogText("DeviceManager reported device added.\n"); + } + else if (msg.Type == Message_DeviceRemoved && msg.pDevice == pManager) + { + LogText("DeviceManager reported device removed.\n"); + } + else if (msg.Type == Message_DeviceAdded && msg.pDevice == pSensor) + { + LogText("Sensor reported device added.\n"); + } + else if (msg.Type == Message_DeviceRemoved && msg.pDevice == pSensor) + { + LogText("Sensor reported device removed.\n"); + } +} + + +void OculusRoomTinyApp::OnGamepad(float padLx, float padLy, float padRx, float padRy) +{ + GamepadMove = Vector3f(padLx * padLx * (padLx > 0 ? 1 : -1), + 0, + padLy * padLy * (padLy > 0 ? -1 : 1)); + GamepadRotate = Vector3f(2 * padRx, -2 * padRy, 0); +} + +void OculusRoomTinyApp::OnMouseMove(int x, int y, int modifiers) +{ + OVR_UNUSED(modifiers); + + // Mouse motion here is always relative. + int dx = x, dy = y; + const float maxPitch = ((3.1415f/2)*0.98f); + + // Apply to rotation. Subtract for right body frame rotation, + // since yaw rotation is positive CCW when looking down on XZ plane. + EyeYaw -= (Sensitivity * dx)/ 360.0f; + + if (!pSensor) + { + EyePitch -= (Sensitivity * dy)/ 360.0f; + + if (EyePitch > maxPitch) + EyePitch = maxPitch; + if (EyePitch < -maxPitch) + EyePitch = -maxPitch; + } +} + +void OculusRoomTinyApp::OnKey(unsigned vk, bool down) +{ + switch (vk) + { + case 'Q': + if (down && ControlDown) + Quit = true; + break; + case VK_ESCAPE: + if (!down) + Quit = true; + break; + + // Handle player movement keys. + // We just update movement state here, while the actual translation is done in OnIdle() + // based on time. + case 'W': MoveForward = down ? (MoveForward | 1) : (MoveForward & ~1); break; + case 'S': MoveBack = down ? (MoveBack | 1) : (MoveBack & ~1); break; + case 'A': MoveLeft = down ? (MoveLeft | 1) : (MoveLeft & ~1); break; + case 'D': MoveRight = down ? (MoveRight | 1) : (MoveRight & ~1); break; + case VK_UP: MoveForward = down ? (MoveForward | 2) : (MoveForward & ~2); break; + case VK_DOWN: MoveBack = down ? (MoveBack | 2) : (MoveBack & ~2); break; + + case 'R': + SFusion.Reset(); + break; + + case 'P': + if (down) + { + // Toggle chromatic aberration correction on/off. + RenderDevice::PostProcessShader shader = pRender->GetPostProcessShader(); + + if (shader == RenderDevice::PostProcessShader_Distortion) + { + pRender->SetPostProcessShader(RenderDevice::PostProcessShader_DistortionAndChromAb); + } + else if (shader == RenderDevice::PostProcessShader_DistortionAndChromAb) + { + pRender->SetPostProcessShader(RenderDevice::PostProcessShader_Distortion); + } + else + OVR_ASSERT(false); + } + break; + + // Switch rendering modes/distortion. + case VK_F1: + SConfig.SetStereoMode(Stereo_None); + PostProcess = PostProcess_None; + break; + case VK_F2: + SConfig.SetStereoMode(Stereo_LeftRight_Multipass); + PostProcess = PostProcess_None; + break; + case VK_F3: + SConfig.SetStereoMode(Stereo_LeftRight_Multipass); + PostProcess = PostProcess_Distortion; + break; + + // Stereo IPD adjustments, in meter (default IPD is 64mm). + case VK_OEM_PLUS: + case VK_INSERT: + if (down) + SConfig.SetIPD(SConfig.GetIPD() + 0.0005f * (ShiftDown ? 5.0f : 1.0f)); + break; + case VK_OEM_MINUS: + case VK_DELETE: + if (down) + SConfig.SetIPD(SConfig.GetIPD() - 0.0005f * (ShiftDown ? 5.0f : 1.0f)); + break; + + // Holding down Shift key accelerates adjustment velocity. + case VK_SHIFT: + ShiftDown = down; + break; + case VK_CONTROL: + ControlDown = down; + break; + } +} + + +void OculusRoomTinyApp::OnIdle() +{ + double curtime = GetAppTime(); + float dt = float(curtime - LastUpdate); + LastUpdate = curtime; + + + // Handle Sensor motion. + // We extract Yaw, Pitch, Roll instead of directly using the orientation + // to allow "additional" yaw manipulation with mouse/controller. + if (pSensor) + { + Quatf hmdOrient = SFusion.GetOrientation(); + float yaw = 0.0f; + + hmdOrient.GetEulerAngles(&yaw, &EyePitch, &EyeRoll); + + EyeYaw += (yaw - LastSensorYaw); + LastSensorYaw = yaw; + } + + + // Gamepad rotation. + EyeYaw -= GamepadRotate.x * dt; + + if (!pSensor) + { + // Allow gamepad to look up/down, but only if there is no Rift sensor. + EyePitch -= GamepadRotate.y * dt; + + const float maxPitch = ((3.1415f/2)*0.98f); + if (EyePitch > maxPitch) + EyePitch = maxPitch; + if (EyePitch < -maxPitch) + EyePitch = -maxPitch; + } + + // Handle keyboard movement. + // This translates EyePos based on Yaw vector direction and keys pressed. + // Note that Pitch and Roll do not affect movement (they only affect view). + if (MoveForward || MoveBack || MoveLeft || MoveRight) + { + Vector3f localMoveVector(0,0,0); + Matrix4f yawRotate = Matrix4f::RotationY(EyeYaw); + + if (MoveForward) + localMoveVector = ForwardVector; + else if (MoveBack) + localMoveVector = -ForwardVector; + + if (MoveRight) + localMoveVector += RightVector; + else if (MoveLeft) + localMoveVector -= RightVector; + + // Normalize vector so we don't move faster diagonally. + localMoveVector.Normalize(); + Vector3f orientationVector = yawRotate.Transform(localMoveVector); + orientationVector *= MoveSpeed * dt * (ShiftDown ? 3.0f : 1.0f); + + EyePos += orientationVector; + } + + else if (GamepadMove.LengthSq() > 0) + { + Matrix4f yawRotate = Matrix4f::RotationY(EyeYaw); + Vector3f orientationVector = yawRotate.Transform(GamepadMove); + orientationVector *= MoveSpeed * dt; + EyePos += orientationVector; + } + + + // Rotate and position View Camera, using YawPitchRoll in BodyFrame coordinates. + // + Matrix4f rollPitchYaw = Matrix4f::RotationY(EyeYaw) * Matrix4f::RotationX(EyePitch) * + Matrix4f::RotationZ(EyeRoll); + Vector3f up = rollPitchYaw.Transform(UpVector); + Vector3f forward = rollPitchYaw.Transform(ForwardVector); + + + // Minimal head modelling. + float headBaseToEyeHeight = 0.15f; // Vertical height of eye from base of head + float headBaseToEyeProtrusion = 0.09f; // Distance forward of eye from base of head + + Vector3f eyeCenterInHeadFrame(0.0f, headBaseToEyeHeight, -headBaseToEyeProtrusion); + Vector3f shiftedEyePos = EyePos + rollPitchYaw.Transform(eyeCenterInHeadFrame); + shiftedEyePos.y -= eyeCenterInHeadFrame.y; // Bring the head back down to original height + + View = Matrix4f::LookAtRH(shiftedEyePos, shiftedEyePos + forward, up); + + // This is what transformation would be without head modeling. + // View = Matrix4f::LookAtRH(EyePos, EyePos + forward, up); + + switch(SConfig.GetStereoMode()) + { + case Stereo_None: + Render(SConfig.GetEyeRenderParams(StereoEye_Center)); + break; + + case Stereo_LeftRight_Multipass: + Render(SConfig.GetEyeRenderParams(StereoEye_Left)); + Render(SConfig.GetEyeRenderParams(StereoEye_Right)); + break; + } + + pRender->Present(); + // Force GPU to flush the scene, resulting in the lowest possible latency. + pRender->ForceFlushGPU(); +} + + +// Render the scene for one eye. +void OculusRoomTinyApp::Render(const StereoEyeParams& stereo) +{ + pRender->BeginScene(PostProcess); + + // Apply Viewport/Projection for the eye. + pRender->ApplyStereoParams(stereo); + pRender->Clear(); + pRender->SetDepthMode(true, true); + + Scene.Render(pRender, stereo.ViewAdjust * View); + + pRender->FinishScene(); +} + + +//------------------------------------------------------------------------------------- +// ***** Win32-Specific Logic + +bool OculusRoomTinyApp::setupWindow() +{ + + WNDCLASS wc; + memset(&wc, 0, sizeof(wc)); + wc.lpszClassName = L"OVRAppWindow"; + wc.style = CS_OWNDC; + wc.lpfnWndProc = systemWindowProc; + wc.cbWndExtra = sizeof(OculusRoomTinyApp*); + RegisterClass(&wc); + + + RECT winSize = { 0, 0, Width, Height }; + AdjustWindowRect(&winSize, WS_POPUP, false); + hWnd = CreateWindowA("OVRAppWindow", "OculusRoomTiny", WS_POPUP|WS_VISIBLE, + HMDInfo.DesktopX, HMDInfo.DesktopY, + winSize.right-winSize.left, winSize.bottom-winSize.top, + NULL, NULL, hInstance, (LPVOID)this); + + + // Initialize Window center in screen coordinates + POINT center = { Width / 2, Height / 2 }; + ::ClientToScreen(hWnd, ¢er); + WindowCenter = center; + + + return (hWnd != NULL); +} + +void OculusRoomTinyApp::destroyWindow() +{ + pRender.Clear(); + + if (hWnd) + { + // Release window resources. + ::DestroyWindow(hWnd); + UnregisterClass(L"OVRAppWindow", hInstance); + hWnd = 0; + Width = Height = 0; + } +} + + +LRESULT CALLBACK OculusRoomTinyApp::systemWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) +{ + if (msg == WM_NCCREATE) + pApp->hWnd = hwnd; + return pApp->windowProc(msg, wp, lp); +} + +void OculusRoomTinyApp::giveUsFocus(bool setFocus) +{ + if (setFocus) + { + ::SetCursorPos(WindowCenter.x, WindowCenter.y); + + MouseCaptured = true; + ::SetCapture(hWnd); + ::ShowCursor(FALSE); + + } + else + { + MouseCaptured = false; + ::ReleaseCapture(); + ::ShowCursor(TRUE); + } +} + +LRESULT OculusRoomTinyApp::windowProc(UINT msg, WPARAM wp, LPARAM lp) +{ + switch (msg) + { + case WM_MOUSEMOVE: + { + if (MouseCaptured) + { + // Convert mouse motion to be relative (report the offset and re-center). + POINT newPos = { LOWORD(lp), HIWORD(lp) }; + ::ClientToScreen(hWnd, &newPos); + if ((newPos.x == WindowCenter.x) && (newPos.y == WindowCenter.y)) + break; + ::SetCursorPos(WindowCenter.x, WindowCenter.y); + + LONG dx = newPos.x - WindowCenter.x; + LONG dy = newPos.y - WindowCenter.y; + pApp->OnMouseMove(dx, dy, 0); + } + } + break; + + case WM_MOVE: + { + RECT r; + GetClientRect(hWnd, &r); + WindowCenter.x = r.right/2; + WindowCenter.y = r.bottom/2; + ::ClientToScreen(hWnd, &WindowCenter); + } + break; + + case WM_KEYDOWN: + OnKey((unsigned)wp, true); + break; + case WM_KEYUP: + OnKey((unsigned)wp, false); + break; + + case WM_SETFOCUS: + giveUsFocus(true); + break; + + case WM_KILLFOCUS: + giveUsFocus(false); + break; + + case WM_CREATE: + // Hack to position mouse in fullscreen window shortly after startup. + SetTimer(hWnd, 0, 100, NULL); + break; + + case WM_TIMER: + KillTimer(hWnd, 0); + giveUsFocus(true); + break; + + case WM_QUIT: + case WM_CLOSE: + Quit = true; + return 0; + } + + return DefWindowProc(hWnd, msg, wp, lp); +} + +static inline float GamepadStick(short in) +{ + float v; + if (abs(in) < 9000) + return 0; + else if (in > 9000) + v = (float) in - 9000; + else + v = (float) in + 9000; + return v / (32767 - 9000); +} + +static inline float GamepadTrigger(BYTE in) +{ + return (in < 30) ? 0.0f : (float(in-30) / 225); +} + + +int OculusRoomTinyApp::Run() +{ + // Loop processing messages until Quit flag is set, + // rendering game scene inside of OnIdle(). + + while (!Quit) + { + MSG msg; + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + else + { + // Read game-pad. + XINPUT_STATE xis; + + if (pXInputGetState && !pXInputGetState(0, &xis) && + (xis.dwPacketNumber != LastPadPacketNo)) + { + OnGamepad(GamepadStick(xis.Gamepad.sThumbLX), + GamepadStick(xis.Gamepad.sThumbLY), + GamepadStick(xis.Gamepad.sThumbRX), + GamepadStick(xis.Gamepad.sThumbRY)); + //pad.LT = GamepadTrigger(xis.Gamepad.bLeftTrigger); + LastPadPacketNo = xis.dwPacketNumber; + } + + pApp->OnIdle(); + + // Keep sleeping when we're minimized. + if (IsIconic(hWnd)) + Sleep(10); + } + } + + return 0; +} + + +//------------------------------------------------------------------------------------- +// ***** Program Startup + +int WINAPI WinMain(HINSTANCE hinst, HINSTANCE, LPSTR inArgs, int) +{ + int exitCode = 0; + + // Initializes LibOVR. This LogMask_All enables maximum logging. + // Custom allocator can also be specified here. + OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); + + // Scope to force application destructor before System::Destroy. + { + OculusRoomTinyApp app(hinst); + //app.hInstance = hinst; + + exitCode = app.OnStartup(inArgs); + if (!exitCode) + { + // Processes messages and calls OnIdle() to do rendering. + exitCode = app.Run(); + } + } + + // No OVR functions involving memory are allowed after this. + OVR::System::Destroy(); + + OVR_DEBUG_STATEMENT(_CrtDumpMemoryLeaks()); + return exitCode; +} diff --git a/Samples/OculusRoomTiny/Win32_OculusRoomTiny.h b/Samples/OculusRoomTiny/Win32_OculusRoomTiny.h new file mode 100644 index 0000000..5a0eedf --- /dev/null +++ b/Samples/OculusRoomTiny/Win32_OculusRoomTiny.h @@ -0,0 +1,189 @@ +/************************************************************************************ + +Filename : OculusRoomTiny.h +Content : Simplest possible first-person view test application for Oculus Rift +Created : March 10, 2012 +Authors : Michael Antonov, Andrew Reisse + +Copyright : Copyright 2012 Oculus, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ +#ifndef INC_OculusRoomTiny_h +#define INC_OculusRoomTiny_h + +#include +#include + +#include "OVR.h" +#include "Util/Util_Render_Stereo.h" +#include "../../LibOVR/Src/Kernel/OVR_Timer.h" +#include "RenderTiny_D3D1X_Device.h" + +using namespace OVR; +using namespace OVR::RenderTiny; + + +//------------------------------------------------------------------------------------- +// ***** OculusRoomTiny Description + +// This app renders a simple flat-shaded room allowing the user to move along the +// floor and look around with an HMD, mouse, keyboard and gamepad. +// By default, the application will start full-screen on Oculus Rift. +// +// The following keys work: +// +// 'W', 'S', 'A', 'D' - Move forward, back; strafe left/right. +// F1 - No stereo, no distortion. +// F2 - Stereo, no distortion. +// F3 - Stereo and distortion. +// + +// The world RHS coordinate system is defines as follows (as seen in perspective view): +// Y - Up +// Z - Back +// X - Right +const Vector3f UpVector(0.0f, 1.0f, 0.0f); +const Vector3f ForwardVector(0.0f, 0.0f, -1.0f); +const Vector3f RightVector(1.0f, 0.0f, 0.0f); + +// We start out looking in the positive Z (180 degree rotation). +const float YawInitial = 3.141592f; +const float Sensitivity = 1.0f; +const float MoveSpeed = 3.0f; // m/s + + +//------------------------------------------------------------------------------------- +// ***** OculusRoomTiny Application class + +// An instance of this class is created on application startup (main/WinMain). +// +// It then works as follows: +// +// OnStartup - Window, graphics and HMD setup is done here. +// This function will initialize OVR::DeviceManager and HMD, +// creating SensorDevice and attaching it to SensorFusion. +// This needs to be done before obtaining sensor data. +// +// OnIdle - Does per-frame processing, processing SensorFusion and +// movement input and rendering the frame. + +class OculusRoomTinyApp : public MessageHandler +{ +public: + OculusRoomTinyApp(HINSTANCE hinst); + ~OculusRoomTinyApp(); + + // Initializes graphics, Rift input and creates world model. + virtual int OnStartup(const char* args); + // Called per frame to sample SensorFucion and render the world. + virtual void OnIdle(); + + // Installed for Oculus device messages. Optional. + virtual void OnMessage(const Message& msg); + + // Handle input events for movement. + virtual void OnGamepad(float padLx, float padLY, float padRx, float padRy); + virtual void OnMouseMove(int x, int y, int modifiers); + virtual void OnKey(unsigned vk, bool down); + + // Render the view for one eye. + void Render(const StereoEyeParams& stereo); + + // Main application loop. + int Run(); + + // Return amount of time passed since application started in seconds. + double GetAppTime() const + { + return (OVR::Timer::GetTicks() - StartupTicks) * (1.0 / (double)OVR::Timer::MksPerSecond); + } + + +protected: + + // Win32 window setup interface. + LRESULT windowProc(UINT msg, WPARAM wp, LPARAM lp); + bool setupWindow(); + void destroyWindow(); + // Win32 static function that delegates to WindowProc member function. + static LRESULT CALLBACK systemWindowProc(HWND window, UINT msg, WPARAM wp, LPARAM lp); + + void giveUsFocus(bool setFocus); + + static OculusRoomTinyApp* pApp; + + // *** Rendering Variables + Ptr pRender; + RendererParams RenderParams; + int Width, Height; + + + // *** Win32 System Variables + HWND hWnd; + HINSTANCE hInstance; + POINT WindowCenter; // In desktop coordinates + bool Quit; + bool MouseCaptured; + + // Dynamically ink to XInput to simplify projects. + typedef DWORD (WINAPI *PFn_XInputGetState)(DWORD dwUserIndex, XINPUT_STATE* pState); + PFn_XInputGetState pXInputGetState; + HMODULE hXInputModule; + UInt32 LastPadPacketNo; + + + // *** Oculus HMD Variables + + Ptr pManager; + Ptr pSensor; + Ptr pHMD; + SensorFusion SFusion; + OVR::HMDInfo HMDInfo; + + // Last update seconds, used for move speed timing. + double LastUpdate; + UInt64 StartupTicks; + + // Position and look. The following apply: + Vector3f EyePos; + float EyeYaw; // Rotation around Y, CCW positive when looking at RHS (X,Z) plane. + float EyePitch; // Pitch. If sensor is plugged in, only read from sensor. + float EyeRoll; // Roll, only accessible from Sensor. + float LastSensorYaw; // Stores previous Yaw value from to support computing delta. + + // Movement state; different bits may be set based on the state of keys. + UByte MoveForward; + UByte MoveBack; + UByte MoveLeft; + UByte MoveRight; + Vector3f GamepadMove, GamepadRotate; + + Matrix4f View; + RenderTiny::Scene Scene; + + // Stereo view parameters. + StereoConfig SConfig; + PostProcessType PostProcess; + + // Shift accelerates movement/adjustment velocity. + bool ShiftDown; + bool ControlDown; +}; + +// Adds sample models and lights to the argument scene. +void PopulateRoomScene(Scene* scene, RenderDevice* render); + + +#endif diff --git a/Samples/OculusWorldDemo/Makefile b/Samples/OculusWorldDemo/Makefile new file mode 100644 index 0000000..5ffc0cc --- /dev/null +++ b/Samples/OculusWorldDemo/Makefile @@ -0,0 +1,5 @@ + +WORLDDEMO_SRCS := Samples/OculusWorldDemo/OculusWorldDemo.cpp \ + $(PLATFORM_SRCS) $(RENDER_SRCS) + +$(eval $(call BUILD_RENDER_APP, Samples/OculusRoomTiny/$(C)/OculusWorldDemo, $(WORLDDEMO_SRCS))) diff --git a/Samples/OculusWorldDemo/OculusWorldDemo.cpp b/Samples/OculusWorldDemo/OculusWorldDemo.cpp new file mode 100644 index 0000000..18e614c --- /dev/null +++ b/Samples/OculusWorldDemo/OculusWorldDemo.cpp @@ -0,0 +1,1836 @@ +/************************************************************************************ + +Filename : OculusWorldDemo.cpp +Content : First-person view test application for Oculus Rift +Created : October 4, 2012 +Authors : Michael Antonov, Andrew Reisse, Steve LaValle + Peter Hoff, Dan Goodman, Bryan Croteau + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#include "OVR.h" + +#include "Player.h" + +#include "../CommonSrc/Platform/Platform_Default.h" +#include "../CommonSrc/Render/Render_Device.h" +#include "../CommonSrc/Render/Render_XMLSceneLoader.h" +#include "../CommonSrc/Render/Render_FontEmbed_DejaVu48.h" +#include "../CommonSrc/Platform/Gamepad.h" + +#include +#include +#include + +// Filename to be loaded by default, searching specified paths. +#define WORLDDEMO_ASSET_FILE "Tuscany.xml" +#define WORLDDEMO_ASSET_PATH1 "Assets/Tuscany/" +#define WORLDDEMO_ASSET_PATH2 "../Assets/Tuscany/" +// This path allows the shortcut to work. +#define WORLDDEMO_ASSET_PATH3 "Samples/OculusWorldDemo/Assets/Tuscany/" + + +using namespace OVR; +using namespace OVR::Platform; +using namespace OVR::Render; + +//------------------------------------------------------------------------------------- +// ***** OculusWorldDemo Description + +// This app renders a simple flat-shaded room allowing the user to move along the +// floor and look around with an HMD, mouse and keyboard. The following keys work: +// +// 'W', 'S', 'A', 'D' and Arrow Keys - Move forward, back; strafe left/right. +// F1 - No stereo, no distortion. +// F2 - Stereo, no distortion. +// F3 - Stereo and distortion. +// F4 - Toggle MSAA. +// F9 - Cycle through fullscreen and windowed modes. Necessary for previewing content with Rift. +// +// Important Oculus-specific logic can be found at following locations: +// +// OculusWorldDemoApp::OnStartup - This function will initialize OVR::DeviceManager and HMD, +// creating SensorDevice and attaching it to SensorFusion. +// This needs to be done before obtaining sensor data. +// +// OculusWorldDemoApp::OnIdle - Here we poll SensorFusion for orientation, apply it +// to the scene and handle movement. +// Stereo rendering is also done here, by delegating to +// to Render function for each eye. +// + +//------------------------------------------------------------------------------------- +// ***** OculusWorldDemo Application class + +// An instance of this class is created on application startup (main/WinMain). +// It then works as follows: +// - Graphics and HMD setup is done OculusWorldDemoApp::OnStartup(). This function +// also creates the room model from Slab declarations. +// - Per-frame processing is done in OnIdle(). This function processes +// sensor and movement input and then renders the frame. +// - Additional input processing is done in OnMouse, OnKey. + +class OculusWorldDemoApp : public Application, public MessageHandler +{ +public: + OculusWorldDemoApp(); + ~OculusWorldDemoApp(); + + virtual int OnStartup(int argc, const char** argv); + virtual void OnIdle(); + + virtual void OnMouseMove(int x, int y, int modifiers); + virtual void OnKey(KeyCode key, int chr, bool down, int modifiers); + virtual void OnResize(int width, int height); + + virtual void OnMessage(const Message& msg); + + void Render(const StereoEyeParams& stereo); + + // Sets temporarily displayed message for adjustments + void SetAdjustMessage(const char* format, ...); + // Overrides current timeout, in seconds (not the future default value); + // intended to be called right after SetAdjustMessage. + void SetAdjustMessageTimeout(float timeout); + + // Stereo setting adjustment functions. + // Called with deltaTime when relevant key is held. + void AdjustFov(float dt); + void AdjustAspect(float dt); + void AdjustIPD(float dt); + void AdjustEyeHeight(float dt); + + void AdjustMotionPrediction(float dt); + + void AdjustDistortion(float dt, int kIndex, const char* label); + void AdjustDistortionK0(float dt) { AdjustDistortion(dt, 0, "K0"); } + void AdjustDistortionK1(float dt) { AdjustDistortion(dt, 1, "K1"); } + void AdjustDistortionK2(float dt) { AdjustDistortion(dt, 2, "K2"); } + void AdjustDistortionK3(float dt) { AdjustDistortion(dt, 3, "K3"); } + + // Adds room model to scene. + void PopulateScene(const char* fileName); + void PopulatePreloadScene(); + void ClearScene(); + + // Magnetometer calibration procedure + void UpdateManualMagCalibration(); + +protected: + RenderDevice* pRender; + RendererParams RenderParams; + int Width, Height; + int Screen; + int FirstScreenInCycle; + + // Magnetometer calibration and yaw correction + Util::MagCalibration MagCal; + bool MagAwaitingForwardLook; + + // *** Oculus HMD Variables + Ptr pManager; + Ptr pSensor; + Ptr pHMD; + SensorFusion SFusion; + HMDInfo HMDInfo; + + Ptr pLatencyTester; + Util::LatencyTest LatencyUtil; + + double LastUpdate; + int FPS; + int FrameCounter; + double NextFPSUpdate; + + Array > CollisionModels; + Array > GroundCollisionModels; + + // Loading process displays screenshot in first frame + // and then proceeds to load until finished. + enum LoadingStateType + { + LoadingState_Frame0, + LoadingState_DoLoad, + LoadingState_Finished + }; + + // Player + Player Player; + Matrix4f View; + Scene MainScene; + Scene LoadingScene; + Scene GridScene; + Scene YawMarkGreenScene; + Scene YawMarkRedScene; + Scene YawLinesScene; + + LoadingStateType LoadingState; + + Ptr LitSolid, LitTextures[4]; + + // Stereo view parameters. + StereoConfig SConfig; + PostProcessType PostProcess; + + // LOD + String MainFilePath; + Array LODFilePaths; + int ConsecutiveLowFPSFrames; + int CurrentLODFileIndex; + + float DistortionK0; + float DistortionK1; + float DistortionK2; + float DistortionK3; + + String AdjustMessage; + double AdjustMessageTimeout; + + // Saved distortion state. + float SavedK0, SavedK1, SavedK2, SavedK3; + float SavedESD, SavedAspect, SavedEyeDistance; + + // Allows toggling color around distortion. + Color DistortionClearColor; + + // Stereo settings adjustment state. + typedef void (OculusWorldDemoApp::*AdjustFuncType)(float); + bool ShiftDown; + AdjustFuncType pAdjustFunc; + float AdjustDirection; + + enum SceneRenderMode + { + Scene_World, + Scene_Grid, + Scene_Both, + Scene_YawView + }; + SceneRenderMode SceneMode; + + + enum TextScreen + { + Text_None, + Text_Orientation, + Text_Config, + Text_Help, + Text_Count + }; + TextScreen TextScreen; + + struct DeviceStatusNotificationDesc + { + DeviceHandle Handle; + MessageType Action; + + DeviceStatusNotificationDesc():Action(Message_None) {} + DeviceStatusNotificationDesc(MessageType mt, const DeviceHandle& dev) + : Handle(dev), Action(mt) {} + }; + Array DeviceStatusNotificationsQueue; + + + Model* CreateModel(Vector3f pos, struct SlabModel* sm); + Model* CreateBoundingModel(CollisionModel &cm); + void PopulateLODFileNames(); + void DropLOD(); + void RaiseLOD(); + void CycleDisplay(); + void GamepadStateChanged(const GamepadState& pad); + + // Variable used by UpdateManualCalibration + float FirstMagYaw; +}; + +//------------------------------------------------------------------------------------- + +OculusWorldDemoApp::OculusWorldDemoApp() + : pRender(0), + LastUpdate(0), + LoadingState(LoadingState_Frame0), + // Initial location + SConfig(), + PostProcess(PostProcess_Distortion), + DistortionClearColor(0, 0, 0), + + ShiftDown(false), + pAdjustFunc(0), + AdjustDirection(1.0f), + SceneMode(Scene_World), + TextScreen(Text_None) +{ + Width = 1280; + Height = 800; + Screen = 0; + FirstScreenInCycle = 0; + + FPS = 0; + FrameCounter = 0; + NextFPSUpdate = 0; + + ConsecutiveLowFPSFrames = 0; + CurrentLODFileIndex = 0; + + AdjustMessageTimeout = 0; +} + +OculusWorldDemoApp::~OculusWorldDemoApp() +{ + RemoveHandlerFromDevices(); + + if(DejaVu.fill) + { + DejaVu.fill->Release(); + } + pLatencyTester.Clear(); + pSensor.Clear(); + pHMD.Clear(); + + CollisionModels.ClearAndRelease(); + GroundCollisionModels.ClearAndRelease(); +} + +int OculusWorldDemoApp::OnStartup(int argc, const char** argv) +{ + + // *** Oculus HMD & Sensor Initialization + + // Create DeviceManager and first available HMDDevice from it. + // Sensor object is created from the HMD, to ensure that it is on the + // correct device. + + pManager = *DeviceManager::Create(); + + // We'll handle it's messages in this case. + pManager->SetMessageHandler(this); + + pHMD = *pManager->EnumerateDevices().CreateDevice(); + if(pHMD) + { + pSensor = *pHMD->GetSensor(); + + // This will initialize HMDInfo with information about configured IPD, + // screen size and other variables needed for correct projection. + // We pass HMD DisplayDeviceName into the renderer to select the + // correct monitor in full-screen mode. + if(pHMD->GetDeviceInfo(&HMDInfo)) + { + //RenderParams.MonitorName = hmd.DisplayDeviceName; + SConfig.SetHMDInfo(HMDInfo); + } + } + else + { + // If we didn't detect an HMD, try to create the sensor directly. + // This is useful for debugging sensor interaction; it is not needed in + // a shipping app. + pSensor = *pManager->EnumerateDevices().CreateDevice(); + } + + // Create the Latency Tester device and assign it to the LatencyTesterUtil object. + pLatencyTester = *pManager->EnumerateDevices().CreateDevice(); + if (pLatencyTester) + { + LatencyUtil.SetDevice(pLatencyTester); + } + // Make the user aware which devices are present. + if(pHMD == NULL && pSensor == NULL) + { + SetAdjustMessage("---------------------------------\nNO HMD DETECTED\nNO SENSOR DETECTED\n---------------------------------"); + } + else if(pHMD == NULL) + { + SetAdjustMessage("----------------------------\nNO HMD DETECTED\n----------------------------"); + } + else if(pSensor == NULL) + { + SetAdjustMessage("---------------------------------\nNO SENSOR DETECTED\n---------------------------------"); + } + else + { + SetAdjustMessage("--------------------------------------------\n" + "Press F9 for Full-Screen on Rift\n" + "--------------------------------------------"); + } + + // First message should be extra-long. + SetAdjustMessageTimeout(10.0f); + + + if(HMDInfo.HResolution > 0) + { + Width = HMDInfo.HResolution; + Height = HMDInfo.VResolution; + } + + if(!pPlatform->SetupWindow(Width, Height)) + { + return 1; + } + + String Title = "Oculus World Demo"; + if(HMDInfo.ProductName[0]) + { + Title += " : "; + Title += HMDInfo.ProductName; + } + pPlatform->SetWindowTitle(Title); + + // Report relative mouse motion in OnMouseMove + pPlatform->SetMouseMode(Mouse_Relative); + + if(pSensor) + { + // We need to attach sensor to SensorFusion object for it to receive + // body frame messages and update orientation. SFusion.GetOrientation() + // is used in OnIdle() to orient the view. + SFusion.AttachToSensor(pSensor); + + SFusion.SetDelegateMessageHandler(this); + + SFusion.SetPredictionEnabled(true); + } + + + // *** Initialize Rendering + + const char* graphics = "d3d11"; + + // Select renderer based on command line arguments. + for(int i = 1; i < argc; i++) + { + if(!strcmp(argv[i], "-r") && i < argc - 1) + { + graphics = argv[i + 1]; + } + else if(!strcmp(argv[i], "-fs")) + { + RenderParams.Fullscreen = true; + } + } + + // Enable multi-sampling by default. + RenderParams.Multisample = 4; + pRender = pPlatform->SetupGraphics(OVR_DEFAULT_RENDER_DEVICE_SET, + graphics, RenderParams); + + + + // *** Configure Stereo settings. + + SConfig.SetFullViewport(Viewport(0, 0, Width, Height)); + SConfig.SetStereoMode(Stereo_LeftRight_Multipass); + + // Configure proper Distortion Fit. + // For 7" screen, fit to touch left side of the view, leaving a bit of + // invisible screen on the top (saves on rendering cost). + // For smaller screens (5.5"), fit to the top. + if (HMDInfo.HScreenSize > 0.0f) + { + if (HMDInfo.HScreenSize > 0.140f) // 7" + SConfig.SetDistortionFitPointVP(-1.0f, 0.0f); + else + SConfig.SetDistortionFitPointVP(0.0f, 1.0f); + } + + pRender->SetSceneRenderScale(SConfig.GetDistortionScale()); + //pRender->SetSceneRenderScale(0.8f); + + SConfig.Set2DAreaFov(DegreeToRad(85.0f)); + + + // *** Identify Scene File & Prepare for Loading + + // This creates lights and models. + if (argc == 2) + { + MainFilePath = argv[1]; + PopulateLODFileNames(); + } + else + { + fprintf(stderr, "Usage: OculusWorldDemo [input XML]\n"); + MainFilePath = WORLDDEMO_ASSET_FILE; + } + + // Try to modify path for correctness in case specified file is not found. + if (!SysFile(MainFilePath).IsValid()) + { + String prefixPath1(pPlatform->GetContentDirectory() + "/" + WORLDDEMO_ASSET_PATH1), + prefixPath2(WORLDDEMO_ASSET_PATH2), + prefixPath3(WORLDDEMO_ASSET_PATH3); + if (SysFile(prefixPath1 + MainFilePath).IsValid()) + MainFilePath = prefixPath1 + MainFilePath; + else if (SysFile(prefixPath2 + MainFilePath).IsValid()) + MainFilePath = prefixPath2 + MainFilePath; + else if (SysFile(prefixPath3 + MainFilePath).IsValid()) + MainFilePath = prefixPath3 + MainFilePath; + } + + PopulatePreloadScene(); + + LastUpdate = pPlatform->GetAppTime(); + //pPlatform->PlayMusicFile(L"Loop.wav"); + + return 0; +} + +void OculusWorldDemoApp::OnMessage(const Message& msg) +{ + if (msg.Type == Message_DeviceAdded || msg.Type == Message_DeviceRemoved) + { + if (msg.pDevice == pManager) + { + const MessageDeviceStatus& statusMsg = + static_cast(msg); + + { // limit the scope of the lock + Lock::Locker lock(pManager->GetHandlerLock()); + DeviceStatusNotificationsQueue.PushBack( + DeviceStatusNotificationDesc(statusMsg.Type, statusMsg.Handle)); + } + + switch (statusMsg.Type) + { + case OVR::Message_DeviceAdded: + LogText("DeviceManager reported device added.\n"); + break; + + case OVR::Message_DeviceRemoved: + LogText("DeviceManager reported device removed.\n"); + break; + + default: OVR_ASSERT(0); // unexpected type + } + } + } +} + +void OculusWorldDemoApp::OnResize(int width, int height) +{ + Width = width; + Height = height; + SConfig.SetFullViewport(Viewport(0, 0, Width, Height)); +} + +void OculusWorldDemoApp::OnMouseMove(int x, int y, int modifiers) +{ + if(modifiers & Mod_MouseRelative) + { + // Get Delta + int dx = x, dy = y; + + const float maxPitch = ((3.1415f / 2) * 0.98f); + + // Apply to rotation. Subtract for right body frame rotation, + // since yaw rotation is positive CCW when looking down on XZ plane. + Player.EyeYaw -= (Sensitivity * dx) / 360.0f; + + if(!pSensor) + { + Player.EyePitch -= (Sensitivity * dy) / 360.0f; + + if(Player.EyePitch > maxPitch) + { + Player.EyePitch = maxPitch; + } + if(Player.EyePitch < -maxPitch) + { + Player.EyePitch = -maxPitch; + } + } + } +} + + +void OculusWorldDemoApp::OnKey(KeyCode key, int chr, bool down, int modifiers) +{ + OVR_UNUSED(chr); + + switch(key) + { + case Key_Q: + if (down && (modifiers & Mod_Control)) + { + pPlatform->Exit(0); + } + break; + + // Handle player movement keys. + // We just update movement state here, while the actual translation is done in OnIdle() + // based on time. + case Key_W: + Player.MoveForward = down ? (Player.MoveForward | 1) : (Player.MoveForward & ~1); + break; + case Key_S: + Player.MoveBack = down ? (Player.MoveBack | 1) : (Player.MoveBack & ~1); + break; + case Key_A: + Player.MoveLeft = down ? (Player.MoveLeft | 1) : (Player.MoveLeft & ~1); + break; + case Key_D: + Player.MoveRight = down ? (Player.MoveRight | 1) : (Player.MoveRight & ~1); + break; + case Key_Up: + Player.MoveForward = down ? (Player.MoveForward | 2) : (Player.MoveForward & ~2); + break; + case Key_Down: + Player.MoveBack = down ? (Player.MoveBack | 2) : (Player.MoveBack & ~2); + break; + case Key_Left: + Player.MoveLeft = down ? (Player.MoveLeft | 2) : (Player.MoveLeft & ~2); + break; + case Key_Right: + Player.MoveRight = down ? (Player.MoveRight | 2) : (Player.MoveRight & ~2); + break; + + case Key_Minus: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustEyeHeight : 0; + AdjustDirection = -1; + break; + case Key_Equal: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustEyeHeight : 0; + AdjustDirection = 1; + break; + + case Key_B: + if (down) + { + if(SConfig.GetDistortionScale() == 1.0f) + { + if(SConfig.GetHMDInfo().HScreenSize > 0.140f) // 7" + { + SConfig.SetDistortionFitPointVP(-1.0f, 0.0f); + } + else + { + SConfig.SetDistortionFitPointVP(0.0f, 1.0f); + } + } + else + { + // No fitting; scale == 1.0. + SConfig.SetDistortionFitPointVP(0, 0); + } + } + break; + + // Support toggling background color for distortion so that we can see + // the effect on the periphery. + case Key_V: + if (down) + { + if(DistortionClearColor.B == 0) + { + DistortionClearColor = Color(0, 128, 255); + } + else + { + DistortionClearColor = Color(0, 0, 0); + } + + pRender->SetDistortionClearColor(DistortionClearColor); + } + break; + + + case Key_F1: + SConfig.SetStereoMode(Stereo_None); + PostProcess = PostProcess_None; + SetAdjustMessage("StereoMode: None"); + break; + case Key_F2: + SConfig.SetStereoMode(Stereo_LeftRight_Multipass); + PostProcess = PostProcess_None; + SetAdjustMessage("StereoMode: Stereo + No Distortion"); + break; + case Key_F3: + SConfig.SetStereoMode(Stereo_LeftRight_Multipass); + PostProcess = PostProcess_Distortion; + SetAdjustMessage("StereoMode: Stereo + Distortion"); + break; + + case Key_R: + SFusion.Reset(); + SetAdjustMessage("Sensor Fusion Reset"); + break; + + case Key_Space: + if (!down) + { + TextScreen = (enum TextScreen)((TextScreen + 1) % Text_Count); + } + break; + + case Key_F4: + if (!down) + { + RenderParams = pRender->GetParams(); + RenderParams.Multisample = RenderParams.Multisample > 1 ? 1 : 4; + pRender->SetParams(RenderParams); + if(RenderParams.Multisample > 1) + { + SetAdjustMessage("Multisampling On"); + } + else + { + SetAdjustMessage("Multisampling Off"); + } + } + break; + case Key_F9: + if (!down) + { + CycleDisplay(); + } + break; + +#ifdef OVR_OS_MAC + case Key_F10: // F11 is reserved on Mac +#else + case Key_F11: +#endif + if (!down) + { + RenderParams = pRender->GetParams(); + RenderParams.Display = DisplayId(SConfig.GetHMDInfo().DisplayDeviceName,SConfig.GetHMDInfo().DisplayId); + pRender->SetParams(RenderParams); + + pPlatform->SetMouseMode(Mouse_Normal); + pPlatform->SetFullscreen(RenderParams, pRender->IsFullscreen() ? Display_Window : Display_FakeFullscreen); + pPlatform->SetMouseMode(Mouse_Relative); // Avoid mode world rotation jump. + // If using an HMD, enable post-process (for distortion) and stereo. + if(RenderParams.IsDisplaySet() && pRender->IsFullscreen()) + { + SConfig.SetStereoMode(Stereo_LeftRight_Multipass); + PostProcess = PostProcess_Distortion; + } + } + break; + + case Key_Escape: + if(!down) + { + // switch to primary screen windowed mode + pPlatform->SetFullscreen(RenderParams, Display_Window); + RenderParams.Display = pPlatform->GetDisplay(0); + pRender->SetParams(RenderParams); + Screen = 0; + } + break; + + // Stereo adjustments. + case Key_BracketLeft: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustFov : 0; + AdjustDirection = 1; + break; + case Key_BracketRight: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustFov : 0; + AdjustDirection = -1; + break; + + case Key_Insert: + case Key_Num0: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustIPD : 0; + AdjustDirection = 1; + break; + case Key_Delete: + case Key_Num9: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustIPD : 0; + AdjustDirection = -1; + break; + + case Key_PageUp: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustAspect : 0; + AdjustDirection = 1; + break; + case Key_PageDown: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustAspect : 0; + AdjustDirection = -1; + break; + + // Distortion correction adjustments + case Key_H: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK0 : NULL; + AdjustDirection = -1; + break; + case Key_Y: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK0 : NULL; + AdjustDirection = 1; + break; + case Key_J: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK1 : NULL; + AdjustDirection = -1; + break; + case Key_U: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK1 : NULL; + AdjustDirection = 1; + break; + case Key_K: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK2 : NULL; + AdjustDirection = -1; + break; + case Key_I: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK2 : NULL; + AdjustDirection = 1; + break; + case Key_L: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK3 : NULL; + AdjustDirection = -1; + break; + case Key_O: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK3 : NULL; + AdjustDirection = 1; + break; + + + case Key_Tab: + if (down) + { + float t0 = SConfig.GetDistortionK(0), + t1 = SConfig.GetDistortionK(1), + t2 = SConfig.GetDistortionK(2), + t3 = SConfig.GetDistortionK(3); + float tESD = SConfig.GetEyeToScreenDistance(), + taspect = SConfig.GetAspectMultiplier(), + tipd = SConfig.GetIPD(); + + if(SavedK0 > 0.0f) + { + SConfig.SetDistortionK(0, SavedK0); + SConfig.SetDistortionK(1, SavedK1); + SConfig.SetDistortionK(2, SavedK2); + SConfig.SetDistortionK(3, SavedK3); + SConfig.SetEyeToScreenDistance(SavedESD); + SConfig.SetAspectMultiplier(SavedAspect); + SConfig.SetIPD(SavedEyeDistance); + + SetAdjustMessage("Restored:\n" + "ESD:\t120 %.3f\t350 Eye:\t490 %.3f\n" + "K0: \t120 %.4f\t350 K2: \t490 %.4f\n" + "K1: \t120 %.4f\t350 K3: \t490 %.4f", + SavedESD, SavedEyeDistance, + SavedK0, SavedK2, + SavedK1, SavedK3); + } + else + { + SetAdjustMessage("Setting Saved"); + } + + SavedK0 = t0; + SavedK1 = t1; + SavedK2 = t2; + SavedK3 = t3; + SavedESD = tESD; + SavedAspect = taspect; + SavedEyeDistance = tipd; + } + break; + + case Key_G: + if (down) + { + if(SceneMode == Scene_World) + { + SceneMode = Scene_Grid; + SetAdjustMessage("Grid Only"); + } + else if(SceneMode == Scene_Grid) + { + SceneMode = Scene_Both; + SetAdjustMessage("Grid Overlay"); + } + else if(SceneMode == Scene_Both) + { + SceneMode = Scene_World; + SetAdjustMessage("Grid Off"); + } + } + break; + + // Holding down Shift key accelerates adjustment velocity. + case Key_Shift: + ShiftDown = down; + break; + + // Reset the camera position in case we get stuck + case Key_T: + Player.EyePos = Vector3f(10.0f, 1.6f, 10.0f); + break; + + case Key_F5: + if (!down) + { + UPInt numNodes = MainScene.Models.GetSize(); + for(UPInt i = 0; i < numNodes; i++) + { + Ptr nodePtr = MainScene.Models[i]; + Render::Model* pNode = nodePtr.GetPtr(); + if(pNode->IsCollisionModel) + { + pNode->Visible = !pNode->Visible; + } + } + } + break; + + case Key_N: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustMotionPrediction : NULL; + AdjustDirection = -1; + break; + + case Key_M: + pAdjustFunc = down ? &OculusWorldDemoApp::AdjustMotionPrediction : NULL; + AdjustDirection = 1; + break; + +/* + case Key_N: + RaiseLOD(); + break; + case Key_M: + DropLOD(); + break; +*/ + // Start calibrating magnetometer + case Key_Z: + if (down) + { + if (MagCal.IsManuallyCalibrating()) + MagAwaitingForwardLook = false; + else + { + MagCal.BeginManualCalibration(SFusion); + MagAwaitingForwardLook = true; + } + } + break; + + case Key_X: + if (down) + { + MagCal.BeginAutoCalibration(SFusion); + SetAdjustMessage("Starting Auto Mag Calibration"); + } + break; + + // Set the magnetometer reference point + case Key_Semicolon: + if (down) + { + SFusion.SetMagReference(); + SetAdjustMessage("Magnetometer Reference Set"); + if (SFusion.IsMagReady()) + SFusion.SetYawCorrectionEnabled(true); + } + break; + + // Show view of yaw angles (for mag calibration/analysis) + case Key_F6: + if (down) + { + if (SceneMode != Scene_YawView) + { + SceneMode = Scene_YawView; + SetAdjustMessage("Magnetometer Yaw Angle Marks"); + } + else + { + SceneMode = Scene_World; + SetAdjustMessage("Magnetometer Marks Off"); + } + } + break; + + case Key_C: + if (down) + { + // Toggle chromatic aberration correction on/off. + RenderDevice::PostProcessShader shader = pRender->GetPostProcessShader(); + + if (shader == RenderDevice::PostProcessShader_Distortion) + { + pRender->SetPostProcessShader(RenderDevice::PostProcessShader_DistortionAndChromAb); + SetAdjustMessage("Chromatic Aberration Correction On"); + } + else if (shader == RenderDevice::PostProcessShader_DistortionAndChromAb) + { + pRender->SetPostProcessShader(RenderDevice::PostProcessShader_Distortion); + SetAdjustMessage("Chromatic Aberration Correction Off"); + } + else + OVR_ASSERT(false); + } + break; + + case Key_P: + if (down) + { + // Toggle motion prediction. + if (SFusion.IsPredictionEnabled()) + { + SFusion.SetPredictionEnabled(false); + SetAdjustMessage("Motion Prediction Off"); + } + else + { + SFusion.SetPredictionEnabled(true); + SetAdjustMessage("Motion Prediction On"); + } + } + break; + default: + break; + } +} + +void OculusWorldDemoApp::OnIdle() +{ + + double curtime = pPlatform->GetAppTime(); + float dt = float(curtime - LastUpdate); + LastUpdate = curtime; + + // Update gamepad. + GamepadState gamepadState; + if (GetPlatformCore()->GetGamepadManager()->GetGamepadState(0, &gamepadState)) + { + GamepadStateChanged(gamepadState); + } + + + if (LoadingState == LoadingState_DoLoad) + { + PopulateScene(MainFilePath.ToCStr()); + LoadingState = LoadingState_Finished; + return; + } + + // Check if any new devices were connected. + { + bool queueIsEmpty = false; + while (!queueIsEmpty) + { + DeviceStatusNotificationDesc desc; + + { + Lock::Locker lock(pManager->GetHandlerLock()); + if (DeviceStatusNotificationsQueue.GetSize() == 0) + break; + desc = DeviceStatusNotificationsQueue.Front(); + + // We can't call Clear under the lock since this may introduce a dead lock: + // this thread is locked by HandlerLock and the Clear might cause + // call of Device->Release, which will use Manager->DeviceLock. The bkg + // thread is most likely locked by opposite way: + // Manager->DeviceLock ==> HandlerLock, therefore - a dead lock. + // So, just grab the first element, save a copy of it and remove + // the element (Device->Release won't be called since we made a copy). + + DeviceStatusNotificationsQueue.RemoveAt(0); + queueIsEmpty = (DeviceStatusNotificationsQueue.GetSize() == 0); + } + + bool wasAlreadyCreated = desc.Handle.IsCreated(); + + if (desc.Action == Message_DeviceAdded) + { + switch(desc.Handle.GetType()) + { + case Device_Sensor: + if (desc.Handle.IsAvailable() && !desc.Handle.IsCreated()) + { + if (!pSensor) + { + pSensor = *desc.Handle.CreateDeviceTyped(); + SFusion.AttachToSensor(pSensor); + SetAdjustMessage("---------------------------\n" + "SENSOR connected\n" + "---------------------------"); + } + else if (!wasAlreadyCreated) + { + LogText("A new SENSOR has been detected, but it is not currently used."); + } + } + break; + case Device_LatencyTester: + if (desc.Handle.IsAvailable() && !desc.Handle.IsCreated()) + { + if (!pLatencyTester) + { + pLatencyTester = *desc.Handle.CreateDeviceTyped(); + LatencyUtil.SetDevice(pLatencyTester); + if (!wasAlreadyCreated) + SetAdjustMessage("----------------------------------------\n" + "LATENCY TESTER connected\n" + "----------------------------------------"); + } + } + break; + case Device_HMD: + { + OVR::HMDInfo info; + desc.Handle.GetDeviceInfo(&info); + // if strlen(info.DisplayDeviceName) == 0 then + // this HMD is 'fake' (created using sensor). + if (strlen(info.DisplayDeviceName) > 0 && (!pHMD || !info.IsSameDisplay(HMDInfo))) + { + SetAdjustMessage("------------------------\n" + "HMD connected\n" + "------------------------"); + if (!pHMD || !desc.Handle.IsDevice(pHMD)) + pHMD = *desc.Handle.CreateDeviceTyped(); + // update stereo config with new HMDInfo + if (pHMD && pHMD->GetDeviceInfo(&HMDInfo)) + { + //RenderParams.MonitorName = hmd.DisplayDeviceName; + SConfig.SetHMDInfo(HMDInfo); + } + LogText("HMD device added.\n"); + } + break; + } + default:; + } + } + else if (desc.Action == Message_DeviceRemoved) + { + if (desc.Handle.IsDevice(pSensor)) + { + LogText("Sensor reported device removed.\n"); + SFusion.AttachToSensor(NULL); + pSensor.Clear(); + SetAdjustMessage("-------------------------------\n" + "SENSOR disconnected.\n" + "-------------------------------"); + } + else if (desc.Handle.IsDevice(pLatencyTester)) + { + LogText("Latency Tester reported device removed.\n"); + LatencyUtil.SetDevice(NULL); + pLatencyTester.Clear(); + SetAdjustMessage("---------------------------------------------\n" + "LATENCY SENSOR disconnected.\n" + "---------------------------------------------"); + } + else if (desc.Handle.IsDevice(pHMD)) + { + if (pHMD && !pHMD->IsDisconnected()) + { + SetAdjustMessage("---------------------------\n" + "HMD disconnected\n" + "---------------------------"); + // Disconnect HMD. pSensor is used to restore 'fake' HMD device + // (can be NULL). + pHMD = pHMD->Disconnect(pSensor); + + // This will initialize HMDInfo with information about configured IPD, + // screen size and other variables needed for correct projection. + // We pass HMD DisplayDeviceName into the renderer to select the + // correct monitor in full-screen mode. + if (pHMD && pHMD->GetDeviceInfo(&HMDInfo)) + { + //RenderParams.MonitorName = hmd.DisplayDeviceName; + SConfig.SetHMDInfo(HMDInfo); + } + LogText("HMD device removed.\n"); + } + } + } + else + OVR_ASSERT(0); // unexpected action + } + } + + // If one of Stereo setting adjustment keys is pressed, adjust related state. + if (pAdjustFunc) + { + (this->*pAdjustFunc)(dt * AdjustDirection * (ShiftDown ? 5.0f : 1.0f)); + } + + // Process latency tester results. + const char* results = LatencyUtil.GetResultsString(); + if (results != NULL) + { + LogText("LATENCY TESTER: %s\n", results); + } + + // Have to place this as close as possible to where the HMD orientation is read. + LatencyUtil.ProcessInputs(); + + // Magnetometer calibration procedure + if (MagCal.IsManuallyCalibrating()) + UpdateManualMagCalibration(); + + if (MagCal.IsAutoCalibrating()) + { + MagCal.UpdateAutoCalibration(SFusion); + if (MagCal.IsCalibrated()) + { + if (SFusion.IsMagReady()) + SFusion.SetYawCorrectionEnabled(true); + Vector3f mc = MagCal.GetMagCenter(); + SetAdjustMessage(" Magnetometer Calibration Complete \nCenter: %f %f %f",mc.x,mc.y,mc.z); + } + } + + // Handle Sensor motion. + // We extract Yaw, Pitch, Roll instead of directly using the orientation + // to allow "additional" yaw manipulation with mouse/controller. + if(pSensor) + { + Quatf hmdOrient = SFusion.GetPredictedOrientation(); + + float yaw = 0.0f; + hmdOrient.GetEulerAngles(&yaw, &Player.EyePitch, &Player.EyeRoll); + + Player.EyeYaw += (yaw - Player.LastSensorYaw); + Player.LastSensorYaw = yaw; + + // NOTE: We can get a matrix from orientation as follows: + // Matrix4f hmdMat(hmdOrient); + + // Test logic - assign quaternion result directly to view: + // Quatf hmdOrient = SFusion.GetOrientation(); + // View = Matrix4f(hmdOrient.Inverted()) * Matrix4f::Translation(-EyePos); + } + + + if(curtime >= NextFPSUpdate) + { + NextFPSUpdate = curtime + 1.0; + FPS = FrameCounter; + FrameCounter = 0; + } + FrameCounter++; + + if(FPS < 40) + { + ConsecutiveLowFPSFrames++; + } + else + { + ConsecutiveLowFPSFrames = 0; + } + + if(ConsecutiveLowFPSFrames > 200) + { + DropLOD(); + ConsecutiveLowFPSFrames = 0; + } + + Player.EyeYaw -= Player.GamepadRotate.x * dt; + Player.HandleCollision(dt, &CollisionModels, &GroundCollisionModels, ShiftDown); + + if(!pSensor) + { + Player.EyePitch -= Player.GamepadRotate.y * dt; + + const float maxPitch = ((3.1415f / 2) * 0.98f); + if(Player.EyePitch > maxPitch) + { + Player.EyePitch = maxPitch; + } + if(Player.EyePitch < -maxPitch) + { + Player.EyePitch = -maxPitch; + } + } + + // Rotate and position View Camera, using YawPitchRoll in BodyFrame coordinates. + // + Matrix4f rollPitchYaw = Matrix4f::RotationY(Player.EyeYaw) * Matrix4f::RotationX(Player.EyePitch) * + Matrix4f::RotationZ(Player.EyeRoll); + Vector3f up = rollPitchYaw.Transform(UpVector); + Vector3f forward = rollPitchYaw.Transform(ForwardVector); + + + // Minimal head modeling; should be moved as an option to SensorFusion. + float headBaseToEyeHeight = 0.15f; // Vertical height of eye from base of head + float headBaseToEyeProtrusion = 0.09f; // Distance forward of eye from base of head + + Vector3f eyeCenterInHeadFrame(0.0f, headBaseToEyeHeight, -headBaseToEyeProtrusion); + Vector3f shiftedEyePos = Player.EyePos + rollPitchYaw.Transform(eyeCenterInHeadFrame); + shiftedEyePos.y -= eyeCenterInHeadFrame.y; // Bring the head back down to original height + View = Matrix4f::LookAtRH(shiftedEyePos, shiftedEyePos + forward, up); + + // Transformation without head modeling. + // View = Matrix4f::LookAtRH(EyePos, EyePos + forward, up); + + // This is an alternative to LookAtRH: + // Here we transpose the rotation matrix to get its inverse. + // View = (Matrix4f::RotationY(EyeYaw) * Matrix4f::RotationX(EyePitch) * + // Matrix4f::RotationZ(EyeRoll)).Transposed() * + // Matrix4f::Translation(-EyePos); + + + switch(SConfig.GetStereoMode()) + { + case Stereo_None: + Render(SConfig.GetEyeRenderParams(StereoEye_Center)); + break; + + case Stereo_LeftRight_Multipass: + //case Stereo_LeftDouble_Multipass: + Render(SConfig.GetEyeRenderParams(StereoEye_Left)); + Render(SConfig.GetEyeRenderParams(StereoEye_Right)); + break; + + } + + pRender->Present(); + // Force GPU to flush the scene, resulting in the lowest possible latency. + pRender->ForceFlushGPU(); +} + + + +void OculusWorldDemoApp::UpdateManualMagCalibration() +{ + float tyaw,yaw, pitch, roll; + Quatf hmdOrient = SFusion.GetOrientation(); + // Note that yaw and pitch are used from two different Euler angle combinations. This + // is done so that pitch (looking "up" or "down") is not dependent on yaw angle + hmdOrient.GetEulerAngles(&pitch, &roll, &yaw); + hmdOrient.GetEulerAngles(&tyaw, &roll, &pitch); + Vector3f mag = SFusion.GetMagnetometer(); + float dtr = Math::DegreeToRadFactor; + + switch(MagCal.NumberOfSamples()) + { + case 0: + if (MagAwaitingForwardLook) + SetAdjustMessage("Magnetometer Calibration\n** Step 1: Please Look Forward **\n** and Press Z When Ready **"); + else + if (fabs(pitch) < 10.0f*dtr) + { + MagCal.InsertIfAcceptable(hmdOrient, mag); + FirstMagYaw = yaw; + MagAwaitingForwardLook = false; + SFusion.SetMagReference(); + } + break; + case 1: + SetAdjustMessage("Magnetometer Calibration\n** Step 2: Please Look Up **"); + yaw -= FirstMagYaw; + if (yaw < -Math::Pi) + yaw += Math::TwoPi; + if (yaw > Math::Pi) + yaw -= Math::TwoPi; + if ((pitch > 50.0f*dtr) && (fabs(yaw) < 20.0f*dtr)) + MagCal.InsertIfAcceptable(hmdOrient, mag); + break; + case 2: + SetAdjustMessage("Magnetometer Calibration\n** Step 3: Please Look Left **"); + yaw -= FirstMagYaw; + if (yaw < -Math::Pi) + yaw += Math::TwoPi; + if (yaw > Math::Pi) + yaw -= Math::TwoPi; + if (yaw > 60.0f*dtr) + MagCal.InsertIfAcceptable(hmdOrient, mag); + break; + case 3: + SetAdjustMessage("Magnetometer Calibration\n** Step 4: Please Look Right **"); + yaw -= FirstMagYaw; + if (yaw < -Math::Pi) + yaw += Math::TwoPi; + if (yaw > Math::Pi) + yaw -= Math::TwoPi; + if (yaw < -60.0f*dtr) + MagCal.InsertIfAcceptable(hmdOrient, mag); + break; + case 4: + if (!MagCal.IsCalibrated()) + { + MagCal.SetCalibration(SFusion); + if (SFusion.IsMagReady()) + SFusion.SetYawCorrectionEnabled(true); + Vector3f mc = MagCal.GetMagCenter(); + SetAdjustMessage(" Magnetometer Calibration and Activation \nCenter: %f %f %f\nReference Yaw: %f", + mc.x,mc.y,mc.z,SFusion.GetMagRefYaw()); + } + } +} + +static const char* HelpText = + "F1 \t100 NoStereo \t420 Z \t520 Manual Mag Calib\n" + "F2 \t100 Stereo \t420 X \t520 Auto Mag Calib\n" + "F3 \t100 StereoHMD \t420 ; \t520 Mag Set Ref Point\n" + "F4 \t100 MSAA \t420 F6 \t520 Mag Info\n" + "F9 \t100 FullScreen \t420 R \t520 Reset SensorFusion\n" + "F11 \t100 Fast FullScreen \t500 - + \t660 Adj EyeHeight\n" + "C \t100 Chromatic Ab \t500 [ ] \t660 Adj FOV\n" + "P \t100 Motion Pred \t500 Shift \t660 Adj Faster\n" + "N/M \t180 Adj Motion Pred\n" + "( / ) \t180 Adj EyeDistance" + ; + + +enum DrawTextCenterType +{ + DrawText_NoCenter= 0, + DrawText_VCenter = 0x1, + DrawText_HCenter = 0x2, + DrawText_Center = DrawText_VCenter | DrawText_HCenter +}; + +static void DrawTextBox(RenderDevice* prender, float x, float y, + float textSize, const char* text, + DrawTextCenterType centerType = DrawText_NoCenter) +{ + float ssize[2] = {0.0f, 0.0f}; + + prender->MeasureText(&DejaVu, text, textSize, ssize); + + // Treat 0 a VCenter. + if (centerType & DrawText_HCenter) + { + x = -ssize[0]/2; + } + if (centerType & DrawText_VCenter) + { + y = -ssize[1]/2; + } + + prender->FillRect(x-0.02f, y-0.02f, x+ssize[0]+0.02f, y+ssize[1]+0.02f, Color(40,40,100,210)); + prender->RenderText(&DejaVu, text, x, y, textSize, Color(255,255,0,210)); +} + +void OculusWorldDemoApp::Render(const StereoEyeParams& stereo) +{ + pRender->BeginScene(PostProcess); + + // *** 3D - Configures Viewport/Projection and Render + pRender->ApplyStereoParams(stereo); + pRender->Clear(); + + pRender->SetDepthMode(true, true); + if (SceneMode != Scene_Grid) + { + MainScene.Render(pRender, stereo.ViewAdjust * View); + } + + if (SceneMode == Scene_YawView) + { + Matrix4f calView = Matrix4f(); + float viewYaw = -Player.LastSensorYaw + SFusion.GetMagRefYaw(); + calView.M[0][0] = calView.M[2][2] = cos(viewYaw); + calView.M[0][2] = sin(viewYaw); + calView.M[2][0] = -sin(viewYaw); + //LogText("yaw: %f\n",SFusion.GetMagRefYaw()); + + if (SFusion.IsYawCorrectionInProgress()) + YawMarkGreenScene.Render(pRender, stereo.ViewAdjust); + else + YawMarkRedScene.Render(pRender, stereo.ViewAdjust); + + if (fabs(Player.EyePitch) < Math::Pi * 0.33) + YawLinesScene.Render(pRender, stereo.ViewAdjust * calView); + } + + // *** 2D Text & Grid - Configure Orthographic rendering. + + // Render UI in 2D orthographic coordinate system that maps [-1,1] range + // to a readable FOV area centered at your eye and properly adjusted. + pRender->ApplyStereoParams2D(stereo); + pRender->SetDepthMode(false, false); + + float unitPixel = SConfig.Get2DUnitPixel(); + float textHeight= unitPixel * 22; + + if ((SceneMode == Scene_Grid)||(SceneMode == Scene_Both)) + { // Draw grid two pixels thick. + GridScene.Render(pRender, Matrix4f()); + GridScene.Render(pRender, Matrix4f::Translation(unitPixel,unitPixel,0)); + } + + // Display Loading screen-shot in frame 0. + if (LoadingState != LoadingState_Finished) + { + LoadingScene.Render(pRender, Matrix4f()); + String loadMessage = String("Loading ") + MainFilePath; + DrawTextBox(pRender, 0.0f, 0.25f, textHeight, loadMessage.ToCStr(), DrawText_HCenter); + LoadingState = LoadingState_DoLoad; + } + + if(AdjustMessageTimeout > pPlatform->GetAppTime()) + { + DrawTextBox(pRender,0.0f,0.4f, textHeight, AdjustMessage.ToCStr(), DrawText_HCenter); + } + + switch(TextScreen) + { + case Text_Orientation: + { + char buf[256], gpustat[256]; + OVR_sprintf(buf, sizeof(buf), + " Yaw:%4.0f Pitch:%4.0f Roll:%4.0f \n" + " FPS: %d Frame: %d \n Pos: %3.2f, %3.2f, %3.2f \n" + " EyeHeight: %3.2f", + RadToDegree(Player.EyeYaw), RadToDegree(Player.EyePitch), RadToDegree(Player.EyeRoll), + FPS, FrameCounter, Player.EyePos.x, Player.EyePos.y, Player.EyePos.z, Player.EyePos.y); + size_t texMemInMB = pRender->GetTotalTextureMemoryUsage() / 1058576; + if (texMemInMB) + { + OVR_sprintf(gpustat, sizeof(gpustat), "\n GPU Tex: %u MB", texMemInMB); + OVR_strcat(buf, sizeof(buf), gpustat); + } + + DrawTextBox(pRender, 0.0f, -0.15f, textHeight, buf, DrawText_HCenter); + } + break; + + case Text_Config: + { + char textBuff[2048]; + + OVR_sprintf(textBuff, sizeof(textBuff), + "Fov\t300 %9.4f\n" + "EyeDistance\t300 %9.4f\n" + "DistortionK0\t300 %9.4f\n" + "DistortionK1\t300 %9.4f\n" + "DistortionK2\t300 %9.4f\n" + "DistortionK3\t300 %9.4f\n" + "TexScale\t300 %9.4f", + SConfig.GetYFOVDegrees(), + SConfig.GetIPD(), + SConfig.GetDistortionK(0), + SConfig.GetDistortionK(1), + SConfig.GetDistortionK(2), + SConfig.GetDistortionK(3), + SConfig.GetDistortionScale()); + + DrawTextBox(pRender, 0.0f, 0.0f, textHeight, textBuff, DrawText_Center); + } + break; + + case Text_Help: + DrawTextBox(pRender, 0.0f, -0.1f, textHeight, HelpText, DrawText_Center); + + default: + break; + } + + + // Display colored quad if we're doing a latency test. + Color colorToDisplay; + if (LatencyUtil.DisplayScreenColor(colorToDisplay)) + { + pRender->FillRect(-0.4f, -0.4f, 0.4f, 0.4f, colorToDisplay); + } + + pRender->FinishScene(); +} + + +// Sets temporarily displayed message for adjustments +void OculusWorldDemoApp::SetAdjustMessage(const char* format, ...) +{ + Lock::Locker lock(pManager->GetHandlerLock()); + char textBuff[2048]; + va_list argList; + va_start(argList, format); + OVR_vsprintf(textBuff, sizeof(textBuff), format, argList); + va_end(argList); + + // Message will time out in 4 seconds. + AdjustMessage = textBuff; + AdjustMessageTimeout = pPlatform->GetAppTime() + 4.0f; +} + +void OculusWorldDemoApp::SetAdjustMessageTimeout(float timeout) +{ + AdjustMessageTimeout = pPlatform->GetAppTime() + timeout; +} + +// ***** View Control Adjustments + +void OculusWorldDemoApp::AdjustFov(float dt) +{ + float esd = SConfig.GetEyeToScreenDistance() + 0.01f * dt; + SConfig.SetEyeToScreenDistance(esd); + SetAdjustMessage("ESD:%6.3f FOV: %6.3f", esd, SConfig.GetYFOVDegrees()); +} + +void OculusWorldDemoApp::AdjustAspect(float dt) +{ + float rawAspect = SConfig.GetAspect() / SConfig.GetAspectMultiplier(); + float newAspect = SConfig.GetAspect() + 0.01f * dt; + SConfig.SetAspectMultiplier(newAspect / rawAspect); + SetAdjustMessage("Aspect: %6.3f", newAspect); +} + +void OculusWorldDemoApp::AdjustDistortion(float dt, int kIndex, const char* label) +{ + SConfig.SetDistortionK(kIndex, SConfig.GetDistortionK(kIndex) + 0.03f * dt); + SetAdjustMessage("%s: %6.4f", label, SConfig.GetDistortionK(kIndex)); +} + +void OculusWorldDemoApp::AdjustIPD(float dt) +{ + SConfig.SetIPD(SConfig.GetIPD() + 0.025f * dt); + SetAdjustMessage("EyeDistance: %6.4f", SConfig.GetIPD()); +} + +void OculusWorldDemoApp::AdjustEyeHeight(float dt) +{ + float dist = 0.5f * dt; + + Player.EyeHeight += dist; + Player.EyePos.y += dist; + + SetAdjustMessage("EyeHeight: %4.2f", Player.EyeHeight); +} + +void OculusWorldDemoApp::AdjustMotionPrediction(float dt) +{ + float motionPred = SFusion.GetPredictionDelta() + 0.01f * dt; + + if (motionPred < 0.0f) + { + motionPred = 0.0f; + } + + SFusion.SetPrediction(motionPred); + + SetAdjustMessage("MotionPrediction: %6.3fs", motionPred); +} + + +// Loads the scene data +void OculusWorldDemoApp::PopulateScene(const char *fileName) +{ + XmlHandler xmlHandler; + if(!xmlHandler.ReadFile(fileName, pRender, &MainScene, &CollisionModels, &GroundCollisionModels)) + { + SetAdjustMessage("---------------------------------\nFILE LOAD FAILED\n---------------------------------"); + SetAdjustMessageTimeout(10.0f); + } + + MainScene.SetAmbient(Vector4f(1.0f, 1.0f, 1.0f, 1.0f)); + + // Distortion debug grid (brought up by 'G' key). + Ptr gridModel = *Model::CreateGrid(Vector3f(0,0,0), Vector3f(1.0f/10, 0,0), Vector3f(0,1.0f/10,0), + 10, 10, 5, + Color(0, 255, 0, 255), Color(255, 50, 50, 255) ); + GridScene.World.Add(gridModel); + + // Yaw angle marker and lines (brought up by ';' key). + float shifty = -0.5f; + Ptr yawMarkGreenModel = *Model::CreateBox(Color(0, 255, 0, 255), Vector3f(0.0f, shifty, -2.0f), Vector3f(0.05f, 0.05f, 0.05f)); + YawMarkGreenScene.World.Add(yawMarkGreenModel); + Ptr yawMarkRedModel = *Model::CreateBox(Color(255, 0, 0, 255), Vector3f(0.0f, shifty, -2.0f), Vector3f(0.05f, 0.05f, 0.05f)); + YawMarkRedScene.World.Add(yawMarkRedModel); + + Ptr yawLinesModel = *new Model(Prim_Lines); + float r = 2.0f; + float theta0 = Math::PiOver2; + float theta1 = 0.0f; + Color c = Color(255, 200, 200, 255); + for (int i = 0; i < 35; i++) + { + theta1 = theta0 + Math::Pi / 18.0f; + yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty,-r*sin(theta0)),c), + yawLinesModel->AddVertex(Vector3f(r*cos(theta1),shifty,-r*sin(theta1)),c)); + theta0 = theta1; + yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty,-r*sin(theta0)),c), + yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty+0.1f,-r*sin(theta0)),c)); + theta0 = theta1; + } + theta1 = theta0 + Math::Pi / 18.0f; + yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty,-r*sin(theta0)),c), + yawLinesModel->AddVertex(Vector3f(r*cos(theta1),shifty,-r*sin(theta1)),c)); + yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(0.0f,shifty+0.1f,-r),c), + yawLinesModel->AddVertex(Vector3f(r*sin(0.02f),shifty,-r*cos(0.02f)),c)); + yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(0.0f,shifty+0.1f,-r),c), + yawLinesModel->AddVertex(Vector3f(r*sin(-0.02f),shifty,-r*cos(-0.02f)),c)); + yawLinesModel->SetPosition(Vector3f(0.0f,0.0f,0.0f)); + + YawLinesScene.World.Add(yawLinesModel); +} + + +void OculusWorldDemoApp::PopulatePreloadScene() +{ + // Load-screen screen shot image + String fileName = MainFilePath; + fileName.StripExtension(); + + Ptr imageFile = *new SysFile(fileName + "_LoadScreen.tga"); + Ptr imageTex; + if (imageFile->IsValid()) + imageTex = *LoadTextureTga(pRender, imageFile); + + // Image is rendered as a single quad. + if (imageTex) + { + imageTex->SetSampleMode(Sample_Anisotropic|Sample_Repeat); + Ptr m = *new Model(Prim_Triangles); + m->AddVertex(-0.5f, 0.5f, 0.0f, Color(255,255,255,255), 0.0f, 0.0f); + m->AddVertex( 0.5f, 0.5f, 0.0f, Color(255,255,255,255), 1.0f, 0.0f); + m->AddVertex( 0.5f, -0.5f, 0.0f, Color(255,255,255,255), 1.0f, 1.0f); + m->AddVertex(-0.5f, -0.5f, 0.0f, Color(255,255,255,255), 0.0f, 1.0f); + m->AddTriangle(2,1,0); + m->AddTriangle(0,3,2); + + Ptr fill = *new ShaderFill(*pRender->CreateShaderSet()); + fill->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Vertex, VShader_MVP)); + fill->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Fragment, FShader_Texture)); + fill->SetTexture(0, imageTex); + m->Fill = fill; + + LoadingScene.World.Add(m); + } +} + +void OculusWorldDemoApp::ClearScene() +{ + MainScene.Clear(); + GridScene.Clear(); + YawMarkGreenScene.Clear(); + YawMarkRedScene.Clear(); + YawLinesScene.Clear(); +} + +void OculusWorldDemoApp::PopulateLODFileNames() +{ + //OVR::String mainFilePath = MainFilePath; + LODFilePaths.PushBack(MainFilePath); + int LODIndex = 1; + SPInt pos = strcspn(MainFilePath.ToCStr(), "."); + SPInt len = strlen(MainFilePath.ToCStr()); + SPInt diff = len - pos; + + if (diff == 0) + return; + + while(true) + { + char pathWithoutExt[250]; + char buffer[250]; + for(SPInt i = 0; i < pos; ++i) + { + pathWithoutExt[i] = MainFilePath[(int)i]; + } + pathWithoutExt[pos] = '\0'; + OVR_sprintf(buffer, sizeof(buffer), "%s%i.xml", pathWithoutExt, LODIndex); + FILE* fp = 0; +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + errno_t err = fopen_s(&fp, buffer, "rb"); + if(!fp || err) + { +#else + fp = fopen(buffer, "rb"); + if(!fp) + { +#endif + break; + } + fclose(fp); + OVR::String result = buffer; + LODFilePaths.PushBack(result); + LODIndex++; + } +} + +void OculusWorldDemoApp::DropLOD() +{ + if(CurrentLODFileIndex < (int)(LODFilePaths.GetSize() - 1)) + { + ClearScene(); + CurrentLODFileIndex++; + PopulateScene(LODFilePaths[CurrentLODFileIndex].ToCStr()); + } +} + +void OculusWorldDemoApp::RaiseLOD() +{ + if(CurrentLODFileIndex > 0) + { + ClearScene(); + CurrentLODFileIndex--; + PopulateScene(LODFilePaths[CurrentLODFileIndex].ToCStr()); + } +} + +//----------------------------------------------------------------------------- +void OculusWorldDemoApp::CycleDisplay() +{ + int screenCount = pPlatform->GetDisplayCount(); + + // If Windowed, switch to the HMD screen first in Full-Screen Mode. + // If already Full-Screen, cycle to next screen until we reach FirstScreenInCycle. + + if (pRender->IsFullscreen()) + { + // Right now, we always need to restore window before going to next screen. + pPlatform->SetFullscreen(RenderParams, Display_Window); + + Screen++; + if (Screen == screenCount) + Screen = 0; + + RenderParams.Display = pPlatform->GetDisplay(Screen); + + if (Screen != FirstScreenInCycle) + { + pRender->SetParams(RenderParams); + pPlatform->SetFullscreen(RenderParams, Display_Fullscreen); + } + } + else + { + // Try to find HMD Screen, making it the first screen in full-screen Cycle. + FirstScreenInCycle = 0; + + if (pHMD) + { + DisplayId HMD (SConfig.GetHMDInfo().DisplayDeviceName, SConfig.GetHMDInfo().DisplayId); + for (int i = 0; i< screenCount; i++) + { + if (pPlatform->GetDisplay(i) == HMD) + { + FirstScreenInCycle = i; + break; + } + } + } + + // Switch full-screen on the HMD. + Screen = FirstScreenInCycle; + RenderParams.Display = pPlatform->GetDisplay(Screen); + pRender->SetParams(RenderParams); + pPlatform->SetFullscreen(RenderParams, Display_Fullscreen); + } +} + +void OculusWorldDemoApp::GamepadStateChanged(const GamepadState& pad) +{ + Player.GamepadMove = Vector3f(pad.LX * pad.LX * (pad.LX > 0 ? 1 : -1), + 0, + pad.LY * pad.LY * (pad.LY > 0 ? -1 : 1)); + Player.GamepadRotate = Vector3f(2 * pad.RX, -2 * pad.RY, 0); +} + + +//------------------------------------------------------------------------------------- + +OVR_PLATFORM_APP(OculusWorldDemoApp); diff --git a/Samples/OculusWorldDemo/OculusWorldDemo.rc b/Samples/OculusWorldDemo/OculusWorldDemo.rc new file mode 100644 index 0000000..49a6a5a Binary files /dev/null and b/Samples/OculusWorldDemo/OculusWorldDemo.rc differ diff --git a/Samples/OculusWorldDemo/OculusWorldDemo_Msvc2010.vcxproj b/Samples/OculusWorldDemo/OculusWorldDemo_Msvc2010.vcxproj new file mode 100644 index 0000000..456e3b6 --- /dev/null +++ b/Samples/OculusWorldDemo/OculusWorldDemo_Msvc2010.vcxproj @@ -0,0 +1,211 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {8051B877-2992-4F64-8C3B-FAF88B6D83AA} + Win32Proj + OculusWorldDemo + OculusWorldDemo + + + + Application + true + Unicode + + + Application + true + Unicode + + + Application + false + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + + + + + + + true + $(Configuration)\Obj\ + $(ProjectDir)$(Configuration)\ + + + true + $(Configuration)\Obj\ + $(ProjectDir)$(Configuration)\ + + + false + $(Configuration)\Obj\ + $(ProjectDir)$(Configuration)\ + + + false + $(Configuration)\Obj\ + $(ProjectDir)$(Configuration)\ + + + + + + Level4 + Disabled + OVR_BUILD_DEBUG;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + ../../LibOVR/Include;../../3rdParty/TinyXml;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories) + OldStyle + true + false + + + Windows + true + libovrd.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + ../../LibOVR/Lib/Win32;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories) + + + + + + + Level4 + Disabled + OVR_BUILD_DEBUG;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + ../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories) + OldStyle + true + false + + + Windows + true + libovr64d.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + ../../LibOVR/Lib/x64;$(DXSDK_DIR)/Lib/x64;%(AdditionalLibraryDirectories) + + + + + Level4 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + ../../LibOVR/Include;../../3rdParty/TinyXml;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories) + MultiThreaded + true + OldStyle + + + Windows + false + true + true + ../../LibOVR/Lib/Win32;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories) + libovr.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level4 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + ../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories) + MultiThreaded + true + OldStyle + + + Windows + true + true + true + ../../LibOVR/Lib/x64;$(DXSDK_DIR)/Lib/x64;%(AdditionalLibraryDirectories) + libovr64.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/OculusWorldDemo/OculusWorldDemo_Msvc2010.vcxproj.filters b/Samples/OculusWorldDemo/OculusWorldDemo_Msvc2010.vcxproj.filters new file mode 100644 index 0000000..f024f9a --- /dev/null +++ b/Samples/OculusWorldDemo/OculusWorldDemo_Msvc2010.vcxproj.filters @@ -0,0 +1,89 @@ + + + + + {ba6e9e50-0655-4f12-a136-6d2a06015925} + + + {16e20d8b-eff7-454c-be85-02037c399e97} + + + {1b6d51ae-a405-4f3d-be93-41a50db4f328} + + + + + CommonSrc\Platform + + + CommonSrc\Platform + + + CommonSrc\Render + + + CommonSrc\Render + + + CommonSrc\Render + + + + CommonSrc\Render + + + CommonSrc\Render + + + + + CommonSrc\Render + + + CommonSrc\Render + + + CommonSrc\Platform + + + + + CommonSrc\Platform + + + CommonSrc\Platform + + + CommonSrc\Platform + + + CommonSrc\Render + + + CommonSrc\Render + + + CommonSrc\Render + + + CommonSrc\Render + + + CommonSrc\Render + + + + + CommonSrc\Render + + + CommonSrc\Platform + + + CommonSrc\Platform + + + + + + \ No newline at end of file diff --git a/Samples/OculusWorldDemo/Player.cpp b/Samples/OculusWorldDemo/Player.cpp new file mode 100644 index 0000000..92b6f13 --- /dev/null +++ b/Samples/OculusWorldDemo/Player.cpp @@ -0,0 +1,166 @@ +/************************************************************************************ + +Filename : Player.cpp +Content : Player location and hit-testing logic source +Created : October 4, 2012 + +Copyright : Copyright 2012 Oculus, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#include "Player.h" +#include + +Player::Player(void) + : EyeHeight(1.8f), + EyePos(7.7f, 1.8f, -1.0f), + EyeYaw(YawInitial), EyePitch(0), EyeRoll(0), + LastSensorYaw(0) +{ + MoveForward = MoveBack = MoveLeft = MoveRight = 0; + GamepadMove = Vector3f(0); + GamepadRotate = Vector3f(0); +} + + +Player::~Player(void) +{ +} + +void Player::HandleCollision(double dt, Array >* collisionModels, + Array >* groundCollisionModels, bool shiftDown) +{ + if(MoveForward || MoveBack || MoveLeft || MoveRight || GamepadMove.LengthSq() > 0) + { + Vector3f orientationVector; + // Handle keyboard movement. + // This translates EyePos based on Yaw vector direction and keys pressed. + // Note that Pitch and Roll do not affect movement (they only affect view). + if(MoveForward || MoveBack || MoveLeft || MoveRight) + { + Vector3f localMoveVector(0, 0, 0); + Matrix4f yawRotate = Matrix4f::RotationY(EyeYaw); + + if (MoveForward) + { + localMoveVector = ForwardVector; + } + else if (MoveBack) + { + localMoveVector = -ForwardVector; + } + + if (MoveRight) + { + localMoveVector += RightVector; + } + else if (MoveLeft) + { + localMoveVector -= RightVector; + } + + // Normalize vector so we don't move faster diagonally. + localMoveVector.Normalize(); + orientationVector = yawRotate.Transform(localMoveVector); + } + else if (GamepadMove.LengthSq() > 0) + { + Matrix4f yawRotate = Matrix4f::RotationY(EyeYaw); + GamepadMove.Normalize(); + orientationVector = yawRotate.Transform(GamepadMove); + } + + float moveLength = OVR::Alg::Min(MoveSpeed * (float)dt * (shiftDown ? 3.0f : 1.0f), 1.0f); + + float checkLengthForward = moveLength; + Planef collisionPlaneForward; + float checkLengthLeft = moveLength; + Planef collisionPlaneLeft; + float checkLengthRight = moveLength; + Planef collisionPlaneRight; + bool gotCollision = false; + bool gotCollisionLeft = false; + bool gotCollisionRight = false; + + for(unsigned int i = 0; i < collisionModels->GetSize(); ++i) + { + // Checks for collisions at eye level, which should prevent us from + // slipping under walls + if (collisionModels->At(i)->TestRay(EyePos, orientationVector, checkLengthForward, + &collisionPlaneForward)) + { + gotCollision = true; + } + + Matrix4f leftRotation = Matrix4f::RotationY(45 * (Math::Pi / 180.0f)); + Vector3f leftVector = leftRotation.Transform(orientationVector); + if (collisionModels->At(i)->TestRay(EyePos, leftVector, checkLengthLeft, + &collisionPlaneLeft)) + { + gotCollisionLeft = true; + } + Matrix4f rightRotation = Matrix4f::RotationY(-45 * (Math::Pi / 180.0f)); + Vector3f rightVector = rightRotation.Transform(orientationVector); + if (collisionModels->At(i)->TestRay(EyePos, rightVector, checkLengthRight, + &collisionPlaneRight)) + { + gotCollisionRight = true; + } + } + + if (gotCollision) + { + // Project orientationVector onto the plane + Vector3f slideVector = orientationVector - collisionPlaneForward.N + * (orientationVector * collisionPlaneForward.N); + + // Make sure we aren't in a corner + for(unsigned int j = 0; j < collisionModels->GetSize(); ++j) + { + if (collisionModels->At(j)->TestPoint(EyePos - Vector3f(0.0f, RailHeight, 0.0f) + + (slideVector * (moveLength))) ) + { + moveLength = 0; + } + } + if (moveLength != 0) + { + orientationVector = slideVector; + } + } + // Checks for collisions at foot level, which allows us to follow terrain + orientationVector *= moveLength; + EyePos += orientationVector; + + Planef collisionPlaneDown; + float finalDistanceDown = 10; + + for(unsigned int i = 0; i < groundCollisionModels->GetSize(); ++i) + { + float checkLengthDown = 10; + if (groundCollisionModels->At(i)->TestRay(EyePos, Vector3f(0.0f, -1.0f, 0.0f), + checkLengthDown, &collisionPlaneDown)) + { + finalDistanceDown = Alg::Min(finalDistanceDown, checkLengthDown); + } + } + + // Maintain the minimum camera height + if (EyeHeight - finalDistanceDown < 1.0f) + { + EyePos.y += EyeHeight - finalDistanceDown; + } + } +} diff --git a/Samples/OculusWorldDemo/Player.h b/Samples/OculusWorldDemo/Player.h new file mode 100644 index 0000000..fc762c4 --- /dev/null +++ b/Samples/OculusWorldDemo/Player.h @@ -0,0 +1,79 @@ +/************************************************************************************ + +Filename : Player.h +Content : Player location and hit-testing logic +Created : October 4, 2012 + +Copyright : Copyright 2012 Oculus, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +*************************************************************************************/ + +#ifndef OVR_WorldDemo_Player_h +#define OVR_WorldDemo_Player_h + +#include "OVR.h" +#include "../CommonSrc/Render/Render_Device.h" + +using namespace OVR; +using namespace OVR::Render; + +//------------------------------------------------------------------------------------- +// The RHS coordinate system is defines as follows (as seen in perspective view): +// Y - Up +// Z - Back +// X - Right +const Vector3f UpVector(0.0f, 1.0f, 0.0f); +const Vector3f ForwardVector(0.0f, 0.0f, -1.0f); +const Vector3f RightVector(1.0f, 0.0f, 0.0f); + +// We start out looking in the positive Z (180 degree rotation). +const float YawInitial = 3.141592f; +const float Sensitivity = 1.0f; +const float MoveSpeed = 3.0f; // m/s + +// These are used for collision detection +const float RailHeight = 0.8f; + + +//------------------------------------------------------------------------------------- +// ***** Player + +// Player class describes position and movement state of the player in the 3D world. +class Player +{ +public: + // Position and look. The following apply: + Vector3f EyePos; + float EyeHeight; + float EyeYaw; // Rotation around Y, CCW positive when looking at RHS (X,Z) plane. + float EyePitch; // Pitch. If sensor is plugged in, only read from sensor. + float EyeRoll; // Roll, only accessible from Sensor. + float LastSensorYaw; // Stores previous Yaw value from to support computing delta. + + // Movement state; different bits may be set based on the state of keys. + UByte MoveForward; + UByte MoveBack; + UByte MoveLeft; + UByte MoveRight; + Vector3f GamepadMove, GamepadRotate; + + Player(); + ~Player(); + void HandleCollision(double dt, Array >* collisionModels, + Array >* groundCollisionModels, bool shiftDown); +}; + +#endif diff --git a/Samples/SensorBox/SensorBoxTest.cpp b/Samples/SensorBox/SensorBoxTest.cpp new file mode 100644 index 0000000..637f235 --- /dev/null +++ b/Samples/SensorBox/SensorBoxTest.cpp @@ -0,0 +1,506 @@ +/************************************************************************************ + +Filename : SensorBoxTest.h +Content : Visual orientaion sensor test app; renders a rotating box over axes. +Created : October 1, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus, Inc. All Rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#include "OVR.h" +#include "Kernel/OVR_String.h" + +#include "../CommonSrc/Platform/Platform_Default.h" +#include "../CommonSrc/Render/Render_Device.h" +#include "../CommonSrc/Platform/Gamepad.h" + +using namespace OVR; +using namespace OVR::Platform; +using namespace OVR::Render; + +//------------------------------------------------------------------------------------- +// ***** SensorBoxTest Description + +// This application renders an axes-colored box that rotates with sensor input. App allows +// user to toggle views for debugging purposes by pressing F1, F2, F3 keys. +// Application further allows running multiple sensors at once to compare sensor quality. +// +// The Right-handed coordinate system is defines as follows (as seen in perspective view): +// Y - Up (colored red) +// Z - Back (Out from screen, colored blue) +// X - Right (green) +// All cameras are looking at the origin. + +// Camera view types. +enum ViewType +{ + View_Perspective, + View_XZ_UpY, + View_XY_DownZ, + View_Count +}; + + +//------------------------------------------------------------------------------------- + +class InputTestApp : public Application +{ + RenderDevice* pRender; + + Ptr pManager; + Ptr pHMD; + Ptr pSensor; + Ptr pSensor2; + + SensorFusion SFusion; + SensorFusion SFusion2; + + double LastUpdate; + ViewType CurrentView; + + double LastTitleUpdate; + + Matrix4f Proj; + Matrix4f View; + Scene Sc; + Ptr pAxes; // Model of the coordinate system + Ptr pBox; // Rendered box + Ptr pBox2; // Second model (right now just lines) + + // Applies specified projection/lookAt direction to the scene. + void SetView(ViewType view); + +public: + + InputTestApp(); + ~InputTestApp(); + + virtual int OnStartup(int argc, const char** argv); + virtual void OnIdle(); + + virtual void OnMouseMove(int x, int y, int modifiers); + virtual void OnKey(KeyCode key, int chr, bool down, int modifiers); +}; + +InputTestApp::InputTestApp() + : pRender(0), CurrentView(View_Perspective), + LastUpdate(0), LastTitleUpdate(0), pAxes(0), pBox(0) +{ + +} + + +/* +void UseCase() +{ + using namespace OVR; + + OVR::System::Init(); + + Ptr pManager = 0; + Ptr pHMD = 0; + Ptr pSensor = 0; + SensorFusion FusionResult; + + + // *** Initialization - Create the first available HMD Device + pManager = *DeviceManager::Create(); + pHMD = *pManager->EnumerateDevices().CreateDevice(); + if (!pHMD) + return; + pSensor = *pHMD->GetSensor(); + + // Get DisplayDeviceName, ScreenWidth/Height, etc.. + HMDInfo hmdInfo; + pHMD->GetDeviceInfo(&hmdInfo); + + if (pSensor) + FusionResult.AttachToSensor(pSensor); + + // *** Per Frame + // Get orientation quaternion to control view + Quatf q = FusionResult.GetOrientation(); + + // Create a matrix from quaternion, + // where elements [0][0] through [3][3] contain rotation. + Matrix4f bodyFrameMatrix(q); + + // Get Euler angles from quaternion, in specified axis rotation order. + float yaw, pitch, roll; + q.GetEulerAngles(&yaw, &pitch, &roll); + + // *** Shutdown + pSensor.Clear(); + pHMD.Clear(); + pManager.Clear(); + + OVR::System::Destroy(); +} +*/ + +InputTestApp::~InputTestApp() +{ + pSensor.Clear(); + pManager.Clear(); + +} + + +int InputTestApp::OnStartup(int argc, const char** argv) +{ + if (!pPlatform->SetupWindow(1200,800)) + return 1; + + + pManager = *DeviceManager::Create(); + + // This initialization logic supports running two sensors at the same time. + + DeviceEnumerator isensor = pManager->EnumerateDevices(); + DeviceEnumerator oculusSensor; + DeviceEnumerator oculusSensor2; + + while(isensor) + { + DeviceInfo di; + if (isensor.GetDeviceInfo(&di)) + { + if (strstr(di.ProductName, "Tracker")) + { + if (!oculusSensor) + oculusSensor = isensor; + else if (!oculusSensor2) + oculusSensor2 = isensor; + } + } + + isensor.Next(); + } + + if (oculusSensor) + { + pSensor = *oculusSensor.CreateDevice(); + + if (pSensor) + pSensor->SetRange(SensorRange(4 * 9.81f, 8 * Math::Pi, 1.0f), true); + + if (oculusSensor2) + { + // Second Oculus sensor, useful for comparing firmware behavior & settings. + pSensor2 = *oculusSensor2.CreateDevice(); + + if (pSensor2) + pSensor2->SetRange(SensorRange(4 * 9.81f, 8 * Math::Pi, 1.0f), true); + } + } + + oculusSensor.Clear(); + oculusSensor2.Clear(); + + + /* + DeviceHandle hHMD = pManager->EnumerateDevices(); + HMDInfo hmdInfo; + if (hHMD) + { + hHMD.GetDeviceInfo(&hmdInfo); + } + */ + + if (pSensor) + SFusion.AttachToSensor(pSensor); + if (pSensor2) + SFusion2.AttachToSensor(pSensor2); + + /* + // Test rotation: This give rotations clockwise (CW) while looking from + // origin in the direction of the axis. + + Vector3f xV(1,0,0); + Vector3f zV(0,0,1); + + Vector3f rxV = Matrix4f::RotationZ(DegreeToRad(10.0f)).Transform(xV); + Vector3f ryV = Matrix4f::RotationY(DegreeToRad(10.0f)).Transform(xV); + Vector3f rzV = Matrix4f::RotationX(DegreeToRad(10.0f)).Transform(zV); + */ + + // Report relative mouse motion (not absolute position) + // pPlatform->SetMouseMode(Mouse_Relative); + + const char* graphics = "d3d10"; + for (int i = 1; i < argc; i++) + if (!strcmp(argv[i], "-r") && i < argc-1) + graphics = argv[i+1]; + + pRender = pPlatform->SetupGraphics(OVR_DEFAULT_RENDER_DEVICE_SET, graphics, + RendererParams()); + + //WireframeFill = pRender->CreateSimpleFill(Fill::F_Wireframe); + + + + // *** Rotating Box + + pBox = *new Container; + pBox->Add(Ptr( + *Model::CreateAxisFaceColorBox(-2.0f, 2.0f, Color(0, 0xAA, 0), // x = green + -1.0f, 1.0f, Color(0xAA,0, 0), // y = red + -1.0f, 1.0f, Color(0, 0, 0xAA)) )); // z = blue + // Drop-down line from box, to make it easier to see differences in angle. + Ptr downLine = *new Model(Prim_Lines); + downLine->AddLine(Vertex(0.0f,-4.5f, 0.0f, 0xFFE0B0B0), + Vertex(0.0f, 0.0f, 0.0f, 0xFFE0B0B0)); + pBox->Add(downLine); + Sc.World.Add(pBox); + + + // Secondary rotating coordinate object, if we have two values. + if (pSensor2) + { + pBox2 = *new Container; + + // Drop-down line from box, to make it easier to see differences in angle. + Ptr lines = *new Model(Prim_Lines); + lines->AddLine(Vertex( 0.0f,-4.0f, 0.0f, 0xFFA07070), // -Y + Vertex( 0.0f, 0.0f, 0.0f, 0xFFA07070)); + lines->AddLine(Vertex(-4.0f, 0.0f, 0.0f, 0xFF70A070), // -X + Vertex( 0.0f, 0.0f, 0.0f, 0xFF70A070)); + lines->AddLine(Vertex( 0.0f, 0.0f,-4.0f, 0xFF7070A0), // -Z + Vertex( 0.0f, 0.0f, 0.0f, 0xFF7070A0)); + pBox2->Add(lines); + Sc.World.Add(pBox2); + } + + + // *** World axis X,Y,Z rendering. + + pAxes = *new Model(Prim_Lines); + pAxes->AddLine(Vertex(-8.0f, 0.0f, 0.0f, 0xFF40FF40), + Vertex( 8.0f, 0.0f, 0.0f, 0xFF40FF40)); // X + pAxes->AddLine(Vertex( 7.6f, 0.4f, 0.4f, 0xFF40FF40), + Vertex( 8.0f, 0.0f, 0.0f, 0xFF40FF40)); // X - arrow + pAxes->AddLine(Vertex( 7.6f,-0.4f,-0.4f, 0xFF40FF40), + Vertex( 8.0f, 0.0f, 0.0f, 0xFF40FF40)); // X - arrow + + pAxes->AddLine(Vertex( 0.0f,-8.0f, 0.0f, 0xFFFF4040), + Vertex( 0.0f, 8.0f, 0.0f, 0xFFFF4040)); // Y + pAxes->AddLine(Vertex( 0.4f, 7.6f, 0.0f, 0xFFFF4040), + Vertex( 0.0f, 8.0f, 0.0f, 0xFFFF4040)); // Y - arrow + pAxes->AddLine(Vertex(-0.4f, 7.6f, 0.0f, 0xFFFF4040), + Vertex( 0.0f, 8.0f, 0.0f, 0xFFFF4040)); // Y + + pAxes->AddLine(Vertex( 0.0f, 0.0f,-8.0f, 0xFF4040FF), + Vertex( 0.0f, 0.0f, 8.0f, 0xFF4040FF)); // Z + pAxes->AddLine(Vertex( 0.4f, 0.0f, 7.6f, 0xFF4040FF), + Vertex( 0.0f, 0.0f, 8.0f, 0xFF4040FF)); // Z - arrow + pAxes->AddLine(Vertex(-0.4f, 0.0f, 7.6f, 0xFF4040FF), + Vertex( 0.0f, 0.0f, 8.0f, 0xFF4040FF)); // Z - arrow + Sc.World.Add(pAxes); + + + SetView(CurrentView); + + + LastUpdate = pPlatform->GetAppTime(); + return 0; +} + +void InputTestApp::SetView(ViewType type) +{ + switch(type) + { + case View_Perspective: + View = Matrix4f::LookAtRH(Vector3f(5.0f, 4.0f, 10.0f), // eye + Vector3f(0.0f, 1.5f, 0.0f), // at + Vector3f(0.0f, 1.0f, 0.0f)); + break; + + case View_XY_DownZ: // F2 + View = Matrix4f::LookAtRH(Vector3f(0.0f, 0.0f, 10.0f), // eye + Vector3f(0.0f, 0.0f, 0.0f), // at + Vector3f(0.0f, 1.0f, 0.0f)); + break; + + case View_XZ_UpY: + View = Matrix4f::LookAtRH(Vector3f(0.0f,-10.0f, 0.0f), // eye + Vector3f(0.0f, 0.0f, 0.0f), // at + Vector3f(0.0f, 0.0f, 1.0f)); + + break; + default: + break; + } + + Proj = Matrix4f::PerspectiveRH(DegreeToRad(70.0f), 1280 / (float)800, + 0.3f, 1000.0f); // LH +} + + +void InputTestApp::OnMouseMove(int x, int y, int modifiers) +{ + OVR_UNUSED3(x, y, modifiers); +} + + +static float CalcDownAngleDegrees(Quatf q) +{ + Vector3f downVector(0.0f, -1.0f, 0.0f); + Vector3f val= q.Rotate(downVector); + return RadToDegree(downVector.Angle(val)); +} + +void InputTestApp::OnKey(KeyCode key, int chr, bool down, int modifiers) +{ + OVR_UNUSED2(chr, modifiers); + + switch (key) + { + case Key_Q: + if (!down) + pPlatform->Exit(0); + break; + + case Key_F1: + CurrentView = View_Perspective; + SetView(CurrentView); + //UpdateWindowTitle(); + break; + case Key_F2: + CurrentView = View_XY_DownZ; + SetView(CurrentView); + break; + case Key_F3: + CurrentView = View_XZ_UpY; + SetView(CurrentView); + break; + + case Key_R: + if (down) + { + SFusion.Reset(); + SFusion2.Reset(); + } + break; + + case Key_H: + if (down && pSensor) + { + SensorDevice::CoordinateFrame coord = pSensor->GetCoordinateFrame(); + pSensor->SetCoordinateFrame( + (coord == SensorDevice::Coord_Sensor) ? + SensorDevice::Coord_HMD : SensorDevice::Coord_Sensor); + SFusion.Reset(); + SFusion2.Reset(); + } + break; + + case Key_G: + if (down) + { + SFusion.SetGravityEnabled(!SFusion.IsGravityEnabled()); + SFusion2.SetGravityEnabled(SFusion.IsGravityEnabled()); + } + break; + + case Key_A: + + if (down) + { + if (!pSensor2) + { + LogText("Angle: %2.3f\n", CalcDownAngleDegrees(SFusion.GetOrientation())); + } + else + { + LogText("Angle: %2.3f Secondary Sensor Angle: %2.3f\n", + CalcDownAngleDegrees(SFusion.GetOrientation()), + CalcDownAngleDegrees(SFusion2.GetOrientation())); + } + } + break; + + /* + case Key_End: + if (!down) + { + OriAdjust = OriSensor.Conj(); + Sc.ViewPoint.SetOrientation(Quatf()); + } + break; */ + default: + break; + } +} + +void InputTestApp::OnIdle() +{ + double curtime = pPlatform->GetAppTime(); + // float dt = float(LastUpdate - curtime); + LastUpdate = curtime; + + if (pBox) + { + Quatf q = SFusion.GetOrientation(); + pBox->SetOrientation(q); + + // Test Euler conversion, alternative to the above: + // Vector3f euler; + // SFusion.GetOrientation().GetEulerABC(&euler.y, &euler.x, &euler.z); + // Matrix4f mat = Matrix4f::RotationY(euler.y) * Matrix4f::RotationX(euler.x) * Matrix4f::RotationZ(euler.z); + // pBox->SetMatrix(mat); + + // Update titlebar every 20th of a second. + if ((curtime - LastTitleUpdate) > 0.05f) + { + char titleBuffer[512]; + SensorDevice::CoordinateFrame coord = SensorDevice::Coord_Sensor; + if (pSensor) + coord = pSensor->GetCoordinateFrame(); + + OVR_sprintf(titleBuffer, 512, "OVR SensorBox %s %s Ang: %0.3f", + (SFusion.IsGravityEnabled() ? "" : "[Grav Off]"), + (coord == SensorDevice::Coord_HMD) ? "[HMD Coord]" : "", + CalcDownAngleDegrees(q)); + pPlatform->SetWindowTitle(titleBuffer); + LastTitleUpdate = curtime; + } + } + + if (pBox2) + { + pBox2->SetOrientation(SFusion2.GetOrientation()); + } + + // Render + int w, h; + pPlatform->GetWindowSize(&w, &h); + + pRender->SetViewport(0, 0, w, h); + + pRender->Clear(); + pRender->BeginScene(); + + pRender->SetProjection(Proj); + pRender->SetDepthMode(1,1); + + Sc.Render(pRender, View); + + pRender->Present(); + +} + +OVR_PLATFORM_APP(InputTestApp); diff --git a/Samples/SensorBox/SensorBoxTest.rc b/Samples/SensorBox/SensorBoxTest.rc new file mode 100644 index 0000000..49a6a5a Binary files /dev/null and b/Samples/SensorBox/SensorBoxTest.rc differ diff --git a/Samples/SensorBox/SensorBoxTest_Msvc2010.vcxproj b/Samples/SensorBox/SensorBoxTest_Msvc2010.vcxproj new file mode 100644 index 0000000..dc49cd9 --- /dev/null +++ b/Samples/SensorBox/SensorBoxTest_Msvc2010.vcxproj @@ -0,0 +1,126 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {8051B837-2982-4F64-8C3B-FAF88B6D83AB} + Win32Proj + SensorBoxTest + SensorBoxTest + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + true + $(Configuration)\Obj\ + $(ProjectDir)$(Configuration)\ + + + false + $(Configuration)\Obj\ + $(ProjectDir)$(Configuration)\ + + + + + + Level4 + Disabled + OVR_BUILD_DEBUG;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + ../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories) + OldStyle + true + + + false + + + Windows + true + libovrd.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + ../../LibOVR/Lib/Win32;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories) + + + + + Level4 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + ../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories) + MultiThreaded + true + OldStyle + + + Windows + true + true + true + ../../LibOVR/Lib/Win32;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories) + libovr.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/SensorBox/SensorBoxTest_Msvc2010.vcxproj.filters b/Samples/SensorBox/SensorBoxTest_Msvc2010.vcxproj.filters new file mode 100644 index 0000000..075a68f --- /dev/null +++ b/Samples/SensorBox/SensorBoxTest_Msvc2010.vcxproj.filters @@ -0,0 +1,73 @@ + + + + + {ba673e50-0655-4f12-a166-6d3ab6015925} + + + {16e20aab-eff7-45ff-be85-0203ad399e97} + + + {1b6db3ae-a405-4f65-be93-41a62db4fa21} + + + + + CommonSrc\Platform + + + CommonSrc\Platform + + + CommonSrc\Render + + + CommonSrc\Render + + + CommonSrc\Render + + + CommonSrc\Render + + + + CommonSrc\Platform + + + + + CommonSrc\Platform + + + CommonSrc\Platform + + + CommonSrc\Platform + + + CommonSrc\Render + + + CommonSrc\Render + + + CommonSrc\Render + + + CommonSrc\Render + + + CommonSrc\Render + + + CommonSrc\Platform + + + CommonSrc\Platform + + + + + + \ No newline at end of file diff --git a/Win_OculusWorldDemo.lnk b/Win_OculusWorldDemo.lnk new file mode 100644 index 0000000..d846ead Binary files /dev/null and b/Win_OculusWorldDemo.lnk differ diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..35fa876 --- /dev/null +++ b/readme.txt @@ -0,0 +1,6 @@ +Oculus SDK +(C) Oculus VR, Inc. 2013. All Rights Reserved. + +The latest version of the Oculus SDK is available at http://developer.oculusvr.com. + +Please see the Oculus SDK Overview in the OculusSDK/Doc/ directory for more information. \ No newline at end of file -- cgit v1.2.3